From 570632c8dc8330596d102edc315756271cddce8e Mon Sep 17 00:00:00 2001 From: Alan Zimmer <48699787+alzimmermsft@users.noreply.github.com> Date: Thu, 25 Jul 2019 16:30:30 -0700 Subject: [PATCH] Feature Work for Storage (#4584) * Storage SAS implementation (#4404) * SAS implementation * Fixed some minor formatting issues * Fixed checkstyle problems and test issue * Remove RawClients from Blobs (#4375) Removes RawClients from Storage Blobs * Add deleteContainer to StorageClient and getBlobClient with Snapshot to ContainerClient (#4376) * Removed raw clients * Added deleteContainer to StorageClient * Added getAppendBlob with snapshot to ContainerClient * Storage queue linting, builder refactor, tests (#4383) * Initial check in for storage queue * Initial checkin for Storage file (#4414) * Finished the restructure, refactor builder. Added sleep in record feature. Linting * Merge Storage Blob Client Builders (#4468) Merges AppendBlobClientBuilder, BlobClientBuilder, BlockBlobClientBuilder, and PageBlobClientBuilder into a single builder class BlobClientBuilder. Additionally, JavaDoc comments for the other builder classes, ContainerClientBuilder and StorageAccountClientBuilder, were cleaned up and the way the endpoint is handled in builders was changed. --- .../resources/spotbugs/spotbugs-exclude.xml | 2 + eng/jacoco-test-coverage/pom.xml | 7 +- eng/spotbugs-aggregate-report/pom.xml | 12 + pom.client.xml | 13 +- .../secrets/KeyVaultErrorCodeStrings.java | 1 - .../storage/blob/AppendBlobAsyncClient.java | 53 +- .../blob/AppendBlobAsyncRawClient.java | 192 --- .../azure/storage/blob/AppendBlobClient.java | 21 +- .../storage/blob/AppendBlobClientBuilder.java | 333 ----- .../azure/storage/blob/BlobAsyncClient.java | 832 ++++++++----- .../storage/blob/BlobAsyncRawClient.java | 766 ------------ .../com/azure/storage/blob/BlobClient.java | 632 +++++----- .../azure/storage/blob/BlobClientBuilder.java | 171 ++- .../azure/storage/blob/BlobInputStream.java | 2 +- .../azure/storage/blob/BlobOutputStream.java | 4 +- .../com/azure/storage/blob/BlobURLParts.java | 8 +- .../storage/blob/BlockBlobAsyncClient.java | 54 +- .../storage/blob/BlockBlobAsyncRawClient.java | 364 ------ .../azure/storage/blob/BlockBlobClient.java | 18 +- .../storage/blob/BlockBlobClientBuilder.java | 337 ----- .../com/azure/storage/blob/Constants.java | 15 + .../storage/blob/ContainerAsyncClient.java | 928 ++++++++------ .../storage/blob/ContainerAsyncRawClient.java | 708 ----------- .../azure/storage/blob/ContainerClient.java | 614 +++++----- .../storage/blob/ContainerClientBuilder.java | 53 +- .../storage/blob/ContainerRawClient.java | 652 ---------- .../storage/blob/PageBlobAsyncClient.java | 214 +++- .../storage/blob/PageBlobAsyncRawClient.java | 620 ---------- .../azure/storage/blob/PageBlobClient.java | 16 +- .../storage/blob/PageBlobClientBuilder.java | 333 ----- .../storage/blob/SASQueryParameters.java | 24 +- .../blob/ServiceSASSignatureValues.java | 277 +++-- .../storage/blob/StorageAsyncClient.java | 235 ++-- .../storage/blob/StorageAsyncRawClient.java | 172 --- .../com/azure/storage/blob/StorageClient.java | 191 +-- .../storage/blob/StorageClientBuilder.java | 41 +- .../azure/storage/blob/StorageRawClient.java | 259 ---- .../java/com/azure/storage/blob/Utility.java | 23 + .../credentials/SASTokenCredential.java | 79 +- .../policy/SharedKeyCredentialPolicy.java | 8 + .../src/samples/java/FileTransferExample.java | 5 +- .../com/azure/storage/blob/APISpec.groovy | 10 +- .../com/azure/storage/blob/BlobAPITest.groovy | 7 +- .../storage/blob/ContainerAPITest.groovy | 8 +- .../com/azure/storage/blob/SASTest.groovy | 1079 +++++++++++++++++ .../java/com/azure/storage/blob/Sample.java | 6 +- storage/client/file/README.md | 478 ++++++++ storage/client/file/pom.xml | 75 ++ .../credentials/SharedKeyCredential.java | 43 +- .../common/exception/StorageException.java | 51 + .../common/policy/RequestRetryOptions.java | 5 + .../common/policy/RequestRetryPolicy.java | 5 + .../storage/file/DirectoryAsyncClient.java | 469 ++++++- .../azure/storage/file/DirectoryClient.java | 353 +++++- .../storage/file/DirectoryClientBuilder.java | 352 +++++- .../azure/storage/file/FileAsyncClient.java | 707 ++++++++++- .../com/azure/storage/file/FileClient.java | 432 ++++++- .../azure/storage/file/FileClientBuilder.java | 356 +++++- .../azure/storage/file/FileConfiguration.java | 13 + .../storage/file/FileServiceAsyncClient.java | 314 ++++- .../azure/storage/file/FileServiceClient.java | 236 +++- .../file/FileServiceClientBuilder.java | 306 ++++- .../azure/storage/file/ShareAsyncClient.java | 407 ++++++- .../com/azure/storage/file/ShareClient.java | 339 +++++- .../storage/file/ShareClientBuilder.java | 338 +++++- .../AzureFileStorageBuilder.java | 6 +- .../file/implementation/DirectorysImpl.java | 158 ++- .../file/implementation/FilesImpl.java | 198 +-- .../file/implementation/ServicesImpl.java | 21 +- .../file/implementation/SharesImpl.java | 114 +- .../models/DeleteSnapshotsOptionType.java | 7 +- .../storage/file/models/DirectoryInfo.java | 32 + .../file/models/DirectoryProperties.java | 53 + .../file/models/DirectorySetMetadataInfo.java | 34 + .../storage/file/models/FileCopyInfo.java | 58 + .../file/models/FileDownloadHeaders.java | 7 +- .../storage/file/models/FileDownloadInfo.java | 85 ++ .../file/models/FileGetPropertiesHeaders.java | 5 +- .../storage/file/models/FileHTTPHeaders.java | 5 +- .../azure/storage/file/models/FileInfo.java | 42 + .../storage/file/models/FileMetadataInfo.java | 22 + .../storage/file/models/FileProperties.java | 195 +++ .../azure/storage/file/models/FileRange.java | 47 + .../azure/storage/file/models/FileRef.java | 42 + .../file/models/FileServiceProperties.java | 108 ++ .../storage/file/models/FileUploadInfo.java | 30 + .../file/models/FileUploadRangeHeaders.java | 5 +- ...istFilesAndDirectoriesSegmentResponse.java | 10 +- .../file/models/ListSharesOptions.java | 102 ++ .../models/ServicesGetPropertiesResponse.java | 6 +- .../azure/storage/file/models/ShareInfo.java | 32 + .../storage/file/models/ShareProperties.java | 15 + .../file/models/ShareSnapshotInfo.java | 46 +- .../storage/file/models/ShareStatistics.java | 20 + .../java/com/azure/file/AsyncSample.java | 67 + .../java/com/azure/file/DirectorySample.java | 115 ++ .../java/com/azure/file/FileSample.java | 144 +++ .../com/azure/file/FileServiceSample.java | 67 + .../java/com/azure/file/ShareSample.java | 134 ++ .../file/DirectoryJavaDocCodeSamples.java | 586 +++++++++ .../storage/file/FileJavaDocCodeSamples.java | 785 ++++++++++++ .../file/FileServiceJavaDocCodeSamples.java | 423 +++++++ .../storage/file/ShareJavaDocCodeSamples.java | 580 +++++++++ .../file/DirectoryAsyncClientTests.java | 321 +++++ .../storage/file/DirectoryClientTestBase.java | 113 ++ .../storage/file/DirectoryClientTests.java | 259 ++++ .../storage/file/FileAsyncClientTest.java | 215 ++++ .../azure/storage/file/FileClientTest.java | 202 +++ .../storage/file/FileClientTestBase.java | 81 ++ .../file/FileServiceClientAsyncTests.java | 472 +++++++ .../file/FileServiceClientTestBase.java | 121 ++ .../storage/file/FileServiceClientTests.java | 479 ++++++++ .../azure/storage/file/FileTestHelpers.java | 257 ++++ .../com/azure/storage/file/LargeFileTest.java | 129 ++ .../storage/file/ShareAsyncClientTests.java | 618 ++++++++++ .../storage/file/ShareClientTestBase.java | 170 +++ .../azure/storage/file/ShareClientTests.java | 490 ++++++++ .../resources/session-records/abortCopy.json | 4 + .../clearMetadataFromDirClient.json | 42 + ...DirectoryAlreadyExistsFromShareClient.json | 76 ++ .../createDirectoryFromShareClient.json | 57 + ...teDirectoryInvalidNameFromShareClient.json | 57 + .../createExcessMaxSizeFromFileClient.json | 45 + .../createFileFromDirClient.json | 42 + ...eateFileWithoutCreateDirFromDirClient.json | 23 + .../session-records/createFromFileClient.json | 45 + .../createFromShareClient.json | 38 + .../createInvalidQuotaFromShareClient.json | 61 + .../createMinFromDirClient.json | 23 + .../session-records/createShare.json | 56 + .../createShareInvalidQuota.json | 60 + .../createShareTwiceDifferentMetadata.json | 75 ++ .../createShareTwiceSameMetadata.json | 75 ++ .../session-records/createSubDirectory.json | 42 + .../createSubDirectoryTwiceSameMetadata.json | 61 + .../createSubDirectoryWithMetadata.json | 42 + ...TwiceDifferentMetadataFromShareClient.json | 57 + .../createTwiceFromDirClient.json | 42 + ...reateTwiceSameMetadataFromShareClient.json | 57 + .../createWithMetadataFromDirClient.json | 23 + ...eDirectoryDoesNotExistFromShareClient.json | 57 + .../deleteDirectoryFromShareClient.json | 73 ++ .../deleteDoesNotExistFromShareClient.json | 42 + .../deleteFileFromDirClient.json | 58 + ...eteFileWithoutCreateFileFromDirClient.json | 42 + .../session-records/deleteFromDirClient.json | 39 + .../session-records/deleteFromFileClient.json | 39 + .../deleteFromShareClient.json | 57 + .../deleteNotExistFromDirClient.json | 23 + .../session-records/deleteShare.json | 56 + .../deleteShareDoesNotExist.json | 41 + .../deleteSnapshotFromShareClient.json | 95 ++ .../session-records/deleteSubDirectory.json | 58 + .../deleteThenCreateFromShareClient.json | 72 ++ ...eThenCreateShareFromFileServiceClient.json | 90 ++ ...eateShareTooSoonFromFileServiceClient.json | 75 ++ ...eleteThenCreateTooSoonFromShareClient.json | 76 ++ .../downloadWithProperties.json | 46 + .../forceCloseHandlesFromDirClient.json | 41 + .../forceCloseHandlesFromFileClient.json | 41 + .../getDirectoryDoesNotCreateADirectory.json | 57 + .../getFileClientFromDirClient.json | 4 + .../getHandlesFromDirClient.json | 4 + ...etMetadataDoesNotExistFromShareClient.json | 42 + .../getMetadataFromShareClient.json | 60 + .../session-records/getPolicies.json | 58 + .../getPoliciesDoesNotExist.json | 42 + ...PropertiesDoesNotExistFromShareClient.json | 42 + .../getPropertiesFromDirClient.json | 42 + .../getPropertiesFromFileClient.json | 45 + .../getPropertiesFromShareClient.json | 60 + ...tRootDirectoryDoesNotCreateADirectory.json | 57 + .../getShareDoesNotCreateAShare.json | 41 + .../session-records/getSnapshotId.json | 60 + ...otMetadataDoesNotExistFromShareClient.json | 57 + .../getSnapshotMetadataFromShareClient.json | 82 ++ .../getSnapshotPropertiesDoesNotExist.json | 57 + .../getSnapshotPropertiesFromShareClient.json | 82 ++ .../resources/session-records/getStats.json | 56 + .../session-records/getStatsDoesNotExist.json | 42 + .../getSubDirectoryClient.json | 4 + .../listFilesAndDirectoriesFromDirClient.json | 212 ++++ .../listHandlesFromFileClient.json | 41 + .../listRangesFromFileClient.json | 65 + .../resources/session-records/listShares.json | 142 +++ .../listSharesIncludeMetadata.json | 142 +++ ...listSharesIncludeMetadataAndSnapshots.json | 186 +++ .../listSharesIncludeSnapshots.json | 186 +++ .../listSharesInvalidMaxResults.json | 60 + .../session-records/listSharesWithLimit.json | 142 +++ .../session-records/listSharesWithPrefix.json | 142 +++ .../setFileServiceProperties.json | 110 ++ .../setHttpHeadersFromFileClient.json | 42 + .../setMeatadataFromFileClient.json | 42 + ...etMetadataDoesNotExistFromShareClient.json | 42 + .../setMetadataFromDirClient.json | 42 + .../setMetadataFromShareClient.json | 78 ++ .../setMetadataInvalidKeyFromDirClient.json | 42 + ...etadataInvalidMetadataFromShareClient.json | 57 + .../session-records/setPolicies.json | 77 ++ .../setPoliciesDoesNotExist.json | 43 + .../setPoliciesInvalidPermission.json | 58 + .../setPoliciesTooManyPermissions.json | 58 + ...PropertiesDoesNotExistFromShareClient.json | 42 + .../setPropertiesFromShareClient.json | 98 ++ .../setPropertiesInvalidAllowedHeader.json | 42 + .../setPropertiesInvalidAllowedMethod.json | 42 + .../setPropertiesInvalidAllowedOrigin.json | 42 + .../setPropertiesInvalidExposedHeader.json | 42 + ...PropertiesInvalidQuotaFromShareClient.json | 76 ++ .../setPropertiesTooManyRules.json | 42 + .../resources/session-records/snapshot.json | 60 + .../snapshotDifferentMetadata.json | 104 ++ .../session-records/snapshotDoesNotExist.json | 42 + .../session-records/snapshotSameMetadata.json | 82 ++ .../resources/session-records/startCopy.json | 43 + .../resources/session-records/upload.json | 44 + .../uploadToStorageAndDownloadToFile.json | 90 ++ .../session-records/urlFromDirClient.json | 4 + .../src/test/resources/testfiles/helloworld | 1 + storage/client/pom.xml | 123 ++ storage/client/queue/README.md | 135 ++- storage/client/queue/pom.xml | 78 ++ .../credentials/SharedKeyCredential.java | 39 +- .../common/policy/RequestRetryOptions.java | 6 +- .../common/policy/RequestRetryPolicy.java | 4 + .../azure/storage/queue/QueueAsyncClient.java | 473 +++++++- .../queue/QueueAsyncClientBuilder.java | 5 - .../com/azure/storage/queue/QueueClient.java | 389 ++++++ .../storage/queue/QueueClientBuilder.java | 344 ++++++ .../storage/queue/QueueConfiguration.java | 12 + .../queue/QueueServiceAsyncClient.java | 301 +++++ .../storage/queue/QueueServiceClient.java | 234 ++++ .../queue/QueueServiceClientBuilder.java | 301 +++++ .../java/com/azure/storage/queue/README.md | 377 ++++++ .../AzureQueueStorageBuilder.java | 4 +- .../queue/implementation/MessageIdsImpl.java | 21 +- .../queue/implementation/MessagesImpl.java | 41 +- .../queue/implementation/QueuesImpl.java | 64 +- .../queue/implementation/ServicesImpl.java | 3 +- ...dMessageItem.java => DequeuedMessage.java} | 31 +- .../queue/models/MessagesDequeueResponse.java | 6 +- .../queue/models/MessagesPeekResponse.java | 6 +- ...kedMessageItem.java => PeekedMessage.java} | 23 +- .../storage/queue/models/QueueProperties.java | 38 + .../queue/models/QueuesSegmentOptions.java | 89 ++ .../storage/queue/models/UpdatedMessage.java | 39 + .../com/azure/storage/queue/package-info.java | 7 + .../com/azure/storage/queue/AsyncSamples.java | 42 + .../azure/storage/queue/MessageSamples.java | 73 ++ .../storage/queue/QueueExceptionSamples.java | 69 ++ .../storage/queue/QueueServiceSamples.java | 31 + .../com/azure/storage/queue/SampleHelper.java | 13 + .../javadoc/QueueJavaDocCodeSamples.java | 668 ++++++++++ .../QueueServiceJavaDocCodeSamples.java | 369 ++++++ .../storage/queue/QueueAsyncClientTests.java | 695 +++++++++++ .../azure/storage/queue/QueueClientTests.java | 654 ++++++++++ .../storage/queue/QueueClientTestsBase.java | 148 +++ .../queue/QueueServiceAsyncClientTests.java | 250 ++++ .../queue/QueueServiceClientTests.java | 257 ++++ .../queue/QueueServiceClientTestsBase.java | 67 + .../com/azure/storage/queue/TestHelpers.java | 161 +++ .../session-records/clearMessages.json | 161 +++ .../clearMessagesQueueDoesNotExist.json | 42 + .../session-records/createQueue.json | 90 ++ .../createQueueTwiceDifferentMetadata.json | 90 ++ .../createQueueTwiceSameMetadata.json | 125 ++ .../createQueueWithMetadata.json | 91 ++ .../createTwiceDifferentMetadata.json | 71 ++ .../createTwiceSameMetadata.json | 68 ++ .../session-records/createWithMetadata.json | 72 ++ .../session-records/createWithSASToken.json | 23 + .../session-records/createWithSharedKey.json | 23 + .../session-records/deleteExisting.json | 94 ++ .../session-records/deleteExistingQueue.json | 75 ++ .../session-records/deleteMessage.json | 124 ++ .../deleteMessageInvalidMessageId.json | 109 ++ .../deleteMessageInvalidPopReceipt.json | 109 ++ .../deleteMessageQueueDoesNotExist.json | 42 + .../session-records/deleteMetadata.json | 106 ++ .../session-records/deleteNonExistent.json | 42 + .../deleteNonExistentQueue.json | 42 + .../session-records/dequeueMessage.json | 90 ++ .../dequeueMultipleMessages.json | 109 ++ .../dequeueQueueDoesNotExist.json | 42 + .../dequeueTooManyMessages.json | 71 ++ .../session-records/enqueueEmptyMessage.json | 90 ++ .../session-records/enqueueMessage.json | 90 ++ .../enqueueQueueDoesNotExist.json | 43 + .../enqueueShortTimeToLiveMessage.json | 90 ++ .../session-records/getAccessPolicy.json | 71 ++ .../getAccessPolicyQueueDoesNotExist.json | 42 + .../session-records/getProperties.json | 72 ++ .../getPropertiesQueueDoesNotExist.json | 42 + .../getQueueDoesNotCreateAQueue.json | 43 + .../resources/session-records/listQueues.json | 186 +++ .../listQueuesIncludeMetadata.json | 186 +++ .../session-records/listQueuesWithPrefix.json | 186 +++ .../session-records/peekMessage.json | 90 ++ .../session-records/peekMultipleMessages.json | 109 ++ .../peekQueueDoesNotExist.json | 42 + .../session-records/peekTooManyMessages.json | 71 ++ .../session-records/setAccessPolicy.json | 88 ++ .../setAccessPolicyQueueDoesNotExist.json | 43 + .../setInvalidAccessPolicy.json | 72 ++ .../session-records/setInvalidMetadata.json | 71 ++ .../session-records/setMetadata.json | 88 ++ .../setMetadataQueueDoesNotExist.json | 42 + .../session-records/setProperties.json | 114 ++ .../setTooManyAccessPolicies.json | 72 ++ .../session-records/updateMessage.json | 128 ++ .../updateMessageInvalidMessageId.json | 110 ++ .../updateMessageInvalidPopReceipt.json | 110 ++ .../updateMessageQueueDoesNotExist.json | 43 + 314 files changed, 33989 insertions(+), 7180 deletions(-) delete mode 100644 storage/client/blob/src/main/java/com/azure/storage/blob/AppendBlobAsyncRawClient.java delete mode 100644 storage/client/blob/src/main/java/com/azure/storage/blob/AppendBlobClientBuilder.java delete mode 100644 storage/client/blob/src/main/java/com/azure/storage/blob/BlobAsyncRawClient.java delete mode 100644 storage/client/blob/src/main/java/com/azure/storage/blob/BlockBlobAsyncRawClient.java delete mode 100644 storage/client/blob/src/main/java/com/azure/storage/blob/BlockBlobClientBuilder.java delete mode 100644 storage/client/blob/src/main/java/com/azure/storage/blob/ContainerAsyncRawClient.java delete mode 100644 storage/client/blob/src/main/java/com/azure/storage/blob/ContainerRawClient.java delete mode 100644 storage/client/blob/src/main/java/com/azure/storage/blob/PageBlobAsyncRawClient.java delete mode 100644 storage/client/blob/src/main/java/com/azure/storage/blob/PageBlobClientBuilder.java delete mode 100644 storage/client/blob/src/main/java/com/azure/storage/blob/StorageAsyncRawClient.java delete mode 100644 storage/client/blob/src/main/java/com/azure/storage/blob/StorageRawClient.java create mode 100644 storage/client/blob/src/test/java/com/azure/storage/blob/SASTest.groovy create mode 100644 storage/client/file/README.md create mode 100644 storage/client/file/pom.xml create mode 100644 storage/client/file/src/main/java/com/azure/storage/common/exception/StorageException.java create mode 100644 storage/client/file/src/main/java/com/azure/storage/file/FileConfiguration.java create mode 100644 storage/client/file/src/main/java/com/azure/storage/file/models/DirectorySetMetadataInfo.java create mode 100644 storage/client/file/src/main/java/com/azure/storage/file/models/FileMetadataInfo.java create mode 100644 storage/client/file/src/main/java/com/azure/storage/file/models/FileRange.java create mode 100644 storage/client/file/src/samples/java/com/azure/file/AsyncSample.java create mode 100644 storage/client/file/src/samples/java/com/azure/file/DirectorySample.java create mode 100644 storage/client/file/src/samples/java/com/azure/file/FileSample.java create mode 100644 storage/client/file/src/samples/java/com/azure/file/FileServiceSample.java create mode 100644 storage/client/file/src/samples/java/com/azure/file/ShareSample.java create mode 100644 storage/client/file/src/samples/java/com/azure/storage/file/DirectoryJavaDocCodeSamples.java create mode 100644 storage/client/file/src/samples/java/com/azure/storage/file/FileJavaDocCodeSamples.java create mode 100644 storage/client/file/src/samples/java/com/azure/storage/file/FileServiceJavaDocCodeSamples.java create mode 100644 storage/client/file/src/samples/java/com/azure/storage/file/ShareJavaDocCodeSamples.java create mode 100644 storage/client/file/src/test/java/com/azure/storage/file/DirectoryAsyncClientTests.java create mode 100644 storage/client/file/src/test/java/com/azure/storage/file/DirectoryClientTestBase.java create mode 100644 storage/client/file/src/test/java/com/azure/storage/file/DirectoryClientTests.java create mode 100644 storage/client/file/src/test/java/com/azure/storage/file/FileAsyncClientTest.java create mode 100644 storage/client/file/src/test/java/com/azure/storage/file/FileClientTest.java create mode 100644 storage/client/file/src/test/java/com/azure/storage/file/FileClientTestBase.java create mode 100644 storage/client/file/src/test/java/com/azure/storage/file/FileServiceClientAsyncTests.java create mode 100644 storage/client/file/src/test/java/com/azure/storage/file/FileServiceClientTestBase.java create mode 100644 storage/client/file/src/test/java/com/azure/storage/file/FileServiceClientTests.java create mode 100644 storage/client/file/src/test/java/com/azure/storage/file/FileTestHelpers.java create mode 100644 storage/client/file/src/test/java/com/azure/storage/file/LargeFileTest.java create mode 100644 storage/client/file/src/test/java/com/azure/storage/file/ShareAsyncClientTests.java create mode 100644 storage/client/file/src/test/java/com/azure/storage/file/ShareClientTestBase.java create mode 100644 storage/client/file/src/test/java/com/azure/storage/file/ShareClientTests.java create mode 100644 storage/client/file/src/test/resources/session-records/abortCopy.json create mode 100644 storage/client/file/src/test/resources/session-records/clearMetadataFromDirClient.json create mode 100644 storage/client/file/src/test/resources/session-records/createDirectoryAlreadyExistsFromShareClient.json create mode 100644 storage/client/file/src/test/resources/session-records/createDirectoryFromShareClient.json create mode 100644 storage/client/file/src/test/resources/session-records/createDirectoryInvalidNameFromShareClient.json create mode 100644 storage/client/file/src/test/resources/session-records/createExcessMaxSizeFromFileClient.json create mode 100644 storage/client/file/src/test/resources/session-records/createFileFromDirClient.json create mode 100644 storage/client/file/src/test/resources/session-records/createFileWithoutCreateDirFromDirClient.json create mode 100644 storage/client/file/src/test/resources/session-records/createFromFileClient.json create mode 100644 storage/client/file/src/test/resources/session-records/createFromShareClient.json create mode 100644 storage/client/file/src/test/resources/session-records/createInvalidQuotaFromShareClient.json create mode 100644 storage/client/file/src/test/resources/session-records/createMinFromDirClient.json create mode 100644 storage/client/file/src/test/resources/session-records/createShare.json create mode 100644 storage/client/file/src/test/resources/session-records/createShareInvalidQuota.json create mode 100644 storage/client/file/src/test/resources/session-records/createShareTwiceDifferentMetadata.json create mode 100644 storage/client/file/src/test/resources/session-records/createShareTwiceSameMetadata.json create mode 100644 storage/client/file/src/test/resources/session-records/createSubDirectory.json create mode 100644 storage/client/file/src/test/resources/session-records/createSubDirectoryTwiceSameMetadata.json create mode 100644 storage/client/file/src/test/resources/session-records/createSubDirectoryWithMetadata.json create mode 100644 storage/client/file/src/test/resources/session-records/createTwiceDifferentMetadataFromShareClient.json create mode 100644 storage/client/file/src/test/resources/session-records/createTwiceFromDirClient.json create mode 100644 storage/client/file/src/test/resources/session-records/createTwiceSameMetadataFromShareClient.json create mode 100644 storage/client/file/src/test/resources/session-records/createWithMetadataFromDirClient.json create mode 100644 storage/client/file/src/test/resources/session-records/deleteDirectoryDoesNotExistFromShareClient.json create mode 100644 storage/client/file/src/test/resources/session-records/deleteDirectoryFromShareClient.json create mode 100644 storage/client/file/src/test/resources/session-records/deleteDoesNotExistFromShareClient.json create mode 100644 storage/client/file/src/test/resources/session-records/deleteFileFromDirClient.json create mode 100644 storage/client/file/src/test/resources/session-records/deleteFileWithoutCreateFileFromDirClient.json create mode 100644 storage/client/file/src/test/resources/session-records/deleteFromDirClient.json create mode 100644 storage/client/file/src/test/resources/session-records/deleteFromFileClient.json create mode 100644 storage/client/file/src/test/resources/session-records/deleteFromShareClient.json create mode 100644 storage/client/file/src/test/resources/session-records/deleteNotExistFromDirClient.json create mode 100644 storage/client/file/src/test/resources/session-records/deleteShare.json create mode 100644 storage/client/file/src/test/resources/session-records/deleteShareDoesNotExist.json create mode 100644 storage/client/file/src/test/resources/session-records/deleteSnapshotFromShareClient.json create mode 100644 storage/client/file/src/test/resources/session-records/deleteSubDirectory.json create mode 100644 storage/client/file/src/test/resources/session-records/deleteThenCreateFromShareClient.json create mode 100644 storage/client/file/src/test/resources/session-records/deleteThenCreateShareFromFileServiceClient.json create mode 100644 storage/client/file/src/test/resources/session-records/deleteThenCreateShareTooSoonFromFileServiceClient.json create mode 100644 storage/client/file/src/test/resources/session-records/deleteThenCreateTooSoonFromShareClient.json create mode 100644 storage/client/file/src/test/resources/session-records/downloadWithProperties.json create mode 100644 storage/client/file/src/test/resources/session-records/forceCloseHandlesFromDirClient.json create mode 100644 storage/client/file/src/test/resources/session-records/forceCloseHandlesFromFileClient.json create mode 100644 storage/client/file/src/test/resources/session-records/getDirectoryDoesNotCreateADirectory.json create mode 100644 storage/client/file/src/test/resources/session-records/getFileClientFromDirClient.json create mode 100644 storage/client/file/src/test/resources/session-records/getHandlesFromDirClient.json create mode 100644 storage/client/file/src/test/resources/session-records/getMetadataDoesNotExistFromShareClient.json create mode 100644 storage/client/file/src/test/resources/session-records/getMetadataFromShareClient.json create mode 100644 storage/client/file/src/test/resources/session-records/getPolicies.json create mode 100644 storage/client/file/src/test/resources/session-records/getPoliciesDoesNotExist.json create mode 100644 storage/client/file/src/test/resources/session-records/getPropertiesDoesNotExistFromShareClient.json create mode 100644 storage/client/file/src/test/resources/session-records/getPropertiesFromDirClient.json create mode 100644 storage/client/file/src/test/resources/session-records/getPropertiesFromFileClient.json create mode 100644 storage/client/file/src/test/resources/session-records/getPropertiesFromShareClient.json create mode 100644 storage/client/file/src/test/resources/session-records/getRootDirectoryDoesNotCreateADirectory.json create mode 100644 storage/client/file/src/test/resources/session-records/getShareDoesNotCreateAShare.json create mode 100644 storage/client/file/src/test/resources/session-records/getSnapshotId.json create mode 100644 storage/client/file/src/test/resources/session-records/getSnapshotMetadataDoesNotExistFromShareClient.json create mode 100644 storage/client/file/src/test/resources/session-records/getSnapshotMetadataFromShareClient.json create mode 100644 storage/client/file/src/test/resources/session-records/getSnapshotPropertiesDoesNotExist.json create mode 100644 storage/client/file/src/test/resources/session-records/getSnapshotPropertiesFromShareClient.json create mode 100644 storage/client/file/src/test/resources/session-records/getStats.json create mode 100644 storage/client/file/src/test/resources/session-records/getStatsDoesNotExist.json create mode 100644 storage/client/file/src/test/resources/session-records/getSubDirectoryClient.json create mode 100644 storage/client/file/src/test/resources/session-records/listFilesAndDirectoriesFromDirClient.json create mode 100644 storage/client/file/src/test/resources/session-records/listHandlesFromFileClient.json create mode 100644 storage/client/file/src/test/resources/session-records/listRangesFromFileClient.json create mode 100644 storage/client/file/src/test/resources/session-records/listShares.json create mode 100644 storage/client/file/src/test/resources/session-records/listSharesIncludeMetadata.json create mode 100644 storage/client/file/src/test/resources/session-records/listSharesIncludeMetadataAndSnapshots.json create mode 100644 storage/client/file/src/test/resources/session-records/listSharesIncludeSnapshots.json create mode 100644 storage/client/file/src/test/resources/session-records/listSharesInvalidMaxResults.json create mode 100644 storage/client/file/src/test/resources/session-records/listSharesWithLimit.json create mode 100644 storage/client/file/src/test/resources/session-records/listSharesWithPrefix.json create mode 100644 storage/client/file/src/test/resources/session-records/setFileServiceProperties.json create mode 100644 storage/client/file/src/test/resources/session-records/setHttpHeadersFromFileClient.json create mode 100644 storage/client/file/src/test/resources/session-records/setMeatadataFromFileClient.json create mode 100644 storage/client/file/src/test/resources/session-records/setMetadataDoesNotExistFromShareClient.json create mode 100644 storage/client/file/src/test/resources/session-records/setMetadataFromDirClient.json create mode 100644 storage/client/file/src/test/resources/session-records/setMetadataFromShareClient.json create mode 100644 storage/client/file/src/test/resources/session-records/setMetadataInvalidKeyFromDirClient.json create mode 100644 storage/client/file/src/test/resources/session-records/setMetadataInvalidMetadataFromShareClient.json create mode 100644 storage/client/file/src/test/resources/session-records/setPolicies.json create mode 100644 storage/client/file/src/test/resources/session-records/setPoliciesDoesNotExist.json create mode 100644 storage/client/file/src/test/resources/session-records/setPoliciesInvalidPermission.json create mode 100644 storage/client/file/src/test/resources/session-records/setPoliciesTooManyPermissions.json create mode 100644 storage/client/file/src/test/resources/session-records/setPropertiesDoesNotExistFromShareClient.json create mode 100644 storage/client/file/src/test/resources/session-records/setPropertiesFromShareClient.json create mode 100644 storage/client/file/src/test/resources/session-records/setPropertiesInvalidAllowedHeader.json create mode 100644 storage/client/file/src/test/resources/session-records/setPropertiesInvalidAllowedMethod.json create mode 100644 storage/client/file/src/test/resources/session-records/setPropertiesInvalidAllowedOrigin.json create mode 100644 storage/client/file/src/test/resources/session-records/setPropertiesInvalidExposedHeader.json create mode 100644 storage/client/file/src/test/resources/session-records/setPropertiesInvalidQuotaFromShareClient.json create mode 100644 storage/client/file/src/test/resources/session-records/setPropertiesTooManyRules.json create mode 100644 storage/client/file/src/test/resources/session-records/snapshot.json create mode 100644 storage/client/file/src/test/resources/session-records/snapshotDifferentMetadata.json create mode 100644 storage/client/file/src/test/resources/session-records/snapshotDoesNotExist.json create mode 100644 storage/client/file/src/test/resources/session-records/snapshotSameMetadata.json create mode 100644 storage/client/file/src/test/resources/session-records/startCopy.json create mode 100644 storage/client/file/src/test/resources/session-records/upload.json create mode 100644 storage/client/file/src/test/resources/session-records/uploadToStorageAndDownloadToFile.json create mode 100644 storage/client/file/src/test/resources/session-records/urlFromDirClient.json create mode 100644 storage/client/file/src/test/resources/testfiles/helloworld create mode 100644 storage/client/pom.xml create mode 100644 storage/client/queue/pom.xml delete mode 100644 storage/client/queue/src/main/java/com/azure/storage/queue/QueueAsyncClientBuilder.java create mode 100644 storage/client/queue/src/main/java/com/azure/storage/queue/QueueClient.java create mode 100644 storage/client/queue/src/main/java/com/azure/storage/queue/QueueClientBuilder.java create mode 100644 storage/client/queue/src/main/java/com/azure/storage/queue/QueueConfiguration.java create mode 100644 storage/client/queue/src/main/java/com/azure/storage/queue/QueueServiceAsyncClient.java create mode 100644 storage/client/queue/src/main/java/com/azure/storage/queue/QueueServiceClient.java create mode 100644 storage/client/queue/src/main/java/com/azure/storage/queue/QueueServiceClientBuilder.java create mode 100644 storage/client/queue/src/main/java/com/azure/storage/queue/README.md rename storage/client/queue/src/main/java/com/azure/storage/queue/models/{DequeuedMessageItem.java => DequeuedMessage.java} (85%) rename storage/client/queue/src/main/java/com/azure/storage/queue/models/{PeekedMessageItem.java => PeekedMessage.java} (85%) create mode 100644 storage/client/queue/src/main/java/com/azure/storage/queue/models/QueueProperties.java create mode 100644 storage/client/queue/src/main/java/com/azure/storage/queue/models/QueuesSegmentOptions.java create mode 100644 storage/client/queue/src/main/java/com/azure/storage/queue/models/UpdatedMessage.java create mode 100644 storage/client/queue/src/main/java/com/azure/storage/queue/package-info.java create mode 100644 storage/client/queue/src/samples/java/com/azure/storage/queue/AsyncSamples.java create mode 100644 storage/client/queue/src/samples/java/com/azure/storage/queue/MessageSamples.java create mode 100644 storage/client/queue/src/samples/java/com/azure/storage/queue/QueueExceptionSamples.java create mode 100644 storage/client/queue/src/samples/java/com/azure/storage/queue/QueueServiceSamples.java create mode 100644 storage/client/queue/src/samples/java/com/azure/storage/queue/SampleHelper.java create mode 100644 storage/client/queue/src/samples/java/com/azure/storage/queue/javadoc/QueueJavaDocCodeSamples.java create mode 100644 storage/client/queue/src/samples/java/com/azure/storage/queue/javadoc/QueueServiceJavaDocCodeSamples.java create mode 100644 storage/client/queue/src/test/java/com/azure/storage/queue/QueueAsyncClientTests.java create mode 100644 storage/client/queue/src/test/java/com/azure/storage/queue/QueueClientTests.java create mode 100644 storage/client/queue/src/test/java/com/azure/storage/queue/QueueClientTestsBase.java create mode 100644 storage/client/queue/src/test/java/com/azure/storage/queue/QueueServiceAsyncClientTests.java create mode 100644 storage/client/queue/src/test/java/com/azure/storage/queue/QueueServiceClientTests.java create mode 100644 storage/client/queue/src/test/java/com/azure/storage/queue/QueueServiceClientTestsBase.java create mode 100644 storage/client/queue/src/test/java/com/azure/storage/queue/TestHelpers.java create mode 100644 storage/client/queue/src/test/resources/session-records/clearMessages.json create mode 100644 storage/client/queue/src/test/resources/session-records/clearMessagesQueueDoesNotExist.json create mode 100644 storage/client/queue/src/test/resources/session-records/createQueue.json create mode 100644 storage/client/queue/src/test/resources/session-records/createQueueTwiceDifferentMetadata.json create mode 100644 storage/client/queue/src/test/resources/session-records/createQueueTwiceSameMetadata.json create mode 100644 storage/client/queue/src/test/resources/session-records/createQueueWithMetadata.json create mode 100644 storage/client/queue/src/test/resources/session-records/createTwiceDifferentMetadata.json create mode 100644 storage/client/queue/src/test/resources/session-records/createTwiceSameMetadata.json create mode 100644 storage/client/queue/src/test/resources/session-records/createWithMetadata.json create mode 100644 storage/client/queue/src/test/resources/session-records/createWithSASToken.json create mode 100644 storage/client/queue/src/test/resources/session-records/createWithSharedKey.json create mode 100644 storage/client/queue/src/test/resources/session-records/deleteExisting.json create mode 100644 storage/client/queue/src/test/resources/session-records/deleteExistingQueue.json create mode 100644 storage/client/queue/src/test/resources/session-records/deleteMessage.json create mode 100644 storage/client/queue/src/test/resources/session-records/deleteMessageInvalidMessageId.json create mode 100644 storage/client/queue/src/test/resources/session-records/deleteMessageInvalidPopReceipt.json create mode 100644 storage/client/queue/src/test/resources/session-records/deleteMessageQueueDoesNotExist.json create mode 100644 storage/client/queue/src/test/resources/session-records/deleteMetadata.json create mode 100644 storage/client/queue/src/test/resources/session-records/deleteNonExistent.json create mode 100644 storage/client/queue/src/test/resources/session-records/deleteNonExistentQueue.json create mode 100644 storage/client/queue/src/test/resources/session-records/dequeueMessage.json create mode 100644 storage/client/queue/src/test/resources/session-records/dequeueMultipleMessages.json create mode 100644 storage/client/queue/src/test/resources/session-records/dequeueQueueDoesNotExist.json create mode 100644 storage/client/queue/src/test/resources/session-records/dequeueTooManyMessages.json create mode 100644 storage/client/queue/src/test/resources/session-records/enqueueEmptyMessage.json create mode 100644 storage/client/queue/src/test/resources/session-records/enqueueMessage.json create mode 100644 storage/client/queue/src/test/resources/session-records/enqueueQueueDoesNotExist.json create mode 100644 storage/client/queue/src/test/resources/session-records/enqueueShortTimeToLiveMessage.json create mode 100644 storage/client/queue/src/test/resources/session-records/getAccessPolicy.json create mode 100644 storage/client/queue/src/test/resources/session-records/getAccessPolicyQueueDoesNotExist.json create mode 100644 storage/client/queue/src/test/resources/session-records/getProperties.json create mode 100644 storage/client/queue/src/test/resources/session-records/getPropertiesQueueDoesNotExist.json create mode 100644 storage/client/queue/src/test/resources/session-records/getQueueDoesNotCreateAQueue.json create mode 100644 storage/client/queue/src/test/resources/session-records/listQueues.json create mode 100644 storage/client/queue/src/test/resources/session-records/listQueuesIncludeMetadata.json create mode 100644 storage/client/queue/src/test/resources/session-records/listQueuesWithPrefix.json create mode 100644 storage/client/queue/src/test/resources/session-records/peekMessage.json create mode 100644 storage/client/queue/src/test/resources/session-records/peekMultipleMessages.json create mode 100644 storage/client/queue/src/test/resources/session-records/peekQueueDoesNotExist.json create mode 100644 storage/client/queue/src/test/resources/session-records/peekTooManyMessages.json create mode 100644 storage/client/queue/src/test/resources/session-records/setAccessPolicy.json create mode 100644 storage/client/queue/src/test/resources/session-records/setAccessPolicyQueueDoesNotExist.json create mode 100644 storage/client/queue/src/test/resources/session-records/setInvalidAccessPolicy.json create mode 100644 storage/client/queue/src/test/resources/session-records/setInvalidMetadata.json create mode 100644 storage/client/queue/src/test/resources/session-records/setMetadata.json create mode 100644 storage/client/queue/src/test/resources/session-records/setMetadataQueueDoesNotExist.json create mode 100644 storage/client/queue/src/test/resources/session-records/setProperties.json create mode 100644 storage/client/queue/src/test/resources/session-records/setTooManyAccessPolicies.json create mode 100644 storage/client/queue/src/test/resources/session-records/updateMessage.json create mode 100644 storage/client/queue/src/test/resources/session-records/updateMessageInvalidMessageId.json create mode 100644 storage/client/queue/src/test/resources/session-records/updateMessageInvalidPopReceipt.json create mode 100644 storage/client/queue/src/test/resources/session-records/updateMessageQueueDoesNotExist.json diff --git a/eng/code-quality-reports/src/main/resources/spotbugs/spotbugs-exclude.xml b/eng/code-quality-reports/src/main/resources/spotbugs/spotbugs-exclude.xml index f7e3bcb5eb317..bf083e11e8bff 100755 --- a/eng/code-quality-reports/src/main/resources/spotbugs/spotbugs-exclude.xml +++ b/eng/code-quality-reports/src/main/resources/spotbugs/spotbugs-exclude.xml @@ -368,6 +368,7 @@ + @@ -439,6 +440,7 @@ + diff --git a/eng/jacoco-test-coverage/pom.xml b/eng/jacoco-test-coverage/pom.xml index ea21a08695cb7..cee087f22d298 100644 --- a/eng/jacoco-test-coverage/pom.xml +++ b/eng/jacoco-test-coverage/pom.xml @@ -32,6 +32,7 @@ 4.0.0-preview.1 5.0.0-preview.2 12.0.0-preview.1 + 12.0.0-preview.1 @@ -93,7 +94,11 @@ - + + com.azure + azure-storage-queue + ${azure-storage-queue.version} + diff --git a/eng/spotbugs-aggregate-report/pom.xml b/eng/spotbugs-aggregate-report/pom.xml index a8f5addab7e12..7e86db80baad6 100644 --- a/eng/spotbugs-aggregate-report/pom.xml +++ b/eng/spotbugs-aggregate-report/pom.xml @@ -30,6 +30,8 @@ 4.0.0-preview.1 5.0.0-preview.2 12.0.0-preview.1 + 12.0.0-preview.1 + 12.0.0-preview.1 @@ -183,6 +185,16 @@ + + com.azure + azure-storage-file + ${azure-storage-file.version} + + + com.azure + azure-storage-queue + ${azure-storage-queue.version} + diff --git a/pom.client.xml b/pom.client.xml index 50e496e4f6b0f..ed51d0abaa3db 100644 --- a/pom.client.xml +++ b/pom.client.xml @@ -393,6 +393,14 @@ Azure Storage - Blobs com.azure.storage.blob* + + Azure Storage - Files + com.azure.storage.file* + + + Azure Storage - Queues + com.azure.storage.queue* + https://docs.oracle.com/javase/8/docs/api/ @@ -537,7 +545,8 @@ -snippetpath ${project.basedir}/sdk/eventhubs/azure-eventhubs/src/samples/java -snippetpath ${project.basedir}/sdk/keyvault/azure-keyvault-keys/src/samples/java -snippetpath ${project.basedir}/sdk/keyvault/azure-keyvault-secrets/src/samples/java - + + -snippetpath ${project.basedir}/storage/client/queue/src/samples/java @@ -736,5 +745,7 @@ ./sdk/identity/azure-identity ./storage/client/blob + ./storage/client/file + ./storage/client/queue diff --git a/sdk/keyvault/azure-keyvault-secrets/src/main/java/com/azure/security/keyvault/secrets/KeyVaultErrorCodeStrings.java b/sdk/keyvault/azure-keyvault-secrets/src/main/java/com/azure/security/keyvault/secrets/KeyVaultErrorCodeStrings.java index 7abc270a6108e..c8932bd0e26b8 100644 --- a/sdk/keyvault/azure-keyvault-secrets/src/main/java/com/azure/security/keyvault/secrets/KeyVaultErrorCodeStrings.java +++ b/sdk/keyvault/azure-keyvault-secrets/src/main/java/com/azure/security/keyvault/secrets/KeyVaultErrorCodeStrings.java @@ -45,4 +45,3 @@ private static synchronized void loadProperties() { } - diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/AppendBlobAsyncClient.java b/storage/client/blob/src/main/java/com/azure/storage/blob/AppendBlobAsyncClient.java index 7b4585580e16f..7275f0fb326fe 100644 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/AppendBlobAsyncClient.java +++ b/storage/client/blob/src/main/java/com/azure/storage/blob/AppendBlobAsyncClient.java @@ -5,6 +5,7 @@ import com.azure.core.http.rest.Response; import com.azure.core.http.rest.SimpleResponse; +import com.azure.core.util.Context; import com.azure.storage.blob.implementation.AzureBlobStorageBuilder; import com.azure.storage.blob.models.AppendBlobAccessConditions; import com.azure.storage.blob.models.AppendBlobItem; @@ -13,17 +14,18 @@ import com.azure.storage.blob.models.BlobRange; import com.azure.storage.blob.models.Metadata; import com.azure.storage.blob.models.SourceModifiedAccessConditions; -import io.netty.buffer.Unpooled; +import io.netty.buffer.ByteBuf; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.net.URL; -import java.nio.ByteBuffer; + +import static com.azure.storage.blob.Utility.postProcessResponse; /** - * Client to an append blob. It may only be instantiated through a {@link AppendBlobClientBuilder#buildAsyncClient()}, via - * the method {@link BlobAsyncClient#asAppendBlobAsyncClient()}, or via the method + * Client to an append blob. It may only be instantiated through a {@link BlobClientBuilder#buildAppendBlobAsyncClient()}, + * via the method {@link BlobAsyncClient#asAppendBlobAsyncClient()}, or via the method * {@link ContainerAsyncClient#getAppendBlobAsyncClient(String)}. This class does not hold * any state about a particular blob, but is instead a convenient way of sending appropriate * requests to the resource on the service. @@ -45,8 +47,6 @@ * object through {@link Mono#toFuture()}. */ public final class AppendBlobAsyncClient extends BlobAsyncClient { - final AppendBlobAsyncRawClient appendBlobAsyncRawClient; - /** * Indicates the maximum number of bytes that can be sent in a call to appendBlock. */ @@ -58,12 +58,11 @@ public final class AppendBlobAsyncClient extends BlobAsyncClient { public static final int MAX_BLOCKS = 50000; /** - * Package-private constructor for use by {@link AppendBlobClientBuilder}. + * Package-private constructor for use by {@link BlobClientBuilder}. * @param azureBlobStorageBuilder the API client builder for blob storage API */ AppendBlobAsyncClient(AzureBlobStorageBuilder azureBlobStorageBuilder, String snapshot) { super(azureBlobStorageBuilder, snapshot); - appendBlobAsyncRawClient = new AppendBlobAsyncRawClient(azureBlobStorageBuilder.build()); } /** @@ -90,8 +89,13 @@ public Mono> create() { * A reactive response containing the information of the created appended blob. */ public Mono> create(BlobHTTPHeaders headers, Metadata metadata, BlobAccessConditions accessConditions) { - return appendBlobAsyncRawClient - .create(headers, metadata, accessConditions) + metadata = (metadata == null) ? new Metadata() : metadata; + accessConditions = (accessConditions == null) ? new BlobAccessConditions() : accessConditions; + + return postProcessResponse(this.azureBlobStorage.appendBlobs().createWithRestResponseAsync(null, + null, 0, null, metadata, null, null, + null, null, headers, accessConditions.leaseAccessConditions(), + accessConditions.modifiedAccessConditions(), Context.NONE)) .map(rb -> new SimpleResponse<>(rb, new AppendBlobItem(rb.deserializedHeaders()))); } @@ -111,7 +115,7 @@ public Mono> create(BlobHTTPHeaders headers, Metadata m * @return * A reactive response containing the information of the append blob operation. */ - public Mono> appendBlock(Flux data, long length) { + public Mono> appendBlock(Flux data, long length) { return this.appendBlock(data, length, null); } @@ -133,10 +137,17 @@ public Mono> appendBlock(Flux data, long le * @return * A reactive response containing the information of the append blob operation. */ - public Mono> appendBlock(Flux data, long length, - AppendBlobAccessConditions appendBlobAccessConditions) { - return appendBlobAsyncRawClient - .appendBlock(data.map(Unpooled::wrappedBuffer), length, appendBlobAccessConditions) + public Mono> appendBlock(Flux data, long length, + AppendBlobAccessConditions appendBlobAccessConditions) { + appendBlobAccessConditions = appendBlobAccessConditions == null ? new AppendBlobAccessConditions() + : appendBlobAccessConditions; + + return postProcessResponse(this.azureBlobStorage.appendBlobs().appendBlockWithRestResponseAsync( + null, null, data, length, null, null, + null, null, null, null, + appendBlobAccessConditions.leaseAccessConditions(), + appendBlobAccessConditions.appendPositionAccessConditions(), + appendBlobAccessConditions.modifiedAccessConditions(), Context.NONE)) .map(rb -> new SimpleResponse<>(rb, new AppendBlobItem(rb.deserializedHeaders()))); } @@ -183,8 +194,16 @@ public Mono> appendBlockFromUrl(URL sourceURL, BlobRang public Mono> appendBlockFromUrl(URL sourceURL, BlobRange sourceRange, byte[] sourceContentMD5, AppendBlobAccessConditions destAccessConditions, SourceModifiedAccessConditions sourceAccessConditions) { - return appendBlobAsyncRawClient - .appendBlockFromUrl(sourceURL, sourceRange, sourceContentMD5, destAccessConditions, sourceAccessConditions) + sourceRange = sourceRange == null ? new BlobRange(0) : sourceRange; + destAccessConditions = destAccessConditions == null + ? new AppendBlobAccessConditions() : destAccessConditions; + + return postProcessResponse( + this.azureBlobStorage.appendBlobs().appendBlockFromUrlWithRestResponseAsync(null, null, + sourceURL, 0, sourceRange.toString(), sourceContentMD5, null, null, + destAccessConditions.leaseAccessConditions(), + destAccessConditions.appendPositionAccessConditions(), + destAccessConditions.modifiedAccessConditions(), sourceAccessConditions, Context.NONE)) .map(rb -> new SimpleResponse<>(rb, new AppendBlobItem(rb.deserializedHeaders()))); } } diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/AppendBlobAsyncRawClient.java b/storage/client/blob/src/main/java/com/azure/storage/blob/AppendBlobAsyncRawClient.java deleted file mode 100644 index 15bf8e09e4d1f..0000000000000 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/AppendBlobAsyncRawClient.java +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.azure.storage.blob; - -import com.azure.core.util.Context; -import com.azure.storage.blob.implementation.AzureBlobStorageImpl; -import com.azure.storage.blob.models.AppendBlobAccessConditions; -import com.azure.storage.blob.models.AppendBlobsAppendBlockFromUrlResponse; -import com.azure.storage.blob.models.AppendBlobsAppendBlockResponse; -import com.azure.storage.blob.models.AppendBlobsCreateResponse; -import com.azure.storage.blob.models.BlobAccessConditions; -import com.azure.storage.blob.models.BlobHTTPHeaders; -import com.azure.storage.blob.models.BlobRange; -import com.azure.storage.blob.models.Metadata; -import com.azure.storage.blob.models.SourceModifiedAccessConditions; -import io.netty.buffer.ByteBuf; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import java.net.URL; - -import static com.azure.storage.blob.Utility.postProcessResponse; - - -/** - * Represents a URL to an append blob. It may be obtained by direct construction or via the create method on a - * {@link ContainerAsyncClient} object. This class does not hold any state about a particular append blob but is instead a - * convenient way of sending off appropriate requests to the resource on the service. Please refer to the - * Azure Docs - */ -final class AppendBlobAsyncRawClient extends BlobAsyncRawClient { - - /** - * Indicates the maximum number of bytes that can be sent in a call to appendBlock. - */ - public static final int MAX_APPEND_BLOCK_BYTES = 4 * Constants.MB; - - /** - * Indicates the maximum number of blocks allowed in an append blob. - */ - public static final int MAX_BLOCKS = 50000; - - /** - * Creates a {@code AppendBlobAsyncRawClient} object pointing to the account specified by the URL and using the provided - * pipeline to make HTTP requests. - */ - AppendBlobAsyncRawClient(AzureBlobStorageImpl azureBlobStorage) { - super(azureBlobStorage, null); - } - - /** - * Creates a 0-length append blob. Call AppendBlock to append data to an append blob. For more information, see - * the Azure Docs. - * - * @return Emits the successful response. - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=append_blob "Sample code for AppendBlobAsyncRawClient.create")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono create() { - return this.create(null, null, null); - } - - /** - * Creates a 0-length append blob. Call AppendBlock to append data to an append blob. For more information, see - * the Azure Docs. - * - * @param headers {@link BlobHTTPHeaders} - * @param metadata {@link Metadata} - * @param accessConditions {@link BlobAccessConditions} - * @return Emits the successful response. - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=append_blob "Sample code for AppendBlobAsyncRawClient.create")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono create(BlobHTTPHeaders headers, Metadata metadata, - BlobAccessConditions accessConditions) { - metadata = metadata == null ? new Metadata() : metadata; - accessConditions = accessConditions == null ? new BlobAccessConditions() : accessConditions; - - return postProcessResponse(this.azureBlobStorage.appendBlobs().createWithRestResponseAsync(null, - null, 0, null, metadata, null, null, - null, null, headers, accessConditions.leaseAccessConditions(), - accessConditions.modifiedAccessConditions(), Context.NONE)); - } - - /** - * Commits a new block of data to the end of the existing append blob. For more information, see the - * Azure Docs. - *

- * Note that the data passed must be replayable if retries are enabled (the default). In other words, the - * {@code Flux} must produce the same data each time it is subscribed to. - * - * @param data The data to write to the blob. Note that this {@code Flux} must be replayable if retries are enabled - * (the default). In other words, the Flowable must produce the same data each time it is subscribed to. - * @param length The exact length of the data. It is important that this value match precisely the length of the data - * emitted by the {@code Flux}. - * @return Emits the successful response. - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=append_blob "Sample code for AppendBlobAsyncRawClient.appendBlock")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono appendBlock(Flux data, long length) { - return this.appendBlock(data, length, null); - } - - /** - * Commits a new block of data to the end of the existing append blob. For more information, see the - * Azure Docs. - *

- * Note that the data passed must be replayable if retries are enabled (the default). In other words, the - * {@code Flux} must produce the same data each time it is subscribed to. - * - * @param data The data to write to the blob. Note that this {@code Flux} must be replayable if retries are enabled - * (the default). In other words, the Flowable must produce the same data each time it is subscribed to. - * @param length The exact length of the data. It is important that this value match precisely the length of the data - * emitted by the {@code Flux}. - * @param appendBlobAccessConditions {@link AppendBlobAccessConditions} - * @return Emits the successful response. - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=append_blob "Sample code for AppendBlobAsyncRawClient.appendBlock")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono appendBlock(Flux data, long length, - AppendBlobAccessConditions appendBlobAccessConditions) { - appendBlobAccessConditions = appendBlobAccessConditions == null ? new AppendBlobAccessConditions() - : appendBlobAccessConditions; - - return postProcessResponse(this.azureBlobStorage.appendBlobs().appendBlockWithRestResponseAsync( - null, null, data, length, null, null, - null, null, null, null, - appendBlobAccessConditions.leaseAccessConditions(), - appendBlobAccessConditions.appendPositionAccessConditions(), - appendBlobAccessConditions.modifiedAccessConditions(), Context.NONE)); - } - - /** - * Commits a new block of data from another blob to the end of this append blob. For more information, see the - * Azure Docs. - *

- * - * @param sourceURL The url to the blob that will be the source of the copy. A source blob in the same storage account can - * be authenticated via Shared Key. However, if the source is a blob in another account, the source blob - * must either be public or must be authenticated via a shared access signature. If the source blob is - * public, no authentication is required to perform the operation. - * @param sourceRange The source {@link BlobRange} to copy. - * @return Emits the successful response. - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=append_from_url "Sample code for AppendBlobAsyncRawClient.appendBlockFromUrl")] - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono appendBlockFromUrl(URL sourceURL, BlobRange sourceRange) { - return this.appendBlockFromUrl(sourceURL, sourceRange, null, null, - null); - } - - /** - * Commits a new block of data from another blob to the end of this append blob. For more information, see the - * Azure Docs. - *

- * - * @param sourceURL The url to the blob that will be the source of the copy. A source blob in the same storage account can - * be authenticated via Shared Key. However, if the source is a blob in another account, the source blob - * must either be public or must be authenticated via a shared access signature. If the source blob is - * public, no authentication is required to perform the operation. - * @param sourceRange {@link BlobRange} - * @param sourceContentMD5 An MD5 hash of the block content from the source blob. If specified, the service will calculate the MD5 - * of the received data and fail the request if it does not match the provided MD5. - * @param destAccessConditions {@link AppendBlobAccessConditions} - * @param sourceAccessConditions {@link SourceModifiedAccessConditions} - * @return Emits the successful response. - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=append_from_url "Sample code for AppendBlobAsyncRawClient.appendBlockFromUrl")] - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono appendBlockFromUrl(URL sourceURL, BlobRange sourceRange, - byte[] sourceContentMD5, AppendBlobAccessConditions destAccessConditions, - SourceModifiedAccessConditions sourceAccessConditions) { - - sourceRange = sourceRange == null ? new BlobRange(0) : sourceRange; - destAccessConditions = destAccessConditions == null - ? new AppendBlobAccessConditions() : destAccessConditions; - - return postProcessResponse( - this.azureBlobStorage.appendBlobs().appendBlockFromUrlWithRestResponseAsync(null, null, - sourceURL, 0, sourceRange.toString(), sourceContentMD5, null, null, - destAccessConditions.leaseAccessConditions(), - destAccessConditions.appendPositionAccessConditions(), - destAccessConditions.modifiedAccessConditions(), sourceAccessConditions, Context.NONE)); - } -} diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/AppendBlobClient.java b/storage/client/blob/src/main/java/com/azure/storage/blob/AppendBlobClient.java index 47a379649659f..246fe645af179 100644 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/AppendBlobClient.java +++ b/storage/client/blob/src/main/java/com/azure/storage/blob/AppendBlobClient.java @@ -11,18 +11,19 @@ import com.azure.storage.blob.models.BlobRange; import com.azure.storage.blob.models.Metadata; import com.azure.storage.blob.models.SourceModifiedAccessConditions; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; import java.io.InputStream; import java.net.URL; -import java.nio.ByteBuffer; import java.time.Duration; /** - * Client to an append blob. It may only be instantiated through a {@link AppendBlobClientBuilder}, via + * Client to an append blob. It may only be instantiated through a {@link BlobClientBuilder}, via * the method {@link BlobClient#asAppendBlobClient()}, or via the method * {@link ContainerClient#getAppendBlobClient(String)}. This class does not hold * any state about a particular blob, but is instead a convenient way of sending appropriate @@ -37,28 +38,27 @@ * for more information. */ public final class AppendBlobClient extends BlobClient { - - AppendBlobAsyncClient appendBlobAsyncClient; + private AppendBlobAsyncClient appendBlobAsyncClient; /** * Indicates the maximum number of bytes that can be sent in a call to appendBlock. */ - public static final int MAX_APPEND_BLOCK_BYTES = 4 * Constants.MB; + public static final int MAX_APPEND_BLOCK_BYTES = AppendBlobAsyncClient.MAX_APPEND_BLOCK_BYTES; /** * Indicates the maximum number of blocks allowed in an append blob. */ - public static final int MAX_BLOCKS = 50000; + public static final int MAX_BLOCKS = AppendBlobAsyncClient.MAX_BLOCKS; /** - * Package-private constructor for use by {@link AppendBlobClientBuilder}. + * Package-private constructor for use by {@link BlobClientBuilder}. * @param appendBlobAsyncClient the async append blob client */ AppendBlobClient(AppendBlobAsyncClient appendBlobAsyncClient) { super(appendBlobAsyncClient); this.appendBlobAsyncClient = appendBlobAsyncClient; } - + /** * Creates and opens an output stream to write data to the append blob. If the blob already exists on the service, * it will be overwritten. @@ -160,7 +160,7 @@ public Response appendBlock(InputStream data, long length) { */ public Response appendBlock(InputStream data, long length, AppendBlobAccessConditions appendBlobAccessConditions, Duration timeout) { - Flux fbb = Flux.range(0, (int) Math.ceil((double) length / (double) MAX_APPEND_BLOCK_BYTES)) + Flux fbb = Flux.range(0, (int) Math.ceil((double) length / (double) MAX_APPEND_BLOCK_BYTES)) .map(i -> i * MAX_APPEND_BLOCK_BYTES) .concatMap(pos -> Mono.fromCallable(() -> { long count = pos + MAX_APPEND_BLOCK_BYTES > length ? length - pos : MAX_APPEND_BLOCK_BYTES; @@ -169,7 +169,8 @@ public Response appendBlock(InputStream data, long length, while (read < count) { read += data.read(cache, read, (int) count - read); } - return ByteBuffer.wrap(cache); + + return ByteBufAllocator.DEFAULT.buffer((int) count).writeBytes(cache); })); Mono> response = appendBlobAsyncClient.appendBlock(fbb.subscribeOn(Schedulers.elastic()), length, appendBlobAccessConditions); diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/AppendBlobClientBuilder.java b/storage/client/blob/src/main/java/com/azure/storage/blob/AppendBlobClientBuilder.java deleted file mode 100644 index c46883e7fecf9..0000000000000 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/AppendBlobClientBuilder.java +++ /dev/null @@ -1,333 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.azure.storage.blob; - -import com.azure.core.credentials.TokenCredential; -import com.azure.core.http.HttpClient; -import com.azure.core.http.HttpPipeline; -import com.azure.core.http.policy.AddDatePolicy; -import com.azure.core.http.policy.BearerTokenAuthenticationPolicy; -import com.azure.core.http.policy.HttpLogDetailLevel; -import com.azure.core.http.policy.HttpLoggingPolicy; -import com.azure.core.http.policy.HttpPipelinePolicy; -import com.azure.core.http.policy.RequestIdPolicy; -import com.azure.core.http.policy.UserAgentPolicy; -import com.azure.core.implementation.util.ImplUtils; -import com.azure.core.util.configuration.Configuration; -import com.azure.core.util.configuration.ConfigurationManager; -import com.azure.storage.blob.implementation.AzureBlobStorageBuilder; -import com.azure.storage.common.credentials.SASTokenCredential; -import com.azure.storage.common.credentials.SharedKeyCredential; -import com.azure.storage.common.policy.RequestRetryOptions; -import com.azure.storage.common.policy.RequestRetryPolicy; -import com.azure.storage.common.policy.SASTokenCredentialPolicy; -import com.azure.storage.common.policy.SharedKeyCredentialPolicy; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; - -/** - * Fluent AppendBlobClientBuilder for instantiating a {@link AppendBlobClient} or {@link AppendBlobAsyncClient} - * using {@link AppendBlobClientBuilder#buildAsyncClient()} or {@link AppendBlobClientBuilder#buildAsyncClient()} respectively. - * - *

- * The following information must be provided on this builder: - * - *

    - *
  • the endpoint through {@code .endpoint()}, including the container name and blob name, in the format of {@code https://{accountName}.blob.core.windows.net/{containerName}/{blobName}}. - *
  • the credential through {@code .credential()} or {@code .connectionString()} if the container is not publicly accessible. - *
- * - *

- * Once all the configurations are set on this builder, call {@code .buildClient()} to create a - * {@link AppendBlobClient} or {@code .buildAsyncClient()} to create a {@link AppendBlobAsyncClient}. - */ -public final class AppendBlobClientBuilder { - private static final String ACCOUNT_NAME = "accountname"; - private static final String ACCOUNT_KEY = "accountkey"; - private static final String ENDPOINT_PROTOCOL = "defaultendpointsprotocol"; - private static final String ENDPOINT_SUFFIX = "endpointsuffix"; - - private final List policies; - - private String endpoint; - private String containerName; - private String blobName; - private String snapshot; - private SharedKeyCredential sharedKeyCredential; - private TokenCredential tokenCredential; - private SASTokenCredential sasTokenCredential; - private HttpClient httpClient; - private HttpLogDetailLevel logLevel; - private RequestRetryOptions retryOptions; - private Configuration configuration; - - /** - * Creates a builder instance that is able to configure and construct {@link AppendBlobClient AppendBlobClients} - * and {@link AppendBlobAsyncClient AppendBlobAsyncClients}. - */ - public AppendBlobClientBuilder() { - retryOptions = new RequestRetryOptions(); - logLevel = HttpLogDetailLevel.NONE; - policies = new ArrayList<>(); - } - - private AzureBlobStorageBuilder buildImpl() { - Objects.requireNonNull(endpoint); - Objects.requireNonNull(containerName); - Objects.requireNonNull(blobName); - - // Closest to API goes first, closest to wire goes last. - final List policies = new ArrayList<>(); - - if (configuration == null) { - configuration = ConfigurationManager.getConfiguration(); - } - policies.add(new UserAgentPolicy(BlobConfiguration.NAME, BlobConfiguration.VERSION, configuration)); - policies.add(new RequestIdPolicy()); - policies.add(new AddDatePolicy()); - - if (sharedKeyCredential != null) { - policies.add(new SharedKeyCredentialPolicy(sharedKeyCredential)); - } else if (tokenCredential != null) { - policies.add(new BearerTokenAuthenticationPolicy(tokenCredential, String.format("%s/.default", endpoint))); - } else if (sasTokenCredential != null) { - policies.add(new SASTokenCredentialPolicy(sasTokenCredential)); - } else { - policies.add(new AnonymousCredentialPolicy()); - } - - policies.add(new RequestRetryPolicy(retryOptions)); - - policies.addAll(this.policies); - policies.add(new HttpLoggingPolicy(logLevel)); - - HttpPipeline pipeline = HttpPipeline.builder() - .policies(policies.toArray(new HttpPipelinePolicy[0])) - .httpClient(httpClient) - .build(); - - return new AzureBlobStorageBuilder() - .url(String.format("%s/%s/%s", endpoint, containerName, blobName)) - .pipeline(pipeline); - } - - /** - * @return a {@link AppendBlobClient} created from the configurations in this builder. - */ - public AppendBlobClient buildClient() { - return new AppendBlobClient(buildAsyncClient()); - } - - /** - * @return a {@link AppendBlobAsyncClient} created from the configurations in this builder. - */ - public AppendBlobAsyncClient buildAsyncClient() { - return new AppendBlobAsyncClient(buildImpl(), snapshot); - } - - /** - * Sets the service endpoint, additionally parses it for information (SAS token, container name, blob name) - * @param endpoint URL of the service - * @return the updated AppendBlobClientBuilder object - * @throws IllegalArgumentException If {@code endpoint} is a malformed URL or is using an unknown host. - */ - public AppendBlobClientBuilder endpoint(String endpoint) { - Objects.requireNonNull(endpoint); - URL url; - try { - url = new URL(endpoint); - BlobURLParts parts = URLParser.parse(url); - this.endpoint = parts.scheme() + "://" + parts.host(); - - if (parts.containerName() != null) { - this.containerName = parts.containerName(); - } - - if (parts.blobName() != null) { - this.blobName = parts.blobName(); - } - - if (parts.snapshot() != null) { - this.snapshot = parts.snapshot(); - } - } catch (MalformedURLException ex) { - throw new IllegalArgumentException("The Azure Storage Blob endpoint url is malformed."); - } - - SASTokenCredential credential = SASTokenCredential.fromQuery(url.getQuery()); - if (credential != null) { - this.credential(credential); - } - - return this; - } - - /** - * Sets the name of the container this client is connecting to. - * @param containerName the name of the container - * @return the updated AppendBlobClientBuilder object - */ - public AppendBlobClientBuilder containerName(String containerName) { - this.containerName = containerName; - return this; - } - - /** - * Sets the name of the blob this client is connecting to. - * @param blobName the name of the blob - * @return the updated AppendBlobClientBuilder object - */ - public AppendBlobClientBuilder blobName(String blobName) { - this.blobName = blobName; - return this; - } - - /** - * Sets the snapshot of the blob this client is connecting to. - * @param snapshot the snapshot identifier for the blob - * @return the updated AppendBlobClientBuilder object - */ - public AppendBlobClientBuilder snapshot(String snapshot) { - this.snapshot = snapshot; - return this; - } - - /** - * Sets the credential used to authorize requests sent to the service - * @param credential authorization credential - * @return the updated AppendBlobClientBuilder object - */ - public AppendBlobClientBuilder credential(SharedKeyCredential credential) { - this.sharedKeyCredential = credential; - this.tokenCredential = null; - this.sasTokenCredential = null; - return this; - } - - /** - * Sets the credential used to authorize requests sent to the service - * @param credential authorization credential - * @return the updated AppendBlobClientBuilder object - */ - public AppendBlobClientBuilder credential(TokenCredential credential) { - this.tokenCredential = credential; - this.sharedKeyCredential = null; - this.sasTokenCredential = null; - return this; - } - - /** - * Sets the credential used to authorize requests sent to the service - * @param credential authorization credential - * @return the updated AppendBlobClientBuilder object - */ - public AppendBlobClientBuilder credential(SASTokenCredential credential) { - this.sasTokenCredential = credential; - this.sharedKeyCredential = null; - this.tokenCredential = null; - return this; - } - - /** - * Clears the credential used to authorize requests sent to the service - * @return the updated AppendBlobClientBuilder object - */ - public AppendBlobClientBuilder anonymousCredential() { - this.sharedKeyCredential = null; - this.tokenCredential = null; - this.sasTokenCredential = null; - return this; - } - - /** - * Sets the connection string for the service, parses it for authentication information (account name, account key) - * @param connectionString connection string from access keys section - * @return the updated AppendBlobClientBuilder object - * @throws IllegalArgumentException If {@code connectionString} doesn't contain AccountName or AccountKey. - */ - public AppendBlobClientBuilder connectionString(String connectionString) { - Objects.requireNonNull(connectionString); - - Map connectionKVPs = new HashMap<>(); - for (String s : connectionString.split(";")) { - String[] kvp = s.split("=", 2); - connectionKVPs.put(kvp[0].toLowerCase(Locale.ROOT), kvp[1]); - } - - String accountName = connectionKVPs.get(ACCOUNT_NAME); - String accountKey = connectionKVPs.get(ACCOUNT_KEY); - String endpointProtocol = connectionKVPs.get(ENDPOINT_PROTOCOL); - String endpointSuffix = connectionKVPs.get(ENDPOINT_SUFFIX); - - if (ImplUtils.isNullOrEmpty(accountName) || ImplUtils.isNullOrEmpty(accountKey)) { - throw new IllegalArgumentException("Connection string must contain 'AccountName' and 'AccountKey'."); - } - - if (!ImplUtils.isNullOrEmpty(endpointProtocol) && !ImplUtils.isNullOrEmpty(endpointSuffix)) { - String endpoint = String.format("%s://%s.blob.%s", endpointProtocol, accountName, endpointSuffix.replaceFirst("^\\.", "")); - endpoint(endpoint); - } - - // Use accountName and accountKey to get the SAS token using the credential class. - return credential(new SharedKeyCredential(accountName, accountKey)); - } - - /** - * Sets the http client used to send service requests - * @param httpClient http client to send requests - * @return the updated AppendBlobClientBuilder object - */ - public AppendBlobClientBuilder httpClient(HttpClient httpClient) { - this.httpClient = httpClient; - return this; - } - - /** - * Adds a pipeline policy to apply on each request sent - * @param pipelinePolicy a pipeline policy - * @return the updated AppendBlobClientBuilder object - */ - public AppendBlobClientBuilder addPolicy(HttpPipelinePolicy pipelinePolicy) { - this.policies.add(pipelinePolicy); - return this; - } - - /** - * Sets the logging level for service requests - * @param logLevel logging level - * @return the updated AppendBlobClientBuilder object - */ - public AppendBlobClientBuilder httpLogDetailLevel(HttpLogDetailLevel logLevel) { - this.logLevel = logLevel; - return this; - } - - /** - * Sets the configuration object used to retrieve environment configuration values used to buildClient the client with - * when they are not set in the appendBlobClientBuilder, defaults to Configuration.NONE - * @param configuration configuration store - * @return the updated AppendBlobClientBuilder object - */ - public AppendBlobClientBuilder configuration(Configuration configuration) { - this.configuration = configuration; - return this; - } - - /** - * Sets the request retry options for all the requests made through the client. - * @param retryOptions the options to configure retry behaviors - * @return the updated AppendBlobClientBuilder object - */ - public AppendBlobClientBuilder retryOptions(RequestRetryOptions retryOptions) { - this.retryOptions = retryOptions; - return this; - } -} diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/BlobAsyncClient.java b/storage/client/blob/src/main/java/com/azure/storage/blob/BlobAsyncClient.java index 7ec72ecf30b53..c8ceb3f311d3f 100644 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/BlobAsyncClient.java +++ b/storage/client/blob/src/main/java/com/azure/storage/blob/BlobAsyncClient.java @@ -9,7 +9,9 @@ import com.azure.core.http.rest.VoidResponse; import com.azure.core.implementation.http.UrlBuilder; import com.azure.core.implementation.util.FluxUtil; +import com.azure.core.util.Context; import com.azure.storage.blob.implementation.AzureBlobStorageBuilder; +import com.azure.storage.blob.implementation.AzureBlobStorageImpl; import com.azure.storage.blob.models.AccessTier; import com.azure.storage.blob.models.BlobAccessConditions; import com.azure.storage.blob.models.BlobHTTPHeaders; @@ -20,7 +22,10 @@ import com.azure.storage.blob.models.Metadata; import com.azure.storage.blob.models.ModifiedAccessConditions; import com.azure.storage.blob.models.ReliableDownloadOptions; +import com.azure.storage.blob.models.SourceModifiedAccessConditions; import com.azure.storage.blob.models.StorageAccountInfo; +import com.azure.storage.blob.models.UserDelegationKey; +import com.azure.storage.common.credentials.SharedKeyCredential; import io.netty.buffer.ByteBuf; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -35,85 +40,94 @@ import java.nio.channels.AsynchronousFileChannel; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; +import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.List; +import static com.azure.storage.blob.Utility.postProcessResponse; + /** - * Client to a blob of any type: block, append, or page. It may only be instantiated through a {@link BlobClientBuilder} or via - * the method {@link ContainerAsyncClient#getBlobAsyncClient(String)}. This class does not hold any state about a particular - * blob, but is instead a convenient way of sending appropriate requests to the resource on the service. + * Client to a blob of any type: block, append, or page. It may only be instantiated through a {@link BlobClientBuilder} + * or via the method {@link ContainerAsyncClient#getBlobAsyncClient(String)}. This class does not hold any state about a + * particular blob, but is instead a convenient way of sending appropriate requests to the resource on the service. * *

* This client offers the ability to download blobs. Note that uploading data is specific to each type of blob. Please * refer to the {@link BlockBlobClient}, {@link PageBlobClient}, or {@link AppendBlobClient} for upload options. This - * client can be converted into one of these clients easily through the methods {@link #asBlockBlobAsyncClient}, - * {@link #asPageBlobAsyncClient}, and {@link #asAppendBlobAsyncClient()}. + * client can be converted into one of these clients easily through the methods {@link #asBlockBlobAsyncClient}, {@link + * #asPageBlobAsyncClient}, and {@link #asAppendBlobAsyncClient()}. * *

* This client contains operations on a blob. Operations on a container are available on {@link ContainerAsyncClient}, * and operations on the service are available on {@link StorageAsyncClient}. * *

- * Please refer to the Azure Docs - * for more information. + * Please refer to the Azure + * Docs for more information. * *

- * Note this client is an async client that returns reactive responses from Spring Reactor Core - * project (https://projectreactor.io/). Calling the methods in this client will NOT - * start the actual network operation, until {@code .subscribe()} is called on the reactive response. - * You can simply convert one of these responses to a {@link java.util.concurrent.CompletableFuture} - * object through {@link Mono#toFuture()}. + * Note this client is an async client that returns reactive responses from Spring Reactor Core project + * (https://projectreactor.io/). Calling the methods in this client will NOT start the actual network + * operation, until {@code .subscribe()} is called on the reactive response. You can simply convert one of these + * responses to a {@link java.util.concurrent.CompletableFuture} object through {@link Mono#toFuture()}. */ +@SuppressWarnings({"unused", "WeakerAccess"}) public class BlobAsyncClient { private static final int BLOB_DEFAULT_DOWNLOAD_BLOCK_SIZE = 4 * Constants.MB; private static final int BLOB_MAX_DOWNLOAD_BLOCK_SIZE = 100 * Constants.MB; - final BlobAsyncRawClient blobAsyncRawClient; + final AzureBlobStorageImpl azureBlobStorage; + protected final String snapshot; /** * Package-private constructor for use by {@link BlobClientBuilder}. + * * @param azureBlobStorageBuilder the API client builder for blob storage API */ BlobAsyncClient(AzureBlobStorageBuilder azureBlobStorageBuilder, String snapshot) { - this.blobAsyncRawClient = new BlobAsyncRawClient(azureBlobStorageBuilder.build(), snapshot); + this.azureBlobStorage = azureBlobStorageBuilder.build(); + this.snapshot = snapshot; } /** * Creates a new {@link BlockBlobAsyncClient} to this resource, maintaining configurations. Only do this for blobs * that are known to be block blobs. * - * @return - * A {@link BlockBlobAsyncClient} to this resource. + * @return A {@link BlockBlobAsyncClient} to this resource. */ public BlockBlobAsyncClient asBlockBlobAsyncClient() { - return new BlockBlobAsyncClient(new AzureBlobStorageBuilder().url(getBlobUrl().toString()).pipeline(blobAsyncRawClient.azureBlobStorage.httpPipeline()), blobAsyncRawClient.snapshot); + return new BlockBlobAsyncClient(new AzureBlobStorageBuilder() + .url(getBlobUrl().toString()) + .pipeline(azureBlobStorage.httpPipeline()), snapshot); } /** * Creates a new {@link AppendBlobAsyncClient} to this resource, maintaining configurations. Only do this for blobs * that are known to be append blobs. * - * @return - * A {@link AppendBlobAsyncClient} to this resource. + * @return A {@link AppendBlobAsyncClient} to this resource. */ public AppendBlobAsyncClient asAppendBlobAsyncClient() { - return new AppendBlobAsyncClient(new AzureBlobStorageBuilder().url(getBlobUrl().toString()).pipeline(blobAsyncRawClient.azureBlobStorage.httpPipeline()), blobAsyncRawClient.snapshot); + return new AppendBlobAsyncClient(new AzureBlobStorageBuilder() + .url(getBlobUrl().toString()) + .pipeline(azureBlobStorage.httpPipeline()), snapshot); } /** * Creates a new {@link PageBlobAsyncClient} to this resource, maintaining configurations. Only do this for blobs * that are known to be page blobs. * - * @return - * A {@link PageBlobAsyncClient} to this resource. + * @return A {@link PageBlobAsyncClient} to this resource. */ public PageBlobAsyncClient asPageBlobAsyncClient() { - return new PageBlobAsyncClient(new AzureBlobStorageBuilder().url(getBlobUrl().toString()).pipeline(blobAsyncRawClient.azureBlobStorage.httpPipeline()), blobAsyncRawClient.snapshot); + return new PageBlobAsyncClient(new AzureBlobStorageBuilder() + .url(getBlobUrl().toString()) + .pipeline(azureBlobStorage.httpPipeline()), snapshot); } /** - * Initializes a {@link ContainerAsyncClient} object pointing to the container this blob is in. This method does - * not create a container. It simply constructs the URL to the container and offers access to methods relevant to + * Initializes a {@link ContainerAsyncClient} object pointing to the container this blob is in. This method does not + * create a container. It simply constructs the URL to the container and offers access to methods relevant to * containers. * * @return A {@link ContainerAsyncClient} object pointing to the container containing the blob @@ -122,31 +136,31 @@ public ContainerAsyncClient getContainerAsyncClient() { BlobURLParts parts = URLParser.parse(getBlobUrl()); return new ContainerAsyncClient(new AzureBlobStorageBuilder() .url(String.format("%s://%s/%s", parts.scheme(), parts.host(), parts.containerName())) - .pipeline(blobAsyncRawClient.azureBlobStorage.httpPipeline())); + .pipeline(azureBlobStorage.httpPipeline())); } /** * Gets the URL of the blob represented by this client. + * * @return the URL. * @throws RuntimeException If the blob is using a malformed URL. */ public URL getBlobUrl() { try { - UrlBuilder urlBuilder = UrlBuilder.parse(blobAsyncRawClient.azureBlobStorage.url()); - if (blobAsyncRawClient.snapshot != null) { - urlBuilder.query("snapshot=" + blobAsyncRawClient.snapshot); + UrlBuilder urlBuilder = UrlBuilder.parse(azureBlobStorage.url()); + if (snapshot != null) { + urlBuilder.query("snapshot=" + snapshot); } return urlBuilder.toURL(); } catch (MalformedURLException e) { - throw new RuntimeException(String.format("Invalid URL on %s: %s" + getClass().getSimpleName(), blobAsyncRawClient.azureBlobStorage.url()), e); + throw new RuntimeException(String.format("Invalid URL on %s: %s" + getClass().getSimpleName(), azureBlobStorage.url()), e); } } /** * Gets if the blob this client represents exists in the cloud. * - * @return - * true if the blob exists, false if it doesn't + * @return true if the blob exists, false if it doesn't */ public Mono> exists() { return this.getProperties() @@ -161,11 +175,8 @@ public Mono> exists() { * Copies the data at the source URL to a blob. For more information, see the * Azure Docs * - * @param sourceURL - * The source URL to copy from. URLs outside of Azure may only be copied to block blobs. - * - * @return - * A reactive response containing the copy ID for the long running operation. + * @param sourceURL The source URL to copy from. URLs outside of Azure may only be copied to block blobs. + * @return A reactive response containing the copy ID for the long running operation. */ public Mono> startCopyFromURL(URL sourceURL) { return this.startCopyFromURL(sourceURL, null, null, null); @@ -175,37 +186,40 @@ public Mono> startCopyFromURL(URL sourceURL) { * Copies the data at the source URL to a blob. For more information, see the * Azure Docs * - * @param sourceURL - * The source URL to copy from. URLs outside of Azure may only be copied to block blobs. - * @param metadata - * {@link Metadata} - * @param sourceModifiedAccessConditions - * {@link ModifiedAccessConditions} against the source. Standard HTTP Access conditions related to the - * modification of data. ETag and LastModifiedTime are used to construct conditions related to when the blob - * was changed relative to the given request. The request will fail if the specified condition is not - * satisfied. - * @param destAccessConditions - * {@link BlobAccessConditions} against the destination. - * - * @return - * A reactive response containing the copy ID for the long running operation. - */ - public Mono> startCopyFromURL(URL sourceURL, Metadata metadata, - ModifiedAccessConditions sourceModifiedAccessConditions, BlobAccessConditions destAccessConditions) { - return blobAsyncRawClient - .startCopyFromURL(sourceURL, metadata, sourceModifiedAccessConditions, destAccessConditions) + * @param sourceURL The source URL to copy from. URLs outside of Azure may only be copied to block blobs. + * @param metadata {@link Metadata} + * @param sourceModifiedAccessConditions {@link ModifiedAccessConditions} against the source. Standard HTTP Access + * conditions related to the modification of data. ETag and LastModifiedTime are used to construct conditions + * related to when the blob was changed relative to the given request. The request will fail if the specified + * condition is not satisfied. + * @param destAccessConditions {@link BlobAccessConditions} against the destination. + * @return A reactive response containing the copy ID for the long running operation. + */ + public Mono> startCopyFromURL(URL sourceURL, Metadata metadata, ModifiedAccessConditions sourceModifiedAccessConditions, BlobAccessConditions destAccessConditions) { + metadata = metadata == null ? new Metadata() : metadata; + sourceModifiedAccessConditions = sourceModifiedAccessConditions == null + ? new ModifiedAccessConditions() : sourceModifiedAccessConditions; + destAccessConditions = destAccessConditions == null ? new BlobAccessConditions() : destAccessConditions; + + // We want to hide the SourceAccessConditions type from the user for consistency's sake, so we convert here. + SourceModifiedAccessConditions sourceConditions = new SourceModifiedAccessConditions() + .sourceIfModifiedSince(sourceModifiedAccessConditions.ifModifiedSince()) + .sourceIfUnmodifiedSince(sourceModifiedAccessConditions.ifUnmodifiedSince()) + .sourceIfMatch(sourceModifiedAccessConditions.ifMatch()) + .sourceIfNoneMatch(sourceModifiedAccessConditions.ifNoneMatch()); + + return postProcessResponse(this.azureBlobStorage.blobs().startCopyFromURLWithRestResponseAsync( + null, null, sourceURL, null, metadata, null, sourceConditions, + destAccessConditions.modifiedAccessConditions(), destAccessConditions.leaseAccessConditions(), Context.NONE)) .map(rb -> new SimpleResponse<>(rb, rb.deserializedHeaders().copyId())); } /** * Stops a pending copy that was previously started and leaves a destination blob with 0 length and metadata. * - * @param copyId - * The id of the copy operation to abort. Returned as the {@code copyId} field on the {@link - * BlobStartCopyFromURLHeaders} object. - * - * @return - * A reactive response signalling completion. + * @param copyId The id of the copy operation to abort. Returned as the {@code copyId} field on the {@link + * BlobStartCopyFromURLHeaders} object. + * @return A reactive response signalling completion. */ public Mono abortCopyFromURL(String copyId) { return this.abortCopyFromURL(copyId, null); @@ -214,30 +228,23 @@ public Mono abortCopyFromURL(String copyId) { /** * Stops a pending copy that was previously started and leaves a destination blob with 0 length and metadata. * - * @param copyId - * The id of the copy operation to abort. Returned as the {@code copyId} field on the {@link - * BlobStartCopyFromURLHeaders} object. - * @param leaseAccessConditions - * By setting lease access conditions, requests will fail if the provided lease does not match the active - * lease on the blob. - * - * @return - * A reactive response signalling completion. + * @param copyId The id of the copy operation to abort. Returned as the {@code copyId} field on the {@link + * BlobStartCopyFromURLHeaders} object. + * @param leaseAccessConditions By setting lease access conditions, requests will fail if the provided lease does + * not match the active lease on the blob. + * @return A reactive response signalling completion. */ public Mono abortCopyFromURL(String copyId, LeaseAccessConditions leaseAccessConditions) { - return blobAsyncRawClient - .abortCopyFromURL(copyId, leaseAccessConditions) + return postProcessResponse(this.azureBlobStorage.blobs().abortCopyFromURLWithRestResponseAsync( + null, null, copyId, null, null, leaseAccessConditions, Context.NONE)) .map(VoidResponse::new); } /** * Copies the data at the source URL to a blob and waits for the copy to complete before returning a response. * - * @param copySource - * The source URL to copy from. - * - * @return - * A reactive response containing the copy ID for the long running operation. + * @param copySource The source URL to copy from. + * @return A reactive response containing the copy ID for the long running operation. */ public Mono> copyFromURL(URL copySource) { return this.copyFromURL(copySource, null, null, null); @@ -246,63 +253,108 @@ public Mono> copyFromURL(URL copySource) { /** * Copies the data at the source URL to a blob and waits for the copy to complete before returning a response. * - * @param copySource - * The source URL to copy from. URLs outside of Azure may only be copied to block blobs. - * @param metadata - * {@link Metadata} - * @param sourceModifiedAccessConditions - * {@link ModifiedAccessConditions} against the source. Standard HTTP Access conditions related to the - * modification of data. ETag and LastModifiedTime are used to construct conditions related to when the blob - * was changed relative to the given request. The request will fail if the specified condition is not - * satisfied. - * @param destAccessConditions - * {@link BlobAccessConditions} against the destination. - * - * @return - * A reactive response containing the copy ID for the long running operation. - */ - public Mono> copyFromURL(URL copySource, Metadata metadata, - ModifiedAccessConditions sourceModifiedAccessConditions, BlobAccessConditions destAccessConditions) { - return blobAsyncRawClient - .syncCopyFromURL(copySource, metadata, sourceModifiedAccessConditions, destAccessConditions) + * @param copySource The source URL to copy from. URLs outside of Azure may only be copied to block blobs. + * @param metadata {@link Metadata} + * @param sourceModifiedAccessConditions {@link ModifiedAccessConditions} against the source. Standard HTTP Access + * conditions related to the modification of data. ETag and LastModifiedTime are used to construct conditions + * related to when the blob was changed relative to the given request. The request will fail if the specified + * condition is not satisfied. + * @param destAccessConditions {@link BlobAccessConditions} against the destination. + * @return A reactive response containing the copy ID for the long running operation. + */ + public Mono> copyFromURL(URL copySource, Metadata metadata, ModifiedAccessConditions sourceModifiedAccessConditions, BlobAccessConditions destAccessConditions) { + metadata = metadata == null ? new Metadata() : metadata; + sourceModifiedAccessConditions = sourceModifiedAccessConditions == null + ? new ModifiedAccessConditions() : sourceModifiedAccessConditions; + destAccessConditions = destAccessConditions == null ? new BlobAccessConditions() : destAccessConditions; + + // We want to hide the SourceAccessConditions type from the user for consistency's sake, so we convert here. + SourceModifiedAccessConditions sourceConditions = new SourceModifiedAccessConditions() + .sourceIfModifiedSince(sourceModifiedAccessConditions.ifModifiedSince()) + .sourceIfUnmodifiedSince(sourceModifiedAccessConditions.ifUnmodifiedSince()) + .sourceIfMatch(sourceModifiedAccessConditions.ifMatch()) + .sourceIfNoneMatch(sourceModifiedAccessConditions.ifNoneMatch()); + + return postProcessResponse(this.azureBlobStorage.blobs().copyFromURLWithRestResponseAsync( + null, null, copySource, null, metadata, null, sourceConditions, + destAccessConditions.modifiedAccessConditions(), destAccessConditions.leaseAccessConditions(), Context.NONE)) .map(rb -> new SimpleResponse<>(rb, rb.deserializedHeaders().copyId())); } /** - * Reads the entire blob. Uploading data must be done from the {@link BlockBlobClient}, {@link PageBlobClient}, or {@link AppendBlobClient}. + * Reads the entire blob. Uploading data must be done from the {@link BlockBlobClient}, {@link PageBlobClient}, or + * {@link AppendBlobClient}. * - * @return - * A reactive response containing the blob data. + * @return A reactive response containing the blob data. */ public Mono>> download() { return this.download(null, null, false, null); } /** - * Reads a range of bytes from a blob. Uploading data must be done from the {@link BlockBlobClient}, {@link PageBlobClient}, or {@link AppendBlobClient}. + * Reads a range of bytes from a blob. Uploading data must be done from the {@link BlockBlobClient}, {@link + * PageBlobClient}, or {@link AppendBlobClient}. * - * @param range - * {@link BlobRange} - * @param accessConditions - * {@link BlobAccessConditions} - * @param rangeGetContentMD5 - * Whether the contentMD5 for the specified blob range should be returned. + * @param range {@link BlobRange} + * @param accessConditions {@link BlobAccessConditions} + * @param rangeGetContentMD5 Whether the contentMD5 for the specified blob range should be returned. * @param options {@link ReliableDownloadOptions} - * * @return A reactive response containing the blob data. */ - public Mono>> download(BlobRange range, BlobAccessConditions accessConditions, - boolean rangeGetContentMD5, ReliableDownloadOptions options) { - return blobAsyncRawClient - .download(range, accessConditions, rangeGetContentMD5) + public Mono>> download(BlobRange range, BlobAccessConditions accessConditions, boolean rangeGetContentMD5, ReliableDownloadOptions options) { + return this.download(range, accessConditions, rangeGetContentMD5) .map(response -> new SimpleResponse<>( response.rawResponse(), response.body(options).map(ByteBuf::nioBuffer).switchIfEmpty(Flux.just(ByteBuffer.allocate(0))))); } + /** + * Reads a range of bytes from a blob. The response also includes the blob's properties and metadata. For more + * information, see the Azure Docs. + *

+ * Note that the response body has reliable download functionality built in, meaning that a failed download stream + * will be automatically retried. This behavior may be configured with {@link ReliableDownloadOptions}. + * + * @param range {@link BlobRange} + * @param accessConditions {@link BlobAccessConditions} + * @param rangeGetContentMD5 Whether the contentMD5 for the specified blob range should be returned. + * @return Emits the successful response. + * @apiNote ## Sample Code \n [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=upload_download + * "Sample code for BlobAsyncClient.download")] \n For more samples, please see the [Samples + * file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) + */ + Mono download(BlobRange range, BlobAccessConditions accessConditions, boolean rangeGetContentMD5) { + range = range == null ? new BlobRange(0) : range; + Boolean getMD5 = rangeGetContentMD5 ? rangeGetContentMD5 : null; + accessConditions = accessConditions == null ? new BlobAccessConditions() : accessConditions; + HTTPGetterInfo info = new HTTPGetterInfo() + .offset(range.offset()) + .count(range.count()) + .eTag(accessConditions.modifiedAccessConditions().ifMatch()); + + // TODO: range is BlobRange but expected as String + // TODO: figure out correct response + return postProcessResponse(this.azureBlobStorage.blobs().downloadWithRestResponseAsync( + null, null, snapshot, null, null, range.toHeaderValue(), getMD5, + null, null, null, null, + accessConditions.leaseAccessConditions(), accessConditions.modifiedAccessConditions(), Context.NONE)) + // Convert the autorest response to a DownloadAsyncResponse, which enable reliable download. + .map(response -> { + // If there wasn't an etag originally specified, lock on the one returned. + info.eTag(response.deserializedHeaders().eTag()); + return new DownloadAsyncResponse(response, info, + // In the event of a stream failure, make a new request to pick up where we left off. + newInfo -> + this.download(new BlobRange(newInfo.offset(), newInfo.count()), + new BlobAccessConditions().modifiedAccessConditions( + new ModifiedAccessConditions().ifMatch(info.eTag())), false)); + }); + } + /** * Downloads the entire blob into a file specified by the path. The file will be created if it doesn't exist. - * Uploading data must be done from the {@link BlockBlobClient}, {@link PageBlobClient}, or {@link AppendBlobClient}. + * Uploading data must be done from the {@link BlockBlobClient}, {@link PageBlobClient}, or {@link + * AppendBlobClient}. *

* This method makes an extra HTTP call to get the length of the blob in the beginning. To avoid this extra call, * use the other overload providing the {@link BlobRange} parameter. @@ -316,21 +368,17 @@ public Mono downloadToFile(String filePath) { /** * Downloads a range of bytes blob into a file specified by the path. The file will be created if it doesn't exist. - * Uploading data must be done from the {@link BlockBlobClient}, {@link PageBlobClient}, or {@link AppendBlobClient}. + * Uploading data must be done from the {@link BlockBlobClient}, {@link PageBlobClient}, or {@link + * AppendBlobClient}. *

* This method makes an extra HTTP call to get the length of the blob in the beginning. To avoid this extra call, * provide the {@link BlobRange} parameter. * - * @param filePath - * A non-null {@link OutputStream} instance where the downloaded data will be written. - * @param range - * {@link BlobRange} - * @param blockSize - * the size of a chunk to download at a time, in bytes - * @param accessConditions - * {@link BlobAccessConditions} - * @param rangeGetContentMD5 - * Whether the contentMD5 for the specified blob range should be returned. + * @param filePath A non-null {@link OutputStream} instance where the downloaded data will be written. + * @param range {@link BlobRange} + * @param blockSize the size of a chunk to download at a time, in bytes + * @param accessConditions {@link BlobAccessConditions} + * @param rangeGetContentMD5 Whether the contentMD5 for the specified blob range should be returned. * @param options {@link ReliableDownloadOptions} * @return An empty response * @throws IllegalArgumentException If {@code blockSize} is less than 0 or greater than 100MB. @@ -346,8 +394,7 @@ public Mono downloadToFile(String filePath, BlobRange range, Integer block channel -> Mono.justOrEmpty(range) .switchIfEmpty(getFullBlobRange(accessConditions)) .flatMapMany(rg -> Flux.fromIterable(sliceBlobRange(rg, blockSize))) - .flatMap(chunk -> blobAsyncRawClient - .download(chunk, accessConditions, rangeGetContentMD5) + .flatMap(chunk -> this.download(chunk, accessConditions, rangeGetContentMD5) .subscribeOn(Schedulers.elastic()) .flatMap(dar -> FluxUtil.bytebufStreamToFile(dar.body(options), channel, chunk.offset() - (range == null ? 0 : range.offset())))) .then(), this::downloadToFileCleanup); @@ -393,8 +440,7 @@ private List sliceBlobRange(BlobRange blobRange, Integer blockSize) { /** * Deletes the specified blob or snapshot. Note that deleting a blob also deletes all its snapshots. * - * @return - * A reactive response signalling completion. + * @return A reactive response signalling completion. */ public Mono delete() { return this.delete(null, null); @@ -403,28 +449,26 @@ public Mono delete() { /** * Deletes the specified blob or snapshot. Note that deleting a blob also deletes all its snapshots. * - * @param deleteBlobSnapshotOptions - * Specifies the behavior for deleting the snapshots on this blob. {@code Include} will delete the base blob - * and all snapshots. {@code Only} will delete only the snapshots. If a snapshot is being deleted, you must - * pass null. - * @param accessConditions - * {@link BlobAccessConditions} - * - * @return - * A reactive response signalling completion. + * @param deleteBlobSnapshotOptions Specifies the behavior for deleting the snapshots on this blob. {@code Include} + * will delete the base blob and all snapshots. {@code Only} will delete only the snapshots. If a snapshot is being + * deleted, you must pass null. + * @param accessConditions {@link BlobAccessConditions} + * @return A reactive response signalling completion. */ - public Mono delete(DeleteSnapshotsOptionType deleteBlobSnapshotOptions, - BlobAccessConditions accessConditions) { - return blobAsyncRawClient - .delete(deleteBlobSnapshotOptions, accessConditions) + public Mono delete(DeleteSnapshotsOptionType deleteBlobSnapshotOptions, BlobAccessConditions accessConditions) { + accessConditions = accessConditions == null ? new BlobAccessConditions() : accessConditions; + + return postProcessResponse(this.azureBlobStorage.blobs().deleteWithRestResponseAsync( + null, null, snapshot, null, null, deleteBlobSnapshotOptions, + null, accessConditions.leaseAccessConditions(), accessConditions.modifiedAccessConditions(), + Context.NONE)) .map(VoidResponse::new); } /** * Returns the blob's metadata and properties. * - * @return - * A reactive response containing the blob properties and metadata. + * @return A reactive response containing the blob properties and metadata. */ public Mono> getProperties() { return this.getProperties(null); @@ -433,93 +477,87 @@ public Mono> getProperties() { /** * Returns the blob's metadata and properties. * - * @param accessConditions - * {@link BlobAccessConditions} - * - * @return - * A reactive response containing the blob properties and metadata. + * @param accessConditions {@link BlobAccessConditions} + * @return A reactive response containing the blob properties and metadata. */ public Mono> getProperties(BlobAccessConditions accessConditions) { - return blobAsyncRawClient - .getProperties(accessConditions) + accessConditions = accessConditions == null ? new BlobAccessConditions() : accessConditions; + + return postProcessResponse(this.azureBlobStorage.blobs().getPropertiesWithRestResponseAsync( + null, null, snapshot, null, null, null, + null, null, null, accessConditions.leaseAccessConditions(), + accessConditions.modifiedAccessConditions(), Context.NONE)) .map(rb -> new SimpleResponse<>(rb, new BlobProperties(rb.deserializedHeaders()))); } /** - * Changes a blob's HTTP header properties. if only one HTTP header is updated, the - * others will all be erased. In order to preserve existing values, they must be - * passed alongside the header being changed. For more information, see the + * Changes a blob's HTTP header properties. if only one HTTP header is updated, the others will all be erased. In + * order to preserve existing values, they must be passed alongside the header being changed. For more information, + * see the * Azure Docs. * - * @param headers - * {@link BlobHTTPHeaders} - * - * @return - * A reactive response signalling completion. + * @param headers {@link BlobHTTPHeaders} + * @return A reactive response signalling completion. */ public Mono setHTTPHeaders(BlobHTTPHeaders headers) { return this.setHTTPHeaders(headers, null); } /** - * Changes a blob's HTTP header properties. if only one HTTP header is updated, the - * others will all be erased. In order to preserve existing values, they must be - * passed alongside the header being changed. For more information, see the + * Changes a blob's HTTP header properties. if only one HTTP header is updated, the others will all be erased. In + * order to preserve existing values, they must be passed alongside the header being changed. For more information, + * see the * Azure Docs. * - * @param headers - * {@link BlobHTTPHeaders} - * @param accessConditions - * {@link BlobAccessConditions} - * - * @return - * A reactive response signalling completion. + * @param headers {@link BlobHTTPHeaders} + * @param accessConditions {@link BlobAccessConditions} + * @return A reactive response signalling completion. */ public Mono setHTTPHeaders(BlobHTTPHeaders headers, BlobAccessConditions accessConditions) { - return blobAsyncRawClient - .setHTTPHeaders(headers, accessConditions) + accessConditions = accessConditions == null ? new BlobAccessConditions() : accessConditions; + + return postProcessResponse(this.azureBlobStorage.blobs().setHTTPHeadersWithRestResponseAsync( + null, null, null, null, headers, + accessConditions.leaseAccessConditions(), accessConditions.modifiedAccessConditions(), Context.NONE)) .map(VoidResponse::new); } /** - * Changes a blob's metadata. The specified metadata in this method will replace existing - * metadata. If old values must be preserved, they must be downloaded and included in the - * call to this method. For more information, see the Azure Docs. - * - * @param metadata - * {@link Metadata} + * Changes a blob's metadata. The specified metadata in this method will replace existing metadata. If old values + * must be preserved, they must be downloaded and included in the call to this method. For more information, see the + * Azure Docs. * - * @return - * A reactive response signalling completion. + * @param metadata {@link Metadata} + * @return A reactive response signalling completion. */ public Mono setMetadata(Metadata metadata) { return this.setMetadata(metadata, null); } /** - * Changes a blob's metadata. The specified metadata in this method will replace existing - * metadata. If old values must be preserved, they must be downloaded and included in the - * call to this method. For more information, see the Azure Docs. - * - * @param metadata - * {@link Metadata} - * @param accessConditions - * {@link BlobAccessConditions} + * Changes a blob's metadata. The specified metadata in this method will replace existing metadata. If old values + * must be preserved, they must be downloaded and included in the call to this method. For more information, see the + * Azure Docs. * - * @return - * A reactive response signalling completion. + * @param metadata {@link Metadata} + * @param accessConditions {@link BlobAccessConditions} + * @return A reactive response signalling completion. */ public Mono setMetadata(Metadata metadata, BlobAccessConditions accessConditions) { - return blobAsyncRawClient - .setMetadata(metadata, accessConditions) + metadata = metadata == null ? new Metadata() : metadata; + accessConditions = accessConditions == null ? new BlobAccessConditions() : accessConditions; + + return postProcessResponse(this.azureBlobStorage.blobs().setMetadataWithRestResponseAsync( + null, null, null, metadata, null, null, + null, null, accessConditions.leaseAccessConditions(), + accessConditions.modifiedAccessConditions(), Context.NONE)) .map(VoidResponse::new); } /** * Creates a read-only snapshot of a blob. * - * @return - * A reactive response containing the ID of the new snapshot. + * @return A reactive response containing the ID of the new snapshot. */ public Mono> createSnapshot() { return this.createSnapshot(null, null); @@ -528,30 +566,29 @@ public Mono> createSnapshot() { /** * Creates a read-only snapshot of a blob. * - * @param metadata - * {@link Metadata} - * @param accessConditions - * {@link BlobAccessConditions} - * - * @return - * A reactive response containing the ID of the new snapshot. + * @param metadata {@link Metadata} + * @param accessConditions {@link BlobAccessConditions} + * @return A reactive response containing the ID of the new snapshot. */ public Mono> createSnapshot(Metadata metadata, BlobAccessConditions accessConditions) { - return blobAsyncRawClient - .createSnapshot(metadata, accessConditions) + metadata = metadata == null ? new Metadata() : metadata; + accessConditions = accessConditions == null ? new BlobAccessConditions() : accessConditions; + + return postProcessResponse(this.azureBlobStorage.blobs().createSnapshotWithRestResponseAsync( + null, null, null, metadata, null, null, + null, null, accessConditions.modifiedAccessConditions(), + accessConditions.leaseAccessConditions(), Context.NONE)) .map(rb -> new SimpleResponse<>(rb, rb.deserializedHeaders().snapshot())); } /** * Sets the tier on a blob. The operation is allowed on a page blob in a premium storage account or a block blob in * a blob storage or GPV2 account. A premium page blob's tier determines the allowed size, IOPS, and bandwidth of - * the blob. A block blob's tier determines the Hot/Cool/Archive storage type. This does not update the blob's etag. + * the blob. A block blob's tier determines the Hot/Cool/Archive storage type. This does not update the blob's + * etag. * - * @param tier - * The new tier for the blob. - * - * @return - * A reactive response signalling completion. + * @param tier The new tier for the blob. + * @return A reactive response signalling completion. */ public Mono setTier(AccessTier tier) { return this.setTier(tier, null); @@ -560,32 +597,30 @@ public Mono setTier(AccessTier tier) { /** * Sets the tier on a blob. The operation is allowed on a page blob in a premium storage account or a block blob in * a blob storage or GPV2 account. A premium page blob's tier determines the allowed size, IOPS, and bandwidth of - * the blob. A block blob's tier determines the Hot/Cool/Archive storage type. This does not update the blob's etag. - * - * @param tier - * The new tier for the blob. - * @param leaseAccessConditions - * By setting lease access conditions, requests will fail if the provided lease does not match the active - * lease on the blob. + * the blob. A block blob's tier determines the Hot/Cool/Archive storage type. This does not update the blob's + * etag. * - * @return - * A reactive response signalling completion. + * @param tier The new tier for the blob. + * @param leaseAccessConditions By setting lease access conditions, requests will fail if the provided lease does + * not match the active lease on the blob. + * @return A reactive response signalling completion. */ public Mono setTier(AccessTier tier, LeaseAccessConditions leaseAccessConditions) { - return blobAsyncRawClient - .setTier(tier, leaseAccessConditions) + Utility.assertNotNull("tier", tier); + + return postProcessResponse(this.azureBlobStorage.blobs().setTierWithRestResponseAsync( + null, null, tier, null, null, leaseAccessConditions, Context.NONE)) .map(VoidResponse::new); } /** * Undelete restores the content and metadata of a soft-deleted blob and/or any associated soft-deleted snapshots. * - * @return - * A reactive response signalling completion. + * @return A reactive response signalling completion. */ public Mono undelete() { - return blobAsyncRawClient - .undelete() + return postProcessResponse(this.azureBlobStorage.blobs().undeleteWithRestResponseAsync(null, + null, Context.NONE)) .map(VoidResponse::new); } @@ -593,14 +628,10 @@ public Mono undelete() { * Acquires a lease on the blob for write and delete operations. The lease duration must be between 15 to 60 * seconds, or infinite (-1). * - * @param proposedId - * A {@code String} in any valid GUID format. May be null. - * @param duration - * The duration of the lease, in seconds, or negative one (-1) for a lease that - * never expires. A non-infinite lease can be between 15 and 60 seconds. - * - * @return - * A reactive response containing the lease ID. + * @param proposedId A {@code String} in any valid GUID format. May be null. + * @param duration The duration of the lease, in seconds, or negative one (-1) for a lease that never expires. A + * non-infinite lease can be between 15 and 60 seconds. + * @return A reactive response containing the lease ID. */ public Mono> acquireLease(String proposedId, int duration) { return this.acquireLease(proposedId, duration, null); @@ -610,33 +641,33 @@ public Mono> acquireLease(String proposedId, int duration) { * Acquires a lease on the blob for write and delete operations. The lease duration must be between 15 to 60 * seconds, or infinite (-1). * - * @param proposedID - * A {@code String} in any valid GUID format. May be null. - * @param duration - * The duration of the lease, in seconds, or negative one (-1) for a lease that - * never expires. A non-infinite lease can be between 15 and 60 seconds. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * - * @return - * A reactive response containing the lease ID. + * @param proposedID A {@code String} in any valid GUID format. May be null. + * @param duration The duration of the lease, in seconds, or negative one (-1) for a lease that never expires. A + * non-infinite lease can be between 15 and 60 seconds. + * @param modifiedAccessConditions Standard HTTP Access conditions related to the modification of data. ETag and + * LastModifiedTime are used to construct conditions related to when the blob was changed relative to the given + * request. The request will fail if the specified condition is not satisfied. + * @return A reactive response containing the lease ID. + * @throws IllegalArgumentException If {@code duration} is outside the bounds of 15 to 60 or isn't -1. */ public Mono> acquireLease(String proposedID, int duration, ModifiedAccessConditions modifiedAccessConditions) { - return blobAsyncRawClient - .acquireLease(proposedID, duration, modifiedAccessConditions) + if (!(duration == -1 || (duration >= 15 && duration <= 60))) { + // Throwing is preferred to Mono.error because this will error out immediately instead of waiting until + // subscription. + throw new IllegalArgumentException("Duration must be -1 or between 15 and 60."); + } + + return postProcessResponse(this.azureBlobStorage.blobs().acquireLeaseWithRestResponseAsync( + null, null, null, duration, proposedID, null, + modifiedAccessConditions, Context.NONE)) .map(rb -> new SimpleResponse<>(rb, rb.deserializedHeaders().leaseId())); } /** * Renews the blob's previously-acquired lease. * - * @param leaseID - * The leaseId of the active lease on the blob. - * - * @return - * A reactive response containing the renewed lease ID. + * @param leaseID The leaseId of the active lease on the blob. + * @return A reactive response containing the renewed lease ID. */ public Mono> renewLease(String leaseID) { return this.renewLease(leaseID, null); @@ -645,30 +676,23 @@ public Mono> renewLease(String leaseID) { /** * Renews the blob's previously-acquired lease. * - * @param leaseID - * The leaseId of the active lease on the blob. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * - * @return - * A reactive response containing the renewed lease ID. + * @param leaseID The leaseId of the active lease on the blob. + * @param modifiedAccessConditions Standard HTTP Access conditions related to the modification of data. ETag and + * LastModifiedTime are used to construct conditions related to when the blob was changed relative to the given + * request. The request will fail if the specified condition is not satisfied. + * @return A reactive response containing the renewed lease ID. */ public Mono> renewLease(String leaseID, ModifiedAccessConditions modifiedAccessConditions) { - return blobAsyncRawClient - .renewLease(leaseID, modifiedAccessConditions) + return postProcessResponse(this.azureBlobStorage.blobs().renewLeaseWithRestResponseAsync(null, + null, leaseID, null, null, modifiedAccessConditions, Context.NONE)) .map(rb -> new SimpleResponse<>(rb, rb.deserializedHeaders().leaseId())); } /** * Releases the blob's previously-acquired lease. * - * @param leaseID - * The leaseId of the active lease on the blob. - * - * @return - * A reactive response signalling completion. + * @param leaseID The leaseId of the active lease on the blob. + * @return A reactive response signalling completion. */ public Mono releaseLease(String leaseID) { return this.releaseLease(leaseID, null); @@ -677,19 +701,15 @@ public Mono releaseLease(String leaseID) { /** * Releases the blob's previously-acquired lease. * - * @param leaseID - * The leaseId of the active lease on the blob. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * - * @return - * A reactive response signalling completion. + * @param leaseID The leaseId of the active lease on the blob. + * @param modifiedAccessConditions Standard HTTP Access conditions related to the modification of data. ETag and + * LastModifiedTime are used to construct conditions related to when the blob was changed relative to the given + * request. The request will fail if the specified condition is not satisfied. + * @return A reactive response signalling completion. */ public Mono releaseLease(String leaseID, ModifiedAccessConditions modifiedAccessConditions) { - return blobAsyncRawClient - .releaseLease(leaseID, modifiedAccessConditions) + return postProcessResponse(this.azureBlobStorage.blobs().releaseLeaseWithRestResponseAsync(null, + null, leaseID, null, null, modifiedAccessConditions, Context.NONE)) .map(VoidResponse::new); } @@ -697,8 +717,7 @@ public Mono releaseLease(String leaseID, ModifiedAccessConditions * BreakLease breaks the blob's previously-acquired lease (if it exists). Pass the LeaseBreakDefault (-1) constant * to break a fixed-duration lease when it expires or an infinite lease immediately. * - * @return - * A reactive response containing the remaining time in the broken lease in seconds. + * @return A reactive response containing the remaining time in the broken lease in seconds. */ public Mono> breakLease() { return this.breakLease(null, null); @@ -708,70 +727,253 @@ public Mono> breakLease() { * BreakLease breaks the blob's previously-acquired lease (if it exists). Pass the LeaseBreakDefault (-1) constant * to break a fixed-duration lease when it expires or an infinite lease immediately. * - * @param breakPeriodInSeconds - * An optional {@code Integer} representing the proposed duration of seconds that the lease should continue - * before it is broken, between 0 and 60 seconds. This break period is only used if it is shorter than the - * time remaining on the lease. If longer, the time remaining on the lease is used. A new lease will not be - * available before the break period has expired, but the lease may be held for longer than the break - * period. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * - * @return - * A reactive response containing the remaining time in the broken lease in seconds. + * @param breakPeriodInSeconds An optional {@code Integer} representing the proposed duration of seconds that the + * lease should continue before it is broken, between 0 and 60 seconds. This break period is only used if it is + * shorter than the time remaining on the lease. If longer, the time remaining on the lease is used. A new lease + * will not be available before the break period has expired, but the lease may be held for longer than the break + * period. + * @param modifiedAccessConditions Standard HTTP Access conditions related to the modification of data. ETag and + * LastModifiedTime are used to construct conditions related to when the blob was changed relative to the given + * request. The request will fail if the specified condition is not satisfied. + * @return A reactive response containing the remaining time in the broken lease in seconds. */ public Mono> breakLease(Integer breakPeriodInSeconds, ModifiedAccessConditions modifiedAccessConditions) { - return blobAsyncRawClient - .breakLease(breakPeriodInSeconds, modifiedAccessConditions) + return postProcessResponse(this.azureBlobStorage.blobs().breakLeaseWithRestResponseAsync(null, + null, null, breakPeriodInSeconds, null, modifiedAccessConditions, Context.NONE)) .map(rb -> new SimpleResponse<>(rb, rb.deserializedHeaders().leaseTime())); } /** * ChangeLease changes the blob's lease ID. * - * @param leaseId - * The leaseId of the active lease on the blob. - * @param proposedID - * A {@code String} in any valid GUID format. - * - * @return - * A reactive response containing the new lease ID. + * @param leaseId The leaseId of the active lease on the blob. + * @param proposedID A {@code String} in any valid GUID format. + * @return A reactive response containing the new lease ID. */ public Mono> changeLease(String leaseId, String proposedID) { return this.changeLease(leaseId, proposedID, null); } /** - * ChangeLease changes the blob's lease ID. For more information, see the Azure Docs. - * - * @param leaseId - * The leaseId of the active lease on the blob. - * @param proposedID - * A {@code String} in any valid GUID format. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. + * ChangeLease changes the blob's lease ID. For more information, see the Azure + * Docs. * + * @param leaseId The leaseId of the active lease on the blob. + * @param proposedID A {@code String} in any valid GUID format. + * @param modifiedAccessConditions Standard HTTP Access conditions related to the modification of data. ETag and + * LastModifiedTime are used to construct conditions related to when the blob was changed relative to the given + * request. The request will fail if the specified condition is not satisfied. * @return A reactive response containing the new lease ID. */ public Mono> changeLease(String leaseId, String proposedID, ModifiedAccessConditions modifiedAccessConditions) { - return blobAsyncRawClient - .changeLease(leaseId, proposedID, modifiedAccessConditions) + return postProcessResponse(this.azureBlobStorage.blobs().changeLeaseWithRestResponseAsync(null, + null, leaseId, proposedID, null, null, modifiedAccessConditions, Context.NONE)) .map(rb -> new SimpleResponse<>(rb, rb.deserializedHeaders().leaseId())); } /** - * Returns the sku name and account kind for the account. For more information, please see the Azure Docs. + * Returns the sku name and account kind for the account. For more information, please see the Azure Docs. * * @return a reactor response containing the sku name and account kind. */ // TODO (unknown): determine this return type public Mono> getAccountInfo() { - return blobAsyncRawClient - .getAccountInfo() + return postProcessResponse( + this.azureBlobStorage.blobs().getAccountInfoWithRestResponseAsync(null, null, Context.NONE)) .map(rb -> new SimpleResponse<>(rb, new StorageAccountInfo(rb.deserializedHeaders()))); } + + /** + * Generates a user delegation SAS with the specified parameters + * + * @param userDelegationKey The {@code UserDelegationKey} user delegation key for the SAS + * @param accountName The {@code String} account name for the SAS + * @param permissions The {@code ContainerSASPermissions} permission for the SAS + * @param expiryTime The {@code OffsetDateTime} expiry time for the SAS + * @return A string that represents the SAS token + */ + public String generateUserDelegationSAS(UserDelegationKey userDelegationKey, String accountName, + BlobSASPermission permissions, OffsetDateTime expiryTime) { + return this.generateUserDelegationSAS(userDelegationKey, accountName, permissions, expiryTime, null /* + startTime */, null /* version */, null /*sasProtocol */, null /* ipRange */, null /* cacheControl */, null + /*contentDisposition */, null /* contentEncoding */, null /* contentLanguage */, null /* contentType */); + } + + /** + * Generates a user delegation SAS token with the specified parameters + * + * @param userDelegationKey The {@code UserDelegationKey} user delegation key for the SAS + * @param accountName The {@code String} account name for the SAS + * @param permissions The {@code ContainerSASPermissions} permission for the SAS + * @param expiryTime The {@code OffsetDateTime} expiry time for the SAS + * @param startTime An optional {@code OffsetDateTime} start time for the SAS + * @param version An optional {@code String} version for the SAS + * @param sasProtocol An optional {@code SASProtocol} protocol for the SAS + * @param ipRange An optional {@code IPRange} ip address range for the SAS + * @return A string that represents the SAS token + */ + public String generateUserDelegationSAS(UserDelegationKey userDelegationKey, String accountName, + BlobSASPermission permissions, OffsetDateTime expiryTime, OffsetDateTime startTime, String version, + SASProtocol sasProtocol, IPRange ipRange) { + return this.generateUserDelegationSAS(userDelegationKey, accountName, permissions, expiryTime, startTime, + version, sasProtocol, ipRange, null /* cacheControl */, null /* contentDisposition */, null /* + contentEncoding */, null /* contentLanguage */, null /* contentType */); + } + + /** + * Generates a user delegation SAS token with the specified parameters + * + * @param userDelegationKey The {@code UserDelegationKey} user delegation key for the SAS + * @param accountName The {@code String} account name for the SAS + * @param permissions The {@code ContainerSASPermissions} permission for the SAS + * @param expiryTime The {@code OffsetDateTime} expiry time for the SAS + * @param startTime An optional {@code OffsetDateTime} start time for the SAS + * @param version An optional {@code String} version for the SAS + * @param sasProtocol An optional {@code SASProtocol} protocol for the SAS + * @param ipRange An optional {@code IPRange} ip address range for the SAS + * @param cacheControl An optional {@code String} cache-control header for the SAS. + * @param contentDisposition An optional {@code String} content-disposition header for the SAS. + * @param contentEncoding An optional {@code String} content-encoding header for the SAS. + * @param contentLanguage An optional {@code String} content-language header for the SAS. + * @param contentType An optional {@code String} content-type header for the SAS. + * @return A string that represents the SAS token + */ + public String generateUserDelegationSAS(UserDelegationKey userDelegationKey, String accountName, + BlobSASPermission permissions, OffsetDateTime expiryTime, OffsetDateTime startTime, String version, + SASProtocol sasProtocol, IPRange ipRange, String cacheControl, String contentDisposition, + String contentEncoding, String contentLanguage, String contentType) { + + ServiceSASSignatureValues serviceSASSignatureValues = new ServiceSASSignatureValues(version, sasProtocol, + startTime, expiryTime, permissions == null ? null : permissions.toString(), ipRange, null /* identifier*/, + cacheControl, contentDisposition, contentEncoding, contentLanguage, contentType); + + ServiceSASSignatureValues values = configureServiceSASSignatureValues(serviceSASSignatureValues, accountName); + + SASQueryParameters sasQueryParameters = values.generateSASQueryParameters(userDelegationKey); + + return sasQueryParameters.encode(); + } + + /** + * Generates a SAS token with the specified parameters + * + * @param permissions The {@code ContainerSASPermissions} permission for the SAS + * @param expiryTime The {@code OffsetDateTime} expiry time for the SAS + * @return A string that represents the SAS token + */ + public String generateSAS(BlobSASPermission permissions, OffsetDateTime expiryTime) { + return this.generateSAS(null, permissions, expiryTime, null /* startTime */, /* identifier */ null /* + version */, null /* sasProtocol */, null /* ipRange */, null /* cacheControl */, null /* contentLanguage*/, + null /* contentEncoding */, null /* contentLanguage */, null /* contentType */); + } + + /** + * Generates a SAS token with the specified parameters + * + * @param identifier The {@code String} name of the access policy on the container this SAS references if any + * @return A string that represents the SAS token + */ + public String generateSAS(String identifier) { + return this.generateSAS(identifier, null /* permissions */, null /* expiryTime */, null /* startTime */, + null /* version */, null /* sasProtocol */, null /* ipRange */, null /* cacheControl */, null /* + contentLanguage*/, null /* contentEncoding */, null /* contentLanguage */, null /* contentType */); + } + + /** + * Generates a SAS token with the specified parameters + * + * @param identifier The {@code String} name of the access policy on the container this SAS references if any + * @param permissions The {@code ContainerSASPermissions} permission for the SAS + * @param expiryTime The {@code OffsetDateTime} expiry time for the SAS + * @param startTime An optional {@code OffsetDateTime} start time for the SAS + * @param version An optional {@code String} version for the SAS + * @param sasProtocol An optional {@code SASProtocol} protocol for the SAS + * @param ipRange An optional {@code IPRange} ip address range for the SAS + * @return A string that represents the SAS token + */ + public String generateSAS(String identifier, BlobSASPermission permissions, OffsetDateTime expiryTime, + OffsetDateTime startTime, String version, SASProtocol sasProtocol, IPRange ipRange) { + return this.generateSAS(identifier, permissions, expiryTime, startTime, version, sasProtocol, ipRange, null + /* cacheControl */, null /* contentLanguage*/, null /* contentEncoding */, null /* contentLanguage */, + null /* contentType */); + } + + /** + * Generates a SAS token with the specified parameters + * + * @param identifier The {@code String} name of the access policy on the container this SAS references if any + * @param permissions The {@code ContainerSASPermissions} permission for the SAS + * @param expiryTime The {@code OffsetDateTime} expiry time for the SAS + * @param startTime An optional {@code OffsetDateTime} start time for the SAS + * @param version An optional {@code String} version for the SAS + * @param sasProtocol An optional {@code SASProtocol} protocol for the SAS + * @param ipRange An optional {@code IPRange} ip address range for the SAS + * @param cacheControl An optional {@code String} cache-control header for the SAS. + * @param contentDisposition An optional {@code String} content-disposition header for the SAS. + * @param contentEncoding An optional {@code String} content-encoding header for the SAS. + * @param contentLanguage An optional {@code String} content-language header for the SAS. + * @param contentType An optional {@code String} content-type header for the SAS. + * @return A string that represents the SAS token + */ + public String generateSAS(String identifier, BlobSASPermission permissions, OffsetDateTime expiryTime, + OffsetDateTime startTime, String version, SASProtocol sasProtocol, IPRange ipRange, String cacheControl, + String contentDisposition, String contentEncoding, String contentLanguage, String contentType) { + + ServiceSASSignatureValues serviceSASSignatureValues = new ServiceSASSignatureValues(version, sasProtocol, + startTime, expiryTime, permissions == null ? null : permissions.toString(), ipRange, identifier, + cacheControl, contentDisposition, contentEncoding, contentLanguage, contentType); + + SharedKeyCredential sharedKeyCredential = + Utility.getSharedKeyCredential(this.azureBlobStorage.httpPipeline()); + + Utility.assertNotNull("sharedKeyCredential", sharedKeyCredential); + + ServiceSASSignatureValues values = configureServiceSASSignatureValues(serviceSASSignatureValues, + sharedKeyCredential.accountName()); + + SASQueryParameters sasQueryParameters = values.generateSASQueryParameters(sharedKeyCredential); + + return sasQueryParameters.encode(); + } + + /** + * Sets serviceSASSignatureValues parameters dependent on the current blob type + */ + ServiceSASSignatureValues configureServiceSASSignatureValues(ServiceSASSignatureValues serviceSASSignatureValues, + String accountName) { + + // Set canonical name + serviceSASSignatureValues.canonicalName(this.azureBlobStorage.url(), accountName); + + // Set snapshotId + serviceSASSignatureValues.snapshotId(getSnapshotId()); + + // Set resource + if (isSnapshot()) { + serviceSASSignatureValues.resource(Constants.UrlConstants.SAS_BLOB_SNAPSHOT_CONSTANT); + } else { + serviceSASSignatureValues.resource(Constants.UrlConstants.SAS_BLOB_CONSTANT); + } + + return serviceSASSignatureValues; + } + + /** + * Gets the snapshotId for a blob resource + * + * @return A string that represents the snapshotId of the snapshot blob + */ + public String getSnapshotId() { + return this.snapshot; + } + + /** + * Determines if a blob is a snapshot + * + * @return A boolean that indicates if a blob is a snapshot + */ + public boolean isSnapshot() { + return this.snapshot != null; + } } diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/BlobAsyncRawClient.java b/storage/client/blob/src/main/java/com/azure/storage/blob/BlobAsyncRawClient.java deleted file mode 100644 index 002ba761a3bf8..0000000000000 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/BlobAsyncRawClient.java +++ /dev/null @@ -1,766 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.azure.storage.blob; - -import com.azure.core.util.Context; -import com.azure.storage.blob.implementation.AzureBlobStorageImpl; -import com.azure.storage.blob.models.AccessTier; -import com.azure.storage.blob.models.BlobAccessConditions; -import com.azure.storage.blob.models.BlobHTTPHeaders; -import com.azure.storage.blob.models.BlobRange; -import com.azure.storage.blob.models.BlobStartCopyFromURLHeaders; -import com.azure.storage.blob.models.BlobsAbortCopyFromURLResponse; -import com.azure.storage.blob.models.BlobsAcquireLeaseResponse; -import com.azure.storage.blob.models.BlobsBreakLeaseResponse; -import com.azure.storage.blob.models.BlobsChangeLeaseResponse; -import com.azure.storage.blob.models.BlobsCopyFromURLResponse; -import com.azure.storage.blob.models.BlobsCreateSnapshotResponse; -import com.azure.storage.blob.models.BlobsDeleteResponse; -import com.azure.storage.blob.models.BlobsGetAccountInfoResponse; -import com.azure.storage.blob.models.BlobsGetPropertiesResponse; -import com.azure.storage.blob.models.BlobsReleaseLeaseResponse; -import com.azure.storage.blob.models.BlobsRenewLeaseResponse; -import com.azure.storage.blob.models.BlobsSetHTTPHeadersResponse; -import com.azure.storage.blob.models.BlobsSetMetadataResponse; -import com.azure.storage.blob.models.BlobsSetTierResponse; -import com.azure.storage.blob.models.BlobsStartCopyFromURLResponse; -import com.azure.storage.blob.models.BlobsUndeleteResponse; -import com.azure.storage.blob.models.DeleteSnapshotsOptionType; -import com.azure.storage.blob.models.LeaseAccessConditions; -import com.azure.storage.blob.models.Metadata; -import com.azure.storage.blob.models.ModifiedAccessConditions; -import com.azure.storage.blob.models.ReliableDownloadOptions; -import com.azure.storage.blob.models.SourceModifiedAccessConditions; -import reactor.core.publisher.Mono; - -import java.net.URL; - -import static com.azure.storage.blob.Utility.postProcessResponse; - -/** - * Represents a URL to a blob of any type: block, append, or page. It may be obtained by direct construction or via the - * create method on a {@link ContainerAsyncClient} object. This class does not hold any state about a particular blob but is - * instead a convenient way of sending off appropriate requests to the resource on the service. Please refer to the - * Azure Docs for more information. - */ -class BlobAsyncRawClient { - - protected AzureBlobStorageImpl azureBlobStorage; - - final String snapshot; - - /** - * Creates a {@code BlobAsyncRawClient} object pointing to the account specified by the URL and using the provided pipeline to - * make HTTP requests.. - */ - BlobAsyncRawClient(AzureBlobStorageImpl azureBlobStorage, String snapshot) { - this.azureBlobStorage = azureBlobStorage; - this.snapshot = snapshot; - } - - /** - * Copies the data at the source URL to a blob. For more information, see the Azure Docs - * - * @param sourceURL - * The source URL to copy from. URLs outside of Azure may only be copied to block blobs. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=start_copy "Sample code for BlobAsyncRawClient.startCopyFromURL")] \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=start_copy_helper "Helper for start_copy sample.")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono startCopyFromURL(URL sourceURL) { - return this.startCopyFromURL(sourceURL, null, null, null); - } - - /** - * Copies the data at the source URL to a blob. For more information, see the Azure Docs - * - * @param sourceURL - * The source URL to copy from. URLs outside of Azure may only be copied to block blobs. - * @param metadata - * {@link Metadata} - * @param sourceModifiedAccessConditions - * {@link ModifiedAccessConditions} against the source. Standard HTTP Access conditions related to the - * modification of data. ETag and LastModifiedTime are used to construct conditions related to when the blob - * was changed relative to the given request. The request will fail if the specified condition is not - * satisfied. - * @param destAccessConditions - * {@link BlobAccessConditions} against the destination. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=start_copy "Sample code for BlobAsyncRawClient.startCopyFromURL")] \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=start_copy_helper "Helper for start_copy sample.")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono startCopyFromURL(URL sourceURL, Metadata metadata, - ModifiedAccessConditions sourceModifiedAccessConditions, BlobAccessConditions destAccessConditions) { - metadata = metadata == null ? new Metadata() : metadata; - sourceModifiedAccessConditions = sourceModifiedAccessConditions == null - ? new ModifiedAccessConditions() : sourceModifiedAccessConditions; - destAccessConditions = destAccessConditions == null ? new BlobAccessConditions() : destAccessConditions; - - // We want to hide the SourceAccessConditions type from the user for consistency's sake, so we convert here. - SourceModifiedAccessConditions sourceConditions = new SourceModifiedAccessConditions() - .sourceIfModifiedSince(sourceModifiedAccessConditions.ifModifiedSince()) - .sourceIfUnmodifiedSince(sourceModifiedAccessConditions.ifUnmodifiedSince()) - .sourceIfMatch(sourceModifiedAccessConditions.ifMatch()) - .sourceIfNoneMatch(sourceModifiedAccessConditions.ifNoneMatch()); - - return postProcessResponse(this.azureBlobStorage.blobs().startCopyFromURLWithRestResponseAsync( - null, null, sourceURL, null, metadata, null, sourceConditions, - destAccessConditions.modifiedAccessConditions(), destAccessConditions.leaseAccessConditions(), Context.NONE)); - } - - /** - * Stops a pending copy that was previously started and leaves a destination blob with 0 length and metadata. For - * more information, see the Azure Docs. - * - * @param copyId - * The id of the copy operation to abort. Returned as the {@code copyId} field on the {@link - * BlobStartCopyFromURLHeaders} object. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=abort_copy "Sample code for BlobAsyncRawClient.abortCopyFromURL")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono abortCopyFromURL(String copyId) { - return this.abortCopyFromURL(copyId, null); - } - - /** - * Stops a pending copy that was previously started and leaves a destination blob with 0 length and metadata. For - * more information, see the Azure Docs. - * - * @param copyId - * The id of the copy operation to abort. Returned as the {@code copyId} field on the {@link - * BlobStartCopyFromURLHeaders} object. - * @param leaseAccessConditions - * By setting lease access conditions, requests will fail if the provided lease does not match the active - * lease on the blob. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=abort_copy "Sample code for BlobAsyncRawClient.abortCopyFromURL")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono abortCopyFromURL(String copyId, - LeaseAccessConditions leaseAccessConditions) { - return postProcessResponse(this.azureBlobStorage.blobs().abortCopyFromURLWithRestResponseAsync( - null, null, copyId, null, null, leaseAccessConditions, Context.NONE)); - } - - /** - * Copies the data at the source URL to a blob and waits for the copy to complete before returning a response. - * For more information, see the Azure Docs - * - * @param copySource - * The source URL to copy from. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=sync_copy "Sample code for BlobAsyncRawClient.copyFromURL")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono syncCopyFromURL(URL copySource) { - return this.syncCopyFromURL(copySource, null, null, null); - } - - /** - * Copies the data at the source URL to a blob and waits for the copy to complete before returning a response. - * For more information, see the Azure Docs - * - * @param copySource - * The source URL to copy from. URLs outside of Azure may only be copied to block blobs. - * @param metadata - * {@link Metadata} - * @param sourceModifiedAccessConditions - * {@link ModifiedAccessConditions} against the source. Standard HTTP Access conditions related to the - * modification of data. ETag and LastModifiedTime are used to construct conditions related to when the blob - * was changed relative to the given request. The request will fail if the specified condition is not - * satisfied. - * @param destAccessConditions - * {@link BlobAccessConditions} against the destination. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=sync_copy "Sample code for BlobAsyncRawClient.copyFromURL")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono syncCopyFromURL(URL copySource, Metadata metadata, - ModifiedAccessConditions sourceModifiedAccessConditions, BlobAccessConditions destAccessConditions) { - metadata = metadata == null ? new Metadata() : metadata; - sourceModifiedAccessConditions = sourceModifiedAccessConditions == null - ? new ModifiedAccessConditions() : sourceModifiedAccessConditions; - destAccessConditions = destAccessConditions == null ? new BlobAccessConditions() : destAccessConditions; - - // We want to hide the SourceAccessConditions type from the user for consistency's sake, so we convert here. - SourceModifiedAccessConditions sourceConditions = new SourceModifiedAccessConditions() - .sourceIfModifiedSince(sourceModifiedAccessConditions.ifModifiedSince()) - .sourceIfUnmodifiedSince(sourceModifiedAccessConditions.ifUnmodifiedSince()) - .sourceIfMatch(sourceModifiedAccessConditions.ifMatch()) - .sourceIfNoneMatch(sourceModifiedAccessConditions.ifNoneMatch()); - - return postProcessResponse(this.azureBlobStorage.blobs().copyFromURLWithRestResponseAsync( - null, null, copySource, null, metadata, null, sourceConditions, - destAccessConditions.modifiedAccessConditions(), destAccessConditions.leaseAccessConditions(), Context.NONE)); - } - - /** - * Reads a range of bytes from a blob. The response also includes the blob's properties and metadata. For more - * information, see the Azure Docs. - *

- * Note that the response body has reliable download functionality built in, meaning that a failed download stream - * will be automatically retried. This behavior may be configured with {@link ReliableDownloadOptions}. - * - * @return Emits the successful response. - * @apiNote ## Sample Code \n [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=upload_download - * "Sample code for BlobAsyncRawClient.download")] \n For more samples, please see the [Samples - * file](%https://github.com/Azure/azure-storage-java/blob/New-Storage-SDK-V10-Preview/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono download() { - return this.download(null, null, false); - } - - /** - * Reads a range of bytes from a blob. The response also includes the blob's properties and metadata. For more - * information, see the Azure Docs. - *

- * Note that the response body has reliable download functionality built in, meaning that a failed download stream - * will be automatically retried. This behavior may be configured with {@link ReliableDownloadOptions}. - * - * @param range - * {@link BlobRange} - * @param accessConditions - * {@link BlobAccessConditions} - * @param rangeGetContentMD5 - * Whether the contentMD5 for the specified blob range should be returned. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=upload_download "Sample code for BlobAsyncRawClient.download")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono download(BlobRange range, BlobAccessConditions accessConditions, - boolean rangeGetContentMD5) { - range = range == null ? new BlobRange(0) : range; - Boolean getMD5 = rangeGetContentMD5 ? rangeGetContentMD5 : null; - accessConditions = accessConditions == null ? new BlobAccessConditions() : accessConditions; - HTTPGetterInfo info = new HTTPGetterInfo() - .offset(range.offset()) - .count(range.count()) - .eTag(accessConditions.modifiedAccessConditions().ifMatch()); - - // TODO: range is BlobRange but expected as String - // TODO: figure out correct response - return postProcessResponse(this.azureBlobStorage.blobs().downloadWithRestResponseAsync( - null, null, snapshot, null, null, range.toHeaderValue(), getMD5, - null, null, null, null, - accessConditions.leaseAccessConditions(), accessConditions.modifiedAccessConditions(), Context.NONE)) - // Convert the autorest response to a DownloadAsyncResponse, which enable reliable download. - .map(response -> { - // If there wasn't an etag originally specified, lock on the one returned. - info.eTag(response.deserializedHeaders().eTag()); - return new DownloadAsyncResponse(response, info, - // In the event of a stream failure, make a new request to pick up where we left off. - newInfo -> - this.download(new BlobRange(newInfo.offset(), newInfo.count()), - new BlobAccessConditions().modifiedAccessConditions( - new ModifiedAccessConditions().ifMatch(info.eTag())), false)); - }); - } - - /** - * Deletes the specified blob or snapshot. Note that deleting a blob also deletes all its snapshots. For more - * information, see the Azure Docs. - * - * @return Emits the successful response. - * @apiNote ## Sample Code \n [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=blob_delete - * "Sample code for BlobAsyncRawClient.delete")] \n For more samples, please see the [Samples - * file](%https://github.com/Azure/azure-storage-java/blob/New-Storage-SDK-V10-Preview/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono delete() { - return this.delete(null, null); - } - - /** - * Deletes the specified blob or snapshot. Note that deleting a blob also deletes all its snapshots. For more - * information, see the Azure Docs. - * - * @param deleteBlobSnapshotOptions - * Specifies the behavior for deleting the snapshots on this blob. {@code Include} will delete the base blob - * and all snapshots. {@code Only} will delete only the snapshots. If a snapshot is being deleted, you must - * pass null. - * @param accessConditions - * {@link BlobAccessConditions} - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=blob_delete "Sample code for BlobAsyncRawClient.delete")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono delete(DeleteSnapshotsOptionType deleteBlobSnapshotOptions, - BlobAccessConditions accessConditions) { - accessConditions = accessConditions == null ? new BlobAccessConditions() : accessConditions; - - return postProcessResponse(this.azureBlobStorage.blobs().deleteWithRestResponseAsync( - null, null, snapshot, null, null, deleteBlobSnapshotOptions, - null, accessConditions.leaseAccessConditions(), accessConditions.modifiedAccessConditions(), - Context.NONE)); - } - - /** - * Returns the blob's metadata and properties. For more information, see the Azure Docs. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=properties_metadata "Sample code for BlobAsyncRawClient.getProperties")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono getProperties() { - return this.getProperties(null); - } - - /** - * Returns the blob's metadata and properties. For more information, see the Azure Docs. - * - * @param accessConditions - * {@link BlobAccessConditions} - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=properties_metadata "Sample code for BlobAsyncRawClient.getProperties")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono getProperties(BlobAccessConditions accessConditions) { - accessConditions = accessConditions == null ? new BlobAccessConditions() : accessConditions; - - return postProcessResponse(this.azureBlobStorage.blobs().getPropertiesWithRestResponseAsync( - null, null, snapshot, null, null, null, - null, null, null, accessConditions.leaseAccessConditions(), - accessConditions.modifiedAccessConditions(), Context.NONE)); - } - - /** - * Changes a blob's HTTP header properties. For more information, see the Azure - * Docs. - * - * @param headers - * {@link BlobHTTPHeaders} - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=properties_metadata "Sample code for BlobAsyncRawClient.setHTTPHeaders")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono setHTTPHeaders(BlobHTTPHeaders headers) { - return this.setHTTPHeaders(headers, null); - } - - /** - * Changes a blob's HTTP header properties. For more information, see the Azure Docs. - * - * @param headers - * {@link BlobHTTPHeaders} - * @param accessConditions - * {@link BlobAccessConditions} - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=properties_metadata "Sample code for BlobAsyncRawClient.setHTTPHeaders")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono setHTTPHeaders(BlobHTTPHeaders headers, - BlobAccessConditions accessConditions) { - accessConditions = accessConditions == null ? new BlobAccessConditions() : accessConditions; - - return postProcessResponse(this.azureBlobStorage.blobs().setHTTPHeadersWithRestResponseAsync( - null, null, null, null, headers, - accessConditions.leaseAccessConditions(), accessConditions.modifiedAccessConditions(), Context.NONE)); - } - - /** - * Changes a blob's metadata. For more information, see the Azure Docs. - * - * @param metadata - * {@link Metadata} - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=properties_metadata "Sample code for BlobAsyncRawClient.setMetadata")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono setMetadata(Metadata metadata) { - return this.setMetadata(metadata, null); - } - - /** - * Changes a blob's metadata. For more information, see the Azure Docs. - * - * @param metadata - * {@link Metadata} - * @param accessConditions - * {@link BlobAccessConditions} - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=properties_metadata "Sample code for BlobAsyncRawClient.setMetadata")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono setMetadata(Metadata metadata, BlobAccessConditions accessConditions) { - metadata = metadata == null ? new Metadata() : metadata; - accessConditions = accessConditions == null ? new BlobAccessConditions() : accessConditions; - - return postProcessResponse(this.azureBlobStorage.blobs().setMetadataWithRestResponseAsync( - null, null, null, metadata, null, null, - null, null, accessConditions.leaseAccessConditions(), - accessConditions.modifiedAccessConditions(), Context.NONE)); - } - - /** - * Creates a read-only snapshot of a blob. For more information, see the Azure Docs. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=snapshot "Sample code for BlobAsyncRawClient.createSnapshot")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono createSnapshot() { - return this.createSnapshot(null, null); - } - - /** - * Creates a read-only snapshot of a blob. For more information, see the Azure Docs. - * - * @param metadata - * {@link Metadata} - * @param accessConditions - * {@link BlobAccessConditions} - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=snapshot "Sample code for BlobAsyncRawClient.createSnapshot")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono createSnapshot(Metadata metadata, BlobAccessConditions accessConditions) { - metadata = metadata == null ? new Metadata() : metadata; - accessConditions = accessConditions == null ? new BlobAccessConditions() : accessConditions; - - return postProcessResponse(this.azureBlobStorage.blobs().createSnapshotWithRestResponseAsync( - null, null, null, metadata, null, null, - null, null, accessConditions.modifiedAccessConditions(), - accessConditions.leaseAccessConditions(), Context.NONE)); - } - - /** - * Sets the tier on a blob. The operation is allowed on a page blob in a premium storage account or a block blob in - * a blob storage or GPV2 account. A premium page blob's tier determines the allowed size, IOPS, and bandwidth of - * the blob. A block blob's tier determines the Hot/Cool/Archive storage type. This does not update the blob's etag. - *

- * For detailed information about block blob level tiering see the Azure Docs. - * - * @param tier - * The new tier for the blob. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=tier "Sample code for BlobAsyncRawClient.setTier")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono setTier(AccessTier tier) { - return this.setTier(tier, null); - } - - /** - * Sets the tier on a blob. The operation is allowed on a page blob in a premium storage account or a block blob in - * a blob storage or GPV2 account. A premium page blob's tier determines the allowed size, IOPS, and bandwidth of - * the blob. A block blob's tier determines the Hot/Cool/Archive storage type. This does not update the blob's etag. - *

- * For detailed information about block blob level tiering see the Azure Docs. - * - * @param tier - * The new tier for the blob. - * @param leaseAccessConditions - * By setting lease access conditions, requests will fail if the provided lease does not match the active - * lease on the blob. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=tier "Sample code for BlobAsyncRawClient.setTier")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono setTier(AccessTier tier, LeaseAccessConditions leaseAccessConditions) { - Utility.assertNotNull("tier", tier); - - return postProcessResponse(this.azureBlobStorage.blobs().setTierWithRestResponseAsync( - null, null, tier, null, null, leaseAccessConditions, Context.NONE)); - } - - /** - * Undelete restores the content and metadata of a soft-deleted blob and/or any associated soft-deleted snapshots. - * For more information, see the Azure Docs. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=undelete "Sample code for BlobAsyncRawClient.undelete")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono undelete() { - return postProcessResponse(this.azureBlobStorage.blobs().undeleteWithRestResponseAsync(null, - null, Context.NONE)); - } - - /** - * Acquires a lease on the blob for write and delete operations. The lease duration must be between 15 to 60 - * seconds, or infinite (-1). For more information, see the Azure Docs. - * - * @param proposedId - * A {@code String} in any valid GUID format. May be null. - * @param duration - * The duration of the lease, in seconds, or negative one (-1) for a lease that - * never expires. A non-infinite lease can be between 15 and 60 seconds. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=blob_lease "Sample code for BlobAsyncRawClient.acquireLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono acquireLease(String proposedId, int duration) { - return this.acquireLease(proposedId, duration, null); - } - - /** - * Acquires a lease on the blob for write and delete operations. The lease duration must be between 15 to 60 - * seconds, or infinite (-1). For more information, see the Azure Docs. - * - * @param proposedID - * A {@code String} in any valid GUID format. May be null. - * @param duration - * The duration of the lease, in seconds, or negative one (-1) for a lease that - * never expires. A non-infinite lease can be between 15 and 60 seconds. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * - * @return Emits the successful response. - * @throws IllegalArgumentException If {@code duration} is outside the bounds of 15 to 60 or isn't -1. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=blob_lease "Sample code for BlobAsyncRawClient.acquireLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono acquireLease(String proposedID, int duration, - ModifiedAccessConditions modifiedAccessConditions) { - if (!(duration == -1 || (duration >= 15 && duration <= 60))) { - // Throwing is preferred to Mono.error because this will error out immediately instead of waiting until - // subscription. - throw new IllegalArgumentException("Duration must be -1 or between 15 and 60."); - } - - return postProcessResponse(this.azureBlobStorage.blobs().acquireLeaseWithRestResponseAsync( - null, null, null, duration, proposedID, null, - modifiedAccessConditions, Context.NONE)); - } - - /** - * Renews the blob's previously-acquired lease. For more information, see the Azure Docs. - * - * @param leaseID - * The leaseId of the active lease on the blob. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=blob_lease "Sample code for BlobAsyncRawClient.renewLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono renewLease(String leaseID) { - return this.renewLease(leaseID, null); - } - - /** - * Renews the blob's previously-acquired lease. For more information, see the Azure Docs. - * - * @param leaseID - * The leaseId of the active lease on the blob. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=blob_lease "Sample code for BlobAsyncRawClient.renewLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono renewLease(String leaseID, ModifiedAccessConditions modifiedAccessConditions) { - return postProcessResponse(this.azureBlobStorage.blobs().renewLeaseWithRestResponseAsync(null, - null, leaseID, null, null, modifiedAccessConditions, Context.NONE)); - } - - /** - * Releases the blob's previously-acquired lease. For more information, see the Azure Docs. - * - * @param leaseID - * The leaseId of the active lease on the blob. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=blob_lease "Sample code for BlobAsyncRawClient.releaseLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono releaseLease(String leaseID) { - return this.releaseLease(leaseID, null); - } - - /** - * Releases the blob's previously-acquired lease. For more information, see the Azure Docs. - * - * @param leaseID - * The leaseId of the active lease on the blob. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=blob_lease "Sample code for BlobAsyncRawClient.releaseLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono releaseLease(String leaseID, - ModifiedAccessConditions modifiedAccessConditions) { - return postProcessResponse(this.azureBlobStorage.blobs().releaseLeaseWithRestResponseAsync(null, - null, leaseID, null, null, modifiedAccessConditions, Context.NONE)); - } - - /** - * BreakLease breaks the blob's previously-acquired lease (if it exists). Pass the LeaseBreakDefault (-1) constant - * to break a fixed-duration lease when it expires or an infinite lease immediately. For more information, see the - * Azure Docs. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=blob_lease "Sample code for BlobAsyncRawClient.breakLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/New-Storage-SDK-V10-Preview/src/test/java/com/microsoft/azure/storage/Samples.java) - * - * @return - * Emits the successful response. - */ - public Mono breakLease() { - return this.breakLease(null, null); - } - - /** - * BreakLease breaks the blob's previously-acquired lease (if it exists). Pass the LeaseBreakDefault (-1) constant - * to break a fixed-duration lease when it expires or an infinite lease immediately. For more information, see the - * Azure Docs. - * - * @param breakPeriodInSeconds - * An optional {@code Integer} representing the proposed duration of seconds that the lease should continue - * before it is broken, between 0 and 60 seconds. This break period is only used if it is shorter than the - * time remaining on the lease. If longer, the time remaining on the lease is used. A new lease will not be - * available before the break period has expired, but the lease may be held for longer than the break - * period. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=blob_lease "Sample code for BlobAsyncRawClient.breakLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono breakLease(Integer breakPeriodInSeconds, - ModifiedAccessConditions modifiedAccessConditions) { - return postProcessResponse(this.azureBlobStorage.blobs().breakLeaseWithRestResponseAsync(null, - null, null, breakPeriodInSeconds, null, modifiedAccessConditions, Context.NONE)); - } - - /** - * ChangeLease changes the blob's lease ID. For more information, see the Azure Docs. - * - * @param leaseId - * The leaseId of the active lease on the blob. - * @param proposedID - * A {@code String} in any valid GUID format. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=blob_lease "Sample code for BlobAsyncRawClient.changeLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono changeLease(String leaseId, String proposedID) { - return this.changeLease(leaseId, proposedID, null); - } - - /** - * ChangeLease changes the blob's lease ID. For more information, see the Azure Docs. - * - * @param leaseId - * The leaseId of the active lease on the blob. - * @param proposedID - * A {@code String} in any valid GUID format. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=blob_lease "Sample code for BlobAsyncRawClient.changeLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono changeLease(String leaseId, String proposedID, - ModifiedAccessConditions modifiedAccessConditions) { - return postProcessResponse(this.azureBlobStorage.blobs().changeLeaseWithRestResponseAsync(null, - null, leaseId, proposedID, null, null, modifiedAccessConditions, Context.NONE)); - } - - /** - * Returns the sku name and account kind for the account. For more information, please see the Azure Docs. - * - * @return Emits the successful response. - * - * @apiNote ## Sample code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=account_info "Sample code for BlobAsyncRawClient.getAccountInfo")] \n - * For more samples, please see the [Samples file](https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono getAccountInfo() { - return postProcessResponse( - this.azureBlobStorage.blobs().getAccountInfoWithRestResponseAsync(null, null, Context.NONE)); - } -} diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/BlobClient.java b/storage/client/blob/src/main/java/com/azure/storage/blob/BlobClient.java index f47c3cf6d7830..e2c6eab47a768 100644 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/BlobClient.java +++ b/storage/client/blob/src/main/java/com/azure/storage/blob/BlobClient.java @@ -16,6 +16,7 @@ import com.azure.storage.blob.models.ModifiedAccessConditions; import com.azure.storage.blob.models.ReliableDownloadOptions; import com.azure.storage.blob.models.StorageAccountInfo; +import com.azure.storage.blob.models.UserDelegationKey; import reactor.core.publisher.Mono; import java.io.IOException; @@ -23,32 +24,34 @@ import java.io.UncheckedIOException; import java.net.URL; import java.time.Duration; +import java.time.OffsetDateTime; /** - * Client to a blob of any type: block, append, or page. It may only be instantiated through a {@link BlobClientBuilder} or via - * the method {@link ContainerClient#getBlobClient(String)}. This class does not hold any state about a particular - * blob, but is instead a convenient way of sending appropriate requests to the resource on the service. + * Client to a blob of any type: block, append, or page. It may only be instantiated through a {@link BlobClientBuilder} + * or via the method {@link ContainerClient#getBlobClient(String)}. This class does not hold any state about a + * particular blob, but is instead a convenient way of sending appropriate requests to the resource on the service. * *

* This client offers the ability to download blobs. Note that uploading data is specific to each type of blob. Please * refer to the {@link BlockBlobClient}, {@link PageBlobClient}, or {@link AppendBlobClient} for upload options. This - * client can be converted into one of these clients easily through the methods {@link #asBlockBlobClient}, {@link #asPageBlobClient}, - * and {@link #asAppendBlobClient}. + * client can be converted into one of these clients easily through the methods {@link #asBlockBlobClient}, {@link + * #asPageBlobClient}, and {@link #asAppendBlobClient}. * *

- * This client contains operations on a blob. Operations on a container are available on {@link ContainerClient}, - * and operations on the service are available on {@link StorageClient}. + * This client contains operations on a blob. Operations on a container are available on {@link ContainerClient}, and + * operations on the service are available on {@link StorageClient}. * *

- * Please refer to the Azure Docs - * for more information. + * Please refer to the Azure + * Docs for more information. */ +@SuppressWarnings({"unused", "WeakerAccess"}) public class BlobClient { - private static final int BLOB_DEFAULT_DOWNLOAD_BLOCK_SIZE = 4 * Constants.MB; private final BlobAsyncClient blobAsyncClient; /** * Package-private constructor for use by {@link BlobClientBuilder}. + * * @param blobAsyncClient the async blob client */ BlobClient(BlobAsyncClient blobAsyncClient) { @@ -56,45 +59,41 @@ public class BlobClient { } /** - * Creates a new {@link BlockBlobClient} to this resource, maintaining configurations. Only do this for blobs - * that are known to be block blobs. + * Creates a new {@link BlockBlobClient} to this resource, maintaining configurations. Only do this for blobs that + * are known to be block blobs. * - * @return - * A {@link BlockBlobClient} to this resource. + * @return A {@link BlockBlobClient} to this resource. */ public BlockBlobClient asBlockBlobClient() { return new BlockBlobClient(blobAsyncClient.asBlockBlobAsyncClient()); } /** - * Creates a new {@link AppendBlobClient} to this resource, maintaining configurations. Only do this for blobs - * that are known to be append blobs. + * Creates a new {@link AppendBlobClient} to this resource, maintaining configurations. Only do this for blobs that + * are known to be append blobs. * - * @return - * A {@link AppendBlobClient} to this resource. + * @return A {@link AppendBlobClient} to this resource. */ public AppendBlobClient asAppendBlobClient() { return new AppendBlobClient(blobAsyncClient.asAppendBlobAsyncClient()); } /** - * Creates a new {@link PageBlobClient} to this resource, maintaining configurations. Only do this for blobs - * that are known to be page blobs. + * Creates a new {@link PageBlobClient} to this resource, maintaining configurations. Only do this for blobs that + * are known to be page blobs. * - * @return - * A {@link PageBlobClient} to this resource. + * @return A {@link PageBlobClient} to this resource. */ public PageBlobClient asPageBlobClient() { return new PageBlobClient(blobAsyncClient.asPageBlobAsyncClient()); } /** - * Initializes a {@link ContainerClient} object pointing to the container this blob is in. This method does - * not create a container. It simply constructs the URL to the container and offers access to methods relevant to + * Initializes a {@link ContainerClient} object pointing to the container this blob is in. This method does not + * create a container. It simply constructs the URL to the container and offers access to methods relevant to * containers. * - * @return - * A {@link ContainerClient} object pointing to the container containing the blob + * @return A {@link ContainerClient} object pointing to the container containing the blob */ public ContainerClient getContainerClient() { return new ContainerClient(blobAsyncClient.getContainerAsyncClient()); @@ -102,6 +101,7 @@ public ContainerClient getContainerClient() { /** * Gets the URL of the blob represented by this client. + * * @return the URL. */ public URL getBlobUrl() { @@ -113,9 +113,7 @@ public URL getBlobUrl() { *

* * @return An InputStream object that represents the stream to use for reading from the blob. - * - * @throws StorageException - * If a storage service error occurred. + * @throws StorageException If a storage service error occurred. */ public final BlobInputStream openInputStream() { return openInputStream(new BlobRange(0), null); @@ -125,15 +123,11 @@ public final BlobInputStream openInputStream() { * Opens a blob input stream to download the specified range of the blob. *

* - * @param range - * {@link BlobRange} - * @param accessConditions - * An {@link BlobAccessConditions} object that represents the access conditions for the blob. - * + * @param range {@link BlobRange} + * @param accessConditions An {@link BlobAccessConditions} object that represents the access conditions for the + * blob. * @return An InputStream object that represents the stream to use for reading from the blob. - * - * @throws StorageException - * If a storage service error occurred. + * @throws StorageException If a storage service error occurred. */ public final BlobInputStream openInputStream(BlobRange range, BlobAccessConditions accessConditions) { return new BlobInputStream(blobAsyncClient, range.offset(), range.count(), accessConditions); @@ -151,10 +145,8 @@ public Response exists() { /** * Gets if the container this client represents exists in the cloud. * - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. - * @return - * true if the container exists, false if it doesn't + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @return true if the container exists, false if it doesn't */ public Response exists(Duration timeout) { Mono> response = blobAsyncClient.exists(); @@ -166,11 +158,8 @@ public Response exists(Duration timeout) { * Copies the data at the source URL to a blob. For more information, see the * Azure Docs * - * @param sourceURL - * The source URL to copy from. URLs outside of Azure may only be copied to block blobs. - * - * @return - * The copy ID for the long running operation. + * @param sourceURL The source URL to copy from. URLs outside of Azure may only be copied to block blobs. + * @return The copy ID for the long running operation. */ public Response startCopyFromURL(URL sourceURL) { return this.startCopyFromURL(sourceURL, null, null, null, null); @@ -180,26 +169,19 @@ public Response startCopyFromURL(URL sourceURL) { * Copies the data at the source URL to a blob. For more information, see the * Azure Docs * - * @param sourceURL - * The source URL to copy from. URLs outside of Azure may only be copied to block blobs. - * @param metadata - * {@link Metadata} - * @param sourceModifiedAccessConditions - * {@link ModifiedAccessConditions} against the source. Standard HTTP Access conditions related to the - * modification of data. ETag and LastModifiedTime are used to construct conditions related to when the blob - * was changed relative to the given request. The request will fail if the specified condition is not - * satisfied. - * @param destAccessConditions - * {@link BlobAccessConditions} against the destination. - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. - * - * @return - * The copy ID for the long running operation. + * @param sourceURL The source URL to copy from. URLs outside of Azure may only be copied to block blobs. + * @param metadata {@link Metadata} + * @param sourceModifiedAccessConditions {@link ModifiedAccessConditions} against the source. Standard HTTP Access + * conditions related to the modification of data. ETag and LastModifiedTime are used to construct conditions + * related to when the blob was changed relative to the given request. The request will fail if the specified + * condition is not satisfied. + * @param destAccessConditions {@link BlobAccessConditions} against the destination. + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @return The copy ID for the long running operation. */ public Response startCopyFromURL(URL sourceURL, Metadata metadata, - ModifiedAccessConditions sourceModifiedAccessConditions, BlobAccessConditions destAccessConditions, - Duration timeout) { + ModifiedAccessConditions sourceModifiedAccessConditions, BlobAccessConditions destAccessConditions, + Duration timeout) { Mono> response = blobAsyncClient .startCopyFromURL(sourceURL, metadata, sourceModifiedAccessConditions, destAccessConditions); @@ -209,9 +191,8 @@ public Response startCopyFromURL(URL sourceURL, Metadata metadata, /** * Stops a pending copy that was previously started and leaves a destination blob with 0 length and metadata. * - * @param copyId - * The id of the copy operation to abort. Returned as the {@code copyId} field on the {@link - * BlobStartCopyFromURLHeaders} object. + * @param copyId The id of the copy operation to abort. Returned as the {@code copyId} field on the {@link + * BlobStartCopyFromURLHeaders} object. * @return A response containing status code and HTTP headers. */ public VoidResponse abortCopyFromURL(String copyId) { @@ -221,14 +202,11 @@ public VoidResponse abortCopyFromURL(String copyId) { /** * Stops a pending copy that was previously started and leaves a destination blob with 0 length and metadata. * - * @param copyId - * The id of the copy operation to abort. Returned as the {@code copyId} field on the {@link - * BlobStartCopyFromURLHeaders} object. - * @param leaseAccessConditions - * By setting lease access conditions, requests will fail if the provided lease does not match the active - * lease on the blob. - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @param copyId The id of the copy operation to abort. Returned as the {@code copyId} field on the {@link + * BlobStartCopyFromURLHeaders} object. + * @param leaseAccessConditions By setting lease access conditions, requests will fail if the provided lease does + * not match the active lease on the blob. + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. * @return A response containing status code and HTTP headers. */ public VoidResponse abortCopyFromURL(String copyId, LeaseAccessConditions leaseAccessConditions, Duration timeout) { @@ -241,11 +219,8 @@ public VoidResponse abortCopyFromURL(String copyId, LeaseAccessConditions leaseA /** * Copies the data at the source URL to a blob and waits for the copy to complete before returning a response. * - * @param copySource - * The source URL to copy from. - * - * @return - * The copy ID for the long running operation. + * @param copySource The source URL to copy from. + * @return The copy ID for the long running operation. */ public Response copyFromURL(URL copySource) { return this.copyFromURL(copySource, null, null, null, null); @@ -254,26 +229,19 @@ public Response copyFromURL(URL copySource) { /** * Copies the data at the source URL to a blob and waits for the copy to complete before returning a response. * - * @param copySource - * The source URL to copy from. URLs outside of Azure may only be copied to block blobs. - * @param metadata - * {@link Metadata} - * @param sourceModifiedAccessConditions - * {@link ModifiedAccessConditions} against the source. Standard HTTP Access conditions related to the - * modification of data. ETag and LastModifiedTime are used to construct conditions related to when the blob - * was changed relative to the given request. The request will fail if the specified condition is not - * satisfied. - * @param destAccessConditions - * {@link BlobAccessConditions} against the destination. - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. - * - * @return - * The copy ID for the long running operation. + * @param copySource The source URL to copy from. URLs outside of Azure may only be copied to block blobs. + * @param metadata {@link Metadata} + * @param sourceModifiedAccessConditions {@link ModifiedAccessConditions} against the source. Standard HTTP Access + * conditions related to the modification of data. ETag and LastModifiedTime are used to construct conditions + * related to when the blob was changed relative to the given request. The request will fail if the specified + * condition is not satisfied. + * @param destAccessConditions {@link BlobAccessConditions} against the destination. + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @return The copy ID for the long running operation. */ public Response copyFromURL(URL copySource, Metadata metadata, - ModifiedAccessConditions sourceModifiedAccessConditions, BlobAccessConditions destAccessConditions, - Duration timeout) { + ModifiedAccessConditions sourceModifiedAccessConditions, BlobAccessConditions destAccessConditions, + Duration timeout) { Mono> response = blobAsyncClient .copyFromURL(copySource, metadata, sourceModifiedAccessConditions, destAccessConditions); @@ -284,8 +252,7 @@ public Response copyFromURL(URL copySource, Metadata metadata, * Downloads the entire blob into an output stream. Uploading data must be done from the {@link BlockBlobClient}, * {@link PageBlobClient}, or {@link AppendBlobClient}. * - * @param stream - * A non-null {@link OutputStream} instance where the downloaded data will be written. + * @param stream A non-null {@link OutputStream} instance where the downloaded data will be written. * @return A response containing status code and HTTP headers. * @throws UncheckedIOException If an I/O error occurs. */ @@ -294,23 +261,17 @@ public VoidResponse download(OutputStream stream) { } /** - * Downloads a range of bytes from a blob into an output stream. Uploading data must be done from the {@link BlockBlobClient}, - * {@link PageBlobClient}, or {@link AppendBlobClient}. + * Downloads a range of bytes from a blob into an output stream. Uploading data must be done from the {@link + * BlockBlobClient}, {@link PageBlobClient}, or {@link AppendBlobClient}. * - * @param stream - * A non-null {@link OutputStream} instance where the downloaded data will be written. + * @param stream A non-null {@link OutputStream} instance where the downloaded data will be written. * @param options {@link ReliableDownloadOptions} - * @param range - * {@link BlobRange} - * @param accessConditions - * {@link BlobAccessConditions} - * @param rangeGetContentMD5 - * Whether the contentMD5 for the specified blob range should be returned. - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @param range {@link BlobRange} + * @param accessConditions {@link BlobAccessConditions} + * @param rangeGetContentMD5 Whether the contentMD5 for the specified blob range should be returned. + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. * @return A response containing status code and HTTP headers. * @throws UncheckedIOException If an I/O error occurs. - * */ public VoidResponse download(OutputStream stream, ReliableDownloadOptions options, BlobRange range, BlobAccessConditions accessConditions, boolean rangeGetContentMD5, Duration timeout) { @@ -332,37 +293,32 @@ public VoidResponse download(OutputStream stream, ReliableDownloadOptions option /** * Downloads the entire blob into a file specified by the path. The file will be created if it doesn't exist. - * Uploading data must be done from the {@link BlockBlobClient}, {@link PageBlobClient}, or {@link AppendBlobClient}. + * Uploading data must be done from the {@link BlockBlobClient}, {@link PageBlobClient}, or {@link + * AppendBlobClient}. * - * @param filePath - * A non-null {@link OutputStream} instance where the downloaded data will be written. + * @param filePath A non-null {@link OutputStream} instance where the downloaded data will be written. * @throws IOException If an I/O error occurs */ public void downloadToFile(String filePath) throws IOException { - this.downloadToFile(filePath, null, null, BLOB_DEFAULT_DOWNLOAD_BLOCK_SIZE, null, false, null); + blobAsyncClient.downloadToFile(filePath); } /** * Downloads a range of bytes blob into a file specified by the path. The file will be created if it doesn't exist. - * Uploading data must be done from the {@link BlockBlobClient}, {@link PageBlobClient}, or {@link AppendBlobClient}. + * Uploading data must be done from the {@link BlockBlobClient}, {@link PageBlobClient}, or {@link + * AppendBlobClient}. * - * @param filePath - * A non-null {@link OutputStream} instance where the downloaded data will be written. + * @param filePath A non-null {@link OutputStream} instance where the downloaded data will be written. * @param options {@link ReliableDownloadOptions} - * @param range - * {@link BlobRange} - * @param blockSize - * the size of a chunk to download at a time, in bytes - * @param accessConditions - * {@link BlobAccessConditions} - * @param rangeGetContentMD5 - * Whether the contentMD5 for the specified blob range should be returned. - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @param range {@link BlobRange} + * @param blockSize the size of a chunk to download at a time, in bytes + * @param accessConditions {@link BlobAccessConditions} + * @param rangeGetContentMD5 Whether the contentMD5 for the specified blob range should be returned. + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. * @throws IOException If an I/O error occurs */ public void downloadToFile(String filePath, ReliableDownloadOptions options, BlobRange range, Integer blockSize, - BlobAccessConditions accessConditions, boolean rangeGetContentMD5, Duration timeout) throws IOException { + BlobAccessConditions accessConditions, boolean rangeGetContentMD5, Duration timeout) throws IOException { Mono download = blobAsyncClient.downloadToFile(filePath, range, blockSize, accessConditions, rangeGetContentMD5, options); try { @@ -384,19 +340,15 @@ public VoidResponse delete() { /** * Deletes the specified blob or snapshot. Note that deleting a blob also deletes all its snapshots. * - * @param deleteBlobSnapshotOptions - * Specifies the behavior for deleting the snapshots on this blob. {@code Include} will delete the base blob - * and all snapshots. {@code Only} will delete only the snapshots. If a snapshot is being deleted, you must - * pass null. - * @param accessConditions - * {@link BlobAccessConditions} - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. - * + * @param deleteBlobSnapshotOptions Specifies the behavior for deleting the snapshots on this blob. {@code Include} + * will delete the base blob and all snapshots. {@code Only} will delete only the snapshots. If a snapshot is being + * deleted, you must pass null. + * @param accessConditions {@link BlobAccessConditions} + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. * @return A response containing status code and HTTP headers. */ public VoidResponse delete(DeleteSnapshotsOptionType deleteBlobSnapshotOptions, - BlobAccessConditions accessConditions, Duration timeout) { + BlobAccessConditions accessConditions, Duration timeout) { Mono response = blobAsyncClient .delete(deleteBlobSnapshotOptions, accessConditions); @@ -406,8 +358,7 @@ public VoidResponse delete(DeleteSnapshotsOptionType deleteBlobSnapshotOptions, /** * Returns the blob's metadata and properties. * - * @return - * The blob properties and metadata. + * @return The blob properties and metadata. */ public Response getProperties() { return this.getProperties(null, null); @@ -416,13 +367,9 @@ public Response getProperties() { /** * Returns the blob's metadata and properties. * - * @param accessConditions - * {@link BlobAccessConditions} - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. - * - * @return - * The blob properties and metadata. + * @param accessConditions {@link BlobAccessConditions} + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @return The blob properties and metadata. */ public Response getProperties(BlobAccessConditions accessConditions, Duration timeout) { Mono> response = blobAsyncClient @@ -432,13 +379,12 @@ public Response getProperties(BlobAccessConditions accessConditi } /** - * Changes a blob's HTTP header properties. if only one HTTP header is updated, the - * others will all be erased. In order to preserve existing values, they must be - * passed alongside the header being changed. For more information, see the + * Changes a blob's HTTP header properties. if only one HTTP header is updated, the others will all be erased. In + * order to preserve existing values, they must be passed alongside the header being changed. For more information, + * see the * Azure Docs. * - * @param headers - * {@link BlobHTTPHeaders} + * @param headers {@link BlobHTTPHeaders} * @return A response containing status code and HTTP headers. */ public VoidResponse setHTTPHeaders(BlobHTTPHeaders headers) { @@ -446,21 +392,18 @@ public VoidResponse setHTTPHeaders(BlobHTTPHeaders headers) { } /** - * Changes a blob's HTTP header properties. if only one HTTP header is updated, the - * others will all be erased. In order to preserve existing values, they must be - * passed alongside the header being changed. For more information, see the + * Changes a blob's HTTP header properties. if only one HTTP header is updated, the others will all be erased. In + * order to preserve existing values, they must be passed alongside the header being changed. For more information, + * see the * Azure Docs. * - * @param headers - * {@link BlobHTTPHeaders} - * @param accessConditions - * {@link BlobAccessConditions} - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @param headers {@link BlobHTTPHeaders} + * @param accessConditions {@link BlobAccessConditions} + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. * @return A response containing status code and HTTP headers. */ public VoidResponse setHTTPHeaders(BlobHTTPHeaders headers, BlobAccessConditions accessConditions, - Duration timeout) { + Duration timeout) { Mono response = blobAsyncClient .setHTTPHeaders(headers, accessConditions); @@ -468,12 +411,11 @@ public VoidResponse setHTTPHeaders(BlobHTTPHeaders headers, BlobAccessConditions } /** - * Changes a blob's metadata. The specified metadata in this method will replace existing - * metadata. If old values must be preserved, they must be downloaded and included in the - * call to this method. For more information, see the Azure Docs. + * Changes a blob's metadata. The specified metadata in this method will replace existing metadata. If old values + * must be preserved, they must be downloaded and included in the call to this method. For more information, see the + * Azure Docs. * - * @param metadata - * {@link Metadata} + * @param metadata {@link Metadata} * @return A response containing status code and HTTP headers. */ public VoidResponse setMetadata(Metadata metadata) { @@ -481,16 +423,13 @@ public VoidResponse setMetadata(Metadata metadata) { } /** - * Changes a blob's metadata. The specified metadata in this method will replace existing - * metadata. If old values must be preserved, they must be downloaded and included in the - * call to this method. For more information, see the Azure Docs. + * Changes a blob's metadata. The specified metadata in this method will replace existing metadata. If old values + * must be preserved, they must be downloaded and included in the call to this method. For more information, see the + * Azure Docs. * - * @param metadata - * {@link Metadata} - * @param accessConditions - * {@link BlobAccessConditions} - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @param metadata {@link Metadata} + * @param accessConditions {@link BlobAccessConditions} + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. * @return A response containing status code and HTTP headers. */ public VoidResponse setMetadata(Metadata metadata, BlobAccessConditions accessConditions, Duration timeout) { @@ -503,8 +442,7 @@ public VoidResponse setMetadata(Metadata metadata, BlobAccessConditions accessCo /** * Creates a read-only snapshot of a blob. * - * @return - * The ID of the new snapshot. + * @return The ID of the new snapshot. */ public Response createSnapshot() { return this.createSnapshot(null, null, null); @@ -513,15 +451,10 @@ public Response createSnapshot() { /** * Creates a read-only snapshot of a blob. * - * @param metadata - * {@link Metadata} - * @param accessConditions - * {@link BlobAccessConditions} - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. - * - * @return - * The ID of the new snapshot. + * @param metadata {@link Metadata} + * @param accessConditions {@link BlobAccessConditions} + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @return The ID of the new snapshot. */ public Response createSnapshot(Metadata metadata, BlobAccessConditions accessConditions, Duration timeout) { Mono> response = blobAsyncClient @@ -533,10 +466,10 @@ public Response createSnapshot(Metadata metadata, BlobAccessConditions a /** * Sets the tier on a blob. The operation is allowed on a page blob in a premium storage account or a block blob in * a blob storage or GPV2 account. A premium page blob's tier determines the allowed size, IOPS, and bandwidth of - * the blob. A block blob's tier determines the Hot/Cool/Archive storage type. This does not update the blob's etag. + * the blob. A block blob's tier determines the Hot/Cool/Archive storage type. This does not update the blob's + * etag. * - * @param tier - * The new tier for the blob. + * @param tier The new tier for the blob. * @return A response containing status code and HTTP headers. */ public VoidResponse setTier(AccessTier tier) { @@ -546,15 +479,13 @@ public VoidResponse setTier(AccessTier tier) { /** * Sets the tier on a blob. The operation is allowed on a page blob in a premium storage account or a block blob in * a blob storage or GPV2 account. A premium page blob's tier determines the allowed size, IOPS, and bandwidth of - * the blob. A block blob's tier determines the Hot/Cool/Archive storage type. This does not update the blob's etag. - * - * @param tier - * The new tier for the blob. - * @param leaseAccessConditions - * By setting lease access conditions, requests will fail if the provided lease does not match the active - * lease on the blob. - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. + * the blob. A block blob's tier determines the Hot/Cool/Archive storage type. This does not update the blob's + * etag. + * + * @param tier The new tier for the blob. + * @param leaseAccessConditions By setting lease access conditions, requests will fail if the provided lease does + * not match the active lease on the blob. + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. * @return A response containing status code and HTTP headers. */ public VoidResponse setTier(AccessTier tier, LeaseAccessConditions leaseAccessConditions, Duration timeout) { @@ -576,8 +507,7 @@ public VoidResponse undelete() { /** * Undelete restores the content and metadata of a soft-deleted blob and/or any associated soft-deleted snapshots. * - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. * @return A response containing status code and HTTP headers. */ public VoidResponse undelete(Duration timeout) { @@ -591,14 +521,10 @@ public VoidResponse undelete(Duration timeout) { * Acquires a lease on the blob for write and delete operations. The lease duration must be between 15 to 60 * seconds, or infinite (-1). * - * @param proposedId - * A {@code String} in any valid GUID format. May be null. - * @param duration - * The duration of the lease, in seconds, or negative one (-1) for a lease that - * never expires. A non-infinite lease can be between 15 and 60 seconds. - * - * @return - * The lease ID. + * @param proposedId A {@code String} in any valid GUID format. May be null. + * @param duration The duration of the lease, in seconds, or negative one (-1) for a lease that never expires. A + * non-infinite lease can be between 15 and 60 seconds. + * @return The lease ID. */ public Response acquireLease(String proposedId, int duration) { return this.acquireLease(proposedId, duration, null, null); @@ -608,23 +534,17 @@ public Response acquireLease(String proposedId, int duration) { * Acquires a lease on the blob for write and delete operations. The lease duration must be between 15 to 60 * seconds, or infinite (-1). * - * @param proposedID - * A {@code String} in any valid GUID format. May be null. - * @param duration - * The duration of the lease, in seconds, or negative one (-1) for a lease that - * never expires. A non-infinite lease can be between 15 and 60 seconds. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. - * - * @return - * The lease ID. + * @param proposedID A {@code String} in any valid GUID format. May be null. + * @param duration The duration of the lease, in seconds, or negative one (-1) for a lease that never expires. A + * non-infinite lease can be between 15 and 60 seconds. + * @param modifiedAccessConditions Standard HTTP Access conditions related to the modification of data. ETag and + * LastModifiedTime are used to construct conditions related to when the blob was changed relative to the given + * request. The request will fail if the specified condition is not satisfied. + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @return The lease ID. */ public Response acquireLease(String proposedID, int duration, - ModifiedAccessConditions modifiedAccessConditions, Duration timeout) { + ModifiedAccessConditions modifiedAccessConditions, Duration timeout) { Mono> response = blobAsyncClient .acquireLease(proposedID, duration, modifiedAccessConditions); @@ -634,11 +554,8 @@ public Response acquireLease(String proposedID, int duration, /** * Renews the blob's previously-acquired lease. * - * @param leaseID - * The leaseId of the active lease on the blob. - * - * @return - * The renewed lease ID. + * @param leaseID The leaseId of the active lease on the blob. + * @return The renewed lease ID. */ public Response renewLease(String leaseID) { return this.renewLease(leaseID, null, null); @@ -647,20 +564,15 @@ public Response renewLease(String leaseID) { /** * Renews the blob's previously-acquired lease. * - * @param leaseID - * The leaseId of the active lease on the blob. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. - * - * @return - * The renewed lease ID. + * @param leaseID The leaseId of the active lease on the blob. + * @param modifiedAccessConditions Standard HTTP Access conditions related to the modification of data. ETag and + * LastModifiedTime are used to construct conditions related to when the blob was changed relative to the given + * request. The request will fail if the specified condition is not satisfied. + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @return The renewed lease ID. */ public Response renewLease(String leaseID, ModifiedAccessConditions modifiedAccessConditions, - Duration timeout) { + Duration timeout) { Mono> response = blobAsyncClient .renewLease(leaseID, modifiedAccessConditions); @@ -670,8 +582,7 @@ public Response renewLease(String leaseID, ModifiedAccessConditions modi /** * Releases the blob's previously-acquired lease. * - * @param leaseID - * The leaseId of the active lease on the blob. + * @param leaseID The leaseId of the active lease on the blob. * @return A response containing status code and HTTP headers. */ public VoidResponse releaseLease(String leaseID) { @@ -681,18 +592,15 @@ public VoidResponse releaseLease(String leaseID) { /** * Releases the blob's previously-acquired lease. * - * @param leaseID - * The leaseId of the active lease on the blob. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @param leaseID The leaseId of the active lease on the blob. + * @param modifiedAccessConditions Standard HTTP Access conditions related to the modification of data. ETag and + * LastModifiedTime are used to construct conditions related to when the blob was changed relative to the given + * request. The request will fail if the specified condition is not satisfied. + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. * @return A response containing status code and HTTP headers. */ public VoidResponse releaseLease(String leaseID, - ModifiedAccessConditions modifiedAccessConditions, Duration timeout) { + ModifiedAccessConditions modifiedAccessConditions, Duration timeout) { Mono response = blobAsyncClient .releaseLease(leaseID, modifiedAccessConditions); @@ -703,8 +611,7 @@ public VoidResponse releaseLease(String leaseID, * BreakLease breaks the blob's previously-acquired lease (if it exists). Pass the LeaseBreakDefault (-1) constant * to break a fixed-duration lease when it expires or an infinite lease immediately. * - * @return - * The remaining time in the broken lease in seconds. + * @return The remaining time in the broken lease in seconds. */ public Response breakLease() { return this.breakLease(null, null, null); @@ -714,24 +621,19 @@ public Response breakLease() { * BreakLease breaks the blob's previously-acquired lease (if it exists). Pass the LeaseBreakDefault (-1) constant * to break a fixed-duration lease when it expires or an infinite lease immediately. * - * @param breakPeriodInSeconds - * An optional {@code Integer} representing the proposed duration of seconds that the lease should continue - * before it is broken, between 0 and 60 seconds. This break period is only used if it is shorter than the - * time remaining on the lease. If longer, the time remaining on the lease is used. A new lease will not be - * available before the break period has expired, but the lease may be held for longer than the break - * period. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. - * - * @return - * The remaining time in the broken lease in seconds. + * @param breakPeriodInSeconds An optional {@code Integer} representing the proposed duration of seconds that the + * lease should continue before it is broken, between 0 and 60 seconds. This break period is only used if it is + * shorter than the time remaining on the lease. If longer, the time remaining on the lease is used. A new lease + * will not be available before the break period has expired, but the lease may be held for longer than the break + * period. + * @param modifiedAccessConditions Standard HTTP Access conditions related to the modification of data. ETag and + * LastModifiedTime are used to construct conditions related to when the blob was changed relative to the given + * request. The request will fail if the specified condition is not satisfied. + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @return The remaining time in the broken lease in seconds. */ public Response breakLease(Integer breakPeriodInSeconds, - ModifiedAccessConditions modifiedAccessConditions, Duration timeout) { + ModifiedAccessConditions modifiedAccessConditions, Duration timeout) { Mono> response = blobAsyncClient .breakLease(breakPeriodInSeconds, modifiedAccessConditions); @@ -741,36 +643,28 @@ public Response breakLease(Integer breakPeriodInSeconds, /** * ChangeLease changes the blob's lease ID. * - * @param leaseId - * The leaseId of the active lease on the blob. - * @param proposedID - * A {@code String} in any valid GUID format. - * - * @return - * The new lease ID. + * @param leaseId The leaseId of the active lease on the blob. + * @param proposedID A {@code String} in any valid GUID format. + * @return The new lease ID. */ public Response changeLease(String leaseId, String proposedID) { return this.changeLease(leaseId, proposedID, null, null); } /** - * ChangeLease changes the blob's lease ID. For more information, see the Azure Docs. - * - * @param leaseId - * The leaseId of the active lease on the blob. - * @param proposedID - * A {@code String} in any valid GUID format. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. + * ChangeLease changes the blob's lease ID. For more information, see the Azure + * Docs. * + * @param leaseId The leaseId of the active lease on the blob. + * @param proposedID A {@code String} in any valid GUID format. + * @param modifiedAccessConditions Standard HTTP Access conditions related to the modification of data. ETag and + * LastModifiedTime are used to construct conditions related to when the blob was changed relative to the given + * request. The request will fail if the specified condition is not satisfied. + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. * @return The new lease ID. */ public Response changeLease(String leaseId, String proposedID, - ModifiedAccessConditions modifiedAccessConditions, Duration timeout) { + ModifiedAccessConditions modifiedAccessConditions, Duration timeout) { Mono> response = blobAsyncClient .changeLease(leaseId, proposedID, modifiedAccessConditions); @@ -778,7 +672,8 @@ public Response changeLease(String leaseId, String proposedID, } /** - * Returns the sku name and account kind for the account. For more information, please see the Azure Docs. + * Returns the sku name and account kind for the account. For more information, please see the Azure Docs. * * @return The sku name and account kind. */ @@ -787,11 +682,10 @@ public Response getAccountInfo() { } /** - * Returns the sku name and account kind for the account. For more information, please see the Azure Docs. - * - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. + * Returns the sku name and account kind for the account. For more information, please see the Azure Docs. * + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. * @return The sku name and account kind. */ public Response getAccountInfo(Duration timeout) { @@ -800,4 +694,146 @@ public Response getAccountInfo(Duration timeout) { return Utility.blockWithOptionalTimeout(response, timeout); } + + /** + * Generates a user delegation SAS token with the specified parameters + * + * @param userDelegationKey The {@code UserDelegationKey} user delegation key for the SAS + * @param accountName The {@code String} account name for the SAS + * @param permissions The {@code ContainerSASPermissions} permission for the SAS + * @param expiryTime The {@code OffsetDateTime} expiry time for the SAS + * @return A string that represents the SAS token + */ + public String generateUserDelegationSAS(UserDelegationKey userDelegationKey, String accountName, + BlobSASPermission permissions, OffsetDateTime expiryTime) { + return this.blobAsyncClient.generateUserDelegationSAS(userDelegationKey, accountName, permissions, expiryTime); + } + + /** + * Generates a user delegation SAS token with the specified parameters + * + * @param userDelegationKey The {@code UserDelegationKey} user delegation key for the SAS + * @param accountName The {@code String} account name for the SAS + * @param permissions The {@code ContainerSASPermissions} permission for the SAS + * @param expiryTime The {@code OffsetDateTime} expiry time for the SAS + * @param startTime An optional {@code OffsetDateTime} start time for the SAS + * @param version An optional {@code String} version for the SAS + * @param sasProtocol An optional {@code SASProtocol} protocol for the SAS + * @param ipRange An optional {@code IPRange} ip address range for the SAS + * @return A string that represents the SAS token + */ + public String generateUserDelegationSAS(UserDelegationKey userDelegationKey, String accountName, + BlobSASPermission permissions, OffsetDateTime expiryTime, OffsetDateTime startTime, String version, + SASProtocol sasProtocol, IPRange ipRange) { + return this.blobAsyncClient.generateUserDelegationSAS(userDelegationKey, accountName, permissions, expiryTime, + startTime, version, sasProtocol, ipRange); + } + + /** + * Generates a user delegation SAS token with the specified parameters + * + * @param userDelegationKey The {@code UserDelegationKey} user delegation key for the SAS + * @param accountName The {@code String} account name for the SAS + * @param permissions The {@code ContainerSASPermissions} permission for the SAS + * @param expiryTime The {@code OffsetDateTime} expiry time for the SAS + * @param startTime An optional {@code OffsetDateTime} start time for the SAS + * @param version An optional {@code String} version for the SAS + * @param sasProtocol An optional {@code SASProtocol} protocol for the SAS + * @param ipRange An optional {@code IPRange} ip address range for the SAS + * @param cacheControl An optional {@code String} cache-control header for the SAS. + * @param contentDisposition An optional {@code String} content-disposition header for the SAS. + * @param contentEncoding An optional {@code String} content-encoding header for the SAS. + * @param contentLanguage An optional {@code String} content-language header for the SAS. + * @param contentType An optional {@code String} content-type header for the SAS. + * @return A string that represents the SAS token + */ + public String generateUserDelegationSAS(UserDelegationKey userDelegationKey, String accountName, + BlobSASPermission permissions, OffsetDateTime expiryTime, OffsetDateTime startTime, String version, + SASProtocol sasProtocol, IPRange ipRange, String cacheControl, String contentDisposition, + String contentEncoding, String contentLanguage, String contentType) { + return this.blobAsyncClient.generateUserDelegationSAS(userDelegationKey, accountName, permissions, expiryTime, + startTime, version, sasProtocol, ipRange, cacheControl, contentDisposition, contentEncoding, + contentLanguage, contentType); + } + + /** + * Generates a SAS token with the specified parameters + * + * @param expiryTime The {@code OffsetDateTime} expiry time for the SAS + * @param permissions The {@code ContainerSASPermissions} permission for the SAS + * @return A string that represents the SAS token + */ + public String generateSAS(OffsetDateTime expiryTime, BlobSASPermission permissions) { + return this.blobAsyncClient.generateSAS(permissions, expiryTime); + } + + /** + * Generates a SAS token with the specified parameters + * + * @param identifier The {@code String} name of the access policy on the container this SAS references if any + * @return A string that represents the SAS token + */ + public String generateSAS(String identifier) { + return this.blobAsyncClient.generateSAS(identifier); + } + + /** + * Generates a SAS token with the specified parameters + * + * @param identifier The {@code String} name of the access policy on the container this SAS references if any + * @param permissions The {@code ContainerSASPermissions} permission for the SAS + * @param expiryTime The {@code OffsetDateTime} expiry time for the SAS + * @param startTime An optional {@code OffsetDateTime} start time for the SAS + * @param version An optional {@code String} version for the SAS + * @param sasProtocol An optional {@code SASProtocol} protocol for the SAS + * @param ipRange An optional {@code IPRange} ip address range for the SAS + * @return A string that represents the SAS token + */ + public String generateSAS(String identifier, BlobSASPermission permissions, OffsetDateTime expiryTime, + OffsetDateTime startTime, String version, SASProtocol sasProtocol, IPRange ipRange) { + return this.blobAsyncClient.generateSAS(identifier, permissions, expiryTime, startTime, version, sasProtocol, + ipRange); + } + + /** + * Generates a SAS token with the specified parameters + * + * @param identifier The {@code String} name of the access policy on the container this SAS references if any + * @param permissions The {@code ContainerSASPermissions} permission for the SAS + * @param expiryTime The {@code OffsetDateTime} expiry time for the SAS + * @param startTime An optional {@code OffsetDateTime} start time for the SAS + * @param version An optional {@code String} version for the SAS + * @param sasProtocol An optional {@code SASProtocol} protocol for the SAS + * @param ipRange An optional {@code IPRange} ip address range for the SAS + * @param cacheControl An optional {@code String} cache-control header for the SAS. + * @param contentDisposition An optional {@code String} content-disposition header for the SAS. + * @param contentEncoding An optional {@code String} content-encoding header for the SAS. + * @param contentLanguage An optional {@code String} content-language header for the SAS. + * @param contentType An optional {@code String} content-type header for the SAS. + * @return A string that represents the SAS token + */ + public String generateSAS(String identifier, BlobSASPermission permissions, OffsetDateTime expiryTime, + OffsetDateTime startTime, String version, SASProtocol sasProtocol, IPRange ipRange, String cacheControl, + String contentDisposition, String contentEncoding, String contentLanguage, String contentType) { + return this.blobAsyncClient.generateSAS(identifier, permissions, expiryTime, startTime, version, sasProtocol, + ipRange, cacheControl, contentDisposition, contentEncoding, contentLanguage, contentType); + } + + /** + * Gets the snapshotId for a blob resource + * + * @return A string that represents the snapshotId of the snapshot blob + */ + public String getSnapshotId() { + return this.blobAsyncClient.getSnapshotId(); + } + + /** + * Determines if a blob is a snapshot + * + * @return A boolean that indicates if a blob is a snapshot + */ + public boolean isSnapshot() { + return this.blobAsyncClient.isSnapshot(); + } } diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/BlobClientBuilder.java b/storage/client/blob/src/main/java/com/azure/storage/blob/BlobClientBuilder.java index 2238af18e48e9..b0e3c98d8904a 100644 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/BlobClientBuilder.java +++ b/storage/client/blob/src/main/java/com/azure/storage/blob/BlobClientBuilder.java @@ -17,13 +17,17 @@ import com.azure.core.util.configuration.Configuration; import com.azure.core.util.configuration.ConfigurationManager; import com.azure.storage.blob.implementation.AzureBlobStorageBuilder; +import com.azure.storage.blob.models.PageRange; import com.azure.storage.common.credentials.SASTokenCredential; import com.azure.storage.common.credentials.SharedKeyCredential; import com.azure.storage.common.policy.RequestRetryOptions; import com.azure.storage.common.policy.RequestRetryPolicy; import com.azure.storage.common.policy.SASTokenCredentialPolicy; import com.azure.storage.common.policy.SharedKeyCredentialPolicy; +import reactor.core.publisher.Flux; +import java.io.InputStream; +import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; @@ -34,8 +38,7 @@ import java.util.Objects; /** - * Fluent BlobClientBuilder for instantiating a {@link BlobClient} or {@link BlobAsyncClient} - * using {@link BlobClientBuilder#buildClient()} or {@link BlobClientBuilder#buildAsyncClient()} respectively. + * This class provides a fluent builder API to help aid the configuration and instantiation Storage Blob clients. * *

* The following information must be provided on this builder: @@ -46,8 +49,17 @@ * * *

- * Once all the configurations are set on this builder, call {@code .buildClient()} to create a - * {@link BlobClient} or {@code .buildAsyncClient()} to create a {@link BlobAsyncClient}. + * Once all the configurations are set on this builder use the following mapping to construct the given client: + *

    + *
  • {@link BlobClientBuilder#buildBlobClient()} - {@link BlobClient}
  • + *
  • {@link BlobClientBuilder#buildBlobAsyncClient()} - {@link BlobAsyncClient}
  • + *
  • {@link BlobClientBuilder#buildAppendBlobClient()} - {@link AppendBlobClient}
  • + *
  • {@link BlobClientBuilder#buildAppendBlobAsyncClient()} - {@link AppendBlobAsyncClient}
  • + *
  • {@link BlobClientBuilder#buildBlockBlobClient()} - {@link BlockBlobClient}
  • + *
  • {@link BlobClientBuilder#buildBlockBlobAsyncClient()} - {@link BlockBlobAsyncClient}
  • + *
  • {@link BlobClientBuilder#buildPageBlobClient()} - {@link PageBlobClient}
  • + *
  • {@link BlobClientBuilder#buildPageBlobAsyncClient()} - {@link PageBlobAsyncClient}
  • + *
*/ public final class BlobClientBuilder { private static final String ACCOUNT_NAME = "accountname"; @@ -70,8 +82,7 @@ public final class BlobClientBuilder { private Configuration configuration; /** - * Creates a builder instance that is able to configure and construct {@link BlobClient BlobClients} - * and {@link BlobAsyncClient BlobAsyncClients}. + * Creates a builder instance that is able to configure and construct Storage Blob clients. */ public BlobClientBuilder() { retryOptions = new RequestRetryOptions(); @@ -120,53 +131,130 @@ private AzureBlobStorageBuilder buildImpl() { } /** + * Creates a {@link BlobClient} based on options set in the Builder. BlobClients are used to perform generic blob + * methods such as {@link BlobClient#download(OutputStream) download} and + * {@link BlobClient#getProperties() get properties}, use this when the blob type is unknown. + * * @return a {@link BlobClient} created from the configurations in this builder. + * @throws NullPointerException If {@code endpoint}, {@code containerName}, or {@code blobName} is {@code null}. */ - public BlobClient buildClient() { - return new BlobClient(buildAsyncClient()); + public BlobClient buildBlobClient() { + return new BlobClient(buildBlobAsyncClient()); } /** + * Creates a {@link BlobAsyncClient} based on options set in the Builder. BlobAsyncClients are used to perform + * generic blob methods such as {@link BlobAsyncClient#download() download} and + * {@link BlobAsyncClient#getProperties()}, use this when the blob type is unknown. + * * @return a {@link BlobAsyncClient} created from the configurations in this builder. + * @throws NullPointerException If {@code endpoint}, {@code containerName}, or {@code blobName} is {@code null}. */ - public BlobAsyncClient buildAsyncClient() { + public BlobAsyncClient buildBlobAsyncClient() { return new BlobAsyncClient(buildImpl(), snapshot); } + /** + * Creates a {@link AppendBlobClient} based on options set in the Builder. AppendBlobClients are used to perform + * append blob specific operations such as {@link AppendBlobClient#appendBlock(InputStream, long) append block}, + * only use this when the blob is known to be an append blob. + * + * @return a {@link AppendBlobClient} created from the configurations in this builder. + * @throws NullPointerException If {@code endpoint}, {@code containerName}, or {@code blobName} is {@code null}. + */ + public AppendBlobClient buildAppendBlobClient() { + return new AppendBlobClient(buildAppendBlobAsyncClient()); + } + + /** + * Creates a {@link AppendBlobAsyncClient} based on options set in the Builder. AppendBlobAsyncClients are used to + * perform append blob specific operations such as {@link AppendBlobAsyncClient#appendBlock(Flux, long) append blob}, + * only use this when the blob is known to be an append blob. + * + * @return a {@link AppendBlobAsyncClient} created from the configurations in this builder. + * @throws NullPointerException If {@code endpoint}, {@code containerName}, or {@code blobName} is {@code null}. + */ + public AppendBlobAsyncClient buildAppendBlobAsyncClient() { + return new AppendBlobAsyncClient(buildImpl(), snapshot); + } + + /** + * Creates a {@link BlockBlobClient} based on options set in the Builder. BlockBlobClients are used to perform + * generic upload operations such as {@link BlockBlobClient#uploadFromFile(String) upload from file} and block + * blob specific operations such as {@link BlockBlobClient#stageBlock(String, InputStream, long) stage block} and + * {@link BlockBlobClient#commitBlockList(List)}, only use this when the blob is known to be a block blob. + * + * @return a {@link BlockBlobClient} created from the configurations in this builder. + * @throws NullPointerException If {@code endpoint}, {@code containerName}, or {@code blobName} is {@code null}. + */ + public BlockBlobClient buildBlockBlobClient() { + return new BlockBlobClient(buildBlockBlobAsyncClient()); + } + + /** + * Creates a {@link BlockBlobAsyncClient} based on options set in the Builder. BlockBlobAsyncClients are used to + * perform generic upload operations such as {@link BlockBlobAsyncClient#uploadFromFile(String) upload from file} + * and block blob specific operations such as {@link BlockBlobAsyncClient#stageBlock(String, Flux, long) stage block} + * and {@link BlockBlobAsyncClient#commitBlockList(List) commit block list}, only use this when the blob is known to + * be a block blob. + * + * @return a {@link BlockBlobAsyncClient} created from the configurations in this builder. + * @throws NullPointerException If {@code endpoint}, {@code containerName}, or {@code blobName} is {@code null}. + */ + public BlockBlobAsyncClient buildBlockBlobAsyncClient() { + return new BlockBlobAsyncClient(buildImpl(), snapshot); + } + + /** + * Creates a {@link PageBlobClient} based on options set in the Builder. PageBlobClients are used to perform page + * blob specific operations such as {@link PageBlobClient#uploadPages(PageRange, InputStream) upload pages} and + * {@link PageBlobClient#clearPages(PageRange) clear pages}, only use this when the blob is known to be a page blob. + * + * @return a {@link PageBlobClient} created from the configurations in this builder. + * @throws NullPointerException If {@code endpoint}, {@code containerName}, or {@code blobName} is {@code null}. + */ + public PageBlobClient buildPageBlobClient() { + return new PageBlobClient(buildPageBlobAsyncClient()); + } + + /** + * Creates a {@link PageBlobAsyncClient} based on options set in the Builder. PageBlobAsyncClients are used to + * perform page blob specific operations such as {@link PageBlobAsyncClient#uploadPages(PageRange, Flux) upload pages} + * and {@link PageBlobAsyncClient#clearPages(PageRange) clear pages}, only use this when the blob is known to be a + * page blob. + * + * @return a {@link PageBlobAsyncClient} created from the configurations in this builder. + * @throws NullPointerException If {@code endpoint}, {@code containerName}, or {@code blobName} is {@code null}. + */ + public PageBlobAsyncClient buildPageBlobAsyncClient() { + return new PageBlobAsyncClient(buildImpl(), snapshot); + } + /** * Sets the service endpoint, additionally parses it for information (SAS token, container name, blob name) * @param endpoint URL of the service * @return the updated BlobClientBuilder object - * @throws IllegalArgumentException If {@code endpoint} is a malformed URL. + * @throws IllegalArgumentException If {@code endpoint} is {@code null} or is a malformed URL. */ public BlobClientBuilder endpoint(String endpoint) { - Objects.requireNonNull(endpoint); - URL url; try { - url = new URL(endpoint); + URL url = new URL(endpoint); BlobURLParts parts = URLParser.parse(url); - this.endpoint = parts.scheme() + "://" + parts.host(); - - if (parts.containerName() != null) { - this.containerName = parts.containerName(); - } - - if (parts.blobName() != null) { - this.blobName = parts.blobName(); - } - if (parts.snapshot() != null) { - this.snapshot = parts.snapshot(); + this.endpoint = parts.scheme() + "://" + parts.host(); + this.containerName = parts.containerName(); + this.blobName = parts.blobName(); + this.snapshot = parts.snapshot(); + + this.sasTokenCredential = SASTokenCredential.fromQueryParameters(parts.sasQueryParameters()); + if (this.sasTokenCredential != null) { + this.tokenCredential = null; + this.sharedKeyCredential = null; } } catch (MalformedURLException ex) { throw new IllegalArgumentException("The Azure Storage Blob endpoint url is malformed."); } - SASTokenCredential credential = SASTokenCredential.fromQuery(url.getQuery()); - if (credential != null) { - this.credential(credential); - } - return this; } @@ -174,9 +262,10 @@ public BlobClientBuilder endpoint(String endpoint) { * Sets the name of the container this client is connecting to. * @param containerName the name of the container * @return the updated BlobClientBuilder object + * @throws NullPointerException If {@code containerName} is {@code null} */ public BlobClientBuilder containerName(String containerName) { - this.containerName = containerName; + this.containerName = Objects.requireNonNull(containerName); return this; } @@ -184,9 +273,10 @@ public BlobClientBuilder containerName(String containerName) { * Sets the name of the blob this client is connecting to. * @param blobName the name of the blob * @return the updated BlobClientBuilder object + * @throws NullPointerException If {@code blobName} is {@code null} */ public BlobClientBuilder blobName(String blobName) { - this.blobName = blobName; + this.blobName = Objects.requireNonNull(blobName); return this; } @@ -204,9 +294,10 @@ public BlobClientBuilder snapshot(String snapshot) { * Sets the credential used to authorize requests sent to the service * @param credential authorization credential * @return the updated BlobClientBuilder object + * @throws NullPointerException If {@code credential} is {@code null} */ public BlobClientBuilder credential(SharedKeyCredential credential) { - this.sharedKeyCredential = credential; + this.sharedKeyCredential = Objects.requireNonNull(credential); this.tokenCredential = null; this.sasTokenCredential = null; return this; @@ -216,9 +307,10 @@ public BlobClientBuilder credential(SharedKeyCredential credential) { * Sets the credential used to authorize requests sent to the service * @param credential authorization credential * @return the updated BlobClientBuilder object + * @throws NullPointerException If {@code credential} is {@code null} */ public BlobClientBuilder credential(TokenCredential credential) { - this.tokenCredential = credential; + this.tokenCredential = Objects.requireNonNull(credential); this.sharedKeyCredential = null; this.sasTokenCredential = null; return this; @@ -228,9 +320,10 @@ public BlobClientBuilder credential(TokenCredential credential) { * Sets the credential used to authorize requests sent to the service * @param credential authorization credential * @return the updated BlobClientBuilder object + * @throws NullPointerException If {@code credential} is {@code null} */ public BlobClientBuilder credential(SASTokenCredential credential) { - this.sasTokenCredential = credential; + this.sasTokenCredential = Objects.requireNonNull(credential); this.sharedKeyCredential = null; this.tokenCredential = null; return this; @@ -251,6 +344,7 @@ public BlobClientBuilder anonymousCredential() { * Sets the connection string for the service, parses it for authentication information (account name, account key) * @param connectionString connection string from access keys section * @return the updated BlobClientBuilder object + * @throws NullPointerException If {@code connectionString} is {@code null} * @throws IllegalArgumentException If {@code connectionString} doesn't contain AccountName or AccountKey. */ public BlobClientBuilder connectionString(String connectionString) { @@ -284,9 +378,10 @@ public BlobClientBuilder connectionString(String connectionString) { * Sets the http client used to send service requests * @param httpClient http client to send requests * @return the updated BlobClientBuilder object + * @throws NullPointerException If {@code httpClient} is {@code null} */ public BlobClientBuilder httpClient(HttpClient httpClient) { - this.httpClient = httpClient; + this.httpClient = Objects.requireNonNull(httpClient); return this; } @@ -294,9 +389,10 @@ public BlobClientBuilder httpClient(HttpClient httpClient) { * Adds a pipeline policy to apply on each request sent * @param pipelinePolicy a pipeline policy * @return the updated BlobClientBuilder object + * @throws NullPointerException If {@code pipelinePolicy} is {@code null} */ public BlobClientBuilder addPolicy(HttpPipelinePolicy pipelinePolicy) { - this.policies.add(pipelinePolicy); + this.policies.add(Objects.requireNonNull(pipelinePolicy)); return this; } @@ -325,9 +421,10 @@ public BlobClientBuilder configuration(Configuration configuration) { * Sets the request retry options for all the requests made through the client. * @param retryOptions the options to configure retry behaviors * @return the updated BlobClientBuilder object + * @throws NullPointerException If {@code retryOptions} is {@code null} */ public BlobClientBuilder retryOptions(RequestRetryOptions retryOptions) { - this.retryOptions = retryOptions; + this.retryOptions = Objects.requireNonNull(retryOptions); return this; } } diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/BlobInputStream.java b/storage/client/blob/src/main/java/com/azure/storage/blob/BlobInputStream.java index 2e093f50738f2..1a990e179688b 100644 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/BlobInputStream.java +++ b/storage/client/blob/src/main/java/com/azure/storage/blob/BlobInputStream.java @@ -189,7 +189,7 @@ public synchronized void close() throws IOException { */ private synchronized void dispatchRead(final int readLength) throws IOException { try { - this.currentBuffer = this.blobClient.blobAsyncRawClient.download(new BlobRange(this.currentAbsoluteReadPosition, (long) readLength), this.accessCondition, false) + this.currentBuffer = this.blobClient.download(new BlobRange(this.currentAbsoluteReadPosition, (long) readLength), this.accessCondition, false) .flatMap(res -> ByteBufFlux.fromInbound(res.body(null)).aggregate().asByteBuffer()) .block(); diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/BlobOutputStream.java b/storage/client/blob/src/main/java/com/azure/storage/blob/BlobOutputStream.java index 176fe06c321d1..c733a580f2500 100644 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/BlobOutputStream.java +++ b/storage/client/blob/src/main/java/com/azure/storage/blob/BlobOutputStream.java @@ -295,7 +295,7 @@ private Mono writePages(Flux pageData, long offset, long writeLen PageBlobAccessConditions pageBlobAccessConditions = accessCondition == null ? null : new PageBlobAccessConditions().leaseAccessConditions(accessCondition.leaseAccessConditions()).modifiedAccessConditions(accessCondition.modifiedAccessConditions()); - return blobRef.pageBlobAsyncRawClient.uploadPages(new PageRange().start(offset).end(offset + writeLength - 1), pageData, pageBlobAccessConditions) + return blobRef.uploadPages(new PageRange().start(offset).end(offset + writeLength - 1), pageData, pageBlobAccessConditions) .then() .onErrorResume(t -> t instanceof StorageException, e -> { this.lastError = new IOException(e); @@ -312,7 +312,7 @@ private Mono appendBlock(Flux blockData, long offset, long writeL this.appendPositionAccessConditions.appendPosition(offset); AppendBlobAccessConditions appendBlobAccessConditions = accessCondition == null ? null : new AppendBlobAccessConditions().leaseAccessConditions(accessCondition.leaseAccessConditions()).modifiedAccessConditions(accessCondition.modifiedAccessConditions()); - return blobRef.appendBlobAsyncRawClient.appendBlock(blockData, writeLength, appendBlobAccessConditions) + return blobRef.appendBlock(blockData, writeLength, appendBlobAccessConditions) .then() .onErrorResume(t -> t instanceof IOException || t instanceof StorageException, e -> { this.lastError = new IOException(e); diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/BlobURLParts.java b/storage/client/blob/src/main/java/com/azure/storage/blob/BlobURLParts.java index 473d998c446b2..1200e960f7501 100644 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/BlobURLParts.java +++ b/storage/client/blob/src/main/java/com/azure/storage/blob/BlobURLParts.java @@ -76,14 +76,14 @@ public BlobURLParts host(String host) { } /** - * The container name or {@code null} if a {@link StorageAsyncRawClient} was parsed. + * The container name or {@code null} if a {@link StorageAsyncClient} was parsed. */ public String containerName() { return containerName; } /** - * The container name or {@code null} if a {@link StorageAsyncRawClient} was parsed. + * The container name or {@code null} if a {@link StorageAsyncClient} was parsed. */ public BlobURLParts containerName(String containerName) { this.containerName = containerName; @@ -91,14 +91,14 @@ public BlobURLParts containerName(String containerName) { } /** - * The blob name or {@code null} if a {@link StorageAsyncRawClient} or {@link ContainerAsyncClient} was parsed. + * The blob name or {@code null} if a {@link StorageAsyncClient} or {@link ContainerAsyncClient} was parsed. */ public String blobName() { return blobName; } /** - * The blob name or {@code null} if a {@link StorageAsyncRawClient} or {@link ContainerAsyncClient} was parsed. + * The blob name or {@code null} if a {@link StorageAsyncClient} or {@link ContainerAsyncClient} was parsed. */ public BlobURLParts blobName(String blobName) { this.blobName = blobName; diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/BlockBlobAsyncClient.java b/storage/client/blob/src/main/java/com/azure/storage/blob/BlockBlobAsyncClient.java index ada7a125d4ef1..cdb0a06fa68e2 100644 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/BlockBlobAsyncClient.java +++ b/storage/client/blob/src/main/java/com/azure/storage/blob/BlockBlobAsyncClient.java @@ -8,6 +8,7 @@ import com.azure.core.http.rest.SimpleResponse; import com.azure.core.http.rest.VoidResponse; import com.azure.core.implementation.util.FluxUtil; +import com.azure.core.util.Context; import com.azure.storage.blob.implementation.AzureBlobStorageBuilder; import com.azure.storage.blob.models.BlobAccessConditions; import com.azure.storage.blob.models.BlobHTTPHeaders; @@ -15,11 +16,11 @@ import com.azure.storage.blob.models.BlockBlobItem; import com.azure.storage.blob.models.BlockItem; import com.azure.storage.blob.models.BlockListType; +import com.azure.storage.blob.models.BlockLookupList; import com.azure.storage.blob.models.LeaseAccessConditions; import com.azure.storage.blob.models.Metadata; import com.azure.storage.blob.models.SourceModifiedAccessConditions; import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -27,7 +28,6 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.net.URL; -import java.nio.ByteBuffer; import java.nio.channels.AsynchronousFileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Paths; @@ -39,8 +39,10 @@ import java.util.TreeMap; import java.util.UUID; +import static com.azure.storage.blob.Utility.postProcessResponse; + /** - * Client to a block blob. It may only be instantiated through a {@link BlockBlobClientBuilder}, via + * Client to a block blob. It may only be instantiated through a {@link BlobClientBuilder}, via * the method {@link BlobAsyncClient#asBlockBlobAsyncClient()}, or via the method * {@link ContainerAsyncClient#getBlockBlobAsyncClient(String)}. This class does not hold * any state about a particular blob, but is instead a convenient way of sending appropriate @@ -66,8 +68,6 @@ public final class BlockBlobAsyncClient extends BlobAsyncClient { static final int BLOB_DEFAULT_UPLOAD_BLOCK_SIZE = 4 * Constants.MB; static final int BLOB_MAX_UPLOAD_BLOCK_SIZE = 100 * Constants.MB; - final BlockBlobAsyncRawClient blockBlobAsyncRawClient; - /** * Indicates the maximum number of bytes that can be sent in a call to upload. */ @@ -84,12 +84,11 @@ public final class BlockBlobAsyncClient extends BlobAsyncClient { public static final int MAX_BLOCKS = 50000; /** - * Package-private constructor for use by {@link BlockBlobClientBuilder}. + * Package-private constructor for use by {@link BlobClientBuilder}. * @param azureBlobStorageBuilder the API client builder for blob storage API */ BlockBlobAsyncClient(AzureBlobStorageBuilder azureBlobStorageBuilder, String snapshot) { super(azureBlobStorageBuilder, snapshot); - this.blockBlobAsyncRawClient = new BlockBlobAsyncRawClient(azureBlobStorageBuilder.build(), snapshot); } /** @@ -114,7 +113,7 @@ public final class BlockBlobAsyncClient extends BlobAsyncClient { * @return * A reactive response containing the information of the uploaded block blob. */ - public Mono> upload(Flux data, long length) { + public Mono> upload(Flux data, long length) { return this.upload(data, length, null, null, null); } @@ -146,10 +145,15 @@ public Mono> upload(Flux data, long length) * @return * A reactive response containing the information of the uploaded block blob. */ - public Mono> upload(Flux data, long length, BlobHTTPHeaders headers, + public Mono> upload(Flux data, long length, BlobHTTPHeaders headers, Metadata metadata, BlobAccessConditions accessConditions) { - return blockBlobAsyncRawClient - .upload(data.map(Unpooled::wrappedBuffer), length, headers, metadata, accessConditions) + metadata = metadata == null ? new Metadata() : metadata; + accessConditions = accessConditions == null ? new BlobAccessConditions() : accessConditions; + + return postProcessResponse(this.azureBlobStorage.blockBlobs().uploadWithRestResponseAsync(null, + null, data, length, null, metadata, null, null, + null, null, headers, accessConditions.leaseAccessConditions(), + accessConditions.modifiedAccessConditions(), Context.NONE)) .map(rb -> new SimpleResponse<>(rb, new BlockBlobItem(rb.deserializedHeaders()))); } @@ -291,8 +295,9 @@ public Mono stageBlock(String base64BlockID, Flux data, */ public Mono stageBlock(String base64BlockID, Flux data, long length, LeaseAccessConditions leaseAccessConditions) { - return blockBlobAsyncRawClient - .stageBlock(base64BlockID, data, length, leaseAccessConditions) + return postProcessResponse(this.azureBlobStorage.blockBlobs().stageBlockWithRestResponseAsync(null, + null, base64BlockID, length, data, null, null, null, + null, null, null, leaseAccessConditions, Context.NONE)) .map(VoidResponse::new); } @@ -349,8 +354,13 @@ public Mono stageBlockFromURL(String base64BlockID, URL sourceURL, public Mono stageBlockFromURL(String base64BlockID, URL sourceURL, BlobRange sourceRange, byte[] sourceContentMD5, LeaseAccessConditions leaseAccessConditions, SourceModifiedAccessConditions sourceModifiedAccessConditions) { - return blockBlobAsyncRawClient - .stageBlockFromURL(base64BlockID, sourceURL, sourceRange, sourceContentMD5, leaseAccessConditions, sourceModifiedAccessConditions) + sourceRange = sourceRange == null ? new BlobRange(0) : sourceRange; + + return postProcessResponse( + this.azureBlobStorage.blockBlobs().stageBlockFromURLWithRestResponseAsync(null, null, + base64BlockID, 0, sourceURL, sourceRange.toHeaderValue(), sourceContentMD5, null, + null, null, null, null, + leaseAccessConditions, sourceModifiedAccessConditions, Context.NONE)) .map(VoidResponse::new); } @@ -386,8 +396,9 @@ public Flux listBlocks(BlockListType listType) { */ public Flux listBlocks(BlockListType listType, LeaseAccessConditions leaseAccessConditions) { - return blockBlobAsyncRawClient - .listBlocks(listType, leaseAccessConditions) + return postProcessResponse(this.azureBlobStorage.blockBlobs().getBlockListWithRestResponseAsync( + null, null, listType, snapshot, null, null, null, + leaseAccessConditions, Context.NONE)) .map(ResponseBase::value) .flatMapMany(bl -> { Flux committed = Flux.fromIterable(bl.committedBlocks()) @@ -440,8 +451,13 @@ public Mono> commitBlockList(List base64BlockIDs */ public Mono> commitBlockList(List base64BlockIDs, BlobHTTPHeaders headers, Metadata metadata, BlobAccessConditions accessConditions) { - return blockBlobAsyncRawClient - .commitBlockList(base64BlockIDs, headers, metadata, accessConditions) + metadata = metadata == null ? new Metadata() : metadata; + accessConditions = accessConditions == null ? new BlobAccessConditions() : accessConditions; + + return postProcessResponse(this.azureBlobStorage.blockBlobs().commitBlockListWithRestResponseAsync( + null, null, new BlockLookupList().latest(base64BlockIDs), null, metadata, + null, null, null, null, headers, + accessConditions.leaseAccessConditions(), accessConditions.modifiedAccessConditions(), Context.NONE)) .map(rb -> new SimpleResponse<>(rb, new BlockBlobItem(rb.deserializedHeaders()))); } } diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/BlockBlobAsyncRawClient.java b/storage/client/blob/src/main/java/com/azure/storage/blob/BlockBlobAsyncRawClient.java deleted file mode 100644 index 03b7e650167b6..0000000000000 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/BlockBlobAsyncRawClient.java +++ /dev/null @@ -1,364 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.azure.storage.blob; - -import com.azure.core.util.Context; -import com.azure.storage.blob.implementation.AzureBlobStorageImpl; -import com.azure.storage.blob.models.BlobAccessConditions; -import com.azure.storage.blob.models.BlobHTTPHeaders; -import com.azure.storage.blob.models.BlobRange; -import com.azure.storage.blob.models.BlockBlobsCommitBlockListResponse; -import com.azure.storage.blob.models.BlockBlobsGetBlockListResponse; -import com.azure.storage.blob.models.BlockBlobsStageBlockFromURLResponse; -import com.azure.storage.blob.models.BlockBlobsStageBlockResponse; -import com.azure.storage.blob.models.BlockBlobsUploadResponse; -import com.azure.storage.blob.models.BlockListType; -import com.azure.storage.blob.models.BlockLookupList; -import com.azure.storage.blob.models.LeaseAccessConditions; -import com.azure.storage.blob.models.Metadata; -import com.azure.storage.blob.models.SourceModifiedAccessConditions; -import io.netty.buffer.ByteBuf; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import java.net.URL; -import java.util.List; - -import static com.azure.storage.blob.Utility.postProcessResponse; - -/** - * Represents a URL to a block blob. It may be obtained by direct construction or via the create method on a - * {@link ContainerAsyncClient} object. This class does not hold any state about a particular blob but is instead a convenient - * way of sending off appropriate requests to the resource on the service. Please refer to the - * Azure Docs - * for more information on block blobs. - */ -final class BlockBlobAsyncRawClient extends BlobAsyncRawClient { - - /** - * Indicates the maximum number of bytes that can be sent in a call to upload. - */ - public static final int MAX_UPLOAD_BLOB_BYTES = 256 * Constants.MB; - - /** - * Indicates the maximum number of bytes that can be sent in a call to stageBlock. - */ - public static final int MAX_STAGE_BLOCK_BYTES = 100 * Constants.MB; - - /** - * Indicates the maximum number of blocks allowed in a block blob. - */ - public static final int MAX_BLOCKS = 50000; - - /** - * Creates a {@code BlockBlobAsyncRawClient} object pointing to the account specified by the URL and using the provided - */ - BlockBlobAsyncRawClient(AzureBlobStorageImpl azureBlobStorage, String snapshot) { - super(azureBlobStorage, snapshot); - } - - - /** - * 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. Partial updates are not - * supported with PutBlob; the content of the existing blob is overwritten with the new content. To - * perform a partial update of a block blob's, use PutBlock and PutBlockList. - * For more information, see the - * Azure Docs. - *

- * Note that the data passed must be replayable if retries are enabled (the default). In other words, the - * {@code Flux} must produce the same data each time it is subscribed to. - *

- * - * @param data - * The data to write to the blob. Note that this {@code Flux} must be replayable if retries are enabled - * (the default). In other words, the Flowable must produce the same data each time it is subscribed to. - * @param length - * The exact length of the data. It is important that this value match precisely the length of the data - * emitted by the {@code Flux}. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=upload_download "Sample code for BlockBlobAsyncRawClient.upload")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono upload(Flux data, long length) { - return this.upload(data, length, null, null, null); - } - - /** - * 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. Partial updates are not - * supported with PutBlob; the content of the existing blob is overwritten with the new content. To - * perform a partial update of a block blob's, use PutBlock and PutBlockList. - * For more information, see the - * Azure Docs. - *

- * Note that the data passed must be replayable if retries are enabled (the default). In other words, the - * {@code Flux} must produce the same data each time it is subscribed to. - *

- * - * @param data - * The data to write to the blob. Note that this {@code Flux} must be replayable if retries are enabled - * (the default). In other words, the Flowable must produce the same data each time it is subscribed to. - * @param length - * The exact length of the data. It is important that this value match precisely the length of the data - * emitted by the {@code Flux}. - * @param headers - * {@link BlobHTTPHeaders} - * @param metadata - * {@link Metadata} - * @param accessConditions - * {@link BlobAccessConditions} - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=upload_download "Sample code for BlockBlobAsyncRawClient.upload")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono upload(Flux data, long length, BlobHTTPHeaders headers, - Metadata metadata, BlobAccessConditions accessConditions) { - metadata = metadata == null ? new Metadata() : metadata; - accessConditions = accessConditions == null ? new BlobAccessConditions() : accessConditions; - - return postProcessResponse(this.azureBlobStorage.blockBlobs().uploadWithRestResponseAsync(null, - null, data, length, null, metadata, null, null, - null, null, headers, accessConditions.leaseAccessConditions(), - accessConditions.modifiedAccessConditions(), Context.NONE)); - } - - /** - * Uploads the specified block to the block blob's "staging area" to be later committed by a call to - * commitBlockList. For more information, see the - * Azure Docs. - *

- * Note that the data passed must be replayable if retries are enabled (the default). In other words, the - * {@code Flux} must produce the same data each time it is subscribed to. - * - * @param base64BlockID - * A Base64 encoded {@code String} that specifies the ID for this block. Note that all block ids for a given - * blob must be the same length. - * @param data - * The data to write to the block. Note that this {@code Flux} must be replayable if retries are enabled - * (the default). In other words, the Flowable must produce the same data each time it is subscribed to. - * @param length - * The exact length of the data. It is important that this value match precisely the length of the data - * emitted by the {@code Flux}. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=blocks "Sample code for BlockBlobAsyncRawClient.stageBlock")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono stageBlock(String base64BlockID, Flux data, - long length) { - return this.stageBlock(base64BlockID, data, length, null); - } - - /** - * Uploads the specified block to the block blob's "staging area" to be later committed by a call to - * commitBlockList. For more information, see the - * Azure Docs. - *

- * Note that the data passed must be replayable if retries are enabled (the default). In other words, the - * {@code Flux} must produce the same data each time it is subscribed to. - * - * @param base64BlockID - * A Base64 encoded {@code String} that specifies the ID for this block. Note that all block ids for a given - * blob must be the same length. - * @param data - * The data to write to the block. Note that this {@code Flux} must be replayable if retries are enabled - * (the default). In other words, the Flowable must produce the same data each time it is subscribed to. - * @param length - * The exact length of the data. It is important that this value match precisely the length of the data - * emitted by the {@code Flux}. - * @param leaseAccessConditions - * By setting lease access conditions, requests will fail if the provided lease does not match the active - * lease on the blob. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=blocks "Sample code for BlockBlobAsyncRawClient.stageBlock")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono stageBlock(String base64BlockID, Flux data, long length, - LeaseAccessConditions leaseAccessConditions) { - return postProcessResponse(this.azureBlobStorage.blockBlobs().stageBlockWithRestResponseAsync(null, - null, base64BlockID, length, data, null, null, null, - null, null, null, leaseAccessConditions, Context.NONE)); - } - - /** - * Creates a new block to be committed as part of a blob where the contents are read from a URL. For more - * information, see the Azure Docs. - * - * @param base64BlockID - * A Base64 encoded {@code String} that specifies the ID for this block. Note that all block ids for a given - * blob must be the same length. - * @param sourceURL - * The url to the blob that will be the source of the copy. A source blob in the same storage account can be - * authenticated via Shared Key. However, if the source is a blob in another account, the source blob must - * either be public or must be authenticated via a shared access signature. If the source blob is public, no - * authentication is required to perform the operation. - * @param sourceRange - * {@link BlobRange} - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=block_from_url "Sample code for BlockBlobAsyncRawClient.stageBlockFromURL")] - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono stageBlockFromURL(String base64BlockID, URL sourceURL, - BlobRange sourceRange) { - return this.stageBlockFromURL(base64BlockID, sourceURL, sourceRange, null, - null, null); - } - - /** - * Creates a new block to be committed as part of a blob where the contents are read from a URL. For more - * information, see the Azure Docs. - * - * @param base64BlockID - * A Base64 encoded {@code String} that specifies the ID for this block. Note that all block ids for a given - * blob must be the same length. - * @param sourceURL - * The url to the blob that will be the source of the copy. A source blob in the same storage account can - * be authenticated via Shared Key. However, if the source is a blob in another account, the source blob - * must either be public or must be authenticated via a shared access signature. If the source blob is - * public, no authentication is required to perform the operation. - * @param sourceRange - * {@link BlobRange} - * @param sourceContentMD5 - * An MD5 hash of the block content from the source blob. If specified, the service will calculate the MD5 - * of the received data and fail the request if it does not match the provided MD5. - * @param leaseAccessConditions - * By setting lease access conditions, requests will fail if the provided lease does not match the active - * lease on the blob. - * @param sourceModifiedAccessConditions - * {@link SourceModifiedAccessConditions} - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=block_from_url "Sample code for BlockBlobAsyncRawClient.stageBlockFromURL")] - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono stageBlockFromURL(String base64BlockID, URL sourceURL, - BlobRange sourceRange, byte[] sourceContentMD5, LeaseAccessConditions leaseAccessConditions, - SourceModifiedAccessConditions sourceModifiedAccessConditions) { - sourceRange = sourceRange == null ? new BlobRange(0) : sourceRange; - - return postProcessResponse( - this.azureBlobStorage.blockBlobs().stageBlockFromURLWithRestResponseAsync(null, null, - base64BlockID, 0, sourceURL, sourceRange.toHeaderValue(), sourceContentMD5, null, - null, null, null, null, - leaseAccessConditions, sourceModifiedAccessConditions, Context.NONE)); - } - - /** - * Returns the list of blocks that have been uploaded as part of a block blob using the specified block list filter. - * For more information, see the - * Azure Docs. - * - * @param listType - * Specifies which type of blocks to return. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=blocks "Sample code for BlockBlobAsyncRawClient.listBlocks")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono listBlocks(BlockListType listType) { - return this.listBlocks(listType, null); - } - - /** - * Returns the list of blocks that have been uploaded as part of a block blob using the specified block list filter. - * For more information, see the - * Azure Docs. - * - * @param listType - * Specifies which type of blocks to return. - * @param leaseAccessConditions - * By setting lease access conditions, requests will fail if the provided lease does not match the active - * lease on the blob. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=blocks "Sample code for BlockBlobAsyncRawClient.listBlocks")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono listBlocks(BlockListType listType, - LeaseAccessConditions leaseAccessConditions) { - return postProcessResponse(this.azureBlobStorage.blockBlobs().getBlockListWithRestResponseAsync( - null, null, listType, snapshot, null, null, null, - leaseAccessConditions, Context.NONE)); - } - - /** - * Writes a blob by specifying the list of block IDs that are to make up the blob. - * In order to be written as part of a blob, a block must have been successfully written - * to the server in a prior stageBlock operation. You can call commitBlockList to update a blob - * by uploading only those blocks that have changed, then committing the new and existing - * blocks together. Any blocks not specified in the block list and permanently deleted. - * For more information, see the - * Azure Docs. - *

- * - * @param base64BlockIDs - * A list of base64 encode {@code String}s that specifies the block IDs to be committed. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=blocks "Sample code for BlockBlobAsyncRawClient.commitBlockList")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono commitBlockList(List base64BlockIDs) { - return this.commitBlockList(base64BlockIDs, null, null, null); - } - - /** - * Writes a blob by specifying the list of block IDs that are to make up the blob. - * In order to be written as part of a blob, a block must have been successfully written - * to the server in a prior stageBlock operation. You can call commitBlockList to update a blob - * by uploading only those blocks that have changed, then committing the new and existing - * blocks together. Any blocks not specified in the block list and permanently deleted. - * For more information, see the - * Azure Docs. - *

- * - * @param base64BlockIDs - * A list of base64 encode {@code String}s that specifies the block IDs to be committed. - * @param headers - * {@link BlobHTTPHeaders} - * @param metadata - * {@link Metadata} - * @param accessConditions - * {@link BlobAccessConditions} - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=blocks "Sample code for BlockBlobAsyncRawClient.commitBlockList")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono commitBlockList(List base64BlockIDs, - BlobHTTPHeaders headers, Metadata metadata, BlobAccessConditions accessConditions) { - metadata = metadata == null ? new Metadata() : metadata; - accessConditions = accessConditions == null ? new BlobAccessConditions() : accessConditions; - - return postProcessResponse(this.azureBlobStorage.blockBlobs().commitBlockListWithRestResponseAsync( - null, null, new BlockLookupList().latest(base64BlockIDs), null, metadata, - null, null, null, null, headers, - accessConditions.leaseAccessConditions(), accessConditions.modifiedAccessConditions(), Context.NONE)); - } -} diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/BlockBlobClient.java b/storage/client/blob/src/main/java/com/azure/storage/blob/BlockBlobClient.java index 9dced1f87022b..6af92555c2b00 100644 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/BlockBlobClient.java +++ b/storage/client/blob/src/main/java/com/azure/storage/blob/BlockBlobClient.java @@ -4,7 +4,6 @@ package com.azure.storage.blob; import com.azure.core.http.rest.Response; -import com.azure.core.http.rest.SimpleResponse; import com.azure.core.http.rest.VoidResponse; import com.azure.storage.blob.models.BlobAccessConditions; import com.azure.storage.blob.models.BlobHTTPHeaders; @@ -29,7 +28,7 @@ import java.util.List; /** - * Client to a block blob. It may only be instantiated through a {@link BlockBlobClientBuilder}, via + * Client to a block blob. It may only be instantiated through a {@link BlobClientBuilder}, via * the method {@link BlobClient#asBlockBlobClient()}, or via the method * {@link ContainerClient#getBlockBlobClient(String)}. This class does not hold * any state about a particular blob, but is instead a convenient way of sending appropriate @@ -44,25 +43,25 @@ * for more information. */ public final class BlockBlobClient extends BlobClient { + private final BlockBlobAsyncClient blockBlobAsyncClient; - private BlockBlobAsyncClient blockBlobAsyncClient; /** * Indicates the maximum number of bytes that can be sent in a call to upload. */ - public static final int MAX_UPLOAD_BLOB_BYTES = 256 * Constants.MB; + public static final int MAX_UPLOAD_BLOB_BYTES = BlockBlobAsyncClient.MAX_UPLOAD_BLOB_BYTES; /** * Indicates the maximum number of bytes that can be sent in a call to stageBlock. */ - public static final int MAX_STAGE_BLOCK_BYTES = 100 * Constants.MB; + public static final int MAX_STAGE_BLOCK_BYTES = BlockBlobAsyncClient.MAX_STAGE_BLOCK_BYTES; /** * Indicates the maximum number of blocks allowed in a block blob. */ - public static final int MAX_BLOCKS = 50000; + public static final int MAX_BLOCKS = BlockBlobAsyncClient.MAX_BLOCKS; /** - * Package-private constructor for use by {@link BlockBlobClientBuilder}. + * Package-private constructor for use by {@link BlobClientBuilder}. * @param blockBlobAsyncClient the async block blob client */ BlockBlobClient(BlockBlobAsyncClient blockBlobAsyncClient) { @@ -161,9 +160,8 @@ public Response upload(InputStream data, long length, BlobHTTPHea return ByteBufAllocator.DEFAULT.buffer((int) count).writeBytes(cache); })); - Mono> upload = blockBlobAsyncClient.blockBlobAsyncRawClient - .upload(fbb.subscribeOn(Schedulers.elastic()), length, headers, metadata, accessConditions) - .map(rb -> new SimpleResponse<>(rb, new BlockBlobItem(rb.deserializedHeaders()))); + Mono> upload = blockBlobAsyncClient + .upload(fbb.subscribeOn(Schedulers.elastic()), length, headers, metadata, accessConditions); try { return Utility.blockWithOptionalTimeout(upload, timeout); diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/BlockBlobClientBuilder.java b/storage/client/blob/src/main/java/com/azure/storage/blob/BlockBlobClientBuilder.java deleted file mode 100644 index 4774228c27648..0000000000000 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/BlockBlobClientBuilder.java +++ /dev/null @@ -1,337 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.azure.storage.blob; - -import com.azure.core.credentials.TokenCredential; -import com.azure.core.http.HttpClient; -import com.azure.core.http.HttpPipeline; -import com.azure.core.http.policy.AddDatePolicy; -import com.azure.core.http.policy.BearerTokenAuthenticationPolicy; -import com.azure.core.http.policy.HttpLogDetailLevel; -import com.azure.core.http.policy.HttpLoggingPolicy; -import com.azure.core.http.policy.HttpPipelinePolicy; -import com.azure.core.http.policy.RequestIdPolicy; -import com.azure.core.http.policy.UserAgentPolicy; -import com.azure.core.implementation.util.ImplUtils; -import com.azure.core.util.configuration.Configuration; -import com.azure.core.util.configuration.ConfigurationManager; -import com.azure.storage.blob.implementation.AzureBlobStorageBuilder; -import com.azure.storage.common.credentials.SASTokenCredential; -import com.azure.storage.common.credentials.SharedKeyCredential; -import com.azure.storage.common.policy.RequestRetryOptions; -import com.azure.storage.common.policy.RequestRetryPolicy; -import com.azure.storage.common.policy.SASTokenCredentialPolicy; -import com.azure.storage.common.policy.SharedKeyCredentialPolicy; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; - -/** - * Fluent BlockBlobClientBuilder for instantiating a {@link BlockBlobClient} or {@link BlockBlobAsyncClient} - * using {@link BlockBlobClientBuilder#buildClient()} or {@link BlockBlobClientBuilder#buildAsyncClient()} respectively. - * - *

- * The following information must be provided on this builder: - * - *

    - *
  • the endpoint through {@code .endpoint()}, including the container name and blob name, in the format of {@code https://{accountName}.blob.core.windows.net/{containerName}/{blobName}}. - *
  • the credential through {@code .credential()} or {@code .connectionString()} if the container is not publicly accessible. - *
- * - *

- * Once all the configurations are set on this builder, call {@code .buildClient()} to create a - * {@link BlockBlobClient} or {@code .buildAsyncClient()} to create a {@link BlockBlobAsyncClient}. - */ -public final class BlockBlobClientBuilder { - private static final String ACCOUNT_NAME = "accountname"; - private static final String ACCOUNT_KEY = "accountkey"; - private static final String ENDPOINT_PROTOCOL = "defaultendpointsprotocol"; - private static final String ENDPOINT_SUFFIX = "endpointsuffix"; - - private final List policies; - - private String endpoint; - private String containerName; - private String blobName; - private String snapshot; - private SharedKeyCredential sharedKeyCredential; - private TokenCredential tokenCredential; - private SASTokenCredential sasTokenCredential; - private HttpClient httpClient; - private HttpLogDetailLevel logLevel; - private RequestRetryOptions retryOptions; - private Configuration configuration; - - /** - * Creates a builder instance that is able to configure and construct {@link BlockBlobClient BlockBlobClients} - * and {@link BlockBlobAsyncClient BlockBlobAsyncClients}. - */ - public BlockBlobClientBuilder() { - retryOptions = new RequestRetryOptions(); - logLevel = HttpLogDetailLevel.NONE; - policies = new ArrayList<>(); - } - - /** - * Constructs an instance of BlockBlobAsyncClient based on the configurations stored in the appendBlobClientBuilder. - * @return a new client instance - */ - private AzureBlobStorageBuilder buildImpl() { - Objects.requireNonNull(endpoint); - Objects.requireNonNull(containerName); - Objects.requireNonNull(blobName); - - // Closest to API goes first, closest to wire goes last. - final List policies = new ArrayList<>(); - - if (configuration == null) { - configuration = ConfigurationManager.getConfiguration(); - } - policies.add(new UserAgentPolicy(BlobConfiguration.NAME, BlobConfiguration.VERSION, configuration)); - policies.add(new RequestIdPolicy()); - policies.add(new AddDatePolicy()); - - if (sharedKeyCredential != null) { - policies.add(new SharedKeyCredentialPolicy(sharedKeyCredential)); - } else if (tokenCredential != null) { - policies.add(new BearerTokenAuthenticationPolicy(tokenCredential, String.format("%s/.default", endpoint))); - } else if (sasTokenCredential != null) { - policies.add(new SASTokenCredentialPolicy(sasTokenCredential)); - } else { - policies.add(new AnonymousCredentialPolicy()); - } - - policies.add(new RequestRetryPolicy(retryOptions)); - - policies.addAll(this.policies); - policies.add(new HttpLoggingPolicy(logLevel)); - - HttpPipeline pipeline = HttpPipeline.builder() - .policies(policies.toArray(new HttpPipelinePolicy[0])) - .httpClient(httpClient) - .build(); - - return new AzureBlobStorageBuilder() - .url(String.format("%s/%s/%s", endpoint, containerName, blobName)) - .pipeline(pipeline); - } - - /** - * @return a {@link BlockBlobClient} created from the configurations in this builder. - */ - public BlockBlobClient buildClient() { - return new BlockBlobClient(buildAsyncClient()); - } - - /** - * @return a {@link BlockBlobAsyncClient} created from the configurations in this builder. - */ - public BlockBlobAsyncClient buildAsyncClient() { - return new BlockBlobAsyncClient(buildImpl(), snapshot); - } - - /** - * Sets the service endpoint, additionally parses it for information (SAS token, container name, blob name) - * @param endpoint URL of the service - * @return the updated BlockBlobClientBuilder object - * @throws IllegalArgumentException If {@code endpoint} is a malformed URL. - */ - public BlockBlobClientBuilder endpoint(String endpoint) { - Objects.requireNonNull(endpoint); - URL url; - try { - url = new URL(endpoint); - BlobURLParts parts = URLParser.parse(url); - this.endpoint = parts.scheme() + "://" + parts.host(); - - if (parts.containerName() != null) { - this.containerName = parts.containerName(); - } - - if (parts.blobName() != null) { - this.blobName = parts.blobName(); - } - - if (parts.snapshot() != null) { - this.snapshot = parts.snapshot(); - } - } catch (MalformedURLException ex) { - throw new IllegalArgumentException("The Azure Storage Blob endpoint url is malformed."); - } - - SASTokenCredential credential = SASTokenCredential.fromQuery(url.getQuery()); - if (credential != null) { - this.credential(credential); - } - - return this; - } - - /** - * Sets the name of the container this client is connecting to. - * @param containerName the name of the container - * @return the updated BlockBlobClientBuilder object - */ - public BlockBlobClientBuilder containerName(String containerName) { - this.containerName = containerName; - return this; - } - - /** - * Sets the name of the blob this client is connecting to. - * @param blobName the name of the blob - * @return the updated BlockBlobClientBuilder object - */ - public BlockBlobClientBuilder blobName(String blobName) { - this.blobName = blobName; - return this; - } - - /** - * Sets the snapshot of the blob this client is connecting to. - * @param snapshot the snapshot identifier for the blob - * @return the updated BlockBlobClientBuilder object - */ - public BlockBlobClientBuilder snapshot(String snapshot) { - this.snapshot = snapshot; - return this; - } - - /** - * Sets the credential used to authorize requests sent to the service - * @param credential authorization credential - * @return the updated BlockBlobClientBuilder object - */ - public BlockBlobClientBuilder credential(SharedKeyCredential credential) { - this.sharedKeyCredential = credential; - this.tokenCredential = null; - this.sasTokenCredential = null; - return this; - } - - /** - * Sets the credential used to authorize requests sent to the service - * @param credential authorization credential - * @return the updated BlockBlobClientBuilder object - */ - public BlockBlobClientBuilder credential(TokenCredential credential) { - this.tokenCredential = credential; - this.sharedKeyCredential = null; - this.sasTokenCredential = null; - return this; - } - - /** - * Sets the credential used to authorize requests sent to the service - * @param credential authorization credential - * @return the updated BlockBlobClientBuilder object - */ - public BlockBlobClientBuilder credential(SASTokenCredential credential) { - this.sasTokenCredential = credential; - this.sharedKeyCredential = null; - this.tokenCredential = null; - return this; - } - - /** - * Clears the credential used to authorize requests sent to the service - * @return the updated BlockBlobClientBuilder object - */ - public BlockBlobClientBuilder anonymousCredential() { - this.sharedKeyCredential = null; - this.tokenCredential = null; - this.sasTokenCredential = null; - return this; - } - - /** - * Sets the connection string for the service, parses it for authentication information (account name, account key) - * @param connectionString connection string from access keys section - * @return the updated BlockBlobClientBuilder object - * @throws IllegalArgumentException If {@code connectionString} doesn't contain AccountName or AccountKey - */ - public BlockBlobClientBuilder connectionString(String connectionString) { - Objects.requireNonNull(connectionString); - - Map connectionKVPs = new HashMap<>(); - for (String s : connectionString.split(";")) { - String[] kvp = s.split("=", 2); - connectionKVPs.put(kvp[0].toLowerCase(Locale.ROOT), kvp[1]); - } - - String accountName = connectionKVPs.get(ACCOUNT_NAME); - String accountKey = connectionKVPs.get(ACCOUNT_KEY); - String endpointProtocol = connectionKVPs.get(ENDPOINT_PROTOCOL); - String endpointSuffix = connectionKVPs.get(ENDPOINT_SUFFIX); - - if (ImplUtils.isNullOrEmpty(accountName) || ImplUtils.isNullOrEmpty(accountKey)) { - throw new IllegalArgumentException("Connection string must contain 'AccountName' and 'AccountKey'."); - } - - if (!ImplUtils.isNullOrEmpty(endpointProtocol) && !ImplUtils.isNullOrEmpty(endpointSuffix)) { - String endpoint = String.format("%s://%s.blob.%s", endpointProtocol, accountName, endpointSuffix.replaceFirst("^\\.", "")); - endpoint(endpoint); - } - - // Use accountName and accountKey to get the SAS token using the credential class. - return credential(new SharedKeyCredential(accountName, accountKey)); - } - - /** - * Sets the http client used to send service requests - * @param httpClient http client to send requests - * @return the updated BlockBlobClientBuilder object - */ - public BlockBlobClientBuilder httpClient(HttpClient httpClient) { - this.httpClient = httpClient; - return this; - } - - /** - * Adds a pipeline policy to apply on each request sent - * @param pipelinePolicy a pipeline policy - * @return the updated BlockBlobClientBuilder object - */ - public BlockBlobClientBuilder addPolicy(HttpPipelinePolicy pipelinePolicy) { - this.policies.add(pipelinePolicy); - return this; - } - - /** - * Sets the logging level for service requests - * @param logLevel logging level - * @return the updated BlockBlobClientBuilder object - */ - public BlockBlobClientBuilder httpLogDetailLevel(HttpLogDetailLevel logLevel) { - this.logLevel = logLevel; - return this; - } - - /** - * Sets the configuration object used to retrieve environment configuration values used to buildClient the client with - * when they are not set in the appendBlobClientBuilder, defaults to Configuration.NONE - * @param configuration configuration store - * @return the updated BlockBlobClientBuilder object - */ - public BlockBlobClientBuilder configuration(Configuration configuration) { - this.configuration = configuration; - return this; - } - - /** - * Sets the request retry options for all the requests made through the client. - * @param retryOptions the options to configure retry behaviors - * @return the updated BlockBlobClientBuilder object - */ - public BlockBlobClientBuilder retryOptions(RequestRetryOptions retryOptions) { - this.retryOptions = retryOptions; - return this; - } -} diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/Constants.java b/storage/client/blob/src/main/java/com/azure/storage/blob/Constants.java index 653625b572c15..fa812df4b531b 100644 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/Constants.java +++ b/storage/client/blob/src/main/java/com/azure/storage/blob/Constants.java @@ -288,6 +288,21 @@ static final class UrlConstants { */ public static final String SAS_SIGNED_KEY_VERSION = "skv"; + /** + * The SAS blob constant. + */ + public static final String SAS_BLOB_CONSTANT = "b"; + + /** + * The SAS blob snapshot constant. + */ + public static final String SAS_BLOB_SNAPSHOT_CONSTANT = "bs"; + + /** + * The SAS blob snapshot constant. + */ + public static final String SAS_CONTAINER_CONSTANT = "c"; + private UrlConstants() { // Private to prevent construction. } diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/ContainerAsyncClient.java b/storage/client/blob/src/main/java/com/azure/storage/blob/ContainerAsyncClient.java index 406a7440b0c5b..936bab6932528 100644 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/ContainerAsyncClient.java +++ b/storage/client/blob/src/main/java/com/azure/storage/blob/ContainerAsyncClient.java @@ -9,6 +9,7 @@ import com.azure.core.http.rest.VoidResponse; import com.azure.core.util.Context; import com.azure.storage.blob.implementation.AzureBlobStorageBuilder; +import com.azure.storage.blob.implementation.AzureBlobStorageImpl; import com.azure.storage.blob.models.BlobFlatListSegment; import com.azure.storage.blob.models.BlobHierarchyListSegment; import com.azure.storage.blob.models.BlobItem; @@ -24,63 +25,68 @@ import com.azure.storage.blob.models.PublicAccessType; import com.azure.storage.blob.models.SignedIdentifier; import com.azure.storage.blob.models.StorageAccountInfo; +import com.azure.storage.blob.models.UserDelegationKey; +import com.azure.storage.common.credentials.SharedKeyCredential; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.net.MalformedURLException; import java.net.URL; import java.time.Duration; +import java.time.OffsetDateTime; +import java.time.temporal.ChronoUnit; import java.util.List; +import static com.azure.storage.blob.Utility.postProcessResponse; + /** - * Client to a container. It may only be instantiated through a {@link ContainerClientBuilder} or via the method - * {@link StorageAsyncClient#getContainerAsyncClient(String)}. This class does not hold any - * state about a particular blob but is instead a convenient way of sending off appropriate requests to - * the resource on the service. It may also be used to construct URLs to blobs. + * Client to a container. It may only be instantiated through a {@link ContainerClientBuilder} or via the method {@link + * StorageAsyncClient#getContainerAsyncClient(String)}. This class does not hold any state about a particular blob but + * is instead a convenient way of sending off appropriate requests to the resource on the service. It may also be used + * to construct URLs to blobs. * *

* This client contains operations on a container. Operations on a blob are available on {@link BlobAsyncClient} through * {@link #getBlobAsyncClient(String)}, and operations on the service are available on {@link StorageAsyncClient}. * *

- * Please refer to the Azure Docs - * for more information on containers. + * Please refer to the Azure + * Docs for more information on containers. * *

- * Note this client is an async client that returns reactive responses from Spring Reactor Core - * project (https://projectreactor.io/). Calling the methods in this client will NOT - * start the actual network operation, until {@code .subscribe()} is called on the reactive response. - * You can simply convert one of these responses to a {@link java.util.concurrent.CompletableFuture} - * object through {@link Mono#toFuture()}. + * Note this client is an async client that returns reactive responses from Spring Reactor Core project + * (https://projectreactor.io/). Calling the methods in this client will NOT start the actual network + * operation, until {@code .subscribe()} is called on the reactive response. You can simply convert one of these + * responses to a {@link java.util.concurrent.CompletableFuture} object through {@link Mono#toFuture()}. */ public final class ContainerAsyncClient { - ContainerAsyncRawClient containerAsyncRawClient; - public static final String ROOT_CONTAINER_NAME = "$root"; public static final String STATIC_WEBSITE_CONTAINER_NAME = "$web"; public static final String LOG_CONTAINER_NAME = "$logs"; + private final AzureBlobStorageImpl azureBlobStorage; + /** * Package-private constructor for use by {@link ContainerClientBuilder}. + * * @param azureBlobStorageBuilder the API client builder for blob storage API */ ContainerAsyncClient(AzureBlobStorageBuilder azureBlobStorageBuilder) { - this.containerAsyncRawClient = new ContainerAsyncRawClient(azureBlobStorageBuilder.build()); + this.azureBlobStorage = azureBlobStorageBuilder.build(); } /** * Creates a new {@link BlockBlobAsyncClient} object by concatenating the blobName to the end of - * ContainerAsyncClient's URL. The new BlockBlobAsyncClient uses the same request policy pipeline as the ContainerAsyncClient. - * To change the pipeline, create the BlockBlobAsyncClient and then call its WithPipeline method passing in the - * desired pipeline object. Or, call this package's NewBlockBlobAsyncClient instead of calling this object's - * NewBlockBlobAsyncClient method. - * - * @param blobName - * A {@code String} representing the name of the blob. - * - * @return A new {@link BlockBlobAsyncClient} object which references the blob with the specified name in this container. + * ContainerAsyncClient's URL. The new BlockBlobAsyncClient uses the same request policy pipeline as the + * ContainerAsyncClient. To change the pipeline, create the BlockBlobAsyncClient and then call its WithPipeline + * method passing in the desired pipeline object. Or, call this package's NewBlockBlobAsyncClient instead of calling + * this object's NewBlockBlobAsyncClient method. + * + * @param blobName A {@code String} representing the name of the blob. + * @return A new {@link BlockBlobAsyncClient} object which references the blob with the specified name in this + * container. */ public BlockBlobAsyncClient getBlockBlobAsyncClient(String blobName) { return getBlockBlobAsyncClient(blobName, null); @@ -88,106 +94,95 @@ public BlockBlobAsyncClient getBlockBlobAsyncClient(String blobName) { /** * Creates a new {@link BlockBlobAsyncClient} object by concatenating the blobName to the end of - * ContainerAsyncClient's URL. The new BlockBlobAsyncClient uses the same request policy pipeline as the ContainerAsyncClient. - * To change the pipeline, create the BlockBlobAsyncClient and then call its WithPipeline method passing in the - * desired pipeline object. Or, call this package's NewBlockBlobAsyncClient instead of calling this object's - * NewBlockBlobAsyncClient method. - * - * @param blobName - * A {@code String} representing the name of the blob. - * @param snapshot - * the snapshot identifier for the blob. - * - * @return A new {@link BlockBlobAsyncClient} object which references the blob with the specified name in this container. + * ContainerAsyncClient's URL. The new BlockBlobAsyncClient uses the same request policy pipeline as the + * ContainerAsyncClient. To change the pipeline, create the BlockBlobAsyncClient and then call its WithPipeline + * method passing in the desired pipeline object. Or, call this package's NewBlockBlobAsyncClient instead of calling + * this object's NewBlockBlobAsyncClient method. + * + * @param blobName A {@code String} representing the name of the blob. + * @param snapshot the snapshot identifier for the blob. + * @return A new {@link BlockBlobAsyncClient} object which references the blob with the specified name in this + * container. */ public BlockBlobAsyncClient getBlockBlobAsyncClient(String blobName, String snapshot) { return new BlockBlobAsyncClient(new AzureBlobStorageBuilder() .url(Utility.appendToURLPath(getContainerUrl(), blobName).toString()) - .pipeline(containerAsyncRawClient.azureBlobStorage.httpPipeline()), snapshot); + .pipeline(azureBlobStorage.httpPipeline()), snapshot); } /** - * Creates creates a new PageBlobAsyncClient object by concatenating blobName to the end of - * ContainerAsyncClient's URL. The new PageBlobAsyncClient uses the same request policy pipeline as the ContainerAsyncClient. - * To change the pipeline, create the PageBlobAsyncClient and then call its WithPipeline method passing in the - * desired pipeline object. Or, call this package's NewPageBlobAsyncClient instead of calling this object's - * NewPageBlobAsyncClient method. - * - * @param blobName - * A {@code String} representing the name of the blob. - * - * @return A new {@link PageBlobAsyncClient} object which references the blob with the specified name in this container. + * Creates creates a new PageBlobAsyncClient object by concatenating blobName to the end of ContainerAsyncClient's + * URL. The new PageBlobAsyncClient uses the same request policy pipeline as the ContainerAsyncClient. To change the + * pipeline, create the PageBlobAsyncClient and then call its WithPipeline method passing in the desired pipeline + * object. Or, call this package's NewPageBlobAsyncClient instead of calling this object's NewPageBlobAsyncClient + * method. + * + * @param blobName A {@code String} representing the name of the blob. + * @return A new {@link PageBlobAsyncClient} object which references the blob with the specified name in this + * container. */ public PageBlobAsyncClient getPageBlobAsyncClient(String blobName) { return getPageBlobAsyncClient(blobName, null); } /** - * Creates creates a new PageBlobAsyncClient object by concatenating blobName to the end of - * ContainerAsyncClient's URL. The new PageBlobAsyncClient uses the same request policy pipeline as the ContainerAsyncClient. - * To change the pipeline, create the PageBlobAsyncClient and then call its WithPipeline method passing in the - * desired pipeline object. Or, call this package's NewPageBlobAsyncClient instead of calling this object's - * NewPageBlobAsyncClient method. - * - * @param blobName - * A {@code String} representing the name of the blob. - * @param snapshot - * the snapshot identifier for the blob. - * - * @return A new {@link PageBlobAsyncClient} object which references the blob with the specified name in this container. + * Creates creates a new PageBlobAsyncClient object by concatenating blobName to the end of ContainerAsyncClient's + * URL. The new PageBlobAsyncClient uses the same request policy pipeline as the ContainerAsyncClient. To change the + * pipeline, create the PageBlobAsyncClient and then call its WithPipeline method passing in the desired pipeline + * object. Or, call this package's NewPageBlobAsyncClient instead of calling this object's NewPageBlobAsyncClient + * method. + * + * @param blobName A {@code String} representing the name of the blob. + * @param snapshot the snapshot identifier for the blob. + * @return A new {@link PageBlobAsyncClient} object which references the blob with the specified name in this + * container. */ public PageBlobAsyncClient getPageBlobAsyncClient(String blobName, String snapshot) { return new PageBlobAsyncClient(new AzureBlobStorageBuilder() .url(Utility.appendToURLPath(getContainerUrl(), blobName).toString()) - .pipeline(containerAsyncRawClient.azureBlobStorage.httpPipeline()), snapshot); + .pipeline(azureBlobStorage.httpPipeline()), snapshot); } /** - * Creates creates a new AppendBlobAsyncClient object by concatenating blobName to the end of - * ContainerAsyncClient's URL. The new AppendBlobAsyncClient uses the same request policy pipeline as the ContainerAsyncClient. - * To change the pipeline, create the AppendBlobAsyncClient and then call its WithPipeline method passing in the - * desired pipeline object. Or, call this package's NewAppendBlobAsyncClient instead of calling this object's + * Creates creates a new AppendBlobAsyncClient object by concatenating blobName to the end of ContainerAsyncClient's + * URL. The new AppendBlobAsyncClient uses the same request policy pipeline as the ContainerAsyncClient. To change + * the pipeline, create the AppendBlobAsyncClient and then call its WithPipeline method passing in the desired + * pipeline object. Or, call this package's NewAppendBlobAsyncClient instead of calling this object's * NewAppendBlobAsyncClient method. * - * @param blobName - * A {@code String} representing the name of the blob. - * - * @return A new {@link AppendBlobAsyncClient} object which references the blob with the specified name in this container. + * @param blobName A {@code String} representing the name of the blob. + * @return A new {@link AppendBlobAsyncClient} object which references the blob with the specified name in this + * container. */ public AppendBlobAsyncClient getAppendBlobAsyncClient(String blobName) { return getAppendBlobAsyncClient(blobName, null); } /** - * Creates creates a new AppendBlobAsyncClient object by concatenating blobName to the end of - * ContainerAsyncClient's URL. The new AppendBlobAsyncClient uses the same request policy pipeline as the ContainerAsyncClient. - * To change the pipeline, create the AppendBlobAsyncClient and then call its WithPipeline method passing in the - * desired pipeline object. Or, call this package's NewAppendBlobAsyncClient instead of calling this object's + * Creates creates a new AppendBlobAsyncClient object by concatenating blobName to the end of ContainerAsyncClient's + * URL. The new AppendBlobAsyncClient uses the same request policy pipeline as the ContainerAsyncClient. To change + * the pipeline, create the AppendBlobAsyncClient and then call its WithPipeline method passing in the desired + * pipeline object. Or, call this package's NewAppendBlobAsyncClient instead of calling this object's * NewAppendBlobAsyncClient method. * - * @param blobName - * A {@code String} representing the name of the blob. - * @param snapshot - * the snapshot identifier for the blob. - * - * @return A new {@link AppendBlobAsyncClient} object which references the blob with the specified name in this container. + * @param blobName A {@code String} representing the name of the blob. + * @param snapshot the snapshot identifier for the blob. + * @return A new {@link AppendBlobAsyncClient} object which references the blob with the specified name in this + * container. */ public AppendBlobAsyncClient getAppendBlobAsyncClient(String blobName, String snapshot) { return new AppendBlobAsyncClient(new AzureBlobStorageBuilder() .url(Utility.appendToURLPath(getContainerUrl(), blobName).toString()) - .pipeline(containerAsyncRawClient.azureBlobStorage.httpPipeline()), snapshot); + .pipeline(azureBlobStorage.httpPipeline()), snapshot); } /** - * Creates a new BlobAsyncClient object by concatenating blobName to the end of - * ContainerAsyncClient's URL. The new BlobAsyncClient uses the same request policy pipeline as the ContainerAsyncClient. - * To change the pipeline, create the BlobAsyncClient and then call its WithPipeline method passing in the - * desired pipeline object. Or, call this package's getBlobAsyncClient instead of calling this object's - * getBlobAsyncClient method. - * - * @param blobName - * A {@code String} representing the name of the blob. + * Creates a new BlobAsyncClient object by concatenating blobName to the end of ContainerAsyncClient's URL. The new + * BlobAsyncClient uses the same request policy pipeline as the ContainerAsyncClient. To change the pipeline, create + * the BlobAsyncClient and then call its WithPipeline method passing in the desired pipeline object. Or, call this + * package's getBlobAsyncClient instead of calling this object's getBlobAsyncClient method. * + * @param blobName A {@code String} representing the name of the blob. * @return A new {@link BlobAsyncClient} object which references the blob with the specified name in this container. */ public BlobAsyncClient getBlobAsyncClient(String blobName) { @@ -195,55 +190,50 @@ public BlobAsyncClient getBlobAsyncClient(String blobName) { } /** - * Creates a new BlobAsyncClient object by concatenating blobName to the end of - * ContainerAsyncClient's URL. The new BlobAsyncClient uses the same request policy pipeline as the ContainerAsyncClient. - * To change the pipeline, create the BlobAsyncClient and then call its WithPipeline method passing in the - * desired pipeline object. Or, call this package's getBlobAsyncClient instead of calling this object's - * getBlobAsyncClient method. - * - * @param blobName - * A {@code String} representing the name of the blob. - * @param snapshot - * the snapshot identifier for the blob. + * Creates a new BlobAsyncClient object by concatenating blobName to the end of ContainerAsyncClient's URL. The new + * BlobAsyncClient uses the same request policy pipeline as the ContainerAsyncClient. To change the pipeline, create + * the BlobAsyncClient and then call its WithPipeline method passing in the desired pipeline object. Or, call this + * package's getBlobAsyncClient instead of calling this object's getBlobAsyncClient method. * + * @param blobName A {@code String} representing the name of the blob. + * @param snapshot the snapshot identifier for the blob. * @return A new {@link BlobAsyncClient} object which references the blob with the specified name in this container. */ public BlobAsyncClient getBlobAsyncClient(String blobName, String snapshot) { return new BlobAsyncClient(new AzureBlobStorageBuilder() .url(Utility.appendToURLPath(getContainerUrl(), blobName).toString()) - .pipeline(containerAsyncRawClient.azureBlobStorage.httpPipeline()), snapshot); + .pipeline(azureBlobStorage.httpPipeline()), snapshot); } /** * Initializes a {@link StorageAsyncClient} object pointing to the storage account this container is in. * - * @return - * A {@link StorageAsyncClient} object pointing to the specified storage account + * @return A {@link StorageAsyncClient} object pointing to the specified storage account */ public StorageAsyncClient getStorageAsyncClient() { return new StorageAsyncClient(new AzureBlobStorageBuilder() .url(Utility.stripLastPathSegment(getContainerUrl()).toString()) - .pipeline(containerAsyncRawClient.azureBlobStorage.httpPipeline())); + .pipeline(azureBlobStorage.httpPipeline())); } /** * Gets the URL of the container represented by this client. + * * @return the URL. * @throws RuntimeException If the container has a malformed URL. */ public URL getContainerUrl() { try { - return new URL(containerAsyncRawClient.azureBlobStorage.url()); + return new URL(azureBlobStorage.url()); } catch (MalformedURLException e) { - throw new RuntimeException(String.format("Invalid URL on %s: %s" + getClass().getSimpleName(), containerAsyncRawClient.azureBlobStorage.url()), e); + throw new RuntimeException(String.format("Invalid URL on %s: %s" + getClass().getSimpleName(), azureBlobStorage.url()), e); } } /** * Gets if the container this client represents exists in the cloud. * - * @return - * true if the container exists, false if it doesn't + * @return true if the container exists, false if it doesn't */ public Mono> exists() { return this.getProperties(null) @@ -259,8 +249,7 @@ public Mono> exists() { * fails. For more information, see the * Azure Docs. * - * @return - * A reactive response signalling completion. + * @return A reactive response signalling completion. */ public Mono create() { return this.create(null, null); @@ -271,47 +260,52 @@ public Mono create() { * fails. For more information, see the * Azure Docs. * - * @param metadata - * {@link Metadata} - * @param accessType - * Specifies how the data in this container is available to the public. See the x-ms-blob-public-access header - * in the Azure Docs for more information. Pass null for no public access. - * - * @return - * A reactive response signalling completion. + * @param metadata {@link Metadata} + * @param accessType Specifies how the data in this container is available to the public. See the + * x-ms-blob-public-access header in the Azure Docs for more information. Pass null for no public access. + * @return A reactive response signalling completion. */ public Mono create(Metadata metadata, PublicAccessType accessType) { - return containerAsyncRawClient - .create(metadata, accessType) + metadata = metadata == null ? new Metadata() : metadata; + + return postProcessResponse(this.azureBlobStorage.containers().createWithRestResponseAsync( + null, null, metadata, accessType, null, Context.NONE)) .map(VoidResponse::new); } /** - * Marks the specified container for deletion. The container and any blobs contained within it are later - * deleted during garbage collection. For more information, see the + * Marks the specified container for deletion. The container and any blobs contained within it are later deleted + * during garbage collection. For more information, see the * Azure Docs. * - * @return - * A reactive response signalling completion. + * @return A reactive response signalling completion. */ public Mono delete() { return this.delete(null); } /** - * Marks the specified container for deletion. The container and any blobs contained within it are later - * deleted during garbage collection. For more information, see the + * Marks the specified container for deletion. The container and any blobs contained within it are later deleted + * during garbage collection. For more information, see the * Azure Docs. * - * @param accessConditions - * {@link ContainerAccessConditions} - * - * @return - * A reactive response signalling completion. + * @param accessConditions {@link ContainerAccessConditions} + * @return A reactive response signalling completion. + * @throws UnsupportedOperationException If {@link ContainerAccessConditions#modifiedAccessConditions()} has either + * {@link ModifiedAccessConditions#ifMatch()} or {@link ModifiedAccessConditions#ifNoneMatch()} set. */ public Mono delete(ContainerAccessConditions accessConditions) { - return containerAsyncRawClient - .delete(accessConditions) + accessConditions = accessConditions == null ? new ContainerAccessConditions() : accessConditions; + + if (!validateNoEtag(accessConditions.modifiedAccessConditions())) { + // Throwing is preferred to Single.error because this will error out immediately instead of waiting until + // subscription. + throw new UnsupportedOperationException("ETag access conditions are not supported for this API."); + } + + return postProcessResponse(this.azureBlobStorage.containers() + .deleteWithRestResponseAsync(null, null, null, + accessConditions.leaseAccessConditions(), accessConditions.modifiedAccessConditions(), Context.NONE)) .map(VoidResponse::new); } @@ -319,8 +313,7 @@ public Mono delete(ContainerAccessConditions accessConditions) { * Returns the container's metadata and system properties. For more information, see the * Azure Docs. * - * @return - * A reactive response containing the container properties. + * @return A reactive response containing the container properties. */ public Mono> getProperties() { return this.getProperties(null); @@ -330,16 +323,14 @@ public Mono> getProperties() { * Returns the container's metadata and system properties. For more information, see the * Azure Docs. * - * @param leaseAccessConditions - * By setting lease access conditions, requests will fail if the provided lease does not match the active - * lease on the blob. - * - * @return - * A reactive response containing the container properties. + * @param leaseAccessConditions By setting lease access conditions, requests will fail if the provided lease does + * not match the active lease on the blob. + * @return A reactive response containing the container properties. */ public Mono> getProperties(LeaseAccessConditions leaseAccessConditions) { - return containerAsyncRawClient - .getProperties(leaseAccessConditions) + return postProcessResponse(this.azureBlobStorage.containers() + .getPropertiesWithRestResponseAsync(null, null, null, + leaseAccessConditions, Context.NONE)) .map(rb -> new SimpleResponse<>(rb, new ContainerProperties(rb.deserializedHeaders()))); } @@ -347,11 +338,8 @@ public Mono> getProperties(LeaseAccessConditions l * Sets the container's metadata. For more information, see the * Azure Docs. * - * @param metadata - * {@link Metadata} - * - * @return - * A reactive response signalling completion. + * @param metadata {@link Metadata} + * @return A reactive response signalling completion. */ public Mono setMetadata(Metadata metadata) { return this.setMetadata(metadata, null); @@ -361,18 +349,26 @@ public Mono setMetadata(Metadata metadata) { * Sets the container's metadata. For more information, see the * Azure Docs. * - * @param metadata - * {@link Metadata} - * @param accessConditions - * {@link ContainerAccessConditions} - * - * @return - * A reactive response signalling completion. + * @param metadata {@link Metadata} + * @param accessConditions {@link ContainerAccessConditions} + * @return A reactive response signalling completion. + * @throws UnsupportedOperationException If {@link ContainerAccessConditions#modifiedAccessConditions()} has + * anything set other than {@link ModifiedAccessConditions#ifModifiedSince()}. */ - public Mono setMetadata(Metadata metadata, - ContainerAccessConditions accessConditions) { - return containerAsyncRawClient - .setMetadata(metadata, accessConditions) + public Mono setMetadata(Metadata metadata, ContainerAccessConditions accessConditions) { + metadata = metadata == null ? new Metadata() : metadata; + accessConditions = accessConditions == null ? new ContainerAccessConditions() : accessConditions; + if (!validateNoEtag(accessConditions.modifiedAccessConditions()) + || accessConditions.modifiedAccessConditions().ifUnmodifiedSince() != null) { + // Throwing is preferred to Single.error because this will error out immediately instead of waiting until + // subscription. + throw new UnsupportedOperationException( + "If-Modified-Since is the only HTTP access condition supported for this API"); + } + + return postProcessResponse(this.azureBlobStorage.containers() + .setMetadataWithRestResponseAsync(null, null, metadata, null, + accessConditions.leaseAccessConditions(), accessConditions.modifiedAccessConditions(), Context.NONE)) .map(VoidResponse::new); } @@ -381,8 +377,7 @@ public Mono setMetadata(Metadata metadata, * For more information, see the * Azure Docs. * - * @return - * A reactive response containing the container access policy. + * @return A reactive response containing the container access policy. */ public Mono> getAccessPolicy() { return this.getAccessPolicy(null); @@ -393,15 +388,13 @@ public Mono> getAccessPolicy() { * For more information, see the * Azure Docs. * - * @param leaseAccessConditions - * By setting lease access conditions, requests will fail if the provided lease does not match the active - * lease on the blob. - * - * @return - * A reactive response containing the container access policy. + * @param leaseAccessConditions By setting lease access conditions, requests will fail if the provided lease does + * not match the active lease on the blob. + * @return A reactive response containing the container access policy. */ public Mono> getAccessPolicy(LeaseAccessConditions leaseAccessConditions) { - return containerAsyncRawClient.getAccessPolicy(leaseAccessConditions); + return postProcessResponse(this.azureBlobStorage.containers().getAccessPolicyWithRestResponseAsync(null, null, null, leaseAccessConditions, Context.NONE) + .map(response -> new SimpleResponse<>(response, new ContainerAccessPolicies(response.deserializedHeaders().blobPublicAccess(), response.value())))); } /** @@ -410,19 +403,16 @@ public Mono> getAccessPolicy(LeaseAccessCondit * ensure the time formatting is compatible with the service. For more information, see the * Azure Docs. * - * @param accessType - * Specifies how the data in this container is available to the public. See the x-ms-blob-public-access header - * in the Azure Docs for more information. Pass null for no public access. - * @param identifiers - * A list of {@link SignedIdentifier} objects that specify the permissions for the container. Please see - * here - * for more information. Passing null will clear all access policies. - * - * @return - * A reactive response signalling completion. + * @param accessType Specifies how the data in this container is available to the public. See the + * x-ms-blob-public-access header in the Azure Docs for more information. Pass null for no public access. + * @param identifiers A list of {@link SignedIdentifier} objects that specify the permissions for the container. + * Please see + * here + * for more information. Passing null will clear all access policies. + * @return A reactive response signalling completion. */ public Mono setAccessPolicy(PublicAccessType accessType, - List identifiers) { + List identifiers) { return this.setAccessPolicy(accessType, identifiers, null); } @@ -432,79 +422,128 @@ public Mono setAccessPolicy(PublicAccessType accessType, * ensure the time formatting is compatible with the service. For more information, see the * Azure Docs. * - * @param accessType - * Specifies how the data in this container is available to the public. See the x-ms-blob-public-access header - * in the Azure Docs for more information. Pass null for no public access. - * @param identifiers - * A list of {@link SignedIdentifier} objects that specify the permissions for the container. Please see - * here - * for more information. Passing null will clear all access policies. - * @param accessConditions - * {@link ContainerAccessConditions} - * - * @return - * A reactive response signalling completion. + * @param accessType Specifies how the data in this container is available to the public. See the + * x-ms-blob-public-access header in the Azure Docs for more information. Pass null for no public access. + * @param identifiers A list of {@link SignedIdentifier} objects that specify the permissions for the container. + * Please see + * here + * for more information. Passing null will clear all access policies. + * @param accessConditions {@link ContainerAccessConditions} + * @return A reactive response signalling completion. + * @throws UnsupportedOperationException If {@link ContainerAccessConditions#modifiedAccessConditions()} has either + * {@link ModifiedAccessConditions#ifMatch()} or {@link ModifiedAccessConditions#ifNoneMatch()} set. */ - public Mono setAccessPolicy(PublicAccessType accessType, - List identifiers, ContainerAccessConditions accessConditions) { - return containerAsyncRawClient - .setAccessPolicy(accessType, identifiers, accessConditions) + public Mono setAccessPolicy(PublicAccessType accessType, List identifiers, ContainerAccessConditions accessConditions) { + accessConditions = accessConditions == null ? new ContainerAccessConditions() : accessConditions; + + if (!validateNoEtag(accessConditions.modifiedAccessConditions())) { + // Throwing is preferred to Single.error because this will error out immediately instead of waiting until + // subscription. + throw new UnsupportedOperationException("ETag access conditions are not supported for this API."); + } + + /* + We truncate to seconds because the service only supports nanoseconds or seconds, but doing an + OffsetDateTime.now will only give back milliseconds (more precise fields are zeroed and not serialized). This + allows for proper serialization with no real detriment to users as sub-second precision on active time for + signed identifiers is not really necessary. + */ + if (identifiers != null) { + for (SignedIdentifier identifier : identifiers) { + if (identifier.accessPolicy() != null && identifier.accessPolicy().start() != null) { + identifier.accessPolicy().start( + identifier.accessPolicy().start().truncatedTo(ChronoUnit.SECONDS)); + } + if (identifier.accessPolicy() != null && identifier.accessPolicy().expiry() != null) { + identifier.accessPolicy().expiry( + identifier.accessPolicy().expiry().truncatedTo(ChronoUnit.SECONDS)); + } + } + } + + return postProcessResponse(this.azureBlobStorage.containers() + .setAccessPolicyWithRestResponseAsync(null, identifiers, null, accessType, + null, accessConditions.leaseAccessConditions(), accessConditions.modifiedAccessConditions(), + Context.NONE)) .map(VoidResponse::new); } /** - * Returns a reactive Publisher emitting all the blobs in this container lazily as needed. - * The directories are flattened and only actual blobs and no directories are returned. + * Returns a reactive Publisher emitting all the blobs in this container lazily as needed. The directories are + * flattened and only actual blobs and no directories are returned. * *

* Blob names are returned in lexicographic order. For more information, see the * Azure Docs. * *

- * E.g. listing a container containing a 'foo' folder, which contains blobs 'foo1' and 'foo2', and a blob - * on the root level 'bar', will return + * E.g. listing a container containing a 'foo' folder, which contains blobs 'foo1' and 'foo2', and a blob on the + * root level 'bar', will return * *

    - *
  • foo/foo1 - *
  • foo/foo2 - *
  • bar + *
  • foo/foo1 + *
  • foo/foo2 + *
  • bar *
* - * @return - * A reactive response emitting the flattened blobs. + * @return A reactive response emitting the flattened blobs. */ public Flux listBlobsFlat() { return this.listBlobsFlat(new ListBlobsOptions()); } /** - * Returns a reactive Publisher emitting all the blobs in this container lazily as needed. - * The directories are flattened and only actual blobs and no directories are returned. + * Returns a reactive Publisher emitting all the blobs in this container lazily as needed. The directories are + * flattened and only actual blobs and no directories are returned. * *

* Blob names are returned in lexicographic order. For more information, see the * Azure Docs. * *

- * E.g. listing a container containing a 'foo' folder, which contains blobs 'foo1' and 'foo2', and a blob - * on the root level 'bar', will return + * E.g. listing a container containing a 'foo' folder, which contains blobs 'foo1' and 'foo2', and a blob on the + * root level 'bar', will return * *

    - *
  • foo/foo1 - *
  • foo/foo2 - *
  • bar + *
  • foo/foo1 + *
  • foo/foo2 + *
  • bar *
* + * @param options {@link ListBlobsOptions} + * @return A reactive response emitting the listed blobs, flattened. + */ + public Flux listBlobsFlat(ListBlobsOptions options) { + return listBlobsFlatSegment(null, options).flatMapMany(response -> listBlobsFlatHelper(options, response)); + } + + /* + * Returns a single segment of blobs starting from the specified Marker. Use an empty + * marker to start enumeration from the beginning. Blob names are returned in lexicographic order. + * After getting a segment, process it, and then call ListBlobs again (passing the the previously-returned + * Marker) to get the next segment. For more information, see the + * Azure Docs. + * + * @param marker + * Identifies the portion of the list to be returned with the next list operation. + * This value is returned in the response of a previous list operation as the + * ListBlobsFlatSegmentResponse.body().nextMarker(). Set to null to list the first segment. * @param options * {@link ListBlobsOptions} * - * @return - * A reactive response emitting the listed blobs, flattened. + * @return Emits the successful response. + * + * @apiNote ## Sample Code \n + * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=list_blobs_flat "Sample code for ContainerAsyncClient.listBlobsFlatSegment")] \n + * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=list_blobs_flat_helper "helper code for ContainerAsyncClient.listBlobsFlatSegment")] \n + * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) */ - public Flux listBlobsFlat(ListBlobsOptions options) { - return containerAsyncRawClient - .listBlobsFlatSegment(null, options) - .flatMapMany(response -> listBlobsFlatHelper(options, response)); + private Mono listBlobsFlatSegment(String marker, ListBlobsOptions options) { + options = options == null ? new ListBlobsOptions() : options; + + return postProcessResponse(this.azureBlobStorage.containers() + .listBlobFlatSegmentWithRestResponseAsync(null, options.prefix(), marker, + options.maxResults(), options.details().toList(), null, null, Context.NONE)); } private Flux listBlobsFlatHelper(ListBlobsOptions options, ContainersListBlobFlatSegmentResponse response) { @@ -518,7 +557,7 @@ private Flux listBlobsFlatHelper(ListBlobsOptions options, ContainersL if (response.value().nextMarker() != null) { // Recursively add the continuation items to the observable. - result = result.concatWith(containerAsyncRawClient.listBlobsFlatSegment(response.value().nextMarker(), options) + result = result.concatWith(listBlobsFlatSegment(response.value().nextMarker(), options) .flatMapMany(r -> listBlobsFlatHelper(options, r))); } @@ -526,80 +565,109 @@ private Flux listBlobsFlatHelper(ListBlobsOptions options, ContainersL } /** - * Returns a reactive Publisher emitting all the blobs and directories (prefixes) under - * the given directory (prefix). Directories will have {@link BlobItem#isPrefix()} set to - * true. + * Returns a reactive Publisher emitting all the blobs and directories (prefixes) under the given directory + * (prefix). Directories will have {@link BlobItem#isPrefix()} set to true. * *

* Blob names are returned in lexicographic order. For more information, see the * Azure Docs. * *

- * E.g. listing a container containing a 'foo' folder, which contains blobs 'foo1' and 'foo2', and a blob - * on the root level 'bar', will return the following results when prefix=null: + * E.g. listing a container containing a 'foo' folder, which contains blobs 'foo1' and 'foo2', and a blob on the + * root level 'bar', will return the following results when prefix=null: * *

    - *
  • foo/ (isPrefix = true) - *
  • bar (isPrefix = false) + *
  • foo/ (isPrefix = true) + *
  • bar (isPrefix = false) *
*

* will return the following results when prefix="foo/": * *

    - *
  • foo/foo1 (isPrefix = false) - *
  • foo/foo2 (isPrefix = false) + *
  • foo/foo1 (isPrefix = false) + *
  • foo/foo2 (isPrefix = false) *
* - * @param directory - * The directory to list blobs underneath - * - * @return - * A reactive response emitting the prefixes and blobs. + * @param directory The directory to list blobs underneath + * @return A reactive response emitting the prefixes and blobs. */ public Flux listBlobsHierarchy(String directory) { return this.listBlobsHierarchy("/", new ListBlobsOptions().prefix(directory)); } /** - * Returns a reactive Publisher emitting all the blobs and prefixes (directories) under - * the given prefix (directory). Directories will have {@link BlobItem#isPrefix()} set to - * true. + * Returns a reactive Publisher emitting all the blobs and prefixes (directories) under the given prefix + * (directory). Directories will have {@link BlobItem#isPrefix()} set to true. * *

* Blob names are returned in lexicographic order. For more information, see the * Azure Docs. * *

- * E.g. listing a container containing a 'foo' folder, which contains blobs 'foo1' and 'foo2', and a blob - * on the root level 'bar', will return the following results when prefix=null: + * E.g. listing a container containing a 'foo' folder, which contains blobs 'foo1' and 'foo2', and a blob on the + * root level 'bar', will return the following results when prefix=null: * *

    - *
  • foo/ (isPrefix = true) - *
  • bar (isPrefix = false) + *
  • foo/ (isPrefix = true) + *
  • bar (isPrefix = false) *
*

* will return the following results when prefix="foo/": * *

    - *
  • foo/foo1 (isPrefix = false) - *
  • foo/foo2 (isPrefix = false) + *
  • foo/foo1 (isPrefix = false) + *
  • foo/foo2 (isPrefix = false) *
* + * @param delimiter The delimiter for blob hierarchy, "/" for hierarchy based on directories + * @param options {@link ListBlobsOptions} + * @return A reactive response emitting the prefixes and blobs. + */ + public Flux listBlobsHierarchy(String delimiter, ListBlobsOptions options) { + return listBlobsHierarchySegment(null, delimiter, options) + .flatMapMany(response -> listBlobsHierarchyHelper(delimiter, options, Context.NONE, response)); + } + + /* + * Returns a single segment of blobs and blob prefixes starting from the specified Marker. Use an empty + * marker to start enumeration from the beginning. Blob names are returned in lexicographic order. + * After getting a segment, process it, and then call ListBlobs again (passing the the previously-returned + * Marker) to get the next segment. For more information, see the + * Azure Docs. + * + * @param marker + * Identifies the portion of the list to be returned with the next list operation. + * This value is returned in the response of a previous list operation as the + * ListBlobsHierarchySegmentResponse.body().nextMarker(). Set to null to list the first segment. * @param delimiter - * The delimiter for blob hierarchy, "/" for hierarchy based on directories + * The operation returns a BlobPrefix element in the response body that acts as a placeholder for all blobs + * whose names begin with the same substring up to the appearance of the delimiter character. The delimiter may + * be a single character or a string. * @param options * {@link ListBlobsOptions} * - * @return - * A reactive response emitting the prefixes and blobs. + * @return Emits the successful response. + * @throws UnsupportedOperationException If {@link ListBlobsOptions#details()} has {@link BlobListDetails#snapshots()} + * set. + * + * @apiNote ## Sample Code \n + * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=list_blobs_hierarchy "Sample code for ContainerAsyncClient.listBlobsHierarchySegment")] \n + * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=list_blobs_hierarchy_helper "helper code for ContainerAsyncClient.listBlobsHierarchySegment")] \n + * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) */ - public Flux listBlobsHierarchy(String delimiter, ListBlobsOptions options) { - return containerAsyncRawClient.listBlobsHierarchySegment(null, delimiter, options) - .flatMapMany(response -> listBlobsHierarchyHelper(delimiter, options, Context.NONE, response)); + private Mono listBlobsHierarchySegment(String marker, String delimiter, ListBlobsOptions options) { + options = options == null ? new ListBlobsOptions() : options; + if (options.details().snapshots()) { + throw new UnsupportedOperationException("Including snapshots in a hierarchical listing is not supported."); + } + + return postProcessResponse(this.azureBlobStorage.containers() + .listBlobHierarchySegmentWithRestResponseAsync(null, delimiter, options.prefix(), marker, + options.maxResults(), options.details().toList(), null, null, Context.NONE)); } private Flux listBlobsHierarchyHelper(String delimiter, ListBlobsOptions options, - Context context, ContainersListBlobHierarchySegmentResponse response) { + Context context, ContainersListBlobHierarchySegmentResponse response) { Flux blobs; Flux prefixes; BlobHierarchyListSegment segment = response.value().segment(); @@ -617,7 +685,7 @@ private Flux listBlobsHierarchyHelper(String delimiter, ListBlobsOptio if (response.value().nextMarker() != null) { // Recursively add the continuation items to the observable. - result = result.concatWith(containerAsyncRawClient.listBlobsHierarchySegment(response.value().nextMarker(), delimiter, options) + result = result.concatWith(listBlobsHierarchySegment(response.value().nextMarker(), delimiter, options) .flatMapMany(r -> listBlobsHierarchyHelper(delimiter, options, context, r))); } @@ -696,14 +764,10 @@ private Flux listBlobsHierarchyHelper(String delimiter, ListBlobsOptio * Acquires a lease on the blob for write and delete operations. The lease duration must be between 15 to 60 * seconds, or infinite (-1). * - * @param proposedId - * A {@code String} in any valid GUID format. May be null. - * @param duration - * The duration of the lease, in seconds, or negative one (-1) for a lease that - * never expires. A non-infinite lease can be between 15 and 60 seconds. - * - * @return - * A reactive response containing the lease ID. + * @param proposedId A {@code String} in any valid GUID format. May be null. + * @param duration The duration of the lease, in seconds, or negative one (-1) for a lease that never expires. A + * non-infinite lease can be between 15 and 60 seconds. + * @return A reactive response containing the lease ID. */ public Mono> acquireLease(String proposedId, int duration) { return this.acquireLease(proposedId, duration, null); @@ -713,33 +777,34 @@ public Mono> acquireLease(String proposedId, int duration) { * Acquires a lease on the blob for write and delete operations. The lease duration must be between 15 to 60 * seconds, or infinite (-1). * - * @param proposedID - * A {@code String} in any valid GUID format. May be null. - * @param duration - * The duration of the lease, in seconds, or negative one (-1) for a lease that - * never expires. A non-infinite lease can be between 15 and 60 seconds. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * - * @return - * A reactive response containing the lease ID. + * @param proposedID A {@code String} in any valid GUID format. May be null. + * @param duration The duration of the lease, in seconds, or negative one (-1) for a lease that never expires. A + * non-infinite lease can be between 15 and 60 seconds. + * @param modifiedAccessConditions Standard HTTP Access conditions related to the modification of data. ETag and + * LastModifiedTime are used to construct conditions related to when the blob was changed relative to the given + * request. The request will fail if the specified condition is not satisfied. + * @return A reactive response containing the lease ID. + * @throws UnsupportedOperationException If either {@link ModifiedAccessConditions#ifMatch()} or {@link + * ModifiedAccessConditions#ifNoneMatch()} is set. */ public Mono> acquireLease(String proposedID, int duration, ModifiedAccessConditions modifiedAccessConditions) { - return containerAsyncRawClient - .acquireLease(proposedID, duration, modifiedAccessConditions) + if (!this.validateNoEtag(modifiedAccessConditions)) { + // Throwing is preferred to Single.error because this will error out immediately instead of waiting until + // subscription. + throw new UnsupportedOperationException( + "ETag access conditions are not supported for this API."); + } + + return postProcessResponse(this.azureBlobStorage.containers().acquireLeaseWithRestResponseAsync( + null, null, duration, proposedID, null, modifiedAccessConditions, Context.NONE)) .map(rb -> new SimpleResponse<>(rb, rb.deserializedHeaders().leaseId())); } /** * Renews the blob's previously-acquired lease. * - * @param leaseID - * The leaseId of the active lease on the blob. - * - * @return - * A reactive response containing the renewed lease ID. + * @param leaseID The leaseId of the active lease on the blob. + * @return A reactive response containing the renewed lease ID. */ public Mono> renewLease(String leaseID) { return this.renewLease(leaseID, null); @@ -748,30 +813,32 @@ public Mono> renewLease(String leaseID) { /** * Renews the blob's previously-acquired lease. * - * @param leaseID - * The leaseId of the active lease on the blob. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * - * @return - * A reactive response containing the renewed lease ID. + * @param leaseID The leaseId of the active lease on the blob. + * @param modifiedAccessConditions Standard HTTP Access conditions related to the modification of data. ETag and + * LastModifiedTime are used to construct conditions related to when the blob was changed relative to the given + * request. The request will fail if the specified condition is not satisfied. + * @return A reactive response containing the renewed lease ID. + * @throws UnsupportedOperationException If either {@link ModifiedAccessConditions#ifMatch()} or {@link + * ModifiedAccessConditions#ifNoneMatch()} is set. */ public Mono> renewLease(String leaseID, ModifiedAccessConditions modifiedAccessConditions) { - return containerAsyncRawClient - .renewLease(leaseID, modifiedAccessConditions) + if (!this.validateNoEtag(modifiedAccessConditions)) { + // Throwing is preferred to Single.error because this will error out immediately instead of waiting until + // subscription. + throw new UnsupportedOperationException( + "ETag access conditions are not supported for this API."); + } + + return postProcessResponse(this.azureBlobStorage.containers().renewLeaseWithRestResponseAsync(null, + leaseID, null, null, modifiedAccessConditions, Context.NONE)) .map(rb -> new SimpleResponse<>(rb, rb.deserializedHeaders().leaseId())); } /** * Releases the blob's previously-acquired lease. * - * @param leaseID - * The leaseId of the active lease on the blob. - * - * @return - * A reactive response signalling completion. + * @param leaseID The leaseId of the active lease on the blob. + * @return A reactive response signalling completion. */ public Mono releaseLease(String leaseID) { return this.releaseLease(leaseID, null); @@ -780,19 +847,24 @@ public Mono releaseLease(String leaseID) { /** * Releases the blob's previously-acquired lease. * - * @param leaseID - * The leaseId of the active lease on the blob. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * - * @return - * A reactive response signalling completion. + * @param leaseID The leaseId of the active lease on the blob. + * @param modifiedAccessConditions Standard HTTP Access conditions related to the modification of data. ETag and + * LastModifiedTime are used to construct conditions related to when the blob was changed relative to the given + * request. The request will fail if the specified condition is not satisfied. + * @return A reactive response signalling completion. + * @throws UnsupportedOperationException If either {@link ModifiedAccessConditions#ifMatch()} or {@link + * ModifiedAccessConditions#ifNoneMatch()} is set. */ public Mono releaseLease(String leaseID, ModifiedAccessConditions modifiedAccessConditions) { - return containerAsyncRawClient - .releaseLease(leaseID, modifiedAccessConditions) + if (!this.validateNoEtag(modifiedAccessConditions)) { + // Throwing is preferred to Single.error because this will error out immediately instead of waiting until + // subscription. + throw new UnsupportedOperationException( + "ETag access conditions are not supported for this API."); + } + + return postProcessResponse(this.azureBlobStorage.containers().releaseLeaseWithRestResponseAsync( + null, leaseID, null, null, modifiedAccessConditions, Context.NONE)) .map(VoidResponse::new); } @@ -800,8 +872,7 @@ public Mono releaseLease(String leaseID, ModifiedAccessConditions * BreakLease breaks the blob's previously-acquired lease (if it exists). Pass the LeaseBreakDefault (-1) constant * to break a fixed-duration lease when it expires or an infinite lease immediately. * - * @return - * A reactive response containing the remaining time in the broken lease. + * @return A reactive response containing the remaining time in the broken lease. */ public Mono> breakLease() { return this.breakLease(null, null); @@ -811,58 +882,65 @@ public Mono> breakLease() { * BreakLease breaks the blob's previously-acquired lease (if it exists). Pass the LeaseBreakDefault (-1) constant * to break a fixed-duration lease when it expires or an infinite lease immediately. * - * @param breakPeriodInSeconds - * An optional {@code Integer} representing the proposed duration of seconds that the lease should continue - * before it is broken, between 0 and 60 seconds. This break period is only used if it is shorter than the - * time remaining on the lease. If longer, the time remaining on the lease is used. A new lease will not be - * available before the break period has expired, but the lease may be held for longer than the break - * period. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * - * @return - * A reactive response containing the remaining time in the broken lease. + * @param breakPeriodInSeconds An optional {@code Integer} representing the proposed duration of seconds that the + * lease should continue before it is broken, between 0 and 60 seconds. This break period is only used if it is + * shorter than the time remaining on the lease. If longer, the time remaining on the lease is used. A new lease + * will not be available before the break period has expired, but the lease may be held for longer than the break + * period. + * @param modifiedAccessConditions Standard HTTP Access conditions related to the modification of data. ETag and + * LastModifiedTime are used to construct conditions related to when the blob was changed relative to the given + * request. The request will fail if the specified condition is not satisfied. + * @return A reactive response containing the remaining time in the broken lease. + * @throws UnsupportedOperationException If either {@link ModifiedAccessConditions#ifMatch()} or {@link + * ModifiedAccessConditions#ifNoneMatch()} is set. */ public Mono> breakLease(Integer breakPeriodInSeconds, ModifiedAccessConditions modifiedAccessConditions) { - return containerAsyncRawClient - .breakLease(breakPeriodInSeconds, modifiedAccessConditions) + if (!this.validateNoEtag(modifiedAccessConditions)) { + // Throwing is preferred to Single.error because this will error out immediately instead of waiting until + // subscription. + throw new UnsupportedOperationException( + "ETag access conditions are not supported for this API."); + } + + return postProcessResponse(this.azureBlobStorage.containers().breakLeaseWithRestResponseAsync(null, + null, breakPeriodInSeconds, null, modifiedAccessConditions, Context.NONE)) .map(rb -> new SimpleResponse<>(rb, Duration.ofSeconds(rb.deserializedHeaders().leaseTime()))); } /** * ChangeLease changes the blob's lease ID. * - * @param leaseId - * The leaseId of the active lease on the blob. - * @param proposedID - * A {@code String} in any valid GUID format. - * - * @return - * A reactive response containing the new lease ID. + * @param leaseId The leaseId of the active lease on the blob. + * @param proposedID A {@code String} in any valid GUID format. + * @return A reactive response containing the new lease ID. */ public Mono> changeLease(String leaseId, String proposedID) { return this.changeLease(leaseId, proposedID, null); } /** - * ChangeLease changes the blob's lease ID. For more information, see the Azure Docs. - * - * @param leaseId - * The leaseId of the active lease on the blob. - * @param proposedID - * A {@code String} in any valid GUID format. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * + * ChangeLease changes the blob's lease ID. For more information, see the Azure + * Docs. + * + * @param leaseId The leaseId of the active lease on the blob. + * @param proposedID A {@code String} in any valid GUID format. + * @param modifiedAccessConditions Standard HTTP Access conditions related to the modification of data. ETag and + * LastModifiedTime are used to construct conditions related to when the blob was changed relative to the given + * request. The request will fail if the specified condition is not satisfied. * @return A reactive response containing the new lease ID. + * @throws UnsupportedOperationException If either {@link ModifiedAccessConditions#ifMatch()} or {@link + * ModifiedAccessConditions#ifNoneMatch()} is set. */ public Mono> changeLease(String leaseId, String proposedID, ModifiedAccessConditions modifiedAccessConditions) { - return containerAsyncRawClient - .changeLease(leaseId, proposedID, modifiedAccessConditions) + if (!this.validateNoEtag(modifiedAccessConditions)) { + // Throwing is preferred to Single.error because this will error out immediately instead of waiting until + // subscription. + throw new UnsupportedOperationException( + "ETag access conditions are not supported for this API."); + } + + return postProcessResponse(this.azureBlobStorage.containers().changeLeaseWithRestResponseAsync(null, + leaseId, proposedID, null, null, modifiedAccessConditions, Context.NONE)) .map(rb -> new SimpleResponse<>(rb, rb.deserializedHeaders().leaseId())); } @@ -870,12 +948,184 @@ public Mono> changeLease(String leaseId, String proposedID, Mod * Returns the sku name and account kind for the account. For more information, please see the * Azure Docs. * - * @return - * A reactive response containing the account info. + * @return A reactive response containing the account info. */ public Mono> getAccountInfo() { - return containerAsyncRawClient - .getAccountInfo() + return postProcessResponse( + this.azureBlobStorage.containers().getAccountInfoWithRestResponseAsync(null, Context.NONE)) .map(rb -> new SimpleResponse<>(rb, new StorageAccountInfo(rb.deserializedHeaders()))); } + + private boolean validateNoEtag(ModifiedAccessConditions modifiedAccessConditions) { + if (modifiedAccessConditions == null) { + return true; + } + return modifiedAccessConditions.ifMatch() == null && modifiedAccessConditions.ifNoneMatch() == null; + } + + /** + * Generates a user delegation SAS with the specified parameters + * + * @param userDelegationKey The {@code UserDelegationKey} user delegation key for the SAS + * @param accountName The {@code String} account name for the SAS + * @param permissions The {@code ContainerSASPermissions} permission for the SAS + * @param expiryTime The {@code OffsetDateTime} expiry time for the SAS + * @return A string that represents the SAS token + */ + public String generateUserDelegationSAS(UserDelegationKey userDelegationKey, String accountName, + ContainerSASPermission permissions, OffsetDateTime expiryTime) { + return this.generateUserDelegationSAS(userDelegationKey, accountName, permissions, expiryTime, null, null, + null, null, null, null, null, null, null); + } + + /** + * Generates a user delegation SAS token with the specified parameters + * + * @param userDelegationKey The {@code UserDelegationKey} user delegation key for the SAS + * @param accountName The {@code String} account name for the SAS + * @param permissions The {@code ContainerSASPermissions} permission for the SAS + * @param expiryTime The {@code OffsetDateTime} expiry time for the SAS + * @param startTime An optional {@code OffsetDateTime} start time for the SAS + * @param version An optional {@code String} version for the SAS + * @param sasProtocol An optional {@code SASProtocol} protocol for the SAS + * @param ipRange An optional {@code IPRange} ip address range for the SAS + * @return A string that represents the SAS token + */ + public String generateUserDelegationSAS(UserDelegationKey userDelegationKey, String accountName, + ContainerSASPermission permissions, OffsetDateTime expiryTime, OffsetDateTime startTime, String version, + SASProtocol sasProtocol, IPRange ipRange) { + return this.generateUserDelegationSAS(userDelegationKey, accountName, permissions, expiryTime, startTime, + version, sasProtocol, ipRange, null /* cacheControl */, null /* contentDisposition */, null /* + contentEncoding */, null /* contentLanguage */, null /* contentType */); + } + + /** + * Generates a user delegation SAS token with the specified parameters + * + * @param userDelegationKey The {@code UserDelegationKey} user delegation key for the SAS + * @param accountName The {@code String} account name for the SAS + * @param permissions The {@code ContainerSASPermissions} permission for the SAS + * @param expiryTime The {@code OffsetDateTime} expiry time for the SAS + * @param startTime An optional {@code OffsetDateTime} start time for the SAS + * @param version An optional {@code String} version for the SAS + * @param sasProtocol An optional {@code SASProtocol} protocol for the SAS + * @param ipRange An optional {@code IPRange} ip address range for the SAS + * @param cacheControl An optional {@code String} cache-control header for the SAS. + * @param contentDisposition An optional {@code String} content-disposition header for the SAS. + * @param contentEncoding An optional {@code String} content-encoding header for the SAS. + * @param contentLanguage An optional {@code String} content-language header for the SAS. + * @param contentType An optional {@code String} content-type header for the SAS. + * @return A string that represents the SAS token + */ + public String generateUserDelegationSAS(UserDelegationKey userDelegationKey, String accountName, + ContainerSASPermission permissions, OffsetDateTime expiryTime, OffsetDateTime startTime, String version, + SASProtocol sasProtocol, IPRange ipRange, String cacheControl, String contentDisposition, + String contentEncoding, String contentLanguage, String contentType) { + ServiceSASSignatureValues serviceSASSignatureValues = new ServiceSASSignatureValues(version, sasProtocol, + startTime, expiryTime, permissions == null ? null : permissions.toString(), ipRange, null /* identifier*/, + cacheControl, contentDisposition, contentEncoding, contentLanguage, contentType); + + ServiceSASSignatureValues values = configureServiceSASSignatureValues(serviceSASSignatureValues, accountName); + + SASQueryParameters sasQueryParameters = values.generateSASQueryParameters(userDelegationKey); + + return sasQueryParameters.encode(); + } + + /** + * Generates a SAS token with the specified parameters + * + * @param permissions The {@code ContainerSASPermissions} permission for the SAS + * @param expiryTime The {@code OffsetDateTime} expiry time for the SAS + * @return A string that represents the SAS token + */ + public String generateSAS(ContainerSASPermission permissions, OffsetDateTime expiryTime) { + return this.generateSAS(null, permissions, /* identifier */ expiryTime, null /* startTime */, null /* version + */, null /* sasProtocol */, null /* ipRange */, null /* cacheControl */, null /* contentDisposition */, + null /* contentEncoding */, null /* contentLanguage */, null /*contentType*/); + } + + /** + * Generates a SAS token with the specified parameters + * + * @param identifier The {@code String} name of the access policy on the container this SAS references if any + * @return A string that represents the SAS token + */ + public String generateSAS(String identifier) { + return this.generateSAS(identifier, null /* permissions*/, null /* expiryTime */, null /* startTime */, null + /* version */, null /* sasProtocol */, null /* ipRange */, null /* cacheControl */, null /* + contentDisposition */, null /* contentEncoding */, null /* contentLanguage */, null /*contentType*/); + } + + /** + * Generates a SAS token with the specified parameters + * + * @param identifier The {@code String} name of the access policy on the container this SAS references if any + * @param permissions The {@code ContainerSASPermissions} permission for the SAS + * @param expiryTime The {@code OffsetDateTime} expiry time for the SAS + * @param startTime An optional {@code OffsetDateTime} start time for the SAS + * @param version An optional {@code String} version for the SAS + * @param sasProtocol An optional {@code SASProtocol} protocol for the SAS + * @param ipRange An optional {@code IPRange} ip address range for the SAS + * @return A string that represents the SAS token + */ + public String generateSAS(String identifier, ContainerSASPermission permissions, OffsetDateTime expiryTime, + OffsetDateTime startTime, + String version, SASProtocol sasProtocol, IPRange ipRange) { + return this.generateSAS(identifier, permissions, expiryTime, startTime, version, sasProtocol, ipRange, null + /* cacheControl */, null /* contentDisposition */, null /* contentEncoding */, null /* contentLanguage */, + null /*contentType*/); + } + + /** + * Generates a SAS token with the specified parameters + * + * @param identifier The {@code String} name of the access policy on the container this SAS references if any + * @param permissions The {@code ContainerSASPermissions} permission for the SAS + * @param expiryTime The {@code OffsetDateTime} expiry time for the SAS + * @param startTime An optional {@code OffsetDateTime} start time for the SAS + * @param version An optional {@code String} version for the SAS + * @param sasProtocol An optional {@code SASProtocol} protocol for the SAS + * @param ipRange An optional {@code IPRange} ip address range for the SAS + * @param cacheControl An optional {@code String} cache-control header for the SAS. + * @param contentDisposition An optional {@code String} content-disposition header for the SAS. + * @param contentEncoding An optional {@code String} content-encoding header for the SAS. + * @param contentLanguage An optional {@code String} content-language header for the SAS. + * @param contentType An optional {@code String} content-type header for the SAS. + * @return A string that represents the SAS token + */ + public String generateSAS(String identifier, ContainerSASPermission permissions, OffsetDateTime expiryTime, + OffsetDateTime startTime, String version, SASProtocol sasProtocol, IPRange ipRange, String cacheControl, + String contentDisposition, String contentEncoding, String contentLanguage, String contentType) { + ServiceSASSignatureValues serviceSASSignatureValues = new ServiceSASSignatureValues(version, sasProtocol, + startTime, expiryTime, permissions == null ? null : permissions.toString(), ipRange, identifier, + cacheControl, contentDisposition, contentEncoding, contentLanguage, contentType); + + SharedKeyCredential sharedKeyCredential = + Utility.getSharedKeyCredential(this.azureBlobStorage.httpPipeline()); + + Utility.assertNotNull("sharedKeyCredential", sharedKeyCredential); + + ServiceSASSignatureValues values = configureServiceSASSignatureValues(serviceSASSignatureValues, + sharedKeyCredential.accountName()); + + SASQueryParameters sasQueryParameters = values.generateSASQueryParameters(sharedKeyCredential); + + return sasQueryParameters.encode(); + } + + /** + * Sets serviceSASSignatureValues parameters dependent on the current blob type + */ + private ServiceSASSignatureValues configureServiceSASSignatureValues(ServiceSASSignatureValues serviceSASSignatureValues, String accountName) { + // Set canonical name + serviceSASSignatureValues.canonicalName(this.azureBlobStorage.url(), accountName); + + // Set snapshotId to null + serviceSASSignatureValues.snapshotId(null); + + // Set resource + serviceSASSignatureValues.resource(Constants.UrlConstants.SAS_CONTAINER_CONSTANT); + return serviceSASSignatureValues; + } } diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/ContainerAsyncRawClient.java b/storage/client/blob/src/main/java/com/azure/storage/blob/ContainerAsyncRawClient.java deleted file mode 100644 index e4106549b208c..0000000000000 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/ContainerAsyncRawClient.java +++ /dev/null @@ -1,708 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.azure.storage.blob; - -import com.azure.core.http.rest.Response; -import com.azure.core.http.rest.SimpleResponse; -import com.azure.core.util.Context; -import com.azure.storage.blob.implementation.AzureBlobStorageImpl; -import com.azure.storage.blob.models.BlobListDetails; -import com.azure.storage.blob.models.ContainerAccessConditions; -import com.azure.storage.blob.models.ContainerAccessPolicies; -import com.azure.storage.blob.models.ContainersAcquireLeaseResponse; -import com.azure.storage.blob.models.ContainersBreakLeaseResponse; -import com.azure.storage.blob.models.ContainersChangeLeaseResponse; -import com.azure.storage.blob.models.ContainersCreateResponse; -import com.azure.storage.blob.models.ContainersDeleteResponse; -import com.azure.storage.blob.models.ContainersGetAccountInfoResponse; -import com.azure.storage.blob.models.ContainersGetPropertiesResponse; -import com.azure.storage.blob.models.ContainersListBlobFlatSegmentResponse; -import com.azure.storage.blob.models.ContainersListBlobHierarchySegmentResponse; -import com.azure.storage.blob.models.ContainersReleaseLeaseResponse; -import com.azure.storage.blob.models.ContainersRenewLeaseResponse; -import com.azure.storage.blob.models.ContainersSetAccessPolicyResponse; -import com.azure.storage.blob.models.ContainersSetMetadataResponse; -import com.azure.storage.blob.models.LeaseAccessConditions; -import com.azure.storage.blob.models.ListBlobsOptions; -import com.azure.storage.blob.models.Metadata; -import com.azure.storage.blob.models.ModifiedAccessConditions; -import com.azure.storage.blob.models.PublicAccessType; -import com.azure.storage.blob.models.SignedIdentifier; -import reactor.core.publisher.Mono; - -import java.time.temporal.ChronoUnit; -import java.util.List; - -import static com.azure.storage.blob.Utility.postProcessResponse; - -/** - * Represents a URL to a container. It may be obtained by direct construction or via the create method on a - * {@link StorageAsyncRawClient} object. This class does not hold any state about a particular blob but is instead a convenient way - * of sending off appropriate requests to the resource on the service. It may also be used to construct URLs to blobs. - * Please refer to the - * Azure Docs - * for more information on containers. - */ -final class ContainerAsyncRawClient { - - public static final String ROOT_CONTAINER_NAME = "$root"; - - public static final String STATIC_WEBSITE_CONTAINER_NAME = "$web"; - - public static final String LOG_CONTAINER_NAME = "$logs"; - - AzureBlobStorageImpl azureBlobStorage; - - /** - * Creates a {@code ContainerAsyncClient} object pointing to the account specified by the URL and using the provided - * pipeline to make HTTP requests. - */ - ContainerAsyncRawClient(AzureBlobStorageImpl azureBlobStorage) { - this.azureBlobStorage = azureBlobStorage; - } - - /** - * Creates a new container within a storage account. If a container with the same name already exists, the operation - * fails. For more information, see the - * Azure Docs. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_basic "Sample code for ContainerAsyncClient.create")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono create() { - return this.create(null, null); - } - - /** - * Creates a new container within a storage account. If a container with the same name already exists, the operation - * fails. For more information, see the - * Azure Docs. - * - * @param metadata - * {@link Metadata} - * @param accessType - * Specifies how the data in this container is available to the public. See the x-ms-blob-public-access header - * in the Azure Docs for more information. Pass null for no public access. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_basic "Sample code for ContainerAsyncClient.create")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono create(Metadata metadata, PublicAccessType accessType) { - metadata = metadata == null ? new Metadata() : metadata; - - return postProcessResponse(this.azureBlobStorage.containers().createWithRestResponseAsync( - null, null, metadata, accessType, null, Context.NONE)); - - } - - /** - * Marks the specified container for deletion. The container and any blobs contained within it are later - * deleted during garbage collection. For more information, see the - * Azure Docs. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_basic "Sample code for ContainerAsyncClient.delete")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono delete() { - return this.delete(null); - } - - /** - * Marks the specified container for deletion. The container and any blobs contained within it are later - * deleted during garbage collection. For more information, see the - * Azure Docs. - * - * @param accessConditions - * {@link ContainerAccessConditions} - * - * @return Emits the successful response. - * @throws UnsupportedOperationException If {@link ContainerAccessConditions#modifiedAccessConditions()} has either - * {@link ModifiedAccessConditions#ifMatch()} or {@link ModifiedAccessConditions#ifNoneMatch()} set. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_basic "Sample code for ContainerAsyncClient.delete")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono delete(ContainerAccessConditions accessConditions) { - accessConditions = accessConditions == null ? new ContainerAccessConditions() : accessConditions; - - if (!validateNoEtag(accessConditions.modifiedAccessConditions())) { - // Throwing is preferred to Single.error because this will error out immediately instead of waiting until - // subscription. - throw new UnsupportedOperationException("ETag access conditions are not supported for this API."); - } - - return postProcessResponse(this.azureBlobStorage.containers() - .deleteWithRestResponseAsync(null, null, null, - accessConditions.leaseAccessConditions(), accessConditions.modifiedAccessConditions(), Context.NONE)); - } - - /** - * Returns the container's metadata and system properties. For more information, see the - * Azure Docs. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_basic "Sample code for ContainerAsyncClient.getProperties")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono getProperties() { - return this.getProperties(null); - } - - /** - * Returns the container's metadata and system properties. For more information, see the - * Azure Docs. - * - * @param leaseAccessConditions - * By setting lease access conditions, requests will fail if the provided lease does not match the active - * lease on the blob. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_basic "Sample code for ContainerAsyncClient.getProperties")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono getProperties(LeaseAccessConditions leaseAccessConditions) { - return postProcessResponse(this.azureBlobStorage.containers() - .getPropertiesWithRestResponseAsync(null, null, null, - leaseAccessConditions, Context.NONE)); - } - - /** - * Sets the container's metadata. For more information, see the - * Azure Docs. - * - * @param metadata - * {@link Metadata} - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_basic "Sample code for ContainerAsyncClient.setMetadata")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono setMetadata(Metadata metadata) { - return this.setMetadata(metadata, null); - } - - /** - * Sets the container's metadata. For more information, see the - * Azure Docs. - * - * @param metadata - * {@link Metadata} - * @param accessConditions - * {@link ContainerAccessConditions} - * - * @return Emits the successful response. - * @throws UnsupportedOperationException If {@link ContainerAccessConditions#modifiedAccessConditions()} has anything - * set other than {@link ModifiedAccessConditions#ifModifiedSince()}. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_basic "Sample code for ContainerAsyncClient.setMetadata")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono setMetadata(Metadata metadata, - ContainerAccessConditions accessConditions) { - metadata = metadata == null ? new Metadata() : metadata; - accessConditions = accessConditions == null ? new ContainerAccessConditions() : accessConditions; - if (!validateNoEtag(accessConditions.modifiedAccessConditions()) - || accessConditions.modifiedAccessConditions().ifUnmodifiedSince() != null) { - // Throwing is preferred to Single.error because this will error out immediately instead of waiting until - // subscription. - throw new UnsupportedOperationException( - "If-Modified-Since is the only HTTP access condition supported for this API"); - } - - return postProcessResponse(this.azureBlobStorage.containers() - .setMetadataWithRestResponseAsync(null, null, metadata, null, - accessConditions.leaseAccessConditions(), accessConditions.modifiedAccessConditions(), Context.NONE)); - } - - /** - * Returns the container's permissions. The permissions indicate whether container's blobs may be accessed publicly. - * For more information, see the - * Azure Docs. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_policy "Sample code for ContainerAsyncClient.getAccessPolicy")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono> getAccessPolicy() { - return this.getAccessPolicy(null); - } - - /** - * Returns the container's permissions. The permissions indicate whether container's blobs may be accessed publicly. - * For more information, see the - * Azure Docs. - * - * @param leaseAccessConditions - * By setting lease access conditions, requests will fail if the provided lease does not match the active - * lease on the blob. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_policy "Sample code for ContainerAsyncClient.getAccessPolicy")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono> getAccessPolicy(LeaseAccessConditions leaseAccessConditions) { - return postProcessResponse(this.azureBlobStorage.containers().getAccessPolicyWithRestResponseAsync(null, null, null, leaseAccessConditions, Context.NONE) - .map(response -> new SimpleResponse<>(response, new ContainerAccessPolicies(response.deserializedHeaders().blobPublicAccess(), response.value())))); - } - - /** - * Sets the container's permissions. The permissions indicate whether blobs in a container may be accessed publicly. - * Note that, for each signed identifier, we will truncate the start and expiry times to the nearest second to - * ensure the time formatting is compatible with the service. For more information, see the - * Azure Docs. - * - * @param accessType - * Specifies how the data in this container is available to the public. See the x-ms-blob-public-access header - * in the Azure Docs for more information. Pass null for no public access. - * @param identifiers - * A list of {@link SignedIdentifier} objects that specify the permissions for the container. Please see - * here - * for more information. Passing null will clear all access policies. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_policy "Sample code for ContainerAsyncClient.setAccessPolicy")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono setAccessPolicy(PublicAccessType accessType, - List identifiers) { - return this.setAccessPolicy(accessType, identifiers, null); - } - - /** - * Sets the container's permissions. The permissions indicate whether blobs in a container may be accessed publicly. - * Note that, for each signed identifier, we will truncate the start and expiry times to the nearest second to - * ensure the time formatting is compatible with the service. For more information, see the - * Azure Docs. - * - * @param accessType - * Specifies how the data in this container is available to the public. See the x-ms-blob-public-access header - * in the Azure Docs for more information. Pass null for no public access. - * @param identifiers - * A list of {@link SignedIdentifier} objects that specify the permissions for the container. Please see - * here - * for more information. Passing null will clear all access policies. - * @param accessConditions - * {@link ContainerAccessConditions} - * - * @return Emits the successful response. - * @throws UnsupportedOperationException If {@link ContainerAccessConditions#modifiedAccessConditions()} has either - * {@link ModifiedAccessConditions#ifMatch()} or {@link ModifiedAccessConditions#ifNoneMatch()} set. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_policy "Sample code for ContainerAsyncClient.setAccessPolicy")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono setAccessPolicy(PublicAccessType accessType, - List identifiers, ContainerAccessConditions accessConditions) { - accessConditions = accessConditions == null ? new ContainerAccessConditions() : accessConditions; - - if (!validateNoEtag(accessConditions.modifiedAccessConditions())) { - // Throwing is preferred to Single.error because this will error out immediately instead of waiting until - // subscription. - throw new UnsupportedOperationException("ETag access conditions are not supported for this API."); - } - - /* - We truncate to seconds because the service only supports nanoseconds or seconds, but doing an - OffsetDateTime.now will only give back milliseconds (more precise fields are zeroed and not serialized). This - allows for proper serialization with no real detriment to users as sub-second precision on active time for - signed identifiers is not really necessary. - */ - if (identifiers != null) { - for (SignedIdentifier identifier : identifiers) { - if (identifier.accessPolicy() != null && identifier.accessPolicy().start() != null) { - identifier.accessPolicy().start( - identifier.accessPolicy().start().truncatedTo(ChronoUnit.SECONDS)); - } - if (identifier.accessPolicy() != null && identifier.accessPolicy().expiry() != null) { - identifier.accessPolicy().expiry( - identifier.accessPolicy().expiry().truncatedTo(ChronoUnit.SECONDS)); - } - } - } - - return postProcessResponse(this.azureBlobStorage.containers() - .setAccessPolicyWithRestResponseAsync(null, identifiers, null, accessType, - null, accessConditions.leaseAccessConditions(), accessConditions.modifiedAccessConditions(), - Context.NONE)); - - } - - private boolean validateNoEtag(ModifiedAccessConditions modifiedAccessConditions) { - if (modifiedAccessConditions == null) { - return true; - } - return modifiedAccessConditions.ifMatch() == null && modifiedAccessConditions.ifNoneMatch() == null; - } - - /** - * Acquires a lease on the container for delete operations. The lease duration must be between 15 to - * 60 seconds, or infinite (-1). For more information, see the - * Azure Docs. - * - * @apiNote - * ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_lease "Sample code for ContainerAsyncClient.acquireLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/New-Storage-SDK-V10-Preview/src/test/java/com/microsoft/azure/storage/Samples.java) - * - * @param proposedId - * A {@code String} in any valid GUID format. - * @param duration - * The duration of the lease, in seconds, or negative one (-1) for a lease that never expires. - * A non-infinite lease can be between 15 and 60 seconds. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_lease "Sample code for ContainerAsyncClient.acquireLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono acquireLease(String proposedId, int duration) { - return this.acquireLease(proposedId, duration, null); - } - - /** - * Acquires a lease on the container for delete operations. The lease duration must be between 15 to - * 60 seconds, or infinite (-1). For more information, see the - * Azure Docs. - * - * @param proposedID - * A {@code String} in any valid GUID format. - * @param duration - * The duration of the lease, in seconds, or negative one (-1) for a lease that never expires. - * A non-infinite lease can be between 15 and 60 seconds. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * - * @return Emits the successful response. - * @throws UnsupportedOperationException If either {@link ModifiedAccessConditions#ifMatch()} or - * {@link ModifiedAccessConditions#ifNoneMatch()} is set. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_lease "Sample code for ContainerAsyncClient.acquireLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono acquireLease(String proposedID, int duration, - ModifiedAccessConditions modifiedAccessConditions) { - if (!this.validateNoEtag(modifiedAccessConditions)) { - // Throwing is preferred to Single.error because this will error out immediately instead of waiting until - // subscription. - throw new UnsupportedOperationException( - "ETag access conditions are not supported for this API."); - } - - return postProcessResponse(this.azureBlobStorage.containers().acquireLeaseWithRestResponseAsync( - null, null, duration, proposedID, null, modifiedAccessConditions, Context.NONE)); - } - - /** - * Renews the container's previously-acquired lease. For more information, see the - * Azure Docs. - * - * @param leaseID - * The leaseId of the active lease on the container. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_lease "Sample code for ContainerAsyncClient.renewLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono renewLease(String leaseID) { - return this.renewLease(leaseID, null); - } - - /** - * Renews the container's previously-acquired lease. For more information, see the - * Azure Docs. - * - * @param leaseID - * The leaseId of the active lease on the container. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * - * @return Emits the successful response. - * @throws UnsupportedOperationException If either {@link ModifiedAccessConditions#ifMatch()} or - * {@link ModifiedAccessConditions#ifNoneMatch()} is set. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_lease "Sample code for ContainerAsyncClient.renewLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono renewLease(String leaseID, - ModifiedAccessConditions modifiedAccessConditions) { - if (!this.validateNoEtag(modifiedAccessConditions)) { - // Throwing is preferred to Single.error because this will error out immediately instead of waiting until - // subscription. - throw new UnsupportedOperationException( - "ETag access conditions are not supported for this API."); - } - - return postProcessResponse(this.azureBlobStorage.containers().renewLeaseWithRestResponseAsync(null, - leaseID, null, null, modifiedAccessConditions, Context.NONE)); - } - - /** - * Releases the container's previously-acquired lease. For more information, see the - * Azure Docs. - * - * @param leaseID - * The leaseId of the active lease on the container. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_lease "Sample code for ContainerAsyncClient.releaseLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono releaseLease(String leaseID) { - return this.releaseLease(leaseID, null); - } - - /** - * Releases the container's previously-acquired lease. For more information, see the - * Azure Docs. - * - * @param leaseID - * The leaseId of the active lease on the container. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * - * @return Emits the successful response. - * @throws UnsupportedOperationException If either {@link ModifiedAccessConditions#ifMatch()} or - * {@link ModifiedAccessConditions#ifNoneMatch()} is set. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_lease "Sample code for ContainerAsyncClient.releaseLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono releaseLease(String leaseID, - ModifiedAccessConditions modifiedAccessConditions) { - if (!this.validateNoEtag(modifiedAccessConditions)) { - // Throwing is preferred to Single.error because this will error out immediately instead of waiting until - // subscription. - throw new UnsupportedOperationException( - "ETag access conditions are not supported for this API."); - } - - return postProcessResponse(this.azureBlobStorage.containers().releaseLeaseWithRestResponseAsync( - null, leaseID, null, null, modifiedAccessConditions, Context.NONE)); - } - - /** - * Breaks the container's previously-acquired lease. For more information, see the - * Azure Docs. - * - * @apiNote - * ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_lease "Sample code for ContainerAsyncClient.breakLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/New-Storage-SDK-V10-Preview/src/test/java/com/microsoft/azure/storage/Samples.java) - * - * @return Emits the successful response. - */ - public Mono breakLease() { - return this.breakLease(null, null); - } - - /** - * Breaks the container's previously-acquired lease. For more information, see the - * Azure Docs. - * - * @param breakPeriodInSeconds - * An optional {@code Integer} representing the proposed duration of seconds that the lease should continue - * before it is broken, between 0 and 60 seconds. This break period is only used if it is shorter than the time - * remaining on the lease. If longer, the time remaining on the lease is used. A new lease will not be - * available before the break period has expired, but the lease may be held for longer than the break period. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * - * @return Emits the successful response. - * @throws UnsupportedOperationException If either {@link ModifiedAccessConditions#ifMatch()} or - * {@link ModifiedAccessConditions#ifNoneMatch()} is set. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_lease "Sample code for ContainerAsyncClient.breakLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono breakLease(Integer breakPeriodInSeconds, - ModifiedAccessConditions modifiedAccessConditions) { - if (!this.validateNoEtag(modifiedAccessConditions)) { - // Throwing is preferred to Single.error because this will error out immediately instead of waiting until - // subscription. - throw new UnsupportedOperationException( - "ETag access conditions are not supported for this API."); - } - - return postProcessResponse(this.azureBlobStorage.containers().breakLeaseWithRestResponseAsync(null, - null, breakPeriodInSeconds, null, modifiedAccessConditions, Context.NONE)); - - } - - /** - * Changes the container's leaseAccessConditions. For more information, see the - * Azure Docs. - * - * @param leaseID - * The leaseId of the active lease on the container. - * @param proposedID - * A {@code String} in any valid GUID format. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_lease "Sample code for ContainerAsyncClient.changeLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono changeLease(String leaseID, String proposedID) { - return this.changeLease(leaseID, proposedID, null); - } - - /** - * Changes the container's leaseAccessConditions. For more information, see the - * Azure Docs. - * - * @param leaseID - * The leaseId of the active lease on the container. - * @param proposedID - * A {@code String} in any valid GUID format. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * - * @return Emits the successful response. - * @throws UnsupportedOperationException If either {@link ModifiedAccessConditions#ifMatch()} or - * {@link ModifiedAccessConditions#ifNoneMatch()} is set. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_lease "Sample code for ContainerAsyncClient.changeLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono changeLease(String leaseID, String proposedID, - ModifiedAccessConditions modifiedAccessConditions) { - if (!this.validateNoEtag(modifiedAccessConditions)) { - // Throwing is preferred to Single.error because this will error out immediately instead of waiting until - // subscription. - throw new UnsupportedOperationException( - "ETag access conditions are not supported for this API."); - } - - return postProcessResponse(this.azureBlobStorage.containers().changeLeaseWithRestResponseAsync(null, - leaseID, proposedID, null, null, modifiedAccessConditions, Context.NONE)); - } - - /** - * Returns a single segment of blobs starting from the specified Marker. Use an empty - * marker to start enumeration from the beginning. Blob names are returned in lexicographic order. - * After getting a segment, process it, and then call ListBlobs again (passing the the previously-returned - * Marker) to get the next segment. For more information, see the - * Azure Docs. - * - * @param marker - * Identifies the portion of the list to be returned with the next list operation. - * This value is returned in the response of a previous list operation as the - * ListBlobsFlatSegmentResponse.body().nextMarker(). Set to null to list the first segment. - * @param options - * {@link ListBlobsOptions} - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=list_blobs_flat "Sample code for ContainerAsyncClient.listBlobsFlatSegment")] \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=list_blobs_flat_helper "helper code for ContainerAsyncClient.listBlobsFlatSegment")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono listBlobsFlatSegment(String marker, ListBlobsOptions options) { - options = options == null ? new ListBlobsOptions() : options; - - return postProcessResponse(this.azureBlobStorage.containers() - .listBlobFlatSegmentWithRestResponseAsync(null, options.prefix(), marker, - options.maxResults(), options.details().toList(), null, null, Context.NONE)); - } - - /** - * Returns a single segment of blobs and blob prefixes starting from the specified Marker. Use an empty - * marker to start enumeration from the beginning. Blob names are returned in lexicographic order. - * After getting a segment, process it, and then call ListBlobs again (passing the the previously-returned - * Marker) to get the next segment. For more information, see the - * Azure Docs. - * - * @param marker - * Identifies the portion of the list to be returned with the next list operation. - * This value is returned in the response of a previous list operation as the - * ListBlobsHierarchySegmentResponse.body().nextMarker(). Set to null to list the first segment. - * @param delimiter - * The operation returns a BlobPrefix element in the response body that acts as a placeholder for all blobs - * whose names begin with the same substring up to the appearance of the delimiter character. The delimiter may - * be a single character or a string. - * @param options - * {@link ListBlobsOptions} - * - * @return Emits the successful response. - * @throws UnsupportedOperationException If {@link ListBlobsOptions#details()} has {@link BlobListDetails#snapshots()} - * set. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=list_blobs_hierarchy "Sample code for ContainerAsyncClient.listBlobsHierarchySegment")] \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=list_blobs_hierarchy_helper "helper code for ContainerAsyncClient.listBlobsHierarchySegment")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono listBlobsHierarchySegment(String marker, String delimiter, - ListBlobsOptions options) { - options = options == null ? new ListBlobsOptions() : options; - if (options.details().snapshots()) { - throw new UnsupportedOperationException("Including snapshots in a hierarchical listing is not supported."); - } - - return postProcessResponse(this.azureBlobStorage.containers() - .listBlobHierarchySegmentWithRestResponseAsync(null, delimiter, options.prefix(), marker, - options.maxResults(), options.details().toList(), null, null, Context.NONE)); - } - - /** - * Returns the sku name and account kind for the account. For more information, please see the - * Azure Docs. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=account_info "Sample code for ContainerAsyncClient.getAccountInfo")] \n - * For more samples, please see the [Samples file] (https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono getAccountInfo() { - return postProcessResponse( - this.azureBlobStorage.containers().getAccountInfoWithRestResponseAsync(null, Context.NONE)); - } -} diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/ContainerClient.java b/storage/client/blob/src/main/java/com/azure/storage/blob/ContainerClient.java index b8c1f9d8bd09e..12d5844a696f4 100644 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/ContainerClient.java +++ b/storage/client/blob/src/main/java/com/azure/storage/blob/ContainerClient.java @@ -15,55 +15,55 @@ import com.azure.storage.blob.models.PublicAccessType; import com.azure.storage.blob.models.SignedIdentifier; import com.azure.storage.blob.models.StorageAccountInfo; +import com.azure.storage.blob.models.UserDelegationKey; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.net.URL; import java.time.Duration; +import java.time.OffsetDateTime; import java.util.List; /** - * Client to a container. It may only be instantiated through a {@link ContainerClientBuilder} or via the method - * {@link StorageClient#getContainerClient(String)}. This class does not hold any - * state about a particular container but is instead a convenient way of sending off appropriate requests to - * the resource on the service. It may also be used to construct URLs to blobs. + * Client to a container. It may only be instantiated through a {@link ContainerClientBuilder} or via the method {@link + * StorageClient#getContainerClient(String)}. This class does not hold any state about a particular container but is + * instead a convenient way of sending off appropriate requests to the resource on the service. It may also be used to + * construct URLs to blobs. * *

* This client contains operations on a container. Operations on a blob are available on {@link BlobClient} through * {@link #getBlobClient(String)}, and operations on the service are available on {@link StorageClient}. * *

- * Please refer to the Azure Docs - * for more information on containers. + * Please refer to the Azure + * Docs for more information on containers. */ public final class ContainerClient { - private ContainerAsyncClient containerAsyncClient; - public static final String ROOT_CONTAINER_NAME = "$root"; + public static final String ROOT_CONTAINER_NAME = ContainerAsyncClient.ROOT_CONTAINER_NAME; - public static final String STATIC_WEBSITE_CONTAINER_NAME = "$web"; + public static final String STATIC_WEBSITE_CONTAINER_NAME = ContainerAsyncClient.STATIC_WEBSITE_CONTAINER_NAME; - public static final String LOG_CONTAINER_NAME = "$logs"; + public static final String LOG_CONTAINER_NAME = ContainerAsyncClient.LOG_CONTAINER_NAME; /** * Package-private constructor for use by {@link ContainerClientBuilder}. + * * @param containerAsyncClient the async container client */ ContainerClient(ContainerAsyncClient containerAsyncClient) { - this.containerAsyncClient = containerAsyncClient; + this.containerAsyncClient = containerAsyncClient; } /** - * Creates a new {@link BlockBlobClient} object by concatenating the blobName to the end of - * ContainerAsyncClient's URL. The new BlockBlobClient uses the same request policy pipeline as the ContainerAsyncClient. - * To change the pipeline, create the BlockBlobClient and then call its WithPipeline method passing in the - * desired pipeline object. Or, call this package's NewBlockBlobAsyncClient instead of calling this object's - * NewBlockBlobAsyncClient method. - * - * @param blobName - * A {@code String} representing the name of the blob. + * Creates a new {@link BlockBlobClient} object by concatenating the blobName to the end of ContainerAsyncClient's + * URL. The new BlockBlobClient uses the same request policy pipeline as the ContainerAsyncClient. To change the + * pipeline, create the BlockBlobClient and then call its WithPipeline method passing in the desired pipeline + * object. Or, call this package's NewBlockBlobAsyncClient instead of calling this object's NewBlockBlobAsyncClient + * method. * + * @param blobName A {@code String} representing the name of the blob. * @return A new {@link BlockBlobClient} object which references the blob with the specified name in this container. */ public BlockBlobClient getBlockBlobClient(String blobName) { @@ -71,17 +71,14 @@ public BlockBlobClient getBlockBlobClient(String blobName) { } /** - * Creates a new {@link BlockBlobClient} object by concatenating the blobName to the end of - * ContainerAsyncClient's URL. The new BlockBlobClient uses the same request policy pipeline as the ContainerAsyncClient. - * To change the pipeline, create the BlockBlobClient and then call its WithPipeline method passing in the - * desired pipeline object. Or, call this package's NewBlockBlobAsyncClient instead of calling this object's - * NewBlockBlobAsyncClient method. - * - * @param blobName - * A {@code String} representing the name of the blob. - * @param snapshot - * the snapshot identifier for the blob. + * Creates a new {@link BlockBlobClient} object by concatenating the blobName to the end of ContainerAsyncClient's + * URL. The new BlockBlobClient uses the same request policy pipeline as the ContainerAsyncClient. To change the + * pipeline, create the BlockBlobClient and then call its WithPipeline method passing in the desired pipeline + * object. Or, call this package's NewBlockBlobAsyncClient instead of calling this object's NewBlockBlobAsyncClient + * method. * + * @param blobName A {@code String} representing the name of the blob. + * @param snapshot the snapshot identifier for the blob. * @return A new {@link BlockBlobClient} object which references the blob with the specified name in this container. */ public BlockBlobClient getBlockBlobClient(String blobName, String snapshot) { @@ -89,15 +86,12 @@ public BlockBlobClient getBlockBlobClient(String blobName, String snapshot) { } /** - * Creates creates a new PageBlobClient object by concatenating blobName to the end of - * ContainerAsyncClient's URL. The new PageBlobClient uses the same request policy pipeline as the ContainerAsyncClient. - * To change the pipeline, create the PageBlobClient and then call its WithPipeline method passing in the - * desired pipeline object. Or, call this package's NewPageBlobAsyncClient instead of calling this object's - * NewPageBlobAsyncClient method. - * - * @param blobName - * A {@code String} representing the name of the blob. + * Creates creates a new PageBlobClient object by concatenating blobName to the end of ContainerAsyncClient's URL. + * The new PageBlobClient uses the same request policy pipeline as the ContainerAsyncClient. To change the pipeline, + * create the PageBlobClient and then call its WithPipeline method passing in the desired pipeline object. Or, call + * this package's NewPageBlobAsyncClient instead of calling this object's NewPageBlobAsyncClient method. * + * @param blobName A {@code String} representing the name of the blob. * @return A new {@link PageBlobClient} object which references the blob with the specified name in this container. */ public PageBlobClient getPageBlobClient(String blobName) { @@ -105,17 +99,13 @@ public PageBlobClient getPageBlobClient(String blobName) { } /** - * Creates creates a new PageBlobClient object by concatenating blobName to the end of - * ContainerAsyncClient's URL. The new PageBlobClient uses the same request policy pipeline as the ContainerAsyncClient. - * To change the pipeline, create the PageBlobClient and then call its WithPipeline method passing in the - * desired pipeline object. Or, call this package's NewPageBlobAsyncClient instead of calling this object's - * NewPageBlobAsyncClient method. - * - * @param blobName - * A {@code String} representing the name of the blob. - * @param snapshot - * the snapshot identifier for the blob. + * Creates creates a new PageBlobClient object by concatenating blobName to the end of ContainerAsyncClient's URL. + * The new PageBlobClient uses the same request policy pipeline as the ContainerAsyncClient. To change the pipeline, + * create the PageBlobClient and then call its WithPipeline method passing in the desired pipeline object. Or, call + * this package's NewPageBlobAsyncClient instead of calling this object's NewPageBlobAsyncClient method. * + * @param blobName A {@code String} representing the name of the blob. + * @param snapshot the snapshot identifier for the blob. * @return A new {@link PageBlobClient} object which references the blob with the specified name in this container. */ public PageBlobClient getPageBlobClient(String blobName, String snapshot) { @@ -123,31 +113,43 @@ public PageBlobClient getPageBlobClient(String blobName, String snapshot) { } /** - * Creates creates a new AppendBlobClient object by concatenating blobName to the end of - * ContainerAsyncClient's URL. The new AppendBlobClient uses the same request policy pipeline as the ContainerAsyncClient. - * To change the pipeline, create the AppendBlobClient and then call its WithPipeline method passing in the - * desired pipeline object. Or, call this package's NewAppendBlobAsyncClient instead of calling this object's + * Creates creates a new AppendBlobClient object by concatenating blobName to the end of ContainerAsyncClient's URL. + * The new AppendBlobClient uses the same request policy pipeline as the ContainerAsyncClient. To change the + * pipeline, create the AppendBlobClient and then call its WithPipeline method passing in the desired pipeline + * object. Or, call this package's NewAppendBlobAsyncClient instead of calling this object's * NewAppendBlobAsyncClient method. * - * @param blobName - * A {@code String} representing the name of the blob. - * - * @return A new {@link AppendBlobClient} object which references the blob with the specified name in this container. + * @param blobName A {@code String} representing the name of the blob. + * @return A new {@link AppendBlobClient} object which references the blob with the specified name in this + * container. */ public AppendBlobClient getAppendBlobClient(String blobName) { return new AppendBlobClient(containerAsyncClient.getAppendBlobAsyncClient(blobName)); } /** - * Initializes a new BlobClient object by concatenating blobName to the end of - * ContainerAsyncClient's URL. The new BlobClient uses the same request policy pipeline as the ContainerAsyncClient. - * To change the pipeline, create the BlobClient and then call its WithPipeline method passing in the - * desired pipeline object. Or, call this package's getBlobAsyncClient instead of calling this object's - * getBlobAsyncClient method. + * Creates creates a new AppendBlobClient object by concatenating blobName to the end of ContainerAsyncClient's URL. + * The new AppendBlobClient uses the same request policy pipeline as the ContainerAsyncClient. To change the + * pipeline, create the AppendBlobClient and then call its WithPipeline method passing in the desired pipeline + * object. Or, call this package's NewAppendBlobAsyncClient instead of calling this object's + * NewAppendBlobAsyncClient method. * - * @param blobName - * A {@code String} representing the name of the blob. + * @param blobName A {@code String} representing the name of the blob. + * @param snapshot the snapshot identifier for the blob. + * @return A new {@link AppendBlobClient} object which references the blob with the specified name in this + * container. + */ + public AppendBlobClient getAppendBlobClient(String blobName, String snapshot) { + return new AppendBlobClient(containerAsyncClient.getAppendBlobAsyncClient(blobName, snapshot)); + } + + /** + * Initializes a new BlobClient object by concatenating blobName to the end of ContainerAsyncClient's URL. The new + * BlobClient uses the same request policy pipeline as the ContainerAsyncClient. To change the pipeline, create the + * BlobClient and then call its WithPipeline method passing in the desired pipeline object. Or, call this package's + * getBlobAsyncClient instead of calling this object's getBlobAsyncClient method. * + * @param blobName A {@code String} representing the name of the blob. * @return A new {@link BlobClient} object which references the blob with the specified name in this container. */ public BlobClient getBlobClient(String blobName) { @@ -155,17 +157,13 @@ public BlobClient getBlobClient(String blobName) { } /** - * Initializes a new BlobClient object by concatenating blobName to the end of - * ContainerAsyncClient's URL. The new BlobClient uses the same request policy pipeline as the ContainerAsyncClient. - * To change the pipeline, create the BlobClient and then call its WithPipeline method passing in the - * desired pipeline object. Or, call this package's getBlobAsyncClient instead of calling this object's - * getBlobAsyncClient method. - * - * @param blobName - * A {@code String} representing the name of the blob. - * @param snapshot - * the snapshot identifier for the blob. + * Initializes a new BlobClient object by concatenating blobName to the end of ContainerAsyncClient's URL. The new + * BlobClient uses the same request policy pipeline as the ContainerAsyncClient. To change the pipeline, create the + * BlobClient and then call its WithPipeline method passing in the desired pipeline object. Or, call this package's + * getBlobAsyncClient instead of calling this object's getBlobAsyncClient method. * + * @param blobName A {@code String} representing the name of the blob. + * @param snapshot the snapshot identifier for the blob. * @return A new {@link BlobClient} object which references the blob with the specified name in this container. */ public BlobClient getBlobClient(String blobName, String snapshot) { @@ -175,8 +173,7 @@ public BlobClient getBlobClient(String blobName, String snapshot) { /** * Initializes a {@link StorageClient} object pointing to the storage account this container is in. * - * @return - * A {@link StorageClient} object pointing to the specified storage account + * @return A {@link StorageClient} object pointing to the specified storage account */ public StorageClient getStorageClient() { return new StorageClient(containerAsyncClient.getStorageAsyncClient()); @@ -184,6 +181,7 @@ public StorageClient getStorageClient() { /** * Gets the URL of the container represented by this client. + * * @return the URL. */ public URL getContainerUrl() { @@ -202,10 +200,8 @@ public Response exists() { /** * Gets if the container this client represents exists in the cloud. * - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. - * @return - * true if the container exists, false if it doesn't + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @return true if the container exists, false if it doesn't */ public Response exists(Duration timeout) { Mono> response = containerAsyncClient.exists(); @@ -217,6 +213,7 @@ public Response exists(Duration timeout) { * Creates a new container within a storage account. If a container with the same name already exists, the operation * fails. For more information, see the * Azure Docs. + * * @return A response containing status code and HTTP headers */ public VoidResponse create() { @@ -228,13 +225,10 @@ public VoidResponse create() { * fails. For more information, see the * Azure Docs. * - * @param metadata - * {@link Metadata} - * @param accessType - * Specifies how the data in this container is available to the public. See the x-ms-blob-public-access header - * in the Azure Docs for more information. Pass null for no public access. - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @param metadata {@link Metadata} + * @param accessType Specifies how the data in this container is available to the public. See the + * x-ms-blob-public-access header in the Azure Docs for more information. Pass null for no public access. + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. * @return A response containing status code and HTTP headers */ public VoidResponse create(Metadata metadata, PublicAccessType accessType, Duration timeout) { @@ -244,9 +238,10 @@ public VoidResponse create(Metadata metadata, PublicAccessType accessType, Durat } /** - * Marks the specified container for deletion. The container and any blobs contained within it are later - * deleted during garbage collection. For more information, see the + * Marks the specified container for deletion. The container and any blobs contained within it are later deleted + * during garbage collection. For more information, see the * Azure Docs. + * * @return A response containing status code and HTTP headers */ public VoidResponse delete() { @@ -254,14 +249,12 @@ public VoidResponse delete() { } /** - * Marks the specified container for deletion. The container and any blobs contained within it are later - * deleted during garbage collection. For more information, see the + * Marks the specified container for deletion. The container and any blobs contained within it are later deleted + * during garbage collection. For more information, see the * Azure Docs. * - * @param accessConditions - * {@link ContainerAccessConditions} - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @param accessConditions {@link ContainerAccessConditions} + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. * @return A response containing status code and HTTP headers */ public VoidResponse delete(ContainerAccessConditions accessConditions, Duration timeout) { @@ -274,8 +267,7 @@ public VoidResponse delete(ContainerAccessConditions accessConditions, Duration * Returns the container's metadata and system properties. For more information, see the * Azure Docs. * - * @return - * The container properties. + * @return The container properties. */ public Response getProperties() { return this.getProperties(null, null); @@ -285,17 +277,13 @@ public Response getProperties() { * Returns the container's metadata and system properties. For more information, see the * Azure Docs. * - * @param leaseAccessConditions - * By setting lease access conditions, requests will fail if the provided lease does not match the active - * lease on the blob. - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. - * - * @return - * The container properties. + * @param leaseAccessConditions By setting lease access conditions, requests will fail if the provided lease does + * not match the active lease on the blob. + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @return The container properties. */ public Response getProperties(LeaseAccessConditions leaseAccessConditions, - Duration timeout) { + Duration timeout) { Mono> response = containerAsyncClient.getProperties(leaseAccessConditions); return Utility.blockWithOptionalTimeout(response, timeout); @@ -305,8 +293,7 @@ public Response getProperties(LeaseAccessConditions leaseAc * Sets the container's metadata. For more information, see the * Azure Docs. * - * @param metadata - * {@link Metadata} + * @param metadata {@link Metadata} * @return A response containing status code and HTTP headers */ public VoidResponse setMetadata(Metadata metadata) { @@ -317,16 +304,13 @@ public VoidResponse setMetadata(Metadata metadata) { * Sets the container's metadata. For more information, see the * Azure Docs. * - * @param metadata - * {@link Metadata} - * @param accessConditions - * {@link ContainerAccessConditions} - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @param metadata {@link Metadata} + * @param accessConditions {@link ContainerAccessConditions} + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. * @return A response containing status code and HTTP headers */ public VoidResponse setMetadata(Metadata metadata, - ContainerAccessConditions accessConditions, Duration timeout) { + ContainerAccessConditions accessConditions, Duration timeout) { Mono response = containerAsyncClient.setMetadata(metadata, accessConditions); return Utility.blockWithOptionalTimeout(response, timeout); @@ -337,8 +321,7 @@ public VoidResponse setMetadata(Metadata metadata, * For more information, see the * Azure Docs. * - * @return - * The container access policy. + * @return The container access policy. */ public Response getAccessPolicy() { return this.getAccessPolicy(null, null); @@ -349,14 +332,10 @@ public Response getAccessPolicy() { * For more information, see the * Azure Docs. * - * @param leaseAccessConditions - * By setting lease access conditions, requests will fail if the provided lease does not match the active - * lease on the blob. - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. - * - * @return - * The container access policy. + * @param leaseAccessConditions By setting lease access conditions, requests will fail if the provided lease does + * not match the active lease on the blob. + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @return The container access policy. */ public Response getAccessPolicy(LeaseAccessConditions leaseAccessConditions, Duration timeout) { @@ -371,17 +350,16 @@ public Response getAccessPolicy(LeaseAccessConditions l * ensure the time formatting is compatible with the service. For more information, see the * Azure Docs. * - * @param accessType - * Specifies how the data in this container is available to the public. See the x-ms-blob-public-access header - * in the Azure Docs for more information. Pass null for no public access. - * @param identifiers - * A list of {@link SignedIdentifier} objects that specify the permissions for the container. Please see - * here - * for more information. Passing null will clear all access policies. + * @param accessType Specifies how the data in this container is available to the public. See the + * x-ms-blob-public-access header in the Azure Docs for more information. Pass null for no public access. + * @param identifiers A list of {@link SignedIdentifier} objects that specify the permissions for the container. + * Please see + * here + * for more information. Passing null will clear all access policies. * @return A response containing status code and HTTP headers */ public VoidResponse setAccessPolicy(PublicAccessType accessType, - List identifiers) { + List identifiers) { return this.setAccessPolicy(accessType, identifiers, null, null); } @@ -391,31 +369,27 @@ public VoidResponse setAccessPolicy(PublicAccessType accessType, * ensure the time formatting is compatible with the service. For more information, see the * Azure Docs. * - * @param accessType - * Specifies how the data in this container is available to the public. See the x-ms-blob-public-access header - * in the Azure Docs for more information. Pass null for no public access. - * @param identifiers - * A list of {@link SignedIdentifier} objects that specify the permissions for the container. Please see - * here - * for more information. Passing null will clear all access policies. - * @param accessConditions - * {@link ContainerAccessConditions} - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @param accessType Specifies how the data in this container is available to the public. See the + * x-ms-blob-public-access header in the Azure Docs for more information. Pass null for no public access. + * @param identifiers A list of {@link SignedIdentifier} objects that specify the permissions for the container. + * Please see + * here + * for more information. Passing null will clear all access policies. + * @param accessConditions {@link ContainerAccessConditions} + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. * @return A response containing status code and HTTP headers */ public VoidResponse setAccessPolicy(PublicAccessType accessType, - List identifiers, ContainerAccessConditions accessConditions, - Duration timeout) { + List identifiers, ContainerAccessConditions accessConditions, + Duration timeout) { Mono response = containerAsyncClient.setAccessPolicy(accessType, identifiers, accessConditions); return Utility.blockWithOptionalTimeout(response, timeout); } /** - * Returns a lazy loaded list of blobs in this container, with folder structures flattened. - * The returned {@link Iterable} can be iterated through while new items are automatically - * retrieved as needed. + * Returns a lazy loaded list of blobs in this container, with folder structures flattened. The returned {@link + * Iterable} can be iterated through while new items are automatically retrieved as needed. * *

* Blob names are returned in lexicographic order. @@ -424,17 +398,15 @@ public VoidResponse setAccessPolicy(PublicAccessType accessType, * For more information, see the * Azure Docs. * - * @return - * The listed blobs, flattened. + * @return The listed blobs, flattened. */ public Iterable listBlobsFlat() { return this.listBlobsFlat(new ListBlobsOptions(), null); } /** - * Returns a lazy loaded list of blobs in this container, with folder structures flattened. - * The returned {@link Iterable} can be iterated through while new items are automatically - * retrieved as needed. + * Returns a lazy loaded list of blobs in this container, with folder structures flattened. The returned {@link + * Iterable} can be iterated through while new items are automatically retrieved as needed. * *

* Blob names are returned in lexicographic order. @@ -443,13 +415,9 @@ public Iterable listBlobsFlat() { * For more information, see the * Azure Docs. * - * @param options - * {@link ListBlobsOptions} - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. - * - * @return - * The listed blobs, flattened. + * @param options {@link ListBlobsOptions} + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @return The listed blobs, flattened. */ public Iterable listBlobsFlat(ListBlobsOptions options, Duration timeout) { Flux response = containerAsyncClient.listBlobsFlat(options); @@ -458,74 +426,64 @@ public Iterable listBlobsFlat(ListBlobsOptions options, Duration timeo } /** - * Returns a reactive Publisher emitting all the blobs and directories (prefixes) under - * the given directory (prefix). Directories will have {@link BlobItem#isPrefix()} set to - * true. + * Returns a reactive Publisher emitting all the blobs and directories (prefixes) under the given directory + * (prefix). Directories will have {@link BlobItem#isPrefix()} set to true. * *

* Blob names are returned in lexicographic order. For more information, see the * Azure Docs. * *

- * E.g. listing a container containing a 'foo' folder, which contains blobs 'foo1' and 'foo2', and a blob - * on the root level 'bar', will return the following results when prefix=null: + * E.g. listing a container containing a 'foo' folder, which contains blobs 'foo1' and 'foo2', and a blob on the + * root level 'bar', will return the following results when prefix=null: * *

    - *
  • foo/ (isPrefix = true) - *
  • bar (isPrefix = false) + *
  • foo/ (isPrefix = true) + *
  • bar (isPrefix = false) *
*

* will return the following results when prefix="foo/": * *

    - *
  • foo/foo1 (isPrefix = false) - *
  • foo/foo2 (isPrefix = false) + *
  • foo/foo1 (isPrefix = false) + *
  • foo/foo2 (isPrefix = false) *
* - * @param directory - * The directory to list blobs underneath - * - * @return - * A reactive response emitting the prefixes and blobs. + * @param directory The directory to list blobs underneath + * @return A reactive response emitting the prefixes and blobs. */ public Iterable listBlobsHierarchy(String directory) { return this.listBlobsHierarchy("/", new ListBlobsOptions().prefix(directory), null); } /** - * Returns a reactive Publisher emitting all the blobs and prefixes (directories) under - * the given prefix (directory). Directories will have {@link BlobItem#isPrefix()} set to - * true. + * Returns a reactive Publisher emitting all the blobs and prefixes (directories) under the given prefix + * (directory). Directories will have {@link BlobItem#isPrefix()} set to true. * *

* Blob names are returned in lexicographic order. For more information, see the * Azure Docs. * *

- * E.g. listing a container containing a 'foo' folder, which contains blobs 'foo1' and 'foo2', and a blob - * on the root level 'bar', will return the following results when prefix=null: + * E.g. listing a container containing a 'foo' folder, which contains blobs 'foo1' and 'foo2', and a blob on the + * root level 'bar', will return the following results when prefix=null: * *

    - *
  • foo/ (isPrefix = true) - *
  • bar (isPrefix = false) + *
  • foo/ (isPrefix = true) + *
  • bar (isPrefix = false) *
*

* will return the following results when prefix="foo/": * *

    - *
  • foo/foo1 (isPrefix = false) - *
  • foo/foo2 (isPrefix = false) + *
  • foo/foo1 (isPrefix = false) + *
  • foo/foo2 (isPrefix = false) *
* - * @param delimiter - * The delimiter for blob hierarchy, "/" for hierarchy based on directories - * @param options - * {@link ListBlobsOptions} - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. - * - * @return - * A reactive response emitting the prefixes and blobs. + * @param delimiter The delimiter for blob hierarchy, "/" for hierarchy based on directories + * @param options {@link ListBlobsOptions} + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @return A reactive response emitting the prefixes and blobs. */ public Iterable listBlobsHierarchy(String delimiter, ListBlobsOptions options, Duration timeout) { Flux response = containerAsyncClient.listBlobsHierarchy(delimiter, options); @@ -537,14 +495,10 @@ public Iterable listBlobsHierarchy(String delimiter, ListBlobsOptions * Acquires a lease on the blob for write and delete operations. The lease duration must be between 15 to 60 * seconds, or infinite (-1). * - * @param proposedId - * A {@code String} in any valid GUID format. May be null. - * @param duration - * The duration of the lease, in seconds, or negative one (-1) for a lease that - * never expires. A non-infinite lease can be between 15 and 60 seconds. - * - * @return - * The lease ID. + * @param proposedId A {@code String} in any valid GUID format. May be null. + * @param duration The duration of the lease, in seconds, or negative one (-1) for a lease that never expires. A + * non-infinite lease can be between 15 and 60 seconds. + * @return The lease ID. */ public Response acquireLease(String proposedId, int duration) { return this.acquireLease(proposedId, duration, null, null); @@ -554,23 +508,17 @@ public Response acquireLease(String proposedId, int duration) { * Acquires a lease on the blob for write and delete operations. The lease duration must be between 15 to 60 * seconds, or infinite (-1). * - * @param proposedID - * A {@code String} in any valid GUID format. May be null. - * @param duration - * The duration of the lease, in seconds, or negative one (-1) for a lease that - * never expires. A non-infinite lease can be between 15 and 60 seconds. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. - * - * @return - * The lease ID. + * @param proposedID A {@code String} in any valid GUID format. May be null. + * @param duration The duration of the lease, in seconds, or negative one (-1) for a lease that never expires. A + * non-infinite lease can be between 15 and 60 seconds. + * @param modifiedAccessConditions Standard HTTP Access conditions related to the modification of data. ETag and + * LastModifiedTime are used to construct conditions related to when the blob was changed relative to the given + * request. The request will fail if the specified condition is not satisfied. + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @return The lease ID. */ public Response acquireLease(String proposedID, int duration, - ModifiedAccessConditions modifiedAccessConditions, Duration timeout) { + ModifiedAccessConditions modifiedAccessConditions, Duration timeout) { Mono> response = containerAsyncClient .acquireLease(proposedID, duration, modifiedAccessConditions); @@ -580,11 +528,8 @@ public Response acquireLease(String proposedID, int duration, /** * Renews the blob's previously-acquired lease. * - * @param leaseID - * The leaseId of the active lease on the blob. - * - * @return - * The renewed lease ID. + * @param leaseID The leaseId of the active lease on the blob. + * @return The renewed lease ID. */ public Response renewLease(String leaseID) { return this.renewLease(leaseID, null, null); @@ -593,20 +538,15 @@ public Response renewLease(String leaseID) { /** * Renews the blob's previously-acquired lease. * - * @param leaseID - * The leaseId of the active lease on the blob. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. - * - * @return - * The renewed lease ID. + * @param leaseID The leaseId of the active lease on the blob. + * @param modifiedAccessConditions Standard HTTP Access conditions related to the modification of data. ETag and + * LastModifiedTime are used to construct conditions related to when the blob was changed relative to the given + * request. The request will fail if the specified condition is not satisfied. + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @return The renewed lease ID. */ public Response renewLease(String leaseID, ModifiedAccessConditions modifiedAccessConditions, - Duration timeout) { + Duration timeout) { Mono> response = containerAsyncClient .renewLease(leaseID, modifiedAccessConditions); @@ -616,8 +556,7 @@ public Response renewLease(String leaseID, ModifiedAccessConditions modi /** * Releases the blob's previously-acquired lease. * - * @param leaseID - * The leaseId of the active lease on the blob. + * @param leaseID The leaseId of the active lease on the blob. * @return A response containing status code and HTTP headers */ public VoidResponse releaseLease(String leaseID) { @@ -627,18 +566,15 @@ public VoidResponse releaseLease(String leaseID) { /** * Releases the blob's previously-acquired lease. * - * @param leaseID - * The leaseId of the active lease on the blob. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @param leaseID The leaseId of the active lease on the blob. + * @param modifiedAccessConditions Standard HTTP Access conditions related to the modification of data. ETag and + * LastModifiedTime are used to construct conditions related to when the blob was changed relative to the given + * request. The request will fail if the specified condition is not satisfied. + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. * @return A response containing status code and HTTP headers. */ public VoidResponse releaseLease(String leaseID, - ModifiedAccessConditions modifiedAccessConditions, Duration timeout) { + ModifiedAccessConditions modifiedAccessConditions, Duration timeout) { Mono response = containerAsyncClient .releaseLease(leaseID, modifiedAccessConditions); @@ -649,8 +585,7 @@ public VoidResponse releaseLease(String leaseID, * BreakLease breaks the blob's previously-acquired lease (if it exists). Pass the LeaseBreakDefault (-1) constant * to break a fixed-duration lease when it expires or an infinite lease immediately. * - * @return - * The remaining time in the broken lease. + * @return The remaining time in the broken lease. */ public Response breakLease() { return this.breakLease(null, null, null); @@ -660,24 +595,19 @@ public Response breakLease() { * BreakLease breaks the blob's previously-acquired lease (if it exists). Pass the LeaseBreakDefault (-1) constant * to break a fixed-duration lease when it expires or an infinite lease immediately. * - * @param breakPeriodInSeconds - * An optional {@code Integer} representing the proposed duration of seconds that the lease should continue - * before it is broken, between 0 and 60 seconds. This break period is only used if it is shorter than the - * time remaining on the lease. If longer, the time remaining on the lease is used. A new lease will not be - * available before the break period has expired, but the lease may be held for longer than the break - * period. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. - * - * @return - * The remaining time in the broken lease. + * @param breakPeriodInSeconds An optional {@code Integer} representing the proposed duration of seconds that the + * lease should continue before it is broken, between 0 and 60 seconds. This break period is only used if it is + * shorter than the time remaining on the lease. If longer, the time remaining on the lease is used. A new lease + * will not be available before the break period has expired, but the lease may be held for longer than the break + * period. + * @param modifiedAccessConditions Standard HTTP Access conditions related to the modification of data. ETag and + * LastModifiedTime are used to construct conditions related to when the blob was changed relative to the given + * request. The request will fail if the specified condition is not satisfied. + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @return The remaining time in the broken lease. */ public Response breakLease(Integer breakPeriodInSeconds, - ModifiedAccessConditions modifiedAccessConditions, Duration timeout) { + ModifiedAccessConditions modifiedAccessConditions, Duration timeout) { Mono> response = containerAsyncClient .breakLease(breakPeriodInSeconds, modifiedAccessConditions); @@ -687,36 +617,28 @@ public Response breakLease(Integer breakPeriodInSeconds, /** * ChangeLease changes the blob's lease ID. * - * @param leaseId - * The leaseId of the active lease on the blob. - * @param proposedID - * A {@code String} in any valid GUID format. - * - * @return - * The new lease ID. + * @param leaseId The leaseId of the active lease on the blob. + * @param proposedID A {@code String} in any valid GUID format. + * @return The new lease ID. */ public Response changeLease(String leaseId, String proposedID) { return this.changeLease(leaseId, proposedID, null, null); } /** - * ChangeLease changes the blob's lease ID. For more information, see the Azure Docs. - * - * @param leaseId - * The leaseId of the active lease on the blob. - * @param proposedID - * A {@code String} in any valid GUID format. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. + * ChangeLease changes the blob's lease ID. For more information, see the Azure + * Docs. * + * @param leaseId The leaseId of the active lease on the blob. + * @param proposedID A {@code String} in any valid GUID format. + * @param modifiedAccessConditions Standard HTTP Access conditions related to the modification of data. ETag and + * LastModifiedTime are used to construct conditions related to when the blob was changed relative to the given + * request. The request will fail if the specified condition is not satisfied. + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. * @return The new lease ID. */ public Response changeLease(String leaseId, String proposedID, - ModifiedAccessConditions modifiedAccessConditions, Duration timeout) { + ModifiedAccessConditions modifiedAccessConditions, Duration timeout) { Mono> response = containerAsyncClient .changeLease(leaseId, proposedID, modifiedAccessConditions); @@ -727,15 +649,137 @@ public Response changeLease(String leaseId, String proposedID, * Returns the sku name and account kind for the account. For more information, please see the * Azure Docs. * - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. - * - * @return - * The account info. + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @return The account info. */ public Response getAccountInfo(Duration timeout) { Mono> response = containerAsyncClient.getAccountInfo(); return Utility.blockWithOptionalTimeout(response, timeout); } + + /** + * Generates a user delegation SAS token with the specified parameters + * + * @param userDelegationKey The {@code UserDelegationKey} user delegation key for the SAS + * @param accountName The {@code String} account name for the SAS + * @param permissions The {@code ContainerSASPermissions} permission for the SAS + * @param expiryTime The {@code OffsetDateTime} expiry time for the SAS + * @return A string that represents the SAS token + */ + public String generateUserDelegationSAS(UserDelegationKey userDelegationKey, String accountName, + ContainerSASPermission permissions, OffsetDateTime expiryTime) { + return this.containerAsyncClient.generateUserDelegationSAS(userDelegationKey, accountName, permissions, + expiryTime); + } + + /** + * Generates a user delegation SAS token with the specified parameters + * + * @param userDelegationKey The {@code UserDelegationKey} user delegation key for the SAS + * @param accountName The {@code String} account name for the SAS + * @param permissions The {@code ContainerSASPermissions} permission for the SAS + * @param expiryTime The {@code OffsetDateTime} expiry time for the SAS + * @param startTime An optional {@code OffsetDateTime} start time for the SAS + * @param version An optional {@code String} version for the SAS + * @param sasProtocol An optional {@code SASProtocol} protocol for the SAS + * @param ipRange An optional {@code IPRange} ip address range for the SAS + * @return A string that represents the SAS token + */ + public String generateUserDelegationSAS(UserDelegationKey userDelegationKey, String accountName, + ContainerSASPermission permissions, OffsetDateTime expiryTime, OffsetDateTime startTime, String version, + SASProtocol sasProtocol, IPRange ipRange) { + return this.containerAsyncClient.generateUserDelegationSAS(userDelegationKey, accountName, permissions, + expiryTime, startTime, version, sasProtocol, ipRange); + } + + /** + * Generates a user delegation SAS token with the specified parameters + * + * @param userDelegationKey The {@code UserDelegationKey} user delegation key for the SAS + * @param accountName The {@code String} account name for the SAS + * @param permissions The {@code ContainerSASPermissions} permission for the SAS + * @param expiryTime The {@code OffsetDateTime} expiry time for the SAS + * @param startTime An optional {@code OffsetDateTime} start time for the SAS + * @param version An optional {@code String} version for the SAS + * @param sasProtocol An optional {@code SASProtocol} protocol for the SAS + * @param ipRange An optional {@code IPRange} ip address range for the SAS + * @param cacheControl An optional {@code String} cache-control header for the SAS. + * @param contentDisposition An optional {@code String} content-disposition header for the SAS. + * @param contentEncoding An optional {@code String} content-encoding header for the SAS. + * @param contentLanguage An optional {@code String} content-language header for the SAS. + * @param contentType An optional {@code String} content-type header for the SAS. + * @return A string that represents the SAS token + */ + public String generateUserDelegationSAS(UserDelegationKey userDelegationKey, String accountName, + ContainerSASPermission permissions, OffsetDateTime expiryTime, OffsetDateTime startTime, String version, + SASProtocol sasProtocol, IPRange ipRange, String cacheControl, String contentDisposition, + String contentEncoding, String contentLanguage, String contentType) { + return this.containerAsyncClient.generateUserDelegationSAS(userDelegationKey, accountName, permissions, + expiryTime, startTime, version, sasProtocol, ipRange, cacheControl, contentDisposition, contentEncoding, + contentLanguage, contentType); + } + + /** + * Generates a SAS token with the specified parameters + * + * @param permissions The {@code ContainerSASPermissions} permission for the SAS + * @param expiryTime The {@code OffsetDateTime} expiry time for the SAS + * @return A string that represents the SAS token + */ + public String generateSAS(ContainerSASPermission permissions, OffsetDateTime expiryTime) { + return this.containerAsyncClient.generateSAS(permissions, expiryTime); + } + + /** + * Generates a SAS token with the specified parameters + * + * @param identifier The {@code String} name of the access policy on the container this SAS references if any + * @return A string that represents the SAS token + */ + public String generateSAS(String identifier) { + return this.containerAsyncClient.generateSAS(identifier); + } + + /** + * Generates a SAS token with the specified parameters + * + * @param identifier The {@code String} name of the access policy on the container this SAS references if any + * @param permissions The {@code ContainerSASPermissions} permission for the SAS + * @param expiryTime The {@code OffsetDateTime} expiry time for the SAS + * @param startTime An optional {@code OffsetDateTime} start time for the SAS + * @param version An optional {@code String} version for the SAS + * @param sasProtocol An optional {@code SASProtocol} protocol for the SAS + * @param ipRange An optional {@code IPRange} ip address range for the SAS + * @return A string that represents the SAS token + */ + public String generateSAS(String identifier, ContainerSASPermission permissions, OffsetDateTime expiryTime, + OffsetDateTime startTime, String version, SASProtocol sasProtocol, IPRange ipRange) { + return this.containerAsyncClient.generateSAS(identifier, permissions, expiryTime, startTime, version, + sasProtocol, ipRange); + } + + /** + * Generates a SAS token with the specified parameters + * + * @param identifier The {@code String} name of the access policy on the container this SAS references if any + * @param permissions The {@code ContainerSASPermissions} permission for the SAS + * @param expiryTime The {@code OffsetDateTime} expiry time for the SAS + * @param startTime An optional {@code OffsetDateTime} start time for the SAS + * @param version An optional {@code String} version for the SAS + * @param sasProtocol An optional {@code SASProtocol} protocol for the SAS + * @param ipRange An optional {@code IPRange} ip address range for the SAS + * @param cacheControl An optional {@code String} cache-control header for the SAS. + * @param contentDisposition An optional {@code String} content-disposition header for the SAS. + * @param contentEncoding An optional {@code String} content-encoding header for the SAS. + * @param contentLanguage An optional {@code String} content-language header for the SAS. + * @param contentType An optional {@code String} content-type header for the SAS. + * @return A string that represents the SAS token + */ + public String generateSAS(String identifier, ContainerSASPermission permissions, OffsetDateTime expiryTime, + OffsetDateTime startTime, String version, SASProtocol sasProtocol, IPRange ipRange, String cacheControl, + String contentDisposition, String contentEncoding, String contentLanguage, String contentType) { + return this.containerAsyncClient.generateSAS(identifier, permissions, expiryTime, startTime, version, + sasProtocol, ipRange, cacheControl, contentDisposition, contentEncoding, contentLanguage, contentType); + } } diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/ContainerClientBuilder.java b/storage/client/blob/src/main/java/com/azure/storage/blob/ContainerClientBuilder.java index c1e4e6bbdaeb2..7c9e9543ffd94 100644 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/ContainerClientBuilder.java +++ b/storage/client/blob/src/main/java/com/azure/storage/blob/ContainerClientBuilder.java @@ -121,14 +121,20 @@ private AzureBlobStorageBuilder buildImpl() { } /** + * Creates a {@link ContainerClient} based on options set in the Builder. + * * @return a {@link ContainerClient} created from the configurations in this builder. + * @throws NullPointerException If {@code endpoint} is {@code null} or {@code containerName} is {@code null}. */ public ContainerClient buildClient() { return new ContainerClient(buildAsyncClient()); } /** + * Creates a {@link ContainerAsyncClient} based on options set in the Builder. + * * @return a {@link ContainerAsyncClient} created from the configurations in this builder. + * @throws NullPointerException If {@code endpoint} is {@code null} or {@code containerName} is {@code null}. */ public ContainerAsyncClient buildAsyncClient() { return new ContainerAsyncClient(buildImpl()); @@ -138,32 +144,25 @@ public ContainerAsyncClient buildAsyncClient() { * Sets the service endpoint, additionally parses it for information (SAS token, container name) * @param endpoint URL of the service * @return the updated ContainerClientBuilder object - * @throws IllegalArgumentException If {@code endpoint} is a malformed URL. + * @throws IllegalArgumentException If {@code endpoint} is {@code null} or is a malformed URL. */ public ContainerClientBuilder endpoint(String endpoint) { - Objects.requireNonNull(endpoint); - URL url; try { - url = new URL(endpoint); - this.endpoint = url.getProtocol() + "://" + url.getAuthority(); - String path = url.getPath(); - if (path != null && !path.isEmpty() && !path.equals("/")) { - path = path.replaceAll("^/", "").replaceAll("/$", ""); - if (path.contains("/")) { - throw new IllegalArgumentException("Endpoint should contain exactly 0 or 1 path segments"); - } else { - this.containerName = path; - } + URL url = new URL(endpoint); + BlobURLParts parts = URLParser.parse(url); + + this.endpoint = parts.scheme() + "://" + parts.host(); + this.containerName = parts.containerName(); + + this.sasTokenCredential = SASTokenCredential.fromQueryParameters(parts.sasQueryParameters()); + if (this.sasTokenCredential != null) { + this.tokenCredential = null; + this.sharedKeyCredential = null; } } catch (MalformedURLException ex) { throw new IllegalArgumentException("The Azure Storage Blob endpoint url is malformed."); } - SASTokenCredential credential = SASTokenCredential.fromQuery(url.getQuery()); - if (credential != null) { - this.credential(credential); - } - return this; } @@ -185,9 +184,10 @@ String endpoint() { * Sets the credential used to authorize requests sent to the service * @param credential authorization credential * @return the updated ContainerClientBuilder object + * @throws NullPointerException If {@code credential} is {@code null}. */ public ContainerClientBuilder credential(SharedKeyCredential credential) { - this.sharedKeyCredential = credential; + this.sharedKeyCredential = Objects.requireNonNull(credential); this.tokenCredential = null; this.sasTokenCredential = null; return this; @@ -197,9 +197,10 @@ public ContainerClientBuilder credential(SharedKeyCredential credential) { * Sets the credential used to authorize requests sent to the service * @param credential authorization credential * @return the updated ContainerClientBuilder object + * @throws NullPointerException If {@code credential} is {@code null}. */ public ContainerClientBuilder credential(TokenCredential credential) { - this.tokenCredential = credential; + this.tokenCredential = Objects.requireNonNull(credential); this.sharedKeyCredential = null; this.sasTokenCredential = null; return this; @@ -209,9 +210,10 @@ public ContainerClientBuilder credential(TokenCredential credential) { * Sets the credential used to authorize requests sent to the service * @param credential authorization credential * @return the updated ContainerClientBuilder object + * @throws NullPointerException If {@code credential} is {@code null}. */ public ContainerClientBuilder credential(SASTokenCredential credential) { - this.sasTokenCredential = credential; + this.sasTokenCredential = Objects.requireNonNull(credential); this.sharedKeyCredential = null; this.tokenCredential = null; return this; @@ -265,9 +267,10 @@ public ContainerClientBuilder connectionString(String connectionString) { * Sets the http client used to send service requests * @param httpClient http client to send requests * @return the updated ContainerClientBuilder object + * @throws NullPointerException If {@code httpClient} is {@code null}. */ public ContainerClientBuilder httpClient(HttpClient httpClient) { - this.httpClient = httpClient; + this.httpClient = Objects.requireNonNull(httpClient); return this; } @@ -275,9 +278,10 @@ public ContainerClientBuilder httpClient(HttpClient httpClient) { * Adds a pipeline policy to apply on each request sent * @param pipelinePolicy a pipeline policy * @return the updated ContainerClientBuilder object + * @throws NullPointerException If {@code pipelinePolicy} is {@code null}. */ public ContainerClientBuilder addPolicy(HttpPipelinePolicy pipelinePolicy) { - this.policies.add(pipelinePolicy); + this.policies.add(Objects.requireNonNull(pipelinePolicy)); return this; } @@ -306,9 +310,10 @@ public ContainerClientBuilder configuration(Configuration configuration) { * Sets the request retry options for all the requests made through the client. * @param retryOptions the options to configure retry behaviors * @return the updated ContainerClientBuilder object + * @throws NullPointerException If {@code retryOptions} is {@code null}. */ public ContainerClientBuilder retryOptions(RequestRetryOptions retryOptions) { - this.retryOptions = retryOptions; + this.retryOptions = Objects.requireNonNull(retryOptions); return this; } } diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/ContainerRawClient.java b/storage/client/blob/src/main/java/com/azure/storage/blob/ContainerRawClient.java deleted file mode 100644 index 19d10f5444ee5..0000000000000 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/ContainerRawClient.java +++ /dev/null @@ -1,652 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.azure.storage.blob; - -import com.azure.core.http.rest.Response; -import com.azure.storage.blob.implementation.AzureBlobStorageImpl; -import com.azure.storage.blob.models.ContainerAccessConditions; -import com.azure.storage.blob.models.ContainerAccessPolicies; -import com.azure.storage.blob.models.ContainersAcquireLeaseResponse; -import com.azure.storage.blob.models.ContainersBreakLeaseResponse; -import com.azure.storage.blob.models.ContainersChangeLeaseResponse; -import com.azure.storage.blob.models.ContainersCreateResponse; -import com.azure.storage.blob.models.ContainersDeleteResponse; -import com.azure.storage.blob.models.ContainersGetAccountInfoResponse; -import com.azure.storage.blob.models.ContainersGetPropertiesResponse; -import com.azure.storage.blob.models.ContainersListBlobFlatSegmentResponse; -import com.azure.storage.blob.models.ContainersListBlobHierarchySegmentResponse; -import com.azure.storage.blob.models.ContainersReleaseLeaseResponse; -import com.azure.storage.blob.models.ContainersRenewLeaseResponse; -import com.azure.storage.blob.models.ContainersSetAccessPolicyResponse; -import com.azure.storage.blob.models.ContainersSetMetadataResponse; -import com.azure.storage.blob.models.LeaseAccessConditions; -import com.azure.storage.blob.models.ListBlobsOptions; -import com.azure.storage.blob.models.Metadata; -import com.azure.storage.blob.models.ModifiedAccessConditions; -import com.azure.storage.blob.models.PublicAccessType; -import com.azure.storage.blob.models.SignedIdentifier; -import reactor.core.publisher.Mono; - -import java.time.Duration; -import java.util.List; - -/** - * Represents a URL to a container. It may be obtained by direct construction or via the create method on a - * {@link StorageAsyncRawClient} object. This class does not hold any state about a particular blob but is instead a convenient way - * of sending off appropriate requests to the resource on the service. It may also be used to construct URLs to blobs. - * Please refer to the - * Azure Docs - * for more information on containers. - */ -final class ContainerRawClient { - - private ContainerAsyncRawClient containerAsyncRawClient; - - public static final String ROOT_CONTAINER_NAME = "$root"; - - public static final String STATIC_WEBSITE_CONTAINER_NAME = "$web"; - - public static final String LOG_CONTAINER_NAME = "$logs"; - - - /** - * Creates a {@code ContainerAsyncClient} object pointing to the account specified by the URL and using the provided - * pipeline to make HTTP requests. - */ - ContainerRawClient(AzureBlobStorageImpl azureBlobStorage) { - this.containerAsyncRawClient = new ContainerAsyncRawClient(azureBlobStorage); - } - - /** - * Creates a new container within a storage account. If a container with the same name already exists, the operation - * fails. For more information, see the - * Azure Docs. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_basic "Sample code for ContainerAsyncClient.create")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ContainersCreateResponse create() { - return this.create(null, null, null); - } - - /** - * Creates a new container within a storage account. If a container with the same name already exists, the operation - * fails. For more information, see the - * Azure Docs. - * - * @param metadata - * {@link Metadata} - * @param accessType - * Specifies how the data in this container is available to the public. See the x-ms-blob-public-access header - * in the Azure Docs for more information. Pass null for no public access. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_basic "Sample code for ContainerAsyncClient.create")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ContainersCreateResponse create(Metadata metadata, PublicAccessType accessType, Duration timeout) { - Mono response = containerAsyncRawClient.create(metadata, accessType); - return Utility.blockWithOptionalTimeout(response, timeout); - } - - /** - * Marks the specified container for deletion. The container and any blobs contained within it are later - * deleted during garbage collection. For more information, see the - * Azure Docs. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_basic "Sample code for ContainerAsyncClient.delete")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ContainersDeleteResponse delete() { - return this.delete(null, null); - } - - /** - * Marks the specified container for deletion. The container and any blobs contained within it are later - * deleted during garbage collection. For more information, see the - * Azure Docs. - * - * @param accessConditions - * {@link ContainerAccessConditions} - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_basic "Sample code for ContainerAsyncClient.delete")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ContainersDeleteResponse delete(ContainerAccessConditions accessConditions, Duration timeout) { - Mono response = containerAsyncRawClient.delete(accessConditions); - return Utility.blockWithOptionalTimeout(response, timeout); - } - - /** - * Returns the container's metadata and system properties. For more information, see the - * Azure Docs. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_basic "Sample code for ContainerAsyncClient.getProperties")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ContainersGetPropertiesResponse getProperties() { - return this.getProperties(null, null); - } - - /** - * Returns the container's metadata and system properties. For more information, see the - * Azure Docs. - * - * @param leaseAccessConditions - * By setting lease access conditions, requests will fail if the provided lease does not match the active - * lease on the blob. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_basic "Sample code for ContainerAsyncClient.getProperties")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ContainersGetPropertiesResponse getProperties(LeaseAccessConditions leaseAccessConditions, - Duration timeout) { - Mono response = containerAsyncRawClient.getProperties(leaseAccessConditions); - return Utility.blockWithOptionalTimeout(response, timeout); - } - - /** - * Sets the container's metadata. For more information, see the - * Azure Docs. - * - * @param metadata - * {@link Metadata} - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_basic "Sample code for ContainerAsyncClient.setMetadata")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ContainersSetMetadataResponse setMetadata(Metadata metadata) { - return this.setMetadata(metadata, null, null); - } - - /** - * Sets the container's metadata. For more information, see the - * Azure Docs. - * - * @param metadata - * {@link Metadata} - * @param accessConditions - * {@link ContainerAccessConditions} - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_basic "Sample code for ContainerAsyncClient.setMetadata")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ContainersSetMetadataResponse setMetadata(Metadata metadata, - ContainerAccessConditions accessConditions, Duration timeout) { - Mono response = containerAsyncRawClient.setMetadata(metadata, accessConditions); - return Utility.blockWithOptionalTimeout(response, timeout); - } - - /** - * Returns the container's permissions. The permissions indicate whether container's blobs may be accessed publicly. - * For more information, see the - * Azure Docs. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_policy "Sample code for ContainerAsyncClient.getAccessPolicy")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Response getAccessPolicy() { - return this.getAccessPolicy(null, null); - } - - /** - * Returns the container's permissions. The permissions indicate whether container's blobs may be accessed publicly. - * For more information, see the - * Azure Docs. - * - * @param leaseAccessConditions - * By setting lease access conditions, requests will fail if the provided lease does not match the active - * lease on the blob. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_policy "Sample code for ContainerAsyncClient.getAccessPolicy")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Response getAccessPolicy(LeaseAccessConditions leaseAccessConditions, - Duration timeout) { - Mono> response = containerAsyncRawClient.getAccessPolicy(leaseAccessConditions); - return Utility.blockWithOptionalTimeout(response, timeout); - } - - /** - * Sets the container's permissions. The permissions indicate whether blobs in a container may be accessed publicly. - * Note that, for each signed identifier, we will truncate the start and expiry times to the nearest second to - * ensure the time formatting is compatible with the service. For more information, see the - * Azure Docs. - * - * @param accessType - * Specifies how the data in this container is available to the public. See the x-ms-blob-public-access header - * in the Azure Docs for more information. Pass null for no public access. - * @param identifiers - * A list of {@link SignedIdentifier} objects that specify the permissions for the container. Please see - * here - * for more information. Passing null will clear all access policies. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_policy "Sample code for ContainerAsyncClient.setAccessPolicy")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ContainersSetAccessPolicyResponse setAccessPolicy(PublicAccessType accessType, - List identifiers) { - return this.setAccessPolicy(accessType, identifiers, null, null); - } - - /** - * Sets the container's permissions. The permissions indicate whether blobs in a container may be accessed publicly. - * Note that, for each signed identifier, we will truncate the start and expiry times to the nearest second to - * ensure the time formatting is compatible with the service. For more information, see the - * Azure Docs. - * - * @param accessType - * Specifies how the data in this container is available to the public. See the x-ms-blob-public-access header - * in the Azure Docs for more information. Pass null for no public access. - * @param identifiers - * A list of {@link SignedIdentifier} objects that specify the permissions for the container. Please see - * here - * for more information. Passing null will clear all access policies. - * @param accessConditions - * {@link ContainerAccessConditions} - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_policy "Sample code for ContainerAsyncClient.setAccessPolicy")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ContainersSetAccessPolicyResponse setAccessPolicy(PublicAccessType accessType, - List identifiers, ContainerAccessConditions accessConditions, Duration timeout) { - Mono response = containerAsyncRawClient.setAccessPolicy(accessType, identifiers, accessConditions); - return Utility.blockWithOptionalTimeout(response, timeout); - } - - /** - * Acquires a lease on the container for delete operations. The lease duration must be between 15 to - * 60 seconds, or infinite (-1). For more information, see the - * Azure Docs. - * - * @apiNote - * ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_lease "Sample code for ContainerAsyncClient.acquireLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/New-Storage-SDK-V10-Preview/src/test/java/com/microsoft/azure/storage/Samples.java) - * - * @param proposedId - * A {@code String} in any valid GUID format. - * @param duration - * The duration of the lease, in seconds, or negative one (-1) for a lease that never expires. - * A non-infinite lease can be between 15 and 60 seconds. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_lease "Sample code for ContainerAsyncClient.acquireLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ContainersAcquireLeaseResponse acquireLease(String proposedId, int duration) { - return this.acquireLease(proposedId, duration, null, null); - } - - /** - * Acquires a lease on the container for delete operations. The lease duration must be between 15 to - * 60 seconds, or infinite (-1). For more information, see the - * Azure Docs. - * - * @param proposedID - * A {@code String} in any valid GUID format. - * @param duration - * The duration of the lease, in seconds, or negative one (-1) for a lease that never expires. - * A non-infinite lease can be between 15 and 60 seconds. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_lease "Sample code for ContainerAsyncClient.acquireLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ContainersAcquireLeaseResponse acquireLease(String proposedID, int duration, - ModifiedAccessConditions modifiedAccessConditions, Duration timeout) { - Mono response = containerAsyncRawClient.acquireLease(proposedID, duration, modifiedAccessConditions); - return Utility.blockWithOptionalTimeout(response, timeout); - } - - /** - * Renews the container's previously-acquired lease. For more information, see the - * Azure Docs. - * - * @param leaseID - * The leaseId of the active lease on the container. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_lease "Sample code for ContainerAsyncClient.renewLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ContainersRenewLeaseResponse renewLease(String leaseID) { - return this.renewLease(leaseID, null, null); - } - - /** - * Renews the container's previously-acquired lease. For more information, see the - * Azure Docs. - * - * @param leaseID - * The leaseId of the active lease on the container. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_lease "Sample code for ContainerAsyncClient.renewLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ContainersRenewLeaseResponse renewLease(String leaseID, - ModifiedAccessConditions modifiedAccessConditions, Duration timeout) { - Mono response = containerAsyncRawClient.renewLease(leaseID, modifiedAccessConditions); - return Utility.blockWithOptionalTimeout(response, timeout); - } - - /** - * Releases the container's previously-acquired lease. For more information, see the - * Azure Docs. - * - * @param leaseID - * The leaseId of the active lease on the container. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_lease "Sample code for ContainerAsyncClient.releaseLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ContainersReleaseLeaseResponse releaseLease(String leaseID) { - return this.releaseLease(leaseID, null, null); - } - - /** - * Releases the container's previously-acquired lease. For more information, see the - * Azure Docs. - * - * @param leaseID - * The leaseId of the active lease on the container. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_lease "Sample code for ContainerAsyncClient.releaseLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ContainersReleaseLeaseResponse releaseLease(String leaseID, - ModifiedAccessConditions modifiedAccessConditions, Duration timeout) { - Mono response = containerAsyncRawClient.releaseLease(leaseID, modifiedAccessConditions); - return Utility.blockWithOptionalTimeout(response, timeout); - } - - /** - * Breaks the container's previously-acquired lease. For more information, see the - * Azure Docs. - * - * @apiNote - * ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_lease "Sample code for ContainerAsyncClient.breakLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/New-Storage-SDK-V10-Preview/src/test/java/com/microsoft/azure/storage/Samples.java) - * - * @return Emits the successful response. - */ - public ContainersBreakLeaseResponse breakLease() { - return this.breakLease(null, null, null); - } - - /** - * Breaks the container's previously-acquired lease. For more information, see the - * Azure Docs. - * - * @param breakPeriodInSeconds - * An optional {@code Integer} representing the proposed duration of seconds that the lease should continue - * before it is broken, between 0 and 60 seconds. This break period is only used if it is shorter than the time - * remaining on the lease. If longer, the time remaining on the lease is used. A new lease will not be - * available before the break period has expired, but the lease may be held for longer than the break period. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_lease "Sample code for ContainerAsyncClient.breakLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ContainersBreakLeaseResponse breakLease(Integer breakPeriodInSeconds, - ModifiedAccessConditions modifiedAccessConditions, Duration timeout) { - Mono response = containerAsyncRawClient.breakLease(breakPeriodInSeconds, modifiedAccessConditions); - return Utility.blockWithOptionalTimeout(response, timeout); - } - - /** - * Changes the container's leaseAccessConditions. For more information, see the - * Azure Docs. - * - * @param leaseID - * The leaseId of the active lease on the container. - * @param proposedID - * A {@code String} in any valid GUID format. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_lease "Sample code for ContainerAsyncClient.changeLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ContainersChangeLeaseResponse changeLease(String leaseID, String proposedID) { - return this.changeLease(leaseID, proposedID, null, null); - } - - /** - * Changes the container's leaseAccessConditions. For more information, see the - * Azure Docs. - * - * @param leaseID - * The leaseId of the active lease on the container. - * @param proposedID - * A {@code String} in any valid GUID format. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=container_lease "Sample code for ContainerAsyncClient.changeLease")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ContainersChangeLeaseResponse changeLease(String leaseID, String proposedID, - ModifiedAccessConditions modifiedAccessConditions, Duration timeout) { - Mono response = containerAsyncRawClient.changeLease(leaseID, proposedID, modifiedAccessConditions); - return Utility.blockWithOptionalTimeout(response, timeout); - } - - /** - * Returns a single segment of blobs starting from the specified Marker. Use an empty - * marker to start enumeration from the beginning. Blob names are returned in lexicographic order. - * After getting a segment, process it, and then call ListBlobs again (passing the the previously-returned - * Marker) to get the next segment. For more information, see the - * Azure Docs. - * - * @param marker - * Identifies the portion of the list to be returned with the next list operation. - * This value is returned in the response of a previous list operation as the - * ListBlobsFlatSegmentResponse.body().nextMarker(). Set to null to list the first segment. - * @param options - * {@link ListBlobsOptions} - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=list_blobs_flat "Sample code for ContainerAsyncClient.listBlobsFlatSegment")] \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=list_blobs_flat_helper "helper code for ContainerAsyncClient.listBlobsFlatSegment")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ContainersListBlobFlatSegmentResponse listBlobsFlatSegment(String marker, ListBlobsOptions options) { - return this.listBlobsFlatSegment(marker, options, null); - } - - /** - * Returns a single segment of blobs starting from the specified Marker. Use an empty - * marker to start enumeration from the beginning. Blob names are returned in lexicographic order. - * After getting a segment, process it, and then call ListBlobs again (passing the the previously-returned - * Marker) to get the next segment. For more information, see the - * Azure Docs. - * - * @param marker - * Identifies the portion of the list to be returned with the next list operation. - * This value is returned in the response of a previous list operation as the - * ListBlobsFlatSegmentResponse.body().nextMarker(). Set to null to list the first segment. - * @param options - * {@link ListBlobsOptions} - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=list_blobs_flat "Sample code for ContainerAsyncClient.listBlobsFlatSegment")] \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=list_blobs_flat_helper "helper code for ContainerAsyncClient.listBlobsFlatSegment")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ContainersListBlobFlatSegmentResponse listBlobsFlatSegment(String marker, ListBlobsOptions options, - Duration timeout) { - Mono response = containerAsyncRawClient.listBlobsFlatSegment(marker, options); - return Utility.blockWithOptionalTimeout(response, timeout); - } - - /** - * Returns a single segment of blobs and blob prefixes starting from the specified Marker. Use an empty - * marker to start enumeration from the beginning. Blob names are returned in lexicographic order. - * After getting a segment, process it, and then call ListBlobs again (passing the the previously-returned - * Marker) to get the next segment. For more information, see the - * Azure Docs. - * - * @param marker - * Identifies the portion of the list to be returned with the next list operation. - * This value is returned in the response of a previous list operation as the - * ListBlobsHierarchySegmentResponse.body().nextMarker(). Set to null to list the first segment. - * @param delimiter - * The operation returns a BlobPrefix element in the response body that acts as a placeholder for all blobs - * whose names begin with the same substring up to the appearance of the delimiter character. The delimiter may - * be a single character or a string. - * @param options - * {@link ListBlobsOptions} - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=list_blobs_hierarchy "Sample code for ContainerAsyncClient.listBlobsHierarchySegment")] \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=list_blobs_hierarchy_helper "helper code for ContainerAsyncClient.listBlobsHierarchySegment")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ContainersListBlobHierarchySegmentResponse listBlobsHierarchySegment(String marker, String delimiter, - ListBlobsOptions options) { - return this.listBlobsHierarchySegment(marker, delimiter, options, null); - } - - /** - * Returns a single segment of blobs and blob prefixes starting from the specified Marker. Use an empty - * marker to start enumeration from the beginning. Blob names are returned in lexicographic order. - * After getting a segment, process it, and then call ListBlobs again (passing the the previously-returned - * Marker) to get the next segment. For more information, see the - * Azure Docs. - * - * @param marker - * Identifies the portion of the list to be returned with the next list operation. - * This value is returned in the response of a previous list operation as the - * ListBlobsHierarchySegmentResponse.body().nextMarker(). Set to null to list the first segment. - * @param delimiter - * The operation returns a BlobPrefix element in the response body that acts as a placeholder for all blobs - * whose names begin with the same substring up to the appearance of the delimiter character. The delimiter may - * be a single character or a string. - * @param options - * {@link ListBlobsOptions} - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=list_blobs_hierarchy "Sample code for ContainerAsyncClient.listBlobsHierarchySegment")] \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=list_blobs_hierarchy_helper "helper code for ContainerAsyncClient.listBlobsHierarchySegment")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ContainersListBlobHierarchySegmentResponse listBlobsHierarchySegment(String marker, String delimiter, - ListBlobsOptions options, Duration timeout) { - Mono response = containerAsyncRawClient.listBlobsHierarchySegment(marker, delimiter, options); - return Utility.blockWithOptionalTimeout(response, timeout); - } - - /** - * Returns the sku name and account kind for the account. For more information, please see the - * Azure Docs. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=account_info "Sample code for ContainerAsyncClient.getAccountInfo")] \n - * For more samples, please see the [Samples file] (https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ContainersGetAccountInfoResponse getAccountInfo() { - return this.getAccountInfo(null); - } - - /** - * Returns the sku name and account kind for the account. For more information, please see the - * Azure Docs. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=account_info "Sample code for ContainerAsyncClient.getAccountInfo")] \n - * For more samples, please see the [Samples file] (https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ContainersGetAccountInfoResponse getAccountInfo(Duration timeout) { - Mono response = containerAsyncRawClient.getAccountInfo(); - return Utility.blockWithOptionalTimeout(response, timeout); - } -} diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/PageBlobAsyncClient.java b/storage/client/blob/src/main/java/com/azure/storage/blob/PageBlobAsyncClient.java index 073df29744582..925f167b2ad91 100644 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/PageBlobAsyncClient.java +++ b/storage/client/blob/src/main/java/com/azure/storage/blob/PageBlobAsyncClient.java @@ -5,6 +5,8 @@ import com.azure.core.http.rest.Response; import com.azure.core.http.rest.SimpleResponse; +import com.azure.core.implementation.http.UrlBuilder; +import com.azure.core.util.Context; import com.azure.storage.blob.implementation.AzureBlobStorageBuilder; import com.azure.storage.blob.models.BlobAccessConditions; import com.azure.storage.blob.models.BlobHTTPHeaders; @@ -17,15 +19,17 @@ import com.azure.storage.blob.models.PageRange; import com.azure.storage.blob.models.SequenceNumberActionType; import com.azure.storage.blob.models.SourceModifiedAccessConditions; -import io.netty.buffer.Unpooled; +import io.netty.buffer.ByteBuf; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import java.net.MalformedURLException; import java.net.URL; -import java.nio.ByteBuffer; + +import static com.azure.storage.blob.Utility.postProcessResponse; /** - * Client to a page blob. It may only be instantiated through a {@link PageBlobClientBuilder}, via + * Client to a page blob. It may only be instantiated through a {@link BlobClientBuilder}, via * the method {@link BlobAsyncClient#asPageBlobAsyncClient()}, or via the method * {@link ContainerAsyncClient#getPageBlobAsyncClient(String)}. This class does not hold * any state about a particular blob, but is instead a convenient way of sending appropriate @@ -48,9 +52,6 @@ * object through {@link Mono#toFuture()}. */ public final class PageBlobAsyncClient extends BlobAsyncClient { - - final PageBlobAsyncRawClient pageBlobAsyncRawClient; - /** * Indicates the number of bytes in a page. */ @@ -62,12 +63,11 @@ public final class PageBlobAsyncClient extends BlobAsyncClient { public static final int MAX_PUT_PAGES_BYTES = 4 * Constants.MB; /** - * Package-private constructor for use by {@link PageBlobClientBuilder}. + * Package-private constructor for use by {@link BlobClientBuilder}. * @param azureBlobStorageBuilder the API client builder for blob storage API */ PageBlobAsyncClient(AzureBlobStorageBuilder azureBlobStorageBuilder, String snapshot) { super(azureBlobStorageBuilder, snapshot); - this.pageBlobAsyncRawClient = new PageBlobAsyncRawClient(azureBlobStorageBuilder.build(), snapshot); } /** @@ -104,13 +104,30 @@ public Mono> create(long size) { * @param accessConditions * {@link BlobAccessConditions} * - * @return - * A reactive response containing the information of the created page blob. + * @return A reactive response containing the information of the created page blob. + * @throws IllegalArgumentException If {@code size} isn't a multiple of {@link PageBlobAsyncClient#PAGE_BYTES} + * or {@code sequenceNumber} isn't null and is less than 0. */ public Mono> create(long size, Long sequenceNumber, BlobHTTPHeaders headers, Metadata metadata, BlobAccessConditions accessConditions) { - return pageBlobAsyncRawClient - .create(size, sequenceNumber, headers, metadata, accessConditions) + accessConditions = accessConditions == null ? new BlobAccessConditions() : accessConditions; + + if (size % PAGE_BYTES != 0) { + // Throwing is preferred to Single.error because this will error out immediately instead of waiting until + // subscription. + throw new IllegalArgumentException("size must be a multiple of PageBlobAsyncClient.PAGE_BYTES."); + } + if (sequenceNumber != null && sequenceNumber < 0) { + // Throwing is preferred to Single.error because this will error out immediately instead of waiting until + // subscription. + throw new IllegalArgumentException("SequenceNumber must be greater than or equal to 0."); + } + metadata = metadata == null ? new Metadata() : metadata; + + return postProcessResponse(this.azureBlobStorage.pageBlobs().createWithRestResponseAsync(null, + null, 0, size, null, metadata, null, null, + null, sequenceNumber, null, headers, accessConditions.leaseAccessConditions(), + accessConditions.modifiedAccessConditions(), Context.NONE)) .map(rb -> new SimpleResponse<>(rb, new PageBlobItem(rb.deserializedHeaders()))); } @@ -133,7 +150,7 @@ public Mono> create(long size, Long sequenceNumber, BlobH * @return * A reactive response containing the information of the uploaded pages. */ - public Mono> uploadPages(PageRange pageRange, Flux body) { + public Mono> uploadPages(PageRange pageRange, Flux body) { return this.uploadPages(pageRange, body, null); } @@ -155,13 +172,25 @@ public Mono> uploadPages(PageRange pageRange, Flux> uploadPages(PageRange pageRange, Flux body, + public Mono> uploadPages(PageRange pageRange, Flux body, PageBlobAccessConditions pageBlobAccessConditions) { - return pageBlobAsyncRawClient - .uploadPages(pageRange, body.map(Unpooled::wrappedBuffer), pageBlobAccessConditions) + pageBlobAccessConditions = pageBlobAccessConditions == null ? new PageBlobAccessConditions() : pageBlobAccessConditions; + + if (pageRange == null) { + // Throwing is preferred to Single.error because this will error out immediately instead of waiting until + // subscription. + throw new IllegalArgumentException("pageRange cannot be null."); + } + String pageRangeStr = pageRangeToString(pageRange); + + return postProcessResponse(this.azureBlobStorage.pageBlobs().uploadPagesWithRestResponseAsync(null, + null, body, pageRange.end() - pageRange.start() + 1, null, + null, pageRangeStr, null, null, null, null, + pageBlobAccessConditions.leaseAccessConditions(), pageBlobAccessConditions.sequenceNumberAccessConditions(), + pageBlobAccessConditions.modifiedAccessConditions(), Context.NONE)) .map(rb -> new SimpleResponse<>(rb, new PageBlobItem(rb.deserializedHeaders()))); } @@ -218,15 +247,33 @@ public Mono> uploadPagesFromURL(PageRange range, URL sour * @param sourceAccessConditions * {@link SourceModifiedAccessConditions} * - * @return - * A reactive response containing the information of the uploaded pages. + * @return A reactive response containing the information of the uploaded pages. + * @throws IllegalArgumentException If {@code range} is {@code null} */ public Mono> uploadPagesFromURL(PageRange range, URL sourceURL, Long sourceOffset, byte[] sourceContentMD5, PageBlobAccessConditions destAccessConditions, SourceModifiedAccessConditions sourceAccessConditions) { + if (range == null) { + // Throwing is preferred to Single.error because this will error out immediately instead of waiting until + // subscription. + throw new IllegalArgumentException("range cannot be null."); + } + + String rangeString = pageRangeToString(range); - return pageBlobAsyncRawClient - .uploadPagesFromURL(range, sourceURL, sourceOffset, sourceContentMD5, destAccessConditions, sourceAccessConditions) + if (sourceOffset == null) { + sourceOffset = 0L; + } + + String sourceRangeString = pageRangeToString(new PageRange().start(sourceOffset).end(sourceOffset + (range.end() - range.start()))); + + destAccessConditions = destAccessConditions == null ? new PageBlobAccessConditions() : destAccessConditions; + + return postProcessResponse(this.azureBlobStorage.pageBlobs().uploadPagesFromURLWithRestResponseAsync( + null, null, sourceURL, sourceRangeString, 0, rangeString, sourceContentMD5, + null, null, destAccessConditions.leaseAccessConditions(), + destAccessConditions.sequenceNumberAccessConditions(), destAccessConditions.modifiedAccessConditions(), + sourceAccessConditions, Context.NONE)) .map(rb -> new SimpleResponse<>(rb, new PageBlobItem(rb.deserializedHeaders()))); } @@ -259,13 +306,23 @@ public Mono> clearPages(PageRange pageRange) { * @param pageBlobAccessConditions * {@link PageBlobAccessConditions} * - * @return - * A reactive response containing the information of the cleared pages. + * @return A reactive response containing the information of the cleared pages. + * @throws IllegalArgumentException If {@code pageRange} is {@code null} */ public Mono> clearPages(PageRange pageRange, PageBlobAccessConditions pageBlobAccessConditions) { - return pageBlobAsyncRawClient - .clearPages(pageRange, pageBlobAccessConditions) + pageBlobAccessConditions = pageBlobAccessConditions == null ? new PageBlobAccessConditions() : pageBlobAccessConditions; + if (pageRange == null) { + // Throwing is preferred to Single.error because this will error out immediately instead of waiting until + // subscription. + throw new IllegalArgumentException("pageRange cannot be null."); + } + String pageRangeStr = pageRangeToString(pageRange); + + return postProcessResponse(this.azureBlobStorage.pageBlobs().clearPagesWithRestResponseAsync(null, + null, 0, null, pageRangeStr, null, + pageBlobAccessConditions.leaseAccessConditions(), pageBlobAccessConditions.sequenceNumberAccessConditions(), + pageBlobAccessConditions.modifiedAccessConditions(), Context.NONE)) .map(rb -> new SimpleResponse<>(rb, new PageBlobItem(rb.deserializedHeaders()))); } @@ -295,10 +352,14 @@ public Flux getPageRanges(BlobRange blobRange) { * @return * A reactive response emitting all the page ranges. */ - public Flux getPageRanges(BlobRange blobRange, - BlobAccessConditions accessConditions) { - return pageBlobAsyncRawClient - .getPageRanges(blobRange, accessConditions) + public Flux getPageRanges(BlobRange blobRange, BlobAccessConditions accessConditions) { + blobRange = blobRange == null ? new BlobRange(0) : blobRange; + accessConditions = accessConditions == null ? new BlobAccessConditions() : accessConditions; + + return postProcessResponse(this.azureBlobStorage.pageBlobs().getPageRangesWithRestResponseAsync( + null, null, snapshot, null, null, blobRange.toHeaderValue(), + null, accessConditions.leaseAccessConditions(), accessConditions.modifiedAccessConditions(), + Context.NONE)) .flatMapMany(response -> Flux.fromIterable(response.value().pageRange())); } @@ -333,13 +394,21 @@ public Flux getPageRangesDiff(BlobRange blobRange, String prevSnapsho * @param accessConditions * {@link BlobAccessConditions} * - * @return - * A reactive response emitting all the different page ranges. + * @return A reactive response emitting all the different page ranges. + * @throws IllegalArgumentException If {@code prevSnapshot} is {@code null} */ - public Flux getPageRangesDiff(BlobRange blobRange, String prevSnapshot, - BlobAccessConditions accessConditions) { - return pageBlobAsyncRawClient - .getPageRangesDiff(blobRange, prevSnapshot, accessConditions) + public Flux getPageRangesDiff(BlobRange blobRange, String prevSnapshot, BlobAccessConditions accessConditions) { + blobRange = blobRange == null ? new BlobRange(0) : blobRange; + accessConditions = accessConditions == null ? new BlobAccessConditions() : accessConditions; + + if (prevSnapshot == null) { + throw new IllegalArgumentException("prevSnapshot cannot be null"); + } + + return postProcessResponse(this.azureBlobStorage.pageBlobs().getPageRangesDiffWithRestResponseAsync( + null, null, snapshot, null, null, prevSnapshot, + blobRange.toHeaderValue(), null, accessConditions.leaseAccessConditions(), + accessConditions.modifiedAccessConditions(), Context.NONE)) .flatMapMany(response -> Flux.fromIterable(response.value().pageRange())); } @@ -368,12 +437,20 @@ public Mono> resize(long size) { * @param accessConditions * {@link BlobAccessConditions} * - * @return - * A reactive response emitting the resized page blob. + * @return A reactive response emitting the resized page blob. + * @throws IllegalArgumentException If {@code size} isn't a multiple of {@link PageBlobAsyncClient#PAGE_BYTES} */ public Mono> resize(long size, BlobAccessConditions accessConditions) { - return pageBlobAsyncRawClient - .resize(size, accessConditions) + if (size % PAGE_BYTES != 0) { + // Throwing is preferred to Single.error because this will error out immediately instead of waiting until + // subscription. + throw new IllegalArgumentException("size must be a multiple of PageBlobAsyncClient.PAGE_BYTES."); + } + accessConditions = accessConditions == null ? new BlobAccessConditions() : accessConditions; + + return postProcessResponse(this.azureBlobStorage.pageBlobs().resizeWithRestResponseAsync(null, + null, size, null, null, accessConditions.leaseAccessConditions(), + accessConditions.modifiedAccessConditions(), Context.NONE)) .map(rb -> new SimpleResponse<>(rb, new PageBlobItem(rb.deserializedHeaders()))); } @@ -407,13 +484,22 @@ public Mono> updateSequenceNumber(SequenceNumberActionTyp * @param accessConditions * {@link BlobAccessConditions} * - * @return - * A reactive response emitting the updated page blob. + * @return A reactive response emitting the updated page blob. + * @throws IllegalArgumentException If {@code sequenceNumber} isn't null and is less than 0 */ - public Mono> updateSequenceNumber(SequenceNumberActionType action, - Long sequenceNumber, BlobAccessConditions accessConditions) { - return pageBlobAsyncRawClient - .updateSequenceNumber(action, sequenceNumber, accessConditions) + public Mono> updateSequenceNumber(SequenceNumberActionType action, Long sequenceNumber, BlobAccessConditions accessConditions) { + if (sequenceNumber != null && sequenceNumber < 0) { + // Throwing is preferred to Single.error because this will error out immediately instead of waiting until + // subscription. + throw new IllegalArgumentException("SequenceNumber must be greater than or equal to 0."); + } + accessConditions = accessConditions == null ? new BlobAccessConditions() : accessConditions; + sequenceNumber = action == SequenceNumberActionType.INCREMENT ? null : sequenceNumber; + + return postProcessResponse( + this.azureBlobStorage.pageBlobs().updateSequenceNumberWithRestResponseAsync(null, + null, action, null, sequenceNumber, null, + accessConditions.leaseAccessConditions(), accessConditions.modifiedAccessConditions(), Context.NONE)) .map(rb -> new SimpleResponse<>(rb, new PageBlobItem(rb.deserializedHeaders()))); } @@ -454,13 +540,37 @@ public Mono> copyIncremental(URL source, String snapsho * to construct conditions related to when the blob was changed relative to the given request. The request * will fail if the specified condition is not satisfied. * - * @return - * A reactive response emitting the copy status. + * @return A reactive response emitting the copy status. + * @throws Error If {@code source} and {@code snapshot} form a malformed URL. */ - public Mono> copyIncremental(URL source, String snapshot, - ModifiedAccessConditions modifiedAccessConditions) { - return pageBlobAsyncRawClient - .copyIncremental(source, snapshot, modifiedAccessConditions) + public Mono> copyIncremental(URL source, String snapshot, ModifiedAccessConditions modifiedAccessConditions) { + UrlBuilder builder = UrlBuilder.parse(source); + builder.setQueryParameter(Constants.SNAPSHOT_QUERY_PARAMETER, snapshot); + try { + source = builder.toURL(); + } catch (MalformedURLException e) { + // We are parsing a valid url and adding a query parameter. If this fails, we can't recover. + throw new Error(e); + } + return postProcessResponse(this.azureBlobStorage.pageBlobs().copyIncrementalWithRestResponseAsync( + null, null, source, null, null, modifiedAccessConditions, Context.NONE)) .map(rb -> new SimpleResponse<>(rb, rb.deserializedHeaders().copyStatus())); } + + private static String pageRangeToString(PageRange pageRange) { + if (pageRange.start() < 0 || pageRange.end() <= 0) { + throw new IllegalArgumentException("PageRange's start and end values must be greater than or equal to " + + "0 if specified."); + } + if (pageRange.start() % PAGE_BYTES != 0) { + throw new IllegalArgumentException("PageRange's start value must be a multiple of 512."); + } + if (pageRange.end() % PAGE_BYTES != PAGE_BYTES - 1) { + throw new IllegalArgumentException("PageRange's end value must be 1 less than a multiple of 512."); + } + if (pageRange.end() <= pageRange.start()) { + throw new IllegalArgumentException("PageRange's End value must be after the start."); + } + return "bytes=" + pageRange.start() + '-' + pageRange.end(); + } } diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/PageBlobAsyncRawClient.java b/storage/client/blob/src/main/java/com/azure/storage/blob/PageBlobAsyncRawClient.java deleted file mode 100644 index fba55bd8d7455..0000000000000 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/PageBlobAsyncRawClient.java +++ /dev/null @@ -1,620 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.azure.storage.blob; - -import com.azure.core.implementation.http.UrlBuilder; -import com.azure.core.util.Context; -import com.azure.storage.blob.implementation.AzureBlobStorageImpl; -import com.azure.storage.blob.models.BlobAccessConditions; -import com.azure.storage.blob.models.BlobHTTPHeaders; -import com.azure.storage.blob.models.BlobRange; -import com.azure.storage.blob.models.Metadata; -import com.azure.storage.blob.models.ModifiedAccessConditions; -import com.azure.storage.blob.models.PageBlobAccessConditions; -import com.azure.storage.blob.models.PageBlobsClearPagesResponse; -import com.azure.storage.blob.models.PageBlobsCopyIncrementalResponse; -import com.azure.storage.blob.models.PageBlobsCreateResponse; -import com.azure.storage.blob.models.PageBlobsGetPageRangesDiffResponse; -import com.azure.storage.blob.models.PageBlobsGetPageRangesResponse; -import com.azure.storage.blob.models.PageBlobsResizeResponse; -import com.azure.storage.blob.models.PageBlobsUpdateSequenceNumberResponse; -import com.azure.storage.blob.models.PageBlobsUploadPagesFromURLResponse; -import com.azure.storage.blob.models.PageBlobsUploadPagesResponse; -import com.azure.storage.blob.models.PageRange; -import com.azure.storage.blob.models.SequenceNumberActionType; -import com.azure.storage.blob.models.SourceModifiedAccessConditions; -import io.netty.buffer.ByteBuf; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import java.net.MalformedURLException; -import java.net.URL; - -import static com.azure.storage.blob.Utility.postProcessResponse; - -/** - * Represents a URL to a page blob. It may be obtained by direct construction or via the create method on a - * {@link ContainerAsyncClient} object. This class does not hold any state about a particular blob but is instead a convenient - * way of sending off appropriate requests to the resource on the service. Please refer to the - * Azure Docs - * for more information. - */ -final class PageBlobAsyncRawClient extends BlobAsyncRawClient { - - /** - * Indicates the number of bytes in a page. - */ - public static final int PAGE_BYTES = 512; - - /** - * Indicates the maximum number of bytes that may be sent in a call to putPage. - */ - public static final int MAX_PUT_PAGES_BYTES = 4 * Constants.MB; - - /** - * Creates a {@code PageBlobAsyncRawClient} object pointing to the account specified by the URL and using the provided - * pipeline to make HTTP requests. - * - */ - PageBlobAsyncRawClient(AzureBlobStorageImpl azureBlobStorage, String snapshot) { - super(azureBlobStorage, snapshot); - } - - private static String pageRangeToString(PageRange pageRange) { - if (pageRange.start() < 0 || pageRange.end() <= 0) { - throw new IllegalArgumentException("PageRange's start and end values must be greater than or equal to " - + "0 if specified."); - } - if (pageRange.start() % PageBlobAsyncRawClient.PAGE_BYTES != 0) { - throw new IllegalArgumentException("PageRange's start value must be a multiple of 512."); - } - if (pageRange.end() % PageBlobAsyncRawClient.PAGE_BYTES != PageBlobAsyncRawClient.PAGE_BYTES - 1) { - throw new IllegalArgumentException("PageRange's end value must be 1 less than a multiple of 512."); - } - if (pageRange.end() <= pageRange.start()) { - throw new IllegalArgumentException("PageRange's End value must be after the start."); - } - return new StringBuilder("bytes=").append(pageRange.start()).append('-').append(pageRange.end()).toString(); - } - - /** - * Creates a page blob of the specified length. Call PutPage to upload data data to a page blob. - * For more information, see the - * Azure Docs. - * - * @param size - * Specifies the maximum size for the page blob, up to 8 TB. The page blob size must be aligned to a - * 512-byte boundary. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=page_blob_basic "Sample code for PageBlobAsyncRawClient.create")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono create(long size) { - return this.create(size, null, null, null, null); - } - - /** - * Creates a page blob of the specified length. Call PutPage to upload data data to a page blob. - * For more information, see the - * Azure Docs. - * - * @param size - * Specifies the maximum size for the page blob, up to 8 TB. The page blob size must be aligned to a - * 512-byte boundary. - * @param sequenceNumber - * A user-controlled value that you can use to track requests. The value of the sequence number must be - * between 0 and 2^63 - 1.The default value is 0. - * @param headers - * {@link BlobHTTPHeaders} - * @param metadata - * {@link Metadata} - * @param accessConditions - * {@link BlobAccessConditions} - * - * @return Emits the successful response. - * @throws IllegalArgumentException If {@code size} isn't a multiple of {@link PageBlobAsyncRawClient#PAGE_BYTES} - * or {@code sequenceNumber} isn't null and is less than 0. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=page_blob_basic "Sample code for PageBlobAsyncRawClient.create")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono create(long size, Long sequenceNumber, BlobHTTPHeaders headers, - Metadata metadata, BlobAccessConditions accessConditions) { - accessConditions = accessConditions == null ? new BlobAccessConditions() : accessConditions; - - if (size % PageBlobAsyncRawClient.PAGE_BYTES != 0) { - // Throwing is preferred to Single.error because this will error out immediately instead of waiting until - // subscription. - throw new IllegalArgumentException("size must be a multiple of PageBlobAsyncRawClient.PAGE_BYTES."); - } - if (sequenceNumber != null && sequenceNumber < 0) { - // Throwing is preferred to Single.error because this will error out immediately instead of waiting until - // subscription. - throw new IllegalArgumentException("SequenceNumber must be greater than or equal to 0."); - } - metadata = metadata == null ? new Metadata() : metadata; - - return postProcessResponse(this.azureBlobStorage.pageBlobs().createWithRestResponseAsync(null, - null, 0, size, null, metadata, null, null, - null, sequenceNumber, null, headers, accessConditions.leaseAccessConditions(), - accessConditions.modifiedAccessConditions(), Context.NONE)); - } - - /** - * Writes 1 or more pages to the page blob. The start and end offsets must be a multiple of 512. - * For more information, see the - * Azure Docs. - *

- * Note that the data passed must be replayable if retries are enabled (the default). In other words, the - * {@code Flux} must produce the same data each time it is subscribed to. - * - * @param pageRange - * A {@link PageRange} object. Given that pages must be aligned with 512-byte boundaries, the start offset must - * be a modulus of 512 and the end offset must be a modulus of 512 - 1. Examples of valid byte ranges are - * 0-511, 512-1023, etc. - * @param body - * The data to upload. Note that this {@code Flux} must be replayable if retries are enabled - * (the default). In other words, the Flowable must produce the same data each time it is subscribed to. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=page_blob_basic "Sample code for PageBlobAsyncRawClient.uploadPages")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono uploadPages(PageRange pageRange, Flux body) { - return this.uploadPages(pageRange, body, null); - } - - /** - * Writes 1 or more pages to the page blob. The start and end offsets must be a multiple of 512. - * For more information, see the - * Azure Docs. - *

- * Note that the data passed must be replayable if retries are enabled (the default). In other words, the - * {@code Flux} must produce the same data each time it is subscribed to. - * - * @param pageRange - * A {@link PageRange} object. Given that pages must be aligned with 512-byte boundaries, the start offset - * must be a modulus of 512 and the end offset must be a modulus of 512 - 1. Examples of valid byte ranges - * are 0-511, 512-1023, etc. - * @param body - * The data to upload. Note that this {@code Flux} must be replayable if retries are enabled - * (the default). In other words, the Flowable must produce the same data each time it is subscribed to. - * @param pageBlobAccessConditions - * {@link PageBlobAccessConditions} - * - * @return Emits the successful response. - * @throws IllegalArgumentException If {@code pageRange} is {@code null} - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=page_blob_basic "Sample code for PageBlobAsyncRawClient.uploadPages")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono uploadPages(PageRange pageRange, Flux body, - PageBlobAccessConditions pageBlobAccessConditions) { - pageBlobAccessConditions = pageBlobAccessConditions == null ? new PageBlobAccessConditions() - : pageBlobAccessConditions; - - if (pageRange == null) { - // Throwing is preferred to Single.error because this will error out immediately instead of waiting until - // subscription. - throw new IllegalArgumentException("pageRange cannot be null."); - } - String pageRangeStr = pageRangeToString(pageRange); - - return postProcessResponse(this.azureBlobStorage.pageBlobs().uploadPagesWithRestResponseAsync(null, - null, body, pageRange.end() - pageRange.start() + 1, null, - null, pageRangeStr, null, null, null, null, - pageBlobAccessConditions.leaseAccessConditions(), pageBlobAccessConditions.sequenceNumberAccessConditions(), - pageBlobAccessConditions.modifiedAccessConditions(), Context.NONE)); - } - - /** - * Writes 1 or more pages from the source page blob to this page blob. The start and end offsets must be a multiple - * of 512. - * For more information, see the - * Azure Docs. - *

- * - * @param range - * A {@link PageRange} object. Given that pages must be aligned with 512-byte boundaries, the start offset - * must be a modulus of 512 and the end offset must be a modulus of 512 - 1. Examples of valid byte ranges - * are 0-511, 512-1023, etc. - * @param sourceURL - * The url to the blob that will be the source of the copy. A source blob in the same storage account can be - * authenticated via Shared Key. However, if the source is a blob in another account, the source blob must - * either be public or must be authenticated via a shared access signature. If the source blob is public, no - * authentication is required to perform the operation. - * @param sourceOffset - * The source offset to copy from. Pass null or 0 to copy from the beginning of source page blob. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=page_from_url "Sample code for PageBlobAsyncRawClient.uploadPagesFromURL")] - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono uploadPagesFromURL(PageRange range, URL sourceURL, Long sourceOffset) { - return this.uploadPagesFromURL(range, sourceURL, sourceOffset, null, null, - null); - } - - /** - * Writes 1 or more pages from the source page blob to this page blob. The start and end offsets must be a multiple - * of 512. - * For more information, see the - * Azure Docs. - *

- * - * @param range - * The destination {@link PageRange} range. Given that pages must be aligned with 512-byte boundaries, the start offset - * must be a modulus of 512 and the end offset must be a modulus of 512 - 1. Examples of valid byte ranges - * are 0-511, 512-1023, etc. - * @param sourceURL - * The url to the blob that will be the source of the copy. A source blob in the same storage account can be - * authenticated via Shared Key. However, if the source is a blob in another account, the source blob must - * either be public or must be authenticated via a shared access signature. If the source blob is public, no - * authentication is required to perform the operation. - * @param sourceOffset - * The source offset to copy from. Pass null or 0 to copy from the beginning of source blob. - * @param sourceContentMD5 - * An MD5 hash of the block content from the source blob. If specified, the service will calculate the MD5 - * of the received data and fail the request if it does not match the provided MD5. - * @param destAccessConditions - * {@link PageBlobAccessConditions} - * @param sourceAccessConditions - * {@link SourceModifiedAccessConditions} - * - * @return Emits the successful response. - * @throws IllegalArgumentException If {@code range} is {@code null} - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=page_from_url "Sample code for PageBlobAsyncRawClient.uploadPagesFromURL")] - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono uploadPagesFromURL(PageRange range, URL sourceURL, Long sourceOffset, - byte[] sourceContentMD5, PageBlobAccessConditions destAccessConditions, - SourceModifiedAccessConditions sourceAccessConditions) { - - if (range == null) { - // Throwing is preferred to Single.error because this will error out immediately instead of waiting until - // subscription. - throw new IllegalArgumentException("range cannot be null."); - } - - String rangeString = pageRangeToString(range); - - if (sourceOffset == null) { - sourceOffset = 0L; - } - - String sourceRangeString = pageRangeToString(new PageRange().start(sourceOffset).end(sourceOffset + (range.end() - range.start()))); - - destAccessConditions = destAccessConditions == null ? new PageBlobAccessConditions() : destAccessConditions; - - return postProcessResponse(this.azureBlobStorage.pageBlobs().uploadPagesFromURLWithRestResponseAsync( - null, null, sourceURL, sourceRangeString, 0, rangeString, sourceContentMD5, - null, null, destAccessConditions.leaseAccessConditions(), - destAccessConditions.sequenceNumberAccessConditions(), destAccessConditions.modifiedAccessConditions(), - sourceAccessConditions, Context.NONE)); - } - - /** - * Frees the specified pages from the page blob. - * For more information, see the - * Azure Docs. - * - * @param pageRange - * A {@link PageRange} object. Given that pages must be aligned with 512-byte boundaries, the start offset - * must be a modulus of 512 and the end offset must be a modulus of 512 - 1. Examples of valid byte ranges - * are 0-511, 512-1023, etc. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=page_blob_basic "Sample code for PageBlobAsyncRawClient.clearPages")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono clearPages(PageRange pageRange) { - return this.clearPages(pageRange, null); - } - - /** - * Frees the specified pages from the page blob. - * For more information, see the - * Azure Docs. - * - * @param pageRange - * A {@link PageRange} object. Given that pages must be aligned with 512-byte boundaries, the start offset - * must be a modulus of 512 and the end offset must be a modulus of 512 - 1. Examples of valid byte ranges - * are 0-511, 512-1023, etc. - * @param pageBlobAccessConditions - * {@link PageBlobAccessConditions} - * - * @return Emits the successful response. - * @throws IllegalArgumentException If {@code pageRange} is {@code null} - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=page_blob_basic "Sample code for PageBlobAsyncRawClient.clearPages")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono clearPages(PageRange pageRange, - PageBlobAccessConditions pageBlobAccessConditions) { - pageBlobAccessConditions = pageBlobAccessConditions == null ? new PageBlobAccessConditions() - : pageBlobAccessConditions; - if (pageRange == null) { - // Throwing is preferred to Single.error because this will error out immediately instead of waiting until - // subscription. - throw new IllegalArgumentException("pageRange cannot be null."); - } - String pageRangeStr = pageRangeToString(pageRange); - - return postProcessResponse(this.azureBlobStorage.pageBlobs().clearPagesWithRestResponseAsync(null, - null, 0, null, pageRangeStr, null, - pageBlobAccessConditions.leaseAccessConditions(), pageBlobAccessConditions.sequenceNumberAccessConditions(), - pageBlobAccessConditions.modifiedAccessConditions(), Context.NONE)); - } - - /** - * Returns the list of valid page ranges for a page blob or snapshot of a page blob. - * For more information, see the Azure Docs. - * - * @param blobRange - * {@link BlobRange} - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=page_blob_basic "Sample code for PageBlobAsyncRawClient.getPageRanges")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono getPageRanges(BlobRange blobRange) { - return this.getPageRanges(blobRange, null); - } - - /** - * Returns the list of valid page ranges for a page blob or snapshot of a page blob. - * For more information, see the Azure Docs. - * - * @param blobRange - * {@link BlobRange} - * @param accessConditions - * {@link BlobAccessConditions} - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=page_blob_basic "Sample code for PageBlobAsyncRawClient.getPageRanges")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono getPageRanges(BlobRange blobRange, - BlobAccessConditions accessConditions) { - blobRange = blobRange == null ? new BlobRange(0) : blobRange; - accessConditions = accessConditions == null ? new BlobAccessConditions() : accessConditions; - - return postProcessResponse(this.azureBlobStorage.pageBlobs().getPageRangesWithRestResponseAsync( - null, null, snapshot, null, null, blobRange.toHeaderValue(), - null, accessConditions.leaseAccessConditions(), accessConditions.modifiedAccessConditions(), - Context.NONE)); - } - - /** - * Gets the collection of page ranges that differ between a specified snapshot and this page blob. - * For more information, see the Azure Docs. - * - * @param blobRange - * {@link BlobRange} - * @param prevSnapshot - * Specifies that the response will contain only pages that were changed between target blob and previous - * snapshot. Changed pages include both updated and cleared pages. The target - * blob may be a snapshot, as long as the snapshot specified by prevsnapshot is the older of the two. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=page_diff "Sample code for PageBlobAsyncRawClient.getPageRangesDiff")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono getPageRangesDiff(BlobRange blobRange, String prevSnapshot) { - return this.getPageRangesDiff(blobRange, prevSnapshot, null); - } - - /** - * Gets the collection of page ranges that differ between a specified snapshot and this page blob. - * For more information, see the Azure Docs. - * - * @param blobRange - * {@link BlobRange} - * @param prevSnapshot - * Specifies that the response will contain only pages that were changed between target blob and previous - * snapshot. Changed pages include both updated and cleared pages. The target - * blob may be a snapshot, as long as the snapshot specified by prevsnapshot is the older of the two. - * @param accessConditions - * {@link BlobAccessConditions} - * - * @return Emits the successful response. - * @throws IllegalArgumentException If {@code prevSnapshot} is {@code null} - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=page_diff "Sample code for PageBlobAsyncRawClient.getPageRangesDiff")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono getPageRangesDiff(BlobRange blobRange, String prevSnapshot, - BlobAccessConditions accessConditions) { - blobRange = blobRange == null ? new BlobRange(0) : blobRange; - accessConditions = accessConditions == null ? new BlobAccessConditions() : accessConditions; - - if (prevSnapshot == null) { - throw new IllegalArgumentException("prevSnapshot cannot be null"); - } - - return postProcessResponse(this.azureBlobStorage.pageBlobs().getPageRangesDiffWithRestResponseAsync( - null, null, snapshot, null, null, prevSnapshot, - blobRange.toHeaderValue(), null, accessConditions.leaseAccessConditions(), - accessConditions.modifiedAccessConditions(), Context.NONE)); - } - - /** - * Resizes the page blob to the specified size (which must be a multiple of 512). - * For more information, see the Azure Docs. - * - * @param size - * Resizes a page blob to the specified size. If the specified value is less than the current size of the - * blob, then all pages above the specified value are cleared. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=page_blob_basic "Sample code for PageBlobAsyncRawClient.resize")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono resize(long size) { - return this.resize(size, null); - } - - /** - * Resizes the page blob to the specified size (which must be a multiple of 512). - * For more information, see the Azure Docs. - * - * @param size - * Resizes a page blob to the specified size. If the specified value is less than the current size of the - * blob, then all pages above the specified value are cleared. - * @param accessConditions - * {@link BlobAccessConditions} - * - * @return Emits the successful response. - * @throws IllegalArgumentException If {@code size} isn't a multiple of {@link PageBlobAsyncRawClient#PAGE_BYTES} - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=page_blob_basic "Sample code for PageBlobAsyncRawClient.resize")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono resize(long size, BlobAccessConditions accessConditions) { - if (size % PageBlobAsyncRawClient.PAGE_BYTES != 0) { - // Throwing is preferred to Single.error because this will error out immediately instead of waiting until - // subscription. - throw new IllegalArgumentException("size must be a multiple of PageBlobAsyncRawClient.PAGE_BYTES."); - } - accessConditions = accessConditions == null ? new BlobAccessConditions() : accessConditions; - - return postProcessResponse(this.azureBlobStorage.pageBlobs().resizeWithRestResponseAsync(null, - null, size, null, null, accessConditions.leaseAccessConditions(), - accessConditions.modifiedAccessConditions(), Context.NONE)); - } - - /** - * Sets the page blob's sequence number. - * For more information, see the Azure Docs. - * - * @param action - * Indicates how the service should modify the blob's sequence number. - * @param sequenceNumber - * The blob's sequence number. The sequence number is a user-controlled property that you can use to track - * requests and manage concurrency issues. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=page_blob_basic "Sample code for PageBlobAsyncRawClient.updateSequenceNumber")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono updateSequenceNumber(SequenceNumberActionType action, - Long sequenceNumber) { - return this.updateSequenceNumber(action, sequenceNumber, null); - } - - /** - * Sets the page blob's sequence number. - * For more information, see the Azure Docs. - * - * @param action - * Indicates how the service should modify the blob's sequence number. - * @param sequenceNumber - * The blob's sequence number. The sequence number is a user-controlled property that you can use to track - * requests and manage concurrency issues. - * @param accessConditions - * {@link BlobAccessConditions} - * - * @return Emits the successful response. - * @throws IllegalArgumentException If {@code sequenceNumber} isn't null and is less than 0 - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=page_blob_basic "Sample code for PageBlobAsyncRawClient.updateSequenceNumber")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono updateSequenceNumber(SequenceNumberActionType action, - Long sequenceNumber, BlobAccessConditions accessConditions) { - if (sequenceNumber != null && sequenceNumber < 0) { - // Throwing is preferred to Single.error because this will error out immediately instead of waiting until - // subscription. - throw new IllegalArgumentException("SequenceNumber must be greater than or equal to 0."); - } - accessConditions = accessConditions == null ? new BlobAccessConditions() : accessConditions; - sequenceNumber = action == SequenceNumberActionType.INCREMENT ? null : sequenceNumber; - - return postProcessResponse( - this.azureBlobStorage.pageBlobs().updateSequenceNumberWithRestResponseAsync(null, - null, action, null, sequenceNumber, null, - accessConditions.leaseAccessConditions(), accessConditions.modifiedAccessConditions(), Context.NONE)); - } - - /** - * Begins an operation to start an incremental copy from one page blob's snapshot to this page - * blob. The snapshot is copied such that only the differential changes between the previously copied snapshot are - * transferred to the destination. The copied snapshots are complete copies of the original snapshot and can be read - * or copied from as usual. For more information, see - * the Azure Docs here and - * here. - * - * @param source - * The source page blob. - * @param snapshot - * The snapshot on the copy source. - * - * @return Emits the successful response. - */ - public Mono copyIncremental(URL source, String snapshot) { - return this.copyIncremental(source, snapshot, null); - } - - /** - * Begins an operation to start an incremental copy from one page blob's snapshot to this page - * blob. The snapshot is copied such that only the differential changes between the previously copied snapshot are - * transferred to the destination. The copied snapshots are complete copies of the original snapshot and can be read - * or copied from as usual. For more information, see - * the Azure Docs here and - * here. - * - * @param source - * The source page blob. - * @param snapshot - * The snapshot on the copy source. - * @param modifiedAccessConditions - * Standard HTTP Access conditions related to the modification of data. ETag and LastModifiedTime are used - * to construct conditions related to when the blob was changed relative to the given request. The request - * will fail if the specified condition is not satisfied. - * - * @return Emits the successful response. - * @throws Error If {@code source} and {@code snapshot} form a malformed URL. - */ - public Mono copyIncremental(URL source, String snapshot, - ModifiedAccessConditions modifiedAccessConditions) { - - UrlBuilder builder = UrlBuilder.parse(source); - builder.setQueryParameter(Constants.SNAPSHOT_QUERY_PARAMETER, snapshot); - try { - source = builder.toURL(); - } catch (MalformedURLException e) { - // We are parsing a valid url and adding a query parameter. If this fails, we can't recover. - throw new Error(e); - } - return postProcessResponse(this.azureBlobStorage.pageBlobs().copyIncrementalWithRestResponseAsync( - null, null, source, null, null, modifiedAccessConditions, Context.NONE)); - } -} diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/PageBlobClient.java b/storage/client/blob/src/main/java/com/azure/storage/blob/PageBlobClient.java index 284f74d0216ac..14c0ea4644ded 100644 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/PageBlobClient.java +++ b/storage/client/blob/src/main/java/com/azure/storage/blob/PageBlobClient.java @@ -15,17 +15,18 @@ import com.azure.storage.blob.models.PageRange; import com.azure.storage.blob.models.SequenceNumberActionType; import com.azure.storage.blob.models.SourceModifiedAccessConditions; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; import java.io.InputStream; import java.net.URL; -import java.nio.ByteBuffer; import java.time.Duration; /** - * Client to a page blob. It may only be instantiated through a {@link PageBlobClientBuilder}, via + * Client to a page blob. It may only be instantiated through a {@link BlobClientBuilder}, via * the method {@link BlobClient#asPageBlobClient()}, or via the method * {@link ContainerClient#getPageBlobClient(String)}. This class does not hold * any state about a particular blob, but is instead a convenient way of sending appropriate @@ -45,15 +46,15 @@ public final class PageBlobClient extends BlobClient { /** * Indicates the number of bytes in a page. */ - public static final int PAGE_BYTES = 512; + public static final int PAGE_BYTES = PageBlobAsyncClient.PAGE_BYTES; /** * Indicates the maximum number of bytes that may be sent in a call to putPage. */ - public static final int MAX_PUT_PAGES_BYTES = 4 * Constants.MB; + public static final int MAX_PUT_PAGES_BYTES = PageBlobAsyncClient.MAX_PUT_PAGES_BYTES; /** - * Package-private constructor for use by {@link PageBlobClientBuilder}. + * Package-private constructor for use by {@link BlobClientBuilder}. * @param pageBlobAsyncClient the async page blob client */ PageBlobClient(PageBlobAsyncClient pageBlobAsyncClient) { @@ -189,7 +190,7 @@ public Response uploadPages(PageRange pageRange, InputStream body) public Response uploadPages(PageRange pageRange, InputStream body, PageBlobAccessConditions pageBlobAccessConditions, Duration timeout) { long length = pageRange.end() - pageRange.start(); - Flux fbb = Flux.range(0, (int) Math.ceil((double) length / (double) PAGE_BYTES)) + Flux fbb = Flux.range(0, (int) Math.ceil((double) length / (double) PAGE_BYTES)) .map(i -> i * PAGE_BYTES) .concatMap(pos -> Mono.fromCallable(() -> { byte[] cache = new byte[PAGE_BYTES]; @@ -197,7 +198,8 @@ public Response uploadPages(PageRange pageRange, InputStream body, while (read < PAGE_BYTES) { read += body.read(cache, read, PAGE_BYTES - read); } - return ByteBuffer.wrap(cache); + + return ByteBufAllocator.DEFAULT.buffer(read).writeBytes(cache); })); Mono> response = pageBlobAsyncClient.uploadPages(pageRange, diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/PageBlobClientBuilder.java b/storage/client/blob/src/main/java/com/azure/storage/blob/PageBlobClientBuilder.java deleted file mode 100644 index 77010e0607f84..0000000000000 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/PageBlobClientBuilder.java +++ /dev/null @@ -1,333 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.azure.storage.blob; - -import com.azure.core.credentials.TokenCredential; -import com.azure.core.http.HttpClient; -import com.azure.core.http.HttpPipeline; -import com.azure.core.http.policy.AddDatePolicy; -import com.azure.core.http.policy.BearerTokenAuthenticationPolicy; -import com.azure.core.http.policy.HttpLogDetailLevel; -import com.azure.core.http.policy.HttpLoggingPolicy; -import com.azure.core.http.policy.HttpPipelinePolicy; -import com.azure.core.http.policy.RequestIdPolicy; -import com.azure.core.http.policy.UserAgentPolicy; -import com.azure.core.implementation.util.ImplUtils; -import com.azure.core.util.configuration.Configuration; -import com.azure.core.util.configuration.ConfigurationManager; -import com.azure.storage.blob.implementation.AzureBlobStorageBuilder; -import com.azure.storage.common.credentials.SASTokenCredential; -import com.azure.storage.common.credentials.SharedKeyCredential; -import com.azure.storage.common.policy.RequestRetryOptions; -import com.azure.storage.common.policy.RequestRetryPolicy; -import com.azure.storage.common.policy.SASTokenCredentialPolicy; -import com.azure.storage.common.policy.SharedKeyCredentialPolicy; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; - -/** - * Fluent PageBlobClientBuilder for instantiating a {@link PageBlobClient} or {@link PageBlobAsyncClient} - * using {@link PageBlobClientBuilder#buildClient()} or {@link PageBlobClientBuilder#buildAsyncClient()} respectively. - * - *

- * The following information must be provided on this builder: - * - *

    - *
  • the endpoint through {@code .endpoint()}, including the container name and blob name, in the format of {@code https://{accountName}.blob.core.windows.net/{containerName}/{blobName}}. - *
  • the credential through {@code .credential()} or {@code .connectionString()} if the container is not publicly accessible. - *
- * - *

- * Once all the configurations are set on this builder, call {@code .buildClient()} to create a - * {@link PageBlobClient} or {@code .buildAsyncClient()} to create a {@link PageBlobAsyncClient}. - */ -public final class PageBlobClientBuilder { - private static final String ACCOUNT_NAME = "accountname"; - private static final String ACCOUNT_KEY = "accountkey"; - private static final String ENDPOINT_PROTOCOL = "defaultendpointsprotocol"; - private static final String ENDPOINT_SUFFIX = "endpointsuffix"; - - private final List policies; - - private String endpoint; - private String containerName; - private String blobName; - private String snapshot; - private SharedKeyCredential sharedKeyCredential; - private TokenCredential tokenCredential; - private SASTokenCredential sasTokenCredential; - private HttpClient httpClient; - private HttpLogDetailLevel logLevel; - private RequestRetryOptions retryOptions; - private Configuration configuration; - - /** - * Creates a builder instance that is able to configure and construct {@link PageBlobClient PageBlobClients} - * and {@link PageBlobAsyncClient PageBlobAsyncClients}. - */ - public PageBlobClientBuilder() { - retryOptions = new RequestRetryOptions(); - logLevel = HttpLogDetailLevel.NONE; - policies = new ArrayList<>(); - } - - private AzureBlobStorageBuilder buildImpl() { - Objects.requireNonNull(endpoint); - Objects.requireNonNull(containerName); - Objects.requireNonNull(blobName); - - // Closest to API goes first, closest to wire goes last. - final List policies = new ArrayList<>(); - - if (configuration == null) { - configuration = ConfigurationManager.getConfiguration(); - } - policies.add(new UserAgentPolicy(BlobConfiguration.NAME, BlobConfiguration.VERSION, configuration)); - policies.add(new RequestIdPolicy()); - policies.add(new AddDatePolicy()); - - if (sharedKeyCredential != null) { - policies.add(new SharedKeyCredentialPolicy(sharedKeyCredential)); - } else if (tokenCredential != null) { - policies.add(new BearerTokenAuthenticationPolicy(tokenCredential, String.format("%s/.default", endpoint))); - } else if (sasTokenCredential != null) { - policies.add(new SASTokenCredentialPolicy(sasTokenCredential)); - } else { - policies.add(new AnonymousCredentialPolicy()); - } - - policies.add(new RequestRetryPolicy(retryOptions)); - - policies.addAll(this.policies); - policies.add(new HttpLoggingPolicy(logLevel)); - - HttpPipeline pipeline = HttpPipeline.builder() - .policies(policies.toArray(new HttpPipelinePolicy[0])) - .httpClient(httpClient) - .build(); - - return new AzureBlobStorageBuilder() - .url(String.format("%s/%s/%s", endpoint, containerName, blobName)) - .pipeline(pipeline); - } - - /** - * @return a {@link PageBlobClient} created from the configurations in this builder. - */ - public PageBlobClient buildClient() { - return new PageBlobClient(buildAsyncClient()); - } - - /** - * @return a {@link PageBlobAsyncClient} created from the configurations in this builder. - */ - public PageBlobAsyncClient buildAsyncClient() { - return new PageBlobAsyncClient(buildImpl(), snapshot); - } - - /** - * Sets the service endpoint, additionally parses it for information (SAS token, container name, blob name) - * @param endpoint URL of the service - * @return the updated PageBlobClientBuilder object - * @throws IllegalArgumentException If {@code endpoint} is a malformed URL. - */ - public PageBlobClientBuilder endpoint(String endpoint) { - Objects.requireNonNull(endpoint); - URL url; - try { - url = new URL(endpoint); - BlobURLParts parts = URLParser.parse(url); - this.endpoint = parts.scheme() + "://" + parts.host(); - - if (parts.containerName() != null) { - this.containerName = parts.containerName(); - } - - if (parts.blobName() != null) { - this.blobName = parts.blobName(); - } - - if (parts.snapshot() != null) { - this.snapshot = parts.snapshot(); - } - } catch (MalformedURLException ex) { - throw new IllegalArgumentException("The Azure Storage Blob endpoint url is malformed."); - } - - SASTokenCredential credential = SASTokenCredential.fromQuery(url.getQuery()); - if (credential != null) { - this.credential(credential); - } - - return this; - } - - /** - * Sets the name of the container this client is connecting to. - * @param containerName the name of the container - * @return the updated PageBlobClientBuilder object - */ - public PageBlobClientBuilder containerName(String containerName) { - this.containerName = containerName; - return this; - } - - /** - * Sets the name of the blob this client is connecting to. - * @param blobName the name of the blob - * @return the updated PageBlobClientBuilder object - */ - public PageBlobClientBuilder blobName(String blobName) { - this.blobName = blobName; - return this; - } - - /** - * Sets the snapshot of the blob this client is connecting to. - * @param snapshot the snapshot identifier for the blob - * @return the updated PageBlobClientBuilder object - */ - public PageBlobClientBuilder snapshot(String snapshot) { - this.snapshot = snapshot; - return this; - } - - /** - * Sets the credential used to authorize requests sent to the service - * @param credential authorization credential - * @return the updated PageBlobClientBuilder object - */ - public PageBlobClientBuilder credential(SharedKeyCredential credential) { - this.sharedKeyCredential = credential; - this.tokenCredential = null; - this.sasTokenCredential = null; - return this; - } - - /** - * Sets the credential used to authorize requests sent to the service - * @param credential authorization credential - * @return the updated PageBlobClientBuilder object - */ - public PageBlobClientBuilder credential(TokenCredential credential) { - this.tokenCredential = credential; - this.sharedKeyCredential = null; - this.sasTokenCredential = null; - return this; - } - - /** - * Sets the credential used to authorize requests sent to the service - * @param credential authorization credential - * @return the updated PageBlobClientBuilder object - */ - public PageBlobClientBuilder credential(SASTokenCredential credential) { - this.sasTokenCredential = credential; - this.sharedKeyCredential = null; - this.tokenCredential = null; - return this; - } - - /** - * Clears the credential used to authorize requests sent to the service - * @return the updated PageBlobClientBuilder object - */ - public PageBlobClientBuilder anonymousCredential() { - this.sharedKeyCredential = null; - this.tokenCredential = null; - this.sasTokenCredential = null; - return this; - } - - /** - * Sets the connection string for the service, parses it for authentication information (account name, account key) - * @param connectionString connection string from access keys section - * @return the updated PageBlobClientBuilder object - * @throws IllegalArgumentException If {@code connectionString} doesn't contain AccountName or AccountKey - */ - public PageBlobClientBuilder connectionString(String connectionString) { - Objects.requireNonNull(connectionString); - - Map connectionKVPs = new HashMap<>(); - for (String s : connectionString.split(";")) { - String[] kvp = s.split("=", 2); - connectionKVPs.put(kvp[0].toLowerCase(Locale.ROOT), kvp[1]); - } - - String accountName = connectionKVPs.get(ACCOUNT_NAME); - String accountKey = connectionKVPs.get(ACCOUNT_KEY); - String endpointProtocol = connectionKVPs.get(ENDPOINT_PROTOCOL); - String endpointSuffix = connectionKVPs.get(ENDPOINT_SUFFIX); - - if (ImplUtils.isNullOrEmpty(accountName) || ImplUtils.isNullOrEmpty(accountKey)) { - throw new IllegalArgumentException("Connection string must contain 'AccountName' and 'AccountKey'."); - } - - if (!ImplUtils.isNullOrEmpty(endpointProtocol) && !ImplUtils.isNullOrEmpty(endpointSuffix)) { - String endpoint = String.format("%s://%s.blob.%s", endpointProtocol, accountName, endpointSuffix.replaceFirst("^\\.", "")); - endpoint(endpoint); - } - - // Use accountName and accountKey to get the SAS token using the credential class. - return credential(new SharedKeyCredential(accountName, accountKey)); - } - - /** - * Sets the http client used to send service requests - * @param httpClient http client to send requests - * @return the updated PageBlobClientBuilder object - */ - public PageBlobClientBuilder httpClient(HttpClient httpClient) { - this.httpClient = httpClient; - return this; - } - - /** - * Adds a pipeline policy to apply on each request sent - * @param pipelinePolicy a pipeline policy - * @return the updated PageBlobClientBuilder object - */ - public PageBlobClientBuilder addPolicy(HttpPipelinePolicy pipelinePolicy) { - this.policies.add(pipelinePolicy); - return this; - } - - /** - * Sets the logging level for service requests - * @param logLevel logging level - * @return the updated PageBlobClientBuilder object - */ - public PageBlobClientBuilder httpLogDetailLevel(HttpLogDetailLevel logLevel) { - this.logLevel = logLevel; - return this; - } - - /** - * Sets the configuration object used to retrieve environment configuration values used to buildClient the client with - * when they are not set in the appendBlobClientBuilder, defaults to Configuration.NONE - * @param configuration configuration store - * @return the updated PageBlobClientBuilder object - */ - public PageBlobClientBuilder configuration(Configuration configuration) { - this.configuration = configuration; - return this; - } - - /** - * Sets the request retry options for all the requests made through the client. - * @param retryOptions the options to configure retry behaviors - * @return the updated PageBlobClientBuilder object - */ - public PageBlobClientBuilder retryOptions(RequestRetryOptions retryOptions) { - this.retryOptions = retryOptions; - return this; - } -} diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/SASQueryParameters.java b/storage/client/blob/src/main/java/com/azure/storage/blob/SASQueryParameters.java index b6afd82addcab..399e7ea1b6e63 100644 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/SASQueryParameters.java +++ b/storage/client/blob/src/main/java/com/azure/storage/blob/SASQueryParameters.java @@ -19,7 +19,7 @@ * are existing query parameters, which might affect the appropriate means of appending these query parameters). * NOTE: Instances of this class are immutable to ensure thread safety. */ -final class SASQueryParameters { +public final class SASQueryParameters { private final String version; @@ -297,26 +297,44 @@ public String contentType() { return contentType; } + /** + * @return the object ID of the key. + */ public String keyOid() { return keyOid; } + /** + * @return the tenant ID of the key. + */ public String keyTid() { return keyTid; } + /** + * @return the datetime when the key becomes active. + */ public OffsetDateTime keyStart() { return keyStart; } + /** + * @return the datetime when the key expires. + */ public OffsetDateTime keyExpiry() { return keyExpiry; } + /** + * @return the services that are permitted by the key. + */ public String keyService() { return keyService; } + /** + * @return the service version that created the key. + */ public String keyVersion() { return keyVersion; } @@ -333,9 +351,7 @@ UserDelegationKey userDelegationKey() { private void tryAppendQueryParameter(StringBuilder sb, String param, Object value) { if (value != null) { - if (sb.length() == 0) { - sb.append('?'); - } else { + if (sb.length() != 0) { sb.append('&'); } sb.append(safeURLEncode(param)).append('=').append(safeURLEncode(value.toString())); diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/ServiceSASSignatureValues.java b/storage/client/blob/src/main/java/com/azure/storage/blob/ServiceSASSignatureValues.java index 7450ae699f5a4..f6b4767cada56 100644 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/ServiceSASSignatureValues.java +++ b/storage/client/blob/src/main/java/com/azure/storage/blob/ServiceSASSignatureValues.java @@ -6,14 +6,16 @@ import com.azure.storage.blob.models.UserDelegationKey; import com.azure.storage.common.credentials.SharedKeyCredential; +import java.net.MalformedURLException; +import java.net.URL; import java.security.InvalidKeyException; import java.time.OffsetDateTime; /** - * ServiceSASSignatureValues is used to generate a Shared Access Signature (SAS) for an Azure Storage service. Once - * all the values here are set appropriately, call generateSASQueryParameters to obtain a representation of the SAS - * which can actually be applied to blob urls. Note: that both this class and {@link SASQueryParameters} exist because - * the former is mutable and a logical representation while the latter is immutable and used to generate actual REST + * ServiceSASSignatureValues is used to generate a Shared Access Signature (SAS) for an Azure Storage service. Once all + * the values here are set appropriately, call generateSASQueryParameters to obtain a representation of the SAS which + * can actually be applied to blob urls. Note: that both this class and {@link SASQueryParameters} exist because the + * former is mutable and a logical representation while the latter is immutable and used to generate actual REST * requests. *

* Please see here @@ -22,9 +24,9 @@ * Please see here for * more details on each value, including which are required. * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=service_sas "Sample code for ServiceSASSignatureValues")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) + * @apiNote ## Sample Code \n [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=service_sas + * "Sample code for ServiceSASSignatureValues")] \n For more samples, please see the [Samples + * file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) */ final class ServiceSASSignatureValues { @@ -40,9 +42,9 @@ final class ServiceSASSignatureValues { private IPRange ipRange; - private String containerName; + private String canonicalName; - private String blobName; + private String resource; private String snapshotId; @@ -64,6 +66,45 @@ final class ServiceSASSignatureValues { ServiceSASSignatureValues() { } + /** + * Creates an object with the specified expiry time and permissions + * + * @param expiryTime + * @param permissions + */ + ServiceSASSignatureValues(OffsetDateTime expiryTime, String permissions) { + this.expiryTime = expiryTime; + this.permissions = permissions; + } + + /** + * Creates an object with the specified identifier + * + * @param identifier + */ + ServiceSASSignatureValues(String identifier) { + this.identifier = identifier; + } + + ServiceSASSignatureValues(String version, SASProtocol sasProtocol, OffsetDateTime startTime, + OffsetDateTime expiryTime, String permission, IPRange ipRange, String identifier, String cacheControl, + String contentDisposition, String contentEncoding, String contentLanguage, String contentType) { + if (version != null) { + this.version = version; + } + this.protocol = sasProtocol; + this.startTime = startTime; + this.expiryTime = expiryTime; + this.permissions = permission; + this.ipRange = ipRange; + this.identifier = identifier; + this.cacheControl = cacheControl; + this.contentDisposition = contentDisposition; + this.contentEncoding = contentEncoding; + this.contentLanguage = contentLanguage; + this.contentType = contentType; + } + /** * The version of the service this SAS will target. If not specified, it will default to the version targeted by the * library. @@ -159,32 +200,52 @@ public ServiceSASSignatureValues ipRange(IPRange ipRange) { } /** - * The name of the container the SAS user may access. + * The resource the SAS user may access. */ - public String containerName() { - return containerName; + public String resource() { + return resource; } /** - * The name of the container the SAS user may access. + * The resource the SAS user may access. */ - public ServiceSASSignatureValues containerName(String containerName) { - this.containerName = containerName; + public ServiceSASSignatureValues resource(String resource) { + this.resource = resource; return this; } /** - * The name of the blob the SAS user may access. + * The canonical name of the object the SAS user may access. + */ + public String canonicalName() { + return canonicalName; + } + + /** + * The canonical name of the object the SAS user may access. */ - public String blobName() { - return blobName; + public ServiceSASSignatureValues canonicalName(String canonicalName) { + this.canonicalName = canonicalName; + return this; } /** - * The name of the blob the SAS user may access. + * The canonical name of the object the SAS user may access. + * + * @throws RuntimeException If urlString is a malformed URL. */ - public ServiceSASSignatureValues blobName(String blobName) { - this.blobName = blobName; + public ServiceSASSignatureValues canonicalName(String urlString, String accountName) { + URL url = null; + try { + url = new URL(urlString); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + + StringBuilder canonicalName = new StringBuilder("/blob"); + canonicalName.append('/').append(accountName).append(url.getPath()); + this.canonicalName = canonicalName.toString(); + return this; } @@ -192,7 +253,7 @@ public ServiceSASSignatureValues blobName(String blobName) { * The specific snapshot the SAS user may access. */ public String snapshotId() { - return snapshotId; + return this.snapshotId; } /** @@ -301,21 +362,16 @@ public ServiceSASSignatureValues contentType(String contentType) { * Uses an account's shared key credential to sign these signature values to produce the proper SAS query * parameters. * - * @param sharedKeyCredentials - * A {@link SharedKeyCredential} object used to sign the SAS values. - * + * @param sharedKeyCredentials A {@link SharedKeyCredential} object used to sign the SAS values. * @return {@link SASQueryParameters} * @throws Error If the accountKey is not a valid Base64-encoded string. */ public SASQueryParameters generateSASQueryParameters(SharedKeyCredential sharedKeyCredentials) { Utility.assertNotNull("sharedKeyCredentials", sharedKeyCredentials); - assertGenerateOK(); - - String resource = getResource(); - String verifiedPermissions = getVerifiedPermissions(); + assertGenerateOK(false); // Signature is generated on the un-url-encoded values. - final String stringToSign = stringToSign(verifiedPermissions, resource, sharedKeyCredentials); + final String stringToSign = stringToSign(); String signature = null; try { @@ -325,33 +381,24 @@ public SASQueryParameters generateSASQueryParameters(SharedKeyCredential sharedK } return new SASQueryParameters(this.version, null, null, - this.protocol, this.startTime, this.expiryTime, this.ipRange, this.identifier, resource, - this.permissions, signature, this.cacheControl, this.contentDisposition, this.contentEncoding, - this.contentLanguage, this.contentType, null /* delegate */); + this.protocol, this.startTime, this.expiryTime, this.ipRange, this.identifier, resource, + this.permissions, signature, this.cacheControl, this.contentDisposition, this.contentEncoding, + this.contentLanguage, this.contentType, null /* delegate */); } /** * Uses a user delegation key to sign these signature values to produce the proper SAS query parameters. * - * @param delegationKey - * A {@link UserDelegationKey} object used to sign the SAS values. - * - * @param accountName - * Name of the account holding the resource this SAS is authorizing. - * + * @param delegationKey A {@link UserDelegationKey} object used to sign the SAS values. * @return {@link SASQueryParameters} * @throws Error If the accountKey is not a valid Base64-encoded string. */ - public SASQueryParameters generateSASQueryParameters(UserDelegationKey delegationKey, String accountName) { + public SASQueryParameters generateSASQueryParameters(UserDelegationKey delegationKey) { Utility.assertNotNull("delegationKey", delegationKey); - Utility.assertNotNull("accountName", accountName); - assertGenerateOK(); - - String resource = getResource(); - String verifiedPermissions = getVerifiedPermissions(); + assertGenerateOK(true); // Signature is generated on the un-url-encoded values. - final String stringToSign = stringToSign(verifiedPermissions, resource, delegationKey, accountName); + final String stringToSign = stringToSign(delegationKey); String signature = null; try { @@ -361,110 +408,78 @@ public SASQueryParameters generateSASQueryParameters(UserDelegationKey delegatio } return new SASQueryParameters(this.version, null, null, - this.protocol, this.startTime, this.expiryTime, this.ipRange, null /* identifier */, resource, - this.permissions, signature, this.cacheControl, this.contentDisposition, this.contentEncoding, - this.contentLanguage, this.contentType, delegationKey); + this.protocol, this.startTime, this.expiryTime, this.ipRange, null /* identifier */, resource, + this.permissions, signature, this.cacheControl, this.contentDisposition, this.contentEncoding, + this.contentLanguage, this.contentType, delegationKey); } /** * Common assertions for generateSASQueryParameters overloads. */ - private void assertGenerateOK() { + private void assertGenerateOK(boolean usingUserDelegation) { Utility.assertNotNull("version", this.version); - Utility.assertNotNull("containerName", this.containerName); - if (blobName == null && snapshotId != null) { - throw new IllegalArgumentException("Cannot set a snapshotId without a blobName."); - } - } - - /** - * Gets the resource string for SAS tokens based on object state. - */ - private String getResource() { - String resource = "c"; - if (!Utility.isNullOrEmpty(this.blobName)) { - resource = snapshotId != null && !snapshotId.isEmpty() ? "bs" : "b"; - } - - return resource; - } + Utility.assertNotNull("canonicalName", this.canonicalName); - /** - * Gets the verified permissions string for SAS tokens based on object state. - */ - private String getVerifiedPermissions() { - String verifiedPermissions = null; - // Calling parse and toString guarantees the proper ordering and throws on invalid characters. - if (Utility.isNullOrEmpty(this.blobName)) { - if (this.permissions != null) { - verifiedPermissions = ContainerSASPermission.parse(this.permissions).toString(); + // Ensure either (expiryTime and permissions) or (identifier) is set + if (this.expiryTime == null || this.permissions == null) { + // Identifier is not required if user delegation is being used + if (!usingUserDelegation) { + Utility.assertNotNull("identifier", this.identifier); } } else { - if (this.permissions != null) { - verifiedPermissions = BlobSASPermission.parse(this.permissions).toString(); - } + Utility.assertNotNull("expiryTime", this.expiryTime); + Utility.assertNotNull("permissions", this.permissions); } - return verifiedPermissions; - } - - private String getCanonicalName(String accountName) { - // Container: "/blob/account/containername" - // Blob: "/blob/account/containername/blobname" - StringBuilder canonicalName = new StringBuilder("/blob"); - canonicalName.append('/').append(accountName).append('/').append(this.containerName); - - if (!Utility.isNullOrEmpty(this.blobName)) { - canonicalName.append("/").append(this.blobName); + if (this.resource != null && this.resource.equals(Constants.UrlConstants.SAS_CONTAINER_CONSTANT)) { + if (this.snapshotId != null) { + throw new IllegalArgumentException("Cannot set a snapshotId without resource being a blob."); + } } - - return canonicalName.toString(); } - private String stringToSign(final String verifiedPermissions, final String resource, - final SharedKeyCredential sharedKeyCredentials) { + private String stringToSign() { return String.join("\n", - verifiedPermissions == null ? "" : verifiedPermissions, - this.startTime == null ? "" : Utility.ISO_8601_UTC_DATE_FORMATTER.format(this.startTime), - this.expiryTime == null ? "" : Utility.ISO_8601_UTC_DATE_FORMATTER.format(this.expiryTime), - getCanonicalName(sharedKeyCredentials.accountName()), - this.identifier == null ? "" : this.identifier, - this.ipRange == null ? (new IPRange()).toString() : this.ipRange.toString(), - this.protocol == null ? "" : protocol.toString(), - this.version == null ? "" : this.version, - resource == null ? "" : resource, - this.snapshotId == null ? "" : this.snapshotId, - this.cacheControl == null ? "" : this.cacheControl, - this.contentDisposition == null ? "" : this.contentDisposition, - this.contentEncoding == null ? "" : this.contentEncoding, - this.contentLanguage == null ? "" : this.contentLanguage, - this.contentType == null ? "" : this.contentType + this.permissions == null ? "" : this.permissions, + this.startTime == null ? "" : Utility.ISO_8601_UTC_DATE_FORMATTER.format(this.startTime), + this.expiryTime == null ? "" : Utility.ISO_8601_UTC_DATE_FORMATTER.format(this.expiryTime), + this.canonicalName == null ? "" : this.canonicalName, + this.identifier == null ? "" : this.identifier, + this.ipRange == null ? (new IPRange()).toString() : this.ipRange.toString(), + this.protocol == null ? "" : protocol.toString(), + this.version == null ? "" : this.version, + this.resource == null ? "" : this.resource, + this.snapshotId == null ? "" : this.snapshotId, + this.cacheControl == null ? "" : this.cacheControl, + this.contentDisposition == null ? "" : this.contentDisposition, + this.contentEncoding == null ? "" : this.contentEncoding, + this.contentLanguage == null ? "" : this.contentLanguage, + this.contentType == null ? "" : this.contentType ); } - private String stringToSign(final String verifiedPermissions, final String resource, - final UserDelegationKey key, final String accountName) { + private String stringToSign(final UserDelegationKey key) { return String.join("\n", - verifiedPermissions == null ? "" : verifiedPermissions, - this.startTime == null ? "" : Utility.ISO_8601_UTC_DATE_FORMATTER.format(this.startTime), - this.expiryTime == null ? "" : Utility.ISO_8601_UTC_DATE_FORMATTER.format(this.expiryTime), - getCanonicalName(accountName), - key.signedOid() == null ? "" : key.signedOid(), - key.signedTid() == null ? "" : key.signedTid(), - key.signedStart() == null ? "" : Utility.ISO_8601_UTC_DATE_FORMATTER.format(key.signedStart()), - key.signedExpiry() == null ? "" : Utility.ISO_8601_UTC_DATE_FORMATTER.format(key.signedExpiry()), - key.signedService() == null ? "" : key.signedService(), - key.signedVersion() == null ? "" : key.signedVersion(), - this.ipRange == null ? new IPRange().toString() : this.ipRange.toString(), - this.protocol == null ? "" : this.protocol.toString(), - this.version == null ? "" : this.version, - resource == null ? "" : resource, - this.snapshotId == null ? "" : this.snapshotId, - this.cacheControl == null ? "" : this.cacheControl, - this.contentDisposition == null ? "" : this.contentDisposition, - this.contentEncoding == null ? "" : this.contentEncoding, - this.contentLanguage == null ? "" : this.contentLanguage, - this.contentType == null ? "" : this.contentType + this.permissions == null ? "" : this.permissions, + this.startTime == null ? "" : Utility.ISO_8601_UTC_DATE_FORMATTER.format(this.startTime), + this.expiryTime == null ? "" : Utility.ISO_8601_UTC_DATE_FORMATTER.format(this.expiryTime), + this.canonicalName == null ? "" : this.canonicalName, + key.signedOid() == null ? "" : key.signedOid(), + key.signedTid() == null ? "" : key.signedTid(), + key.signedStart() == null ? "" : Utility.ISO_8601_UTC_DATE_FORMATTER.format(key.signedStart()), + key.signedExpiry() == null ? "" : Utility.ISO_8601_UTC_DATE_FORMATTER.format(key.signedExpiry()), + key.signedService() == null ? "" : key.signedService(), + key.signedVersion() == null ? "" : key.signedVersion(), + this.ipRange == null ? new IPRange().toString() : this.ipRange.toString(), + this.protocol == null ? "" : this.protocol.toString(), + this.version == null ? "" : this.version, + this.resource == null ? "" : this.resource, + this.snapshotId == null ? "" : this.snapshotId, + this.cacheControl == null ? "" : this.cacheControl, + this.contentDisposition == null ? "" : this.contentDisposition, + this.contentEncoding == null ? "" : this.contentEncoding, + this.contentLanguage == null ? "" : this.contentLanguage, + this.contentType == null ? "" : this.contentType ); } } diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/StorageAsyncClient.java b/storage/client/blob/src/main/java/com/azure/storage/blob/StorageAsyncClient.java index d5b0aecc3a3f1..9e4d0fb2140f5 100644 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/StorageAsyncClient.java +++ b/storage/client/blob/src/main/java/com/azure/storage/blob/StorageAsyncClient.java @@ -8,8 +8,11 @@ import com.azure.core.http.rest.Response; import com.azure.core.http.rest.SimpleResponse; import com.azure.core.http.rest.VoidResponse; +import com.azure.core.util.Context; import com.azure.storage.blob.implementation.AzureBlobStorageBuilder; +import com.azure.storage.blob.implementation.AzureBlobStorageImpl; import com.azure.storage.blob.models.ContainerItem; +import com.azure.storage.blob.models.KeyInfo; import com.azure.storage.blob.models.ListContainersOptions; import com.azure.storage.blob.models.Metadata; import com.azure.storage.blob.models.PublicAccessType; @@ -18,6 +21,7 @@ import com.azure.storage.blob.models.StorageServiceProperties; import com.azure.storage.blob.models.StorageServiceStats; import com.azure.storage.blob.models.UserDelegationKey; +import com.azure.storage.common.credentials.SharedKeyCredential; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -25,11 +29,12 @@ import java.net.URL; import java.time.OffsetDateTime; +import static com.azure.storage.blob.Utility.postProcessResponse; + /** - * Client to a storage account. It may only be instantiated through a {@link StorageClientBuilder}. - * This class does not hold any state about a particular storage account but is - * instead a convenient way of sending off appropriate requests to the resource on the service. - * It may also be used to construct URLs to blobs and containers. + * Client to a storage account. It may only be instantiated through a {@link StorageClientBuilder}. This class does not + * hold any state about a particular storage account but is instead a convenient way of sending off appropriate requests + * to the resource on the service. It may also be used to construct URLs to blobs and containers. * *

* This client contains operations on a blob. Operations on a container are available on {@link ContainerAsyncClient} @@ -40,36 +45,35 @@ * information on containers. * *

- * Note this client is an async client that returns reactive responses from Spring Reactor Core - * project (https://projectreactor.io/). Calling the methods in this client will NOT - * start the actual network operation, until {@code .subscribe()} is called on the reactive response. - * You can simply convert one of these responses to a {@link java.util.concurrent.CompletableFuture} - * object through {@link Mono#toFuture()}. + * Note this client is an async client that returns reactive responses from Spring Reactor Core project + * (https://projectreactor.io/). Calling the methods in this client will NOT start the actual network + * operation, until {@code .subscribe()} is called on the reactive response. You can simply convert one of these + * responses to a {@link java.util.concurrent.CompletableFuture} object through {@link Mono#toFuture()}. */ public final class StorageAsyncClient { - StorageAsyncRawClient storageAsyncRawClient; + private final AzureBlobStorageImpl azureBlobStorage; /** * Package-private constructor for use by {@link StorageClientBuilder}. + * * @param azureBlobStorageBuilder the API client builder for blob storage API */ StorageAsyncClient(AzureBlobStorageBuilder azureBlobStorageBuilder) { - this.storageAsyncRawClient = new StorageAsyncRawClient(azureBlobStorageBuilder.build()); + this.azureBlobStorage = azureBlobStorageBuilder.build(); } /** - * Initializes a {@link ContainerAsyncClient} object pointing to the specified container. This method does not create a - * container. It simply constructs the URL to the container and offers access to methods relevant to containers. + * Initializes a {@link ContainerAsyncClient} object pointing to the specified container. This method does not + * create a container. It simply constructs the URL to the container and offers access to methods relevant to + * containers. * - * @param containerName - * The name of the container to point to. - * @return - * A {@link ContainerAsyncClient} object pointing to the specified container + * @param containerName The name of the container to point to. + * @return A {@link ContainerAsyncClient} object pointing to the specified container */ public ContainerAsyncClient getContainerAsyncClient(String containerName) { return new ContainerAsyncClient(new AzureBlobStorageBuilder() .url(Utility.appendToURLPath(getAccountUrl(), containerName).toString()) - .pipeline(storageAsyncRawClient.azureBlobStorage.httpPipeline())); + .pipeline(azureBlobStorage.httpPipeline())); } /** @@ -90,11 +94,9 @@ public Mono> createContainer(String containerName * Azure Docs. * * @param containerName Name of the container to create - * @param metadata - * {@link Metadata} - * @param accessType - * Specifies how the data in this container is available to the public. See the x-ms-blob-public-access header - * in the Azure Docs for more information. Pass null for no public access. + * @param metadata {@link Metadata} + * @param accessType Specifies how the data in this container is available to the public. See the + * x-ms-blob-public-access header in the Azure Docs for more information. Pass null for no public access. * @return A response containing a {@link ContainerAsyncClient} used to interact with the container created. */ public Mono> createContainer(String containerName, Metadata metadata, PublicAccessType accessType) { @@ -104,52 +106,89 @@ public Mono> createContainer(String containerName .map(response -> new SimpleResponse<>(response, containerAsyncClient)); } + /** + * Deletes the specified container in the storage account. If the container doesn't exist the operation fails. For + * more information see the Azure Docs. + * + * @param containerName Name of the container to delete + * @return A response containing status code and HTTP headers + */ + public Mono deleteContainer(String containerName) { + return getContainerAsyncClient(containerName).delete(); + } + /** * Gets the URL of the storage account represented by this client. + * * @return the URL. * @throws RuntimeException If the account URL is malformed. */ public URL getAccountUrl() { try { - return new URL(storageAsyncRawClient.azureBlobStorage.url()); + return new URL(azureBlobStorage.url()); } catch (MalformedURLException e) { - throw new RuntimeException(String.format("Invalid URL on %s: %s" + getClass().getSimpleName(), storageAsyncRawClient.azureBlobStorage.url()), e); + throw new RuntimeException(String.format("Invalid URL on %s: %s" + getClass().getSimpleName(), azureBlobStorage.url()), e); } } /** - * Returns a reactive Publisher emitting all the containers in this account lazily as needed. For more information, see - * the Azure Docs. + * Returns a reactive Publisher emitting all the containers in this account lazily as needed. For more information, + * see the Azure Docs. * - * @return - * A reactive response emitting the list of containers. + * @return A reactive response emitting the list of containers. */ public Flux listContainers() { return this.listContainers(new ListContainersOptions()); } /** - * Returns a reactive Publisher emitting all the containers in this account lazily as needed. For more information, see + * Returns a reactive Publisher emitting all the containers in this account lazily as needed. For more information, + * see the Azure Docs. + * + * @param options A {@link ListContainersOptions} which specifies what data should be returned by the service. + * @return A reactive response emitting the list of containers. + */ + public Flux listContainers(ListContainersOptions options) { + return listContainersSegment(null, options) + .flatMapMany(response -> listContainersHelper(response.value().marker(), options, response)); + } + + /* + * Returns a Mono segment of containers starting from the specified Marker. + * Use an empty marker to start enumeration from the beginning. Container names are returned in lexicographic order. + * After getting a segment, process it, and then call ListContainers again (passing the the previously-returned + * Marker) to get the next segment. For more information, see * the Azure Docs. * + * @param marker + * Identifies the portion of the list to be returned with the next list operation. + * This value is returned in the response of a previous list operation as the + * ListContainersSegmentResponse.body().nextMarker(). Set to null to list the first segment. * @param options * A {@link ListContainersOptions} which specifies what data should be returned by the service. * - * @return - * A reactive response emitting the list of containers. + * @return Emits the successful response. + * + * @apiNote ## Sample Code \n + * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=service_list "Sample code for ServiceURL.listContainersSegment")] \n + * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=service_list_helper "Helper code for ServiceURL.listContainersSegment")] \n + * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) */ - public Flux listContainers(ListContainersOptions options) { - return storageAsyncRawClient - .listContainersSegment(null, options) - .flatMapMany(response -> listContainersHelper(response.value().marker(), options, response)); + private Mono listContainersSegment(String marker, ListContainersOptions options) { + options = options == null ? new ListContainersOptions() : options; + + return postProcessResponse( + this.azureBlobStorage.services().listContainersSegmentWithRestResponseAsync( + options.prefix(), marker, options.maxResults(), options.details().toIncludeType(), null, + null, Context.NONE)); } private Flux listContainersHelper(String marker, ListContainersOptions options, - ServicesListContainersSegmentResponse response) { + ServicesListContainersSegmentResponse response) { Flux result = Flux.fromIterable(response.value().containerItems()); if (response.value().nextMarker() != null) { // Recursively add the continuation items to the observable. - result = result.concatWith(storageAsyncRawClient.listContainersSegment(marker, options) + result = result.concatWith(listContainersSegment(marker, options) .flatMapMany((r) -> listContainersHelper(response.value().nextMarker(), options, r))); } @@ -161,12 +200,11 @@ private Flux listContainersHelper(String marker, ListContainersOp * Gets the properties of a storage account’s Blob service. For more information, see the * Azure Docs. * - * @return - * A reactive response containing the storage account properties. + * @return A reactive response containing the storage account properties. */ public Mono> getProperties() { - return storageAsyncRawClient - .getProperties() + return postProcessResponse( + this.azureBlobStorage.services().getPropertiesWithRestResponseAsync(null, null, Context.NONE)) .map(rb -> new SimpleResponse<>(rb, rb.value())); } @@ -176,48 +214,50 @@ public Mono> getProperties() { * Note that setting the default service version has no effect when using this client because this client explicitly * sets the version header on each request, overriding the default. * - * @param properties - * Configures the service. - * - * @return - * A reactive response containing the storage account properties. + * @param properties Configures the service. + * @return A reactive response containing the storage account properties. */ public Mono setProperties(StorageServiceProperties properties) { - return storageAsyncRawClient - .setProperties(properties) + return postProcessResponse( + this.azureBlobStorage.services().setPropertiesWithRestResponseAsync(properties, null, null, Context.NONE)) .map(VoidResponse::new); } /** - * Gets a user delegation key for use with this account's blob storage. - * Note: This method call is only valid when using {@link TokenCredential} in this object's {@link HttpPipeline}. - * - * @param start - * Start time for the key's validity. Null indicates immediate start. - * @param expiry - * Expiration of the key's validity. + * Gets a user delegation key for use with this account's blob storage. Note: This method call is only valid when + * using {@link TokenCredential} in this object's {@link HttpPipeline}. * - * @return - * A reactive response containing the user delegation key. + * @param start Start time for the key's validity. Null indicates immediate start. + * @param expiry Expiration of the key's validity. + * @return A reactive response containing the user delegation key. + * @throws IllegalArgumentException If {@code start} isn't null and is after {@code expiry}. */ public Mono> getUserDelegationKey(OffsetDateTime start, OffsetDateTime expiry) { - return storageAsyncRawClient - .getUserDelegationKey(start, expiry) - .map(rb -> new SimpleResponse<>(rb, rb.value())); + Utility.assertNotNull("expiry", expiry); + if (start != null && !start.isBefore(expiry)) { + throw new IllegalArgumentException("`start` must be null or a datetime before `expiry`."); + } + + return postProcessResponse( + this.azureBlobStorage.services().getUserDelegationKeyWithRestResponseAsync( + new KeyInfo() + .start(start == null ? "" : Utility.ISO_8601_UTC_DATE_FORMATTER.format(start)) + .expiry(Utility.ISO_8601_UTC_DATE_FORMATTER.format(expiry)), + null, null, Context.NONE) + ).map(rb -> new SimpleResponse<>(rb, rb.value())); } /** - * Retrieves statistics related to replication for the Blob service. It is only available on the secondary - * location endpoint when read-access geo-redundant replication is enabled for the storage account. For more - * information, see the + * Retrieves statistics related to replication for the Blob service. It is only available on the secondary location + * endpoint when read-access geo-redundant replication is enabled for the storage account. For more information, see + * the * Azure Docs. * - * @return - * A reactive response containing the storage account statistics. + * @return A reactive response containing the storage account statistics. */ public Mono> getStatistics() { - return storageAsyncRawClient - .getStatistics() + return postProcessResponse( + this.azureBlobStorage.services().getStatisticsWithRestResponseAsync(null, null, Context.NONE)) .map(rb -> new SimpleResponse<>(rb, rb.value())); } @@ -225,12 +265,63 @@ public Mono> getStatistics() { * Returns the sku name and account kind for the account. For more information, please see the * Azure Docs. * - * @return - * A reactive response containing the storage account info. + * @return A reactive response containing the storage account info. */ public Mono> getAccountInfo() { - return storageAsyncRawClient - .getAccountInfo() + return postProcessResponse(this.azureBlobStorage.services().getAccountInfoWithRestResponseAsync(Context.NONE)) .map(rb -> new SimpleResponse<>(rb, new StorageAccountInfo(rb.deserializedHeaders()))); } + + /** + * Generates an account SAS token with the specified parameters + * + * @param accountSASService The {@code AccountSASService} services for the account SAS + * @param accountSASResourceType An optional {@code AccountSASResourceType} resources for the account SAS + * @param accountSASPermission The {@code AccountSASPermission} permission for the account SAS + * @param expiryTime The {@code OffsetDateTime} expiry time for the account SAS + * @return A string that represents the SAS token + */ + public String generateAccountSAS(AccountSASService accountSASService, AccountSASResourceType accountSASResourceType, + AccountSASPermission accountSASPermission, OffsetDateTime expiryTime) { + return this.generateAccountSAS(accountSASService, accountSASResourceType, accountSASPermission, expiryTime, + null /* startTime */, null /* version */, null /* ipRange */, null /* sasProtocol */); + } + + /** + * Generates an account SAS token with the specified parameters + * + * @param accountSASService The {@code AccountSASService} services for the account SAS + * @param accountSASResourceType An optional {@code AccountSASResourceType} resources for the account SAS + * @param accountSASPermission The {@code AccountSASPermission} permission for the account SAS + * @param expiryTime The {@code OffsetDateTime} expiry time for the account SAS + * @param startTime The {@code OffsetDateTime} start time for the account SAS + * @param version The {@code String} version for the account SAS + * @param ipRange An optional {@code IPRange} ip address range for the SAS + * @param sasProtocol An optional {@code SASProtocol} protocol for the SAS + * @return A string that represents the SAS token + */ + public String generateAccountSAS(AccountSASService accountSASService, AccountSASResourceType accountSASResourceType, + AccountSASPermission accountSASPermission, OffsetDateTime expiryTime, OffsetDateTime startTime, String version, IPRange ipRange, + SASProtocol sasProtocol) { + + AccountSASSignatureValues accountSASSignatureValues = new AccountSASSignatureValues(); + accountSASSignatureValues.services(accountSASService == null ? null : accountSASService.toString()); + accountSASSignatureValues.resourceTypes(accountSASResourceType == null ? null : accountSASResourceType.toString()); + accountSASSignatureValues.permissions(accountSASPermission == null ? null : accountSASPermission.toString()); + accountSASSignatureValues.expiryTime(expiryTime); + accountSASSignatureValues.startTime(startTime); + + if (version != null) { + accountSASSignatureValues.version(version); + } + + accountSASSignatureValues.ipRange(ipRange); + accountSASSignatureValues.protocol(sasProtocol); + + SharedKeyCredential sharedKeyCredential = Utility.getSharedKeyCredential(this.azureBlobStorage.httpPipeline()); + + SASQueryParameters sasQueryParameters = accountSASSignatureValues.generateSASQueryParameters(sharedKeyCredential); + + return sasQueryParameters.encode(); + } } diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/StorageAsyncRawClient.java b/storage/client/blob/src/main/java/com/azure/storage/blob/StorageAsyncRawClient.java deleted file mode 100644 index a2b6be965ed30..0000000000000 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/StorageAsyncRawClient.java +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.azure.storage.blob; - -import com.azure.core.credentials.TokenCredential; -import com.azure.core.http.HttpPipeline; -import com.azure.core.util.Context; -import com.azure.storage.blob.implementation.AzureBlobStorageImpl; -import com.azure.storage.blob.models.KeyInfo; -import com.azure.storage.blob.models.ListContainersOptions; -import com.azure.storage.blob.models.ServicesGetAccountInfoResponse; -import com.azure.storage.blob.models.ServicesGetPropertiesResponse; -import com.azure.storage.blob.models.ServicesGetStatisticsResponse; -import com.azure.storage.blob.models.ServicesGetUserDelegationKeyResponse; -import com.azure.storage.blob.models.ServicesListContainersSegmentResponse; -import com.azure.storage.blob.models.ServicesSetPropertiesResponse; -import com.azure.storage.blob.models.StorageServiceProperties; -import reactor.core.publisher.Mono; - -import java.time.OffsetDateTime; - -import static com.azure.storage.blob.Utility.postProcessResponse; - -/** - * Represents a URL to a storage service. This class does not hold any state about a particular storage account but is - * instead a convenient way of sending off appropriate requests to the resource on the service. - * It may also be used to construct URLs to blobs and containers. - * Please see here for more - * information on containers. - */ -final class StorageAsyncRawClient { - - final AzureBlobStorageImpl azureBlobStorage; - - /** - * Creates a {@code ServiceURL} object pointing to the account specified by the URL and using the provided pipeline - * to make HTTP requests. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=service_url "Sample code for ServiceURL constructor")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - StorageAsyncRawClient(AzureBlobStorageImpl azureBlobStorage) { - this.azureBlobStorage = azureBlobStorage; - } - - /** - * Returns a Mono segment of containers starting from the specified Marker. - * Use an empty marker to start enumeration from the beginning. Container names are returned in lexicographic order. - * After getting a segment, process it, and then call ListContainers again (passing the the previously-returned - * Marker) to get the next segment. For more information, see - * the Azure Docs. - * - * @param marker - * Identifies the portion of the list to be returned with the next list operation. - * This value is returned in the response of a previous list operation as the - * ListContainersSegmentResponse.body().nextMarker(). Set to null to list the first segment. - * @param options - * A {@link ListContainersOptions} which specifies what data should be returned by the service. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=service_list "Sample code for ServiceURL.listContainersSegment")] \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=service_list_helper "Helper code for ServiceURL.listContainersSegment")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono listContainersSegment(String marker, - ListContainersOptions options) { - options = options == null ? new ListContainersOptions() : options; - - return postProcessResponse( - this.azureBlobStorage.services().listContainersSegmentWithRestResponseAsync( - options.prefix(), marker, options.maxResults(), options.details().toIncludeType(), null, - null, Context.NONE)); - } - - /** - * Gets the properties of a storage account’s Blob service. For more information, see the - * Azure Docs. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=service_getsetprops "Sample code for ServiceURL.getProperties")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono getProperties() { - return postProcessResponse( - this.azureBlobStorage.services().getPropertiesWithRestResponseAsync(null, null, Context.NONE)); - } - - /** - * Sets properties for a storage account's Blob service endpoint. For more information, see the - * Azure Docs. - * Note that setting the default service version has no effect when using this client because this client explicitly - * sets the version header on each request, overriding the default. - * - * @param properties - * Configures the service. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=service_getsetprops "Sample code for ServiceURL.setProperties")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono setProperties(StorageServiceProperties properties) { - return postProcessResponse( - this.azureBlobStorage.services().setPropertiesWithRestResponseAsync(properties, null, null, Context.NONE)); - } - - /** - * Gets a user delegation key for use with this account's blob storage. - * Note: This method call is only valid when using {@link TokenCredential} in this object's {@link HttpPipeline}. - * - * @param start - * Start time for the key's validity. Null indicates immediate start. - * @param expiry - * Expiration of the key's validity. - * - * @return Emits the successful response. - * @throws IllegalArgumentException If {@code start} isn't null and is after {@code expiry}. - */ - public Mono getUserDelegationKey(OffsetDateTime start, OffsetDateTime expiry) { - Utility.assertNotNull("expiry", expiry); - if (start != null && !start.isBefore(expiry)) { - throw new IllegalArgumentException("`start` must be null or a datetime before `expiry`."); - } - - return postProcessResponse( - this.azureBlobStorage.services().getUserDelegationKeyWithRestResponseAsync( - new KeyInfo() - .start(start == null ? "" : Utility.ISO_8601_UTC_DATE_FORMATTER.format(start)) - .expiry(Utility.ISO_8601_UTC_DATE_FORMATTER.format(expiry)), - null, null, Context.NONE) - ); - } - - /** - * Retrieves statistics related to replication for the Blob service. It is only available on the secondary - * location endpoint when read-access geo-redundant replication is enabled for the storage account. For more - * information, see the - * Azure Docs. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=service_stats "Sample code for ServiceURL.getStats")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono getStatistics() { - return postProcessResponse( - this.azureBlobStorage.services().getStatisticsWithRestResponseAsync(null, null, Context.NONE)); - } - - /** - * Returns the sku name and account kind for the account. For more information, please see the - * Azure Docs. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=account_info "Sample code for ServiceURL.getAccountInfo")] \n - * For more samples, please see the [Samples file] (https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public Mono getAccountInfo() { - return postProcessResponse( - this.azureBlobStorage.services().getAccountInfoWithRestResponseAsync(Context.NONE)); - } -} diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/StorageClient.java b/storage/client/blob/src/main/java/com/azure/storage/blob/StorageClient.java index f18cf0640fbd9..5749b1b3fe7d7 100644 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/StorageClient.java +++ b/storage/client/blob/src/main/java/com/azure/storage/blob/StorageClient.java @@ -24,25 +24,24 @@ import java.time.OffsetDateTime; /** - * Client to a storage account. It may only be instantiated through a {@link StorageClientBuilder}. - * This class does not hold any state about a particular storage account but is - * instead a convenient way of sending off appropriate requests to the resource on the service. - * It may also be used to construct URLs to blobs and containers. + * Client to a storage account. It may only be instantiated through a {@link StorageClientBuilder}. This class does not + * hold any state about a particular storage account but is instead a convenient way of sending off appropriate requests + * to the resource on the service. It may also be used to construct URLs to blobs and containers. * *

- * This client contains operations on a blob. Operations on a container are available on {@link ContainerClient} - * through {@link #getContainerClient(String)}, and operations on a blob are available on {@link BlobClient}. + * This client contains operations on a blob. Operations on a container are available on {@link ContainerClient} through + * {@link #getContainerClient(String)}, and operations on a blob are available on {@link BlobClient}. * *

* Please see here for more * information on containers. */ public final class StorageClient { - - private StorageAsyncClient storageAsyncClient; + private final StorageAsyncClient storageAsyncClient; /** * Package-private constructor for use by {@link StorageClientBuilder}. + * * @param storageAsyncClient the async storage account client */ StorageClient(StorageAsyncClient storageAsyncClient) { @@ -53,10 +52,8 @@ public final class StorageClient { * Initializes a {@link ContainerClient} object pointing to the specified container. This method does not create a * container. It simply constructs the URL to the container and offers access to methods relevant to containers. * - * @param containerName - * The name of the container to point to. - * @return - * A {@link ContainerClient} object pointing to the specified container + * @param containerName The name of the container to point to. + * @return A {@link ContainerClient} object pointing to the specified container */ public ContainerClient getContainerClient(String containerName) { return new ContainerClient(storageAsyncClient.getContainerAsyncClient(containerName)); @@ -80,11 +77,9 @@ public Response createContainer(String containerName) { * Azure Docs. * * @param containerName Name of the container to create - * @param metadata - * {@link Metadata} - * @param accessType - * Specifies how the data in this container is available to the public. See the x-ms-blob-public-access header - * in the Azure Docs for more information. Pass null for no public access. + * @param metadata {@link Metadata} + * @param accessType Specifies how the data in this container is available to the public. See the + * x-ms-blob-public-access header in the Azure Docs for more information. Pass null for no public access. * @return A response containing a {@link ContainerClient} used to interact with the container created. */ public Response createContainer(String containerName, Metadata metadata, PublicAccessType accessType) { @@ -93,8 +88,20 @@ public Response createContainer(String containerName, Metadata return new SimpleResponse<>(client.create(metadata, accessType, null), client); } + /** + * Deletes the specified container in the storage account. If the container doesn't exist the operation fails. For + * more information see the Azure Docs. + * + * @param containerName Name of the container to delete + * @return A response containing status code and HTTP headers + */ + public VoidResponse deleteContainer(String containerName) { + return storageAsyncClient.deleteContainer(containerName).block(); + } + /** * Gets the URL of the storage account represented by this client. + * * @return the URL. */ public URL getAccountUrl() { @@ -102,29 +109,24 @@ public URL getAccountUrl() { } /** - * Returns a lazy loaded list of containers in this account. The returned {@link Iterable} can be iterated - * through while new items are automatically retrieved as needed. For more information, see - * the Azure Docs. + * Returns a lazy loaded list of containers in this account. The returned {@link Iterable} can be iterated through + * while new items are automatically retrieved as needed. For more information, see the Azure Docs. * - * @return - * The list of containers. + * @return The list of containers. */ public Iterable listContainers() { return this.listContainers(new ListContainersOptions(), null); } /** - * Returns a lazy loaded list of containers in this account. The returned {@link Iterable} can be iterated - * through while new items are automatically retrieved as needed. For more information, see - * the Azure Docs. - * - * @param options - * A {@link ListContainersOptions} which specifies what data should be returned by the service. - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. + * Returns a lazy loaded list of containers in this account. The returned {@link Iterable} can be iterated through + * while new items are automatically retrieved as needed. For more information, see the Azure Docs. * - * @return - * The list of containers. + * @param options A {@link ListContainersOptions} which specifies what data should be returned by the service. + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @return The list of containers. */ public Iterable listContainers(ListContainersOptions options, Duration timeout) { Flux response = storageAsyncClient.listContainers(options); @@ -136,8 +138,7 @@ public Iterable listContainers(ListContainersOptions options, Dur * Gets the properties of a storage account’s Blob service. For more information, see the * Azure Docs. * - * @return - * The storage account properties. + * @return The storage account properties. */ public Response getProperties() { return this.getProperties(null); @@ -147,11 +148,8 @@ public Response getProperties() { * Gets the properties of a storage account’s Blob service. For more information, see the * Azure Docs. * - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. - * - * @return - * The storage account properties. + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @return The storage account properties. */ public Response getProperties(Duration timeout) { @@ -166,11 +164,8 @@ public Response getProperties(Duration timeout) { * Note that setting the default service version has no effect when using this client because this client explicitly * sets the version header on each request, overriding the default. * - * @param properties - * Configures the service. - * - * @return - * The storage account properties. + * @param properties Configures the service. + * @return The storage account properties. */ public VoidResponse setProperties(StorageServiceProperties properties) { return this.setProperties(properties, null); @@ -182,13 +177,9 @@ public VoidResponse setProperties(StorageServiceProperties properties) { * Note that setting the default service version has no effect when using this client because this client explicitly * sets the version header on each request, overriding the default. * - * @param properties - * Configures the service. - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. - * - * @return - * The storage account properties. + * @param properties Configures the service. + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @return The storage account properties. */ public VoidResponse setProperties(StorageServiceProperties properties, Duration timeout) { Mono response = storageAsyncClient.setProperties(properties); @@ -197,66 +188,53 @@ public VoidResponse setProperties(StorageServiceProperties properties, Duration } /** - * Gets a user delegation key for use with this account's blob storage. - * Note: This method call is only valid when using {@link TokenCredential} in this object's {@link HttpPipeline}. - * - * @param start - * Start time for the key's validity. Null indicates immediate start. - * @param expiry - * Expiration of the key's validity. + * Gets a user delegation key for use with this account's blob storage. Note: This method call is only valid when + * using {@link TokenCredential} in this object's {@link HttpPipeline}. * - * @return - * The user delegation key. + * @param start Start time for the key's validity. Null indicates immediate start. + * @param expiry Expiration of the key's validity. + * @return The user delegation key. */ public Response getUserDelegationKey(OffsetDateTime start, OffsetDateTime expiry) { return this.getUserDelegationKey(start, expiry, null); } /** - * Gets a user delegation key for use with this account's blob storage. - * Note: This method call is only valid when using {@link TokenCredential} in this object's {@link HttpPipeline}. + * Gets a user delegation key for use with this account's blob storage. Note: This method call is only valid when + * using {@link TokenCredential} in this object's {@link HttpPipeline}. * - * @param start - * Start time for the key's validity. Null indicates immediate start. - * @param expiry - * Expiration of the key's validity. - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. - * - * @return - * The user delegation key. + * @param start Start time for the key's validity. Null indicates immediate start. + * @param expiry Expiration of the key's validity. + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @return The user delegation key. */ public Response getUserDelegationKey(OffsetDateTime start, OffsetDateTime expiry, - Duration timeout) { + Duration timeout) { Mono> response = storageAsyncClient.getUserDelegationKey(start, expiry); return Utility.blockWithOptionalTimeout(response, timeout); } /** - * Retrieves statistics related to replication for the Blob service. It is only available on the secondary - * location endpoint when read-access geo-redundant replication is enabled for the storage account. For more - * information, see the + * Retrieves statistics related to replication for the Blob service. It is only available on the secondary location + * endpoint when read-access geo-redundant replication is enabled for the storage account. For more information, see + * the * Azure Docs. * - * @return - * The storage account statistics. + * @return The storage account statistics. */ public Response getStatistics() { return this.getStatistics(null); } /** - * Retrieves statistics related to replication for the Blob service. It is only available on the secondary - * location endpoint when read-access geo-redundant replication is enabled for the storage account. For more - * information, see the + * Retrieves statistics related to replication for the Blob service. It is only available on the secondary location + * endpoint when read-access geo-redundant replication is enabled for the storage account. For more information, see + * the * Azure Docs. * - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. - * - * @return - * The storage account statistics. + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @return The storage account statistics. */ public Response getStatistics(Duration timeout) { Mono> response = storageAsyncClient.getStatistics(); @@ -268,8 +246,7 @@ public Response getStatistics(Duration timeout) { * Returns the sku name and account kind for the account. For more information, please see the * Azure Docs. * - * @return - * The storage account info. + * @return The storage account info. */ public Response getAccountInfo() { return this.getAccountInfo(null); @@ -279,15 +256,45 @@ public Response getAccountInfo() { * Returns the sku name and account kind for the account. For more information, please see the * Azure Docs. * - * @param timeout - * An optional timeout value beyond which a {@link RuntimeException} will be raised. - * - * @return - * The storage account info. + * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. + * @return The storage account info. */ public Response getAccountInfo(Duration timeout) { Mono> response = storageAsyncClient.getAccountInfo(); return Utility.blockWithOptionalTimeout(response, timeout); } + + /** + * Generates an account SAS token with the specified parameters + * + * @param accountSASService The {@code AccountSASService} services for the account SAS + * @param accountSASResourceType An optional {@code AccountSASResourceType} resources for the account SAS + * @param accountSASPermission The {@code AccountSASPermission} permission for the account SAS + * @param expiryTime The {@code OffsetDateTime} expiry time for the account SAS + * @return A string that represents the SAS token + */ + public String generateAccountSAS(AccountSASService accountSASService, AccountSASResourceType accountSASResourceType, + AccountSASPermission accountSASPermission, OffsetDateTime expiryTime) { + return this.storageAsyncClient.generateAccountSAS(accountSASService, accountSASResourceType, accountSASPermission, expiryTime); + } + + /** + * Generates an account SAS token with the specified parameters + * + * @param accountSASService The {@code AccountSASService} services for the account SAS + * @param accountSASResourceType An optional {@code AccountSASResourceType} resources for the account SAS + * @param accountSASPermission The {@code AccountSASPermission} permission for the account SAS + * @param expiryTime The {@code OffsetDateTime} expiry time for the account SAS + * @param startTime The {@code OffsetDateTime} start time for the account SAS + * @param version The {@code String} version for the account SAS + * @param ipRange An optional {@code IPRange} ip address range for the SAS + * @param sasProtocol An optional {@code SASProtocol} protocol for the SAS + * @return A string that represents the SAS token + */ + public String generateAccountSAS(AccountSASService accountSASService, AccountSASResourceType accountSASResourceType, + AccountSASPermission accountSASPermission, OffsetDateTime expiryTime, OffsetDateTime startTime, String version, IPRange ipRange, + SASProtocol sasProtocol) { + return this.storageAsyncClient.generateAccountSAS(accountSASService, accountSASResourceType, accountSASPermission, expiryTime, startTime, version, ipRange, sasProtocol); + } } diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/StorageClientBuilder.java b/storage/client/blob/src/main/java/com/azure/storage/blob/StorageClientBuilder.java index 0aff9d26aa029..fbbdc0f438ac0 100644 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/StorageClientBuilder.java +++ b/storage/client/blob/src/main/java/com/azure/storage/blob/StorageClientBuilder.java @@ -115,14 +115,20 @@ private AzureBlobStorageBuilder buildImpl() { } /** + * Creates a {@link StorageClient} based on options set in the Builder. + * * @return a {@link StorageClient} created from the configurations in this builder. + * @throws NullPointerException If {@code endpoint} is {@code null}. */ public StorageClient buildClient() { return new StorageClient(buildAsyncClient()); } /** + * Creates a {@link StorageAsyncClient} based on options set in the Builder. + * * @return a {@link StorageAsyncClient} created from the configurations in this builder. + * @throws NullPointerException If {@code endpoint} is {@code null}. */ public StorageAsyncClient buildAsyncClient() { return new StorageAsyncClient(buildImpl()); @@ -132,23 +138,22 @@ public StorageAsyncClient buildAsyncClient() { * Sets the blob service endpoint, additionally parses it for information (SAS token, queue name) * @param endpoint URL of the service * @return the updated StorageClientBuilder object - * @throws IllegalArgumentException If {@code endpoint} is a malformed URL. + * @throws IllegalArgumentException If {@code endpoint} is {@code null} or is a malformed URL. */ public StorageClientBuilder endpoint(String endpoint) { - Objects.requireNonNull(endpoint); - URL url; try { - url = new URL(endpoint); + URL url = new URL(endpoint); this.endpoint = url.getProtocol() + "://" + url.getAuthority(); + + this.sasTokenCredential = SASTokenCredential.fromQueryParameters(URLParser.parse(url).sasQueryParameters()); + if (this.sasTokenCredential != null) { + this.tokenCredential = null; + this.sharedKeyCredential = null; + } } catch (MalformedURLException ex) { throw new IllegalArgumentException("The Azure Storage endpoint url is malformed."); } - SASTokenCredential credential = SASTokenCredential.fromQuery(url.getQuery()); - if (credential != null) { - this.credential(credential); - } - return this; } @@ -160,9 +165,10 @@ String endpoint() { * Sets the credential used to authorize requests sent to the service * @param credential authorization credential * @return the updated ContainerClientBuilder object + * @throws NullPointerException If {@code credential} is {@code null}. */ public StorageClientBuilder credential(SharedKeyCredential credential) { - this.sharedKeyCredential = credential; + this.sharedKeyCredential = Objects.requireNonNull(credential); this.tokenCredential = null; this.sasTokenCredential = null; return this; @@ -172,9 +178,10 @@ public StorageClientBuilder credential(SharedKeyCredential credential) { * Sets the credential used to authorize requests sent to the service * @param credential authorization credential * @return the updated StorageClientBuilder object + * @throws NullPointerException If {@code credential} is {@code null}. */ public StorageClientBuilder credential(TokenCredential credential) { - this.tokenCredential = credential; + this.tokenCredential = Objects.requireNonNull(credential); this.sharedKeyCredential = null; this.sasTokenCredential = null; return this; @@ -184,9 +191,10 @@ public StorageClientBuilder credential(TokenCredential credential) { * Sets the credential used to authorize requests sent to the service * @param credential authorization credential * @return the updated StorageClientBuilder object + * @throws NullPointerException If {@code credential} is {@code null}. */ public StorageClientBuilder credential(SASTokenCredential credential) { - this.sasTokenCredential = credential; + this.sasTokenCredential = Objects.requireNonNull(credential); this.sharedKeyCredential = null; this.tokenCredential = null; return this; @@ -240,9 +248,10 @@ public StorageClientBuilder connectionString(String connectionString) { * Sets the http client used to send service requests * @param httpClient http client to send requests * @return the updated StorageClientBuilder object + * @throws NullPointerException If {@code httpClient} is {@code null}. */ public StorageClientBuilder httpClient(HttpClient httpClient) { - this.httpClient = httpClient; + this.httpClient = Objects.requireNonNull(httpClient); return this; } @@ -250,9 +259,10 @@ public StorageClientBuilder httpClient(HttpClient httpClient) { * Adds a pipeline policy to apply on each request sent * @param pipelinePolicy a pipeline policy * @return the updated StorageClientBuilder object + * @throws NullPointerException If {@code pipelinePolicy} is {@code null}. */ public StorageClientBuilder addPolicy(HttpPipelinePolicy pipelinePolicy) { - this.policies.add(pipelinePolicy); + this.policies.add(Objects.requireNonNull(pipelinePolicy)); return this; } @@ -281,9 +291,10 @@ public StorageClientBuilder configuration(Configuration configuration) { * Sets the request retry options for all the requests made through the client. * @param retryOptions the options to configure retry behaviors * @return the updated StorageClientBuilder object + * @throws NullPointerException If {@code retryOptions} is {@code null}. */ public StorageClientBuilder retryOptions(RequestRetryOptions retryOptions) { - this.retryOptions = retryOptions; + this.retryOptions = Objects.requireNonNull(retryOptions); return this; } } diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/StorageRawClient.java b/storage/client/blob/src/main/java/com/azure/storage/blob/StorageRawClient.java deleted file mode 100644 index cebfdc451ae67..0000000000000 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/StorageRawClient.java +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.azure.storage.blob; - -import com.azure.core.http.HttpPipeline; -import com.azure.storage.blob.implementation.AzureBlobStorageImpl; -import com.azure.storage.blob.models.ListContainersOptions; -import com.azure.storage.blob.models.ServicesGetAccountInfoResponse; -import com.azure.storage.blob.models.ServicesGetPropertiesResponse; -import com.azure.storage.blob.models.ServicesGetStatisticsResponse; -import com.azure.storage.blob.models.ServicesGetUserDelegationKeyResponse; -import com.azure.storage.blob.models.ServicesListContainersSegmentResponse; -import com.azure.storage.blob.models.ServicesSetPropertiesResponse; -import com.azure.storage.blob.models.StorageServiceProperties; -import com.azure.storage.common.credentials.SASTokenCredential; -import reactor.core.publisher.Mono; - -import java.time.Duration; -import java.time.OffsetDateTime; - -/** - * Represents a URL to a storage service. This class does not hold any state about a particular storage account but is - * instead a convenient way of sending off appropriate requests to the resource on the service. - * It may also be used to construct URLs to blobs and containers. - * Please see here for more - * information on containers. - */ -final class StorageRawClient { - - StorageAsyncRawClient storageAsyncRawClient; - - /** - * Creates a {@code ServiceURL} object pointing to the account specified by the URL and using the provided pipeline - * to make HTTP requests. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=service_url "Sample code for ServiceURL constructor")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - StorageRawClient(AzureBlobStorageImpl azureBlobStorage) { - this.storageAsyncRawClient = new StorageAsyncRawClient(azureBlobStorage); - } - - /** - * Returns a Mono segment of containers starting from the specified Marker. - * Use an empty marker to start enumeration from the beginning. Container names are returned in lexicographic order. - * After getting a segment, process it, and then call ListContainers again (passing the the previously-returned - * Marker) to get the next segment. For more information, see - * the Azure Docs. - * - * @param marker - * Identifies the portion of the list to be returned with the next list operation. - * This value is returned in the response of a previous list operation as the - * ListContainersSegmentResponse.body().nextMarker(). Set to null to list the first segment. - * @param options - * A {@link ListContainersOptions} which specifies what data should be returned by the service. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=service_list "Sample code for ServiceURL.listContainersSegment")] \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=service_list_helper "Helper code for ServiceURL.listContainersSegment")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ServicesListContainersSegmentResponse listContainersSegment(String marker, - ListContainersOptions options) { - return this.listContainersSegment(marker, options, null); - } - - /** - * Returns a Mono segment of containers starting from the specified Marker. - * Use an empty marker to start enumeration from the beginning. Container names are returned in lexicographic order. - * After getting a segment, process it, and then call ListContainers again (passing the the previously-returned - * Marker) to get the next segment. For more information, see - * the Azure Docs. - * - * @param marker - * Identifies the portion of the list to be returned with the next list operation. - * This value is returned in the response of a previous list operation as the - * ListContainersSegmentResponse.body().nextMarker(). Set to null to list the first segment. - * @param options - * A {@link ListContainersOptions} which specifies what data should be returned by the service. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=service_list "Sample code for ServiceURL.listContainersSegment")] \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=service_list_helper "Helper code for ServiceURL.listContainersSegment")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ServicesListContainersSegmentResponse listContainersSegment(String marker, - ListContainersOptions options, Duration timeout) { - Mono response = storageAsyncRawClient.listContainersSegment(marker, options); - return Utility.blockWithOptionalTimeout(response, timeout); - } - - /** - * Gets the properties of a storage account’s Blob service. For more information, see the - * Azure Docs. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=service_getsetprops "Sample code for ServiceURL.getProperties")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ServicesGetPropertiesResponse getProperties() { - return this.getProperties(null); - } - - /** - * Gets the properties of a storage account’s Blob service. For more information, see the - * Azure Docs. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=service_getsetprops "Sample code for ServiceURL.getProperties")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ServicesGetPropertiesResponse getProperties(Duration timeout) { - Mono response = storageAsyncRawClient.getProperties(); - return Utility.blockWithOptionalTimeout(response, timeout); - } - - /** - * Sets properties for a storage account's Blob service endpoint. For more information, see the - * Azure Docs. - * Note that setting the default service version has no effect when using this client because this client explicitly - * sets the version header on each request, overriding the default. - * - * @param properties - * Configures the service. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=service_getsetprops "Sample code for ServiceURL.setProperties")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ServicesSetPropertiesResponse setProperties(StorageServiceProperties properties) { - return this.setProperties(properties, null); - } - - /** - * Sets properties for a storage account's Blob service endpoint. For more information, see the - * Azure Docs. - * Note that setting the default service version has no effect when using this client because this client explicitly - * sets the version header on each request, overriding the default. - * - * @param properties - * Configures the service. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=service_getsetprops "Sample code for ServiceURL.setProperties")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ServicesSetPropertiesResponse setProperties(StorageServiceProperties properties, Duration timeout) { - Mono response = storageAsyncRawClient.setProperties(properties); - return Utility.blockWithOptionalTimeout(response, timeout); - } - - /** - * Gets a user delegation key for use with this account's blob storage. - * Note: This method call is only valid when using {@link SASTokenCredential} in this object's {@link HttpPipeline}. - * - * @param start - * Start time for the key's validity. Null indicates immediate start. - * @param expiry - * Expiration of the key's validity. - * - * @return Emits the successful response. - */ - public ServicesGetUserDelegationKeyResponse getUserDelegationKey(OffsetDateTime start, OffsetDateTime expiry) { - return this.getUserDelegationKey(start, expiry, null); - } - - /** - * Gets a user delegation key for use with this account's blob storage. - * Note: This method call is only valid when using {@link SASTokenCredential} in this object's {@link HttpPipeline}. - * - * @param start - * Start time for the key's validity. Null indicates immediate start. - * @param expiry - * Expiration of the key's validity. - * - * @return Emits the successful response. - */ - public ServicesGetUserDelegationKeyResponse getUserDelegationKey(OffsetDateTime start, OffsetDateTime expiry, - Duration timeout) { - Mono response = storageAsyncRawClient.getUserDelegationKey(start, expiry); - return Utility.blockWithOptionalTimeout(response, timeout); - } - - /** - * Retrieves statistics related to replication for the Blob service. It is only available on the secondary - * location endpoint when read-access geo-redundant replication is enabled for the storage account. For more - * information, see the - * Azure Docs. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=service_stats "Sample code for ServiceURL.getStats")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ServicesGetStatisticsResponse getStatistics() { - return this.getStatistics(null); - } - - /** - * Retrieves statistics related to replication for the Blob service. It is only available on the secondary - * location endpoint when read-access geo-redundant replication is enabled for the storage account. For more - * information, see the - * Azure Docs. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=service_stats "Sample code for ServiceURL.getStats")] \n - * For more samples, please see the [Samples file](%https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ServicesGetStatisticsResponse getStatistics(Duration timeout) { - Mono response = storageAsyncRawClient.getStatistics(); - return Utility.blockWithOptionalTimeout(response, timeout); - } - - /** - * Returns the sku name and account kind for the account. For more information, please see the - * Azure Docs. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=account_info "Sample code for ServiceURL.getAccountInfo")] \n - * For more samples, please see the [Samples file] (https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ServicesGetAccountInfoResponse getAccountInfo() { - return this.getAccountInfo(null); - } - - /** - * Returns the sku name and account kind for the account. For more information, please see the - * Azure Docs. - * - * @return Emits the successful response. - * - * @apiNote ## Sample Code \n - * [!code-java[Sample_Code](../azure-storage-java/src/test/java/com/microsoft/azure/storage/Samples.java?name=account_info "Sample code for ServiceURL.getAccountInfo")] \n - * For more samples, please see the [Samples file] (https://github.com/Azure/azure-storage-java/blob/master/src/test/java/com/microsoft/azure/storage/Samples.java) - */ - public ServicesGetAccountInfoResponse getAccountInfo(Duration timeout) { - Mono response = storageAsyncRawClient.getAccountInfo(); - return Utility.blockWithOptionalTimeout(response, timeout); - } -} diff --git a/storage/client/blob/src/main/java/com/azure/storage/blob/Utility.java b/storage/client/blob/src/main/java/com/azure/storage/blob/Utility.java index 3b5d073c4023e..252a5e83bbaf2 100644 --- a/storage/client/blob/src/main/java/com/azure/storage/blob/Utility.java +++ b/storage/client/blob/src/main/java/com/azure/storage/blob/Utility.java @@ -5,9 +5,13 @@ import com.azure.core.http.HttpHeader; import com.azure.core.http.HttpHeaders; +import com.azure.core.http.HttpPipeline; +import com.azure.core.http.policy.HttpPipelinePolicy; import com.azure.core.implementation.http.UrlBuilder; import com.azure.storage.blob.models.StorageErrorException; import com.azure.storage.blob.models.UserDelegationKey; +import com.azure.storage.common.credentials.SharedKeyCredential; +import com.azure.storage.common.policy.SharedKeyCredentialPolicy; import reactor.core.publisher.Mono; import reactor.util.annotation.Nullable; @@ -387,4 +391,23 @@ static T blockWithOptionalTimeout(Mono response, @Nullable Duration timeo return response.block(timeout); } } + + /** + * Gets the SharedKeyCredential from the HttpPipeline + * + * @param httpPipeline + * The {@code HttpPipeline} httpPipeline from which a sharedKeyCredential will be extracted + * + * @return The {@code SharedKeyCredential} sharedKeyCredential in the httpPipeline + */ + static SharedKeyCredential getSharedKeyCredential(HttpPipeline httpPipeline) { + for (int i = 0; i < httpPipeline.getPolicyCount(); i++) { + HttpPipelinePolicy httpPipelinePolicy = httpPipeline.getPolicy(i); + if (httpPipelinePolicy instanceof SharedKeyCredentialPolicy) { + SharedKeyCredentialPolicy sharedKeyCredentialPolicy = (SharedKeyCredentialPolicy) httpPipelinePolicy; + return sharedKeyCredentialPolicy.sharedKeyCredential(); + } + } + return null; + } } diff --git a/storage/client/blob/src/main/java/com/azure/storage/common/credentials/SASTokenCredential.java b/storage/client/blob/src/main/java/com/azure/storage/common/credentials/SASTokenCredential.java index fe93273e3f64e..8a18f449c8c9f 100644 --- a/storage/client/blob/src/main/java/com/azure/storage/common/credentials/SASTokenCredential.java +++ b/storage/client/blob/src/main/java/com/azure/storage/common/credentials/SASTokenCredential.java @@ -4,33 +4,20 @@ package com.azure.storage.common.credentials; import com.azure.core.implementation.util.ImplUtils; - -import java.util.HashMap; +import com.azure.storage.blob.SASQueryParameters; /** * Holds a SAS token used for authenticating requests. */ public final class SASTokenCredential { - // Required SAS token pieces - private static final String SIGNED_VERSION = "sv"; - private static final String SIGNED_SERVICES = "ss"; - private static final String SIGNED_RESOURCE_TYPES = "srt"; - private static final String SIGNED_PERMISSIONS = "sp"; - private static final String SIGNED_EXPIRY = "se"; - private static final String SIGNATURE = "sig"; - - // Optional SAS token pieces - private static final String SIGNED_START = "st"; - private static final String SIGNED_PROTOCOL = "spr"; - private static final String SIGNED_IP = "sip"; - private final String sasToken; /** * Creates a SAS token credential from the passed SAS token. + * * @param sasToken SAS token used to authenticate requests with the service. */ - public SASTokenCredential(String sasToken) { + private SASTokenCredential(String sasToken) { this.sasToken = sasToken; } @@ -42,55 +29,31 @@ public String sasToken() { } /** - * Creates a SAS token credential from the passed URL query string - * @param query URL query used to build the SAS token - * @return a SAS token credential if the query param contains all the necessary pieces + * Creates a SAS token credential from the passed SAS token. + * + * @param sasToken SAS token + * @return a SAS token credential if {@code sasToken} is not {@code null} or empty, otherwise null. */ - public static SASTokenCredential fromQuery(String query) { - if (ImplUtils.isNullOrEmpty(query)) { + public static SASTokenCredential fromSASTokenString(String sasToken) { + if (ImplUtils.isNullOrEmpty(sasToken)) { return null; } - HashMap queryParams = new HashMap<>(); - for (String queryParam : query.split("&")) { - String key = queryParam.split("=", 2)[0]; - queryParams.put(key, queryParam); - } + return new SASTokenCredential(sasToken); + } - if (queryParams.size() < 6 - || !queryParams.containsKey(SIGNED_VERSION) - || !queryParams.containsKey(SIGNED_SERVICES) - || !queryParams.containsKey(SIGNED_RESOURCE_TYPES) - || !queryParams.containsKey(SIGNED_PERMISSIONS) - || !queryParams.containsKey(SIGNED_EXPIRY) - || !queryParams.containsKey(SIGNATURE)) { + /** + * Creates a SAS token credential from the passed {@link SASQueryParameters}. + * + * @param queryParameters SAS token query parameters object + * @return a SAS token credential if {@code queryParameters} is not {@code null} and has + * {@link SASQueryParameters#signature() signature} set, otherwise returns {@code null}. + */ + public static SASTokenCredential fromQueryParameters(SASQueryParameters queryParameters) { + if (queryParameters == null || ImplUtils.isNullOrEmpty(queryParameters.signature())) { return null; } - StringBuilder sasTokenBuilder = new StringBuilder(queryParams.get(SIGNED_VERSION)) - .append("&").append(queryParams.get(SIGNED_SERVICES)) - .append("&").append(queryParams.get(SIGNED_RESOURCE_TYPES)) - .append("&").append(queryParams.get(SIGNED_PERMISSIONS)); - - // SIGNED_START is optional - if (queryParams.containsKey(SIGNED_START)) { - sasTokenBuilder.append("&").append(queryParams.get(SIGNED_START)); - } - - sasTokenBuilder.append("&").append(queryParams.get(SIGNED_EXPIRY)); - - // SIGNED_IP is optional - if (queryParams.containsKey(SIGNED_IP)) { - sasTokenBuilder.append("&").append(queryParams.get(SIGNED_IP)); - } - - // SIGNED_PROTOCOL is optional - if (queryParams.containsKey(SIGNED_PROTOCOL)) { - sasTokenBuilder.append("&").append(queryParams.get(SIGNED_PROTOCOL)); - } - - sasTokenBuilder.append("&").append(queryParams.get(SIGNATURE)); - - return new SASTokenCredential(sasTokenBuilder.toString()); + return new SASTokenCredential(queryParameters.encode()); } } diff --git a/storage/client/blob/src/main/java/com/azure/storage/common/policy/SharedKeyCredentialPolicy.java b/storage/client/blob/src/main/java/com/azure/storage/common/policy/SharedKeyCredentialPolicy.java index 8ee1284591dd2..e8b54914cb762 100644 --- a/storage/client/blob/src/main/java/com/azure/storage/common/policy/SharedKeyCredentialPolicy.java +++ b/storage/client/blob/src/main/java/com/azure/storage/common/policy/SharedKeyCredentialPolicy.java @@ -18,12 +18,20 @@ public final class SharedKeyCredentialPolicy implements HttpPipelinePolicy { /** * Creates a SharedKey pipeline policy that adds the SharedKey into the request's authorization header. + * * @param credential the SharedKey credential used to create the policy. */ public SharedKeyCredentialPolicy(SharedKeyCredential credential) { this.credential = credential; } + /** + * @return the {@link SharedKeyCredential} linked to the policy. + */ + public SharedKeyCredential sharedKeyCredential() { + return this.credential; + } + @Override public Mono process(HttpPipelineCallContext context, HttpPipelineNextPolicy next) { String authorizationValue = credential.generateAuthorizationHeader(context.httpRequest().url(), diff --git a/storage/client/blob/src/samples/java/FileTransferExample.java b/storage/client/blob/src/samples/java/FileTransferExample.java index ff3b9376bfc50..cfd281c1e3f8c 100644 --- a/storage/client/blob/src/samples/java/FileTransferExample.java +++ b/storage/client/blob/src/samples/java/FileTransferExample.java @@ -9,7 +9,6 @@ import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.net.URL; @@ -139,7 +138,7 @@ private static File createTempEmptyFile(String fileName) throws IOException { } } - private static void createTempFileWithFileSize(File f, long size) throws FileNotFoundException, IOException { + private static void createTempFileWithFileSize(File f, long size) throws IOException { RandomAccessFile raf = new RandomAccessFile(f, "rw"); raf.setLength(size); raf.close(); @@ -148,7 +147,7 @@ private static void createTempFileWithFileSize(File f, long size) throws FileNot private static void checkTwoFilesAreTheSame(File f1, File f2) throws IOException, NoSuchAlgorithmException { String checksumUpload = getFileChecksum(f1); String checksumDownload = getFileChecksum(f2); - if (checksumUpload.equals(checksumDownload)) { + if (!checksumUpload.equals(checksumDownload)) { throw new RuntimeException("The file upload does not match the file download."); } } diff --git a/storage/client/blob/src/test/java/com/azure/storage/blob/APISpec.groovy b/storage/client/blob/src/test/java/com/azure/storage/blob/APISpec.groovy index c59095e5f66c6..fb306c17ff358 100644 --- a/storage/client/blob/src/test/java/com/azure/storage/blob/APISpec.groovy +++ b/storage/client/blob/src/test/java/com/azure/storage/blob/APISpec.groovy @@ -7,10 +7,8 @@ import com.azure.core.http.* import com.azure.core.http.policy.HttpLogDetailLevel import com.azure.core.http.policy.HttpPipelinePolicy import com.azure.core.http.rest.Response -import com.azure.core.util.Context import com.azure.core.util.configuration.ConfigurationManager import com.azure.identity.credential.EnvironmentCredential -import com.azure.storage.blob.BlobProperties import com.azure.storage.blob.models.* import com.azure.storage.common.credentials.SharedKeyCredential import org.junit.Assume @@ -42,8 +40,6 @@ class APISpec extends Specification { static final ByteBuffer defaultData = ByteBuffer.wrap(defaultText.getBytes(StandardCharsets.UTF_8)) - static final Flux defaultFlux = Flux.just(defaultData) - static final Supplier defaultInputStream = new Supplier() { @Override InputStream get() { @@ -112,10 +108,6 @@ class APISpec extends Specification { */ static final String defaultContextKey = "Key" - static final String defaultContextValue = "Value" - - static final Context defaultContext = new Context(defaultContextKey, defaultContextValue) - static String getTestName(ISpecificationContext ctx) { return ctx.getCurrentFeature().name.replace(' ', '').toLowerCase() } @@ -271,11 +263,13 @@ class APISpec extends Specification { } catch (Exception e) { } + try { blobStorageServiceURL = getGenericServiceURL(getGenericCreds("BLOB_STORAGE_")) } catch (Exception e) { } + try { premiumServiceURL = getGenericServiceURL(getGenericCreds("PREMIUM_STORAGE_")) } diff --git a/storage/client/blob/src/test/java/com/azure/storage/blob/BlobAPITest.groovy b/storage/client/blob/src/test/java/com/azure/storage/blob/BlobAPITest.groovy index 0cf250d85d174..d0be4d532642a 100644 --- a/storage/client/blob/src/test/java/com/azure/storage/blob/BlobAPITest.groovy +++ b/storage/client/blob/src/test/java/com/azure/storage/blob/BlobAPITest.groovy @@ -7,7 +7,6 @@ import com.azure.core.http.HttpHeaders import com.azure.core.http.rest.Response import com.azure.core.http.rest.VoidResponse import com.azure.core.implementation.util.ImplUtils -import com.azure.storage.blob.BlobProperties import com.azure.storage.blob.models.* import spock.lang.Unroll @@ -241,7 +240,7 @@ class BlobAPITest extends APISpec { .endpoint(bu.getBlobUrl().toString()) .snapshot(snapshot) .credential(primaryCreds) - .buildClient() + .buildBlobClient() ByteArrayOutputStream snapshotStream = new ByteArrayOutputStream() bu3.download(snapshotStream) snapshotStream.toByteArray() == originalStream.toByteArray() @@ -987,7 +986,7 @@ class BlobAPITest extends APISpec { .endpoint(bu.getBlobUrl().toString()) .credential(primaryCreds) .snapshot(snapshotResponse.value()) - .buildClient() + .buildBlobClient() then: bu2.getProperties().statusCode() == 200 @@ -1015,7 +1014,7 @@ class BlobAPITest extends APISpec { .endpoint(bu.getBlobUrl().toString()) .credential(primaryCreds) .snapshot(response.value()) - .buildClient() + .buildBlobClient() expect: response.statusCode() == 201 diff --git a/storage/client/blob/src/test/java/com/azure/storage/blob/ContainerAPITest.groovy b/storage/client/blob/src/test/java/com/azure/storage/blob/ContainerAPITest.groovy index 9b28d87cd7b31..86da9838d53bb 100644 --- a/storage/client/blob/src/test/java/com/azure/storage/blob/ContainerAPITest.groovy +++ b/storage/client/blob/src/test/java/com/azure/storage/blob/ContainerAPITest.groovy @@ -1539,11 +1539,11 @@ class ContainerAPITest extends APISpec { cu.create() } - AppendBlobClient bu = new AppendBlobClientBuilder() + AppendBlobClient bu = new BlobClientBuilder() .credential(primaryCreds) .endpoint("http://" + primaryCreds.accountName() + ".blob.core.windows.net/\$root/rootblob") .httpClient(getHttpClient()) - .buildClient() + .buildAppendBlobClient() when: Response createResponse = bu.create() @@ -1565,11 +1565,11 @@ class ContainerAPITest extends APISpec { cu.create() } - AppendBlobClient bu = new AppendBlobClientBuilder() + AppendBlobClient bu = new BlobClientBuilder() .credential(primaryCreds) .endpoint("http://" + primaryCreds.accountName() + ".blob.core.windows.net/rootblob") .httpClient(getHttpClient()) - .buildClient() + .buildAppendBlobClient() when: Response createResponse = bu.create() diff --git a/storage/client/blob/src/test/java/com/azure/storage/blob/SASTest.groovy b/storage/client/blob/src/test/java/com/azure/storage/blob/SASTest.groovy new file mode 100644 index 0000000000000..2824ac1d3067d --- /dev/null +++ b/storage/client/blob/src/test/java/com/azure/storage/blob/SASTest.groovy @@ -0,0 +1,1079 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.blob + +import com.azure.storage.blob.models.AccessPolicy +import com.azure.storage.blob.models.BlobRange +import com.azure.storage.blob.models.SignedIdentifier +import com.azure.storage.blob.models.StorageErrorCode +import com.azure.storage.blob.models.UserDelegationKey +import com.azure.storage.common.credentials.SASTokenCredential +import com.azure.storage.common.credentials.SharedKeyCredential +import spock.lang.Unroll + +import java.time.LocalDateTime +import java.time.OffsetDateTime +import java.time.ZoneOffset + +class SASTest extends APISpec { + + def "responseError"() { + when: + cu.listBlobsFlat() + + then: + def e = thrown(StorageException) + e.errorCode() == StorageErrorCode.INVALID_QUERY_PARAMETER_VALUE + e.statusCode() == 400 + e.message().contains("Value for one of the query parameters specified in the request URI is invalid.") + e.getMessage().contains(" testdata = Flux.just(ByteBuffer.wrap(message)); - + Flux testdata = Flux.just(ByteBufAllocator.DEFAULT.buffer(message.length).writeBytes(message)); finished = finished.and(blobClient.upload(testdata, message.length) .then(Mono.defer(() -> { diff --git a/storage/client/file/README.md b/storage/client/file/README.md new file mode 100644 index 0000000000000..db523e4d3e440 --- /dev/null +++ b/storage/client/file/README.md @@ -0,0 +1,478 @@ +# Azure File client library for Java +The Server Message Block (SMB) protocol is the preferred file share protocol used on-premises today. +The Microsoft Azure File service enables customers to leverage the availability and scalability of Azure’s Cloud Infrastructure as a Service (IaaS) SMB without having to rewrite SMB client applications. + +Files stored in Azure File service shares are accessible via the SMB protocol, and also via REST APIs. +The File service offers the following four resources: the storage account, shares, directories, and files. +Shares provide a way to organize sets of files and also can be mounted as an SMB file share that is hosted in the cloud. + +[Source code][source_code] | [Package (Maven)][package] | [API reference documentation][api_documentation] | [Product documentation][azconfig_docs] + +## Getting started + +### Prerequisites + +- [Java Development Kit (JDK)][jdk] with version 8 or above +- [Azure Subscription][azure_subscription] +- [Create Strorage Account][storage_account] + +### Adding the package to your product + +```xml + + com.azure + azure-storage-file + 12.0.0-preview.1 + +``` + +### Create a Storage Account +To create a Storage Account you can use the Azure Portal or [Azure CLI][azure_cli]. + +```Powershell +az group create \ + --name storage-resource-group \ + --location westus +``` + +### Authenticate the client + +In order to interact with the Storage service (File Service, Share, Directory, MessageId, File) you'll need to create an instance of the Service Client class. +To make this possible you'll need the Account SAS (shared access signature) string of Storage account. Learn more at [SAS Token][sas_token] + +#### Get Credentials + +- **SAS Token** + +a. Use the [Azure CLI][azure_cli] snippet below to get the SAS token from the Storage account. + +```Powershell +az storage file generate-sas + --name {account name} + --expiry {date/time to expire SAS token} + --permission {permission to grant} + --connection-string {connection string of the storage account} +``` + +```Powershell +CONNECTION_STRING= + +az storage file generate-sas + --name javasdksas + --expiry 2019-06-05 + --permission rpau + --connection-string $CONNECTION_STRING +``` +b. Alternatively, get the Account SAS Token from the Azure Portal. +``` +Go to your storage account -> Shared access signature -> Click on Generate SAS and connection string +``` + +- **Shared Key Credential** + +a. Use account name and account key. Account name is your storage account name. +``` +// Here is where we get the key +Go to your storage account -> Access keys -> Key 1/ Key 2 -> Key +``` +b. Use the connection string +``` +// Here is where we get the key +Go to your storage account -> Access Keys -> Keys 1/ Key 2 -> Connection string +``` +## Key concepts +### URL format +Files are addressable using the following URL format: +``` +https://.file.core.windows.net/ +``` +The following URL addresses a queue in the diagram: +``` +https://myaccount.file.core.windows.net/images-to-download +``` + +#### Resource URI Syntax +For the storage account, the base URI for queue operations includes the name of the account only: +``` +https://myaccount.file.core.windows.net +``` +For file, the base URI includes the name of the account and the name of the directory/file: +``` +https://myaccount.file.core.windows.net/myshare/mydirectorypath/myfile +``` + +### Handling Exceptions +```java +TODO +``` + +### Resource Names +The URI to reference a share, directory or file must be unique. Within a given storage account, every share must have a unique name. Every file within a given share or directory must also have a unique name within that share or directory. + +If you attempt to create a share, directory, or file with a name that violates naming rules, the request will fail with status code 400 (Bad Request). + +### Share Names +The rules for File service share names are more restrictive than what is prescribed by the SMB protocol for SMB share names, so that the Blob and File services can share similar naming conventions for containers and shares. The naming restrictions for shares are as follows: + +1. A share name must be a valid DNS name. +1. Share names must start with a letter or number, and can contain only letters, numbers, and the dash (-) character. +1. Every dash (-) character must be immediately preceded and followed by a letter or number; consecutive dashes are not permitted in share names. +1. All letters in a share name must be lowercase. +1. Share names must be from 3 through 63 characters long. + +### Directory and File Names +The Azure File service naming rules for directory and file names are as follows: + +1. Directory and file names are case-preserving and case-insensitive. +1. Directory and file component names must be no more than 255 characters in length. +1. Directory names cannot end with the forward slash character (/). If provided, it will be automatically removed. +1. File names must not end with the forward slash character (/). +1. Reserved URL characters must be properly escaped. +1. The following characters are not allowed: " \ / : | < > * ? +1. Illegal URL path characters not allowed. Code points like \uE000, while valid in NTFS filenames, are not valid Unicode characters. In addition, some ASCII or Unicode characters, like control characters (0x00 to 0x1F, \u0081, etc.), are also not allowed. For rules governing Unicode strings in HTTP/1.1 see [RFC 2616, Section 2.2: Basic Rules][RFC_URL_1] and [RFC 3987][RFL_URL_2]. +1. The following file names are not allowed: LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, PRN, AUX, NUL, CON, CLOCK$, dot character (.), and two dot characters (..). + +### Metadata Names +Metadata for a share or file resource is stored as name-value pairs associated with the resource. Directories do not have metadata. Metadata names must adhere to the naming rules for [C# identifiers][C_identifiers]. + +Note that metadata names preserve the case with which they were created, but are case-insensitive when set or read. If two or more metadata headers with the same name are submitted for a resource, the Azure File service returns status code 400 (Bad Request). + +### File Services +The File Service REST API provides operations on accounts and manage file service properties. It allows the operations of listing and deleting shares, getting and setting file service properties. +Once you have the SASToken, you can construct the file service client with `${accountName}`, `${sasToken}` + +``` +String fileServiceURL = String.format("https://%s.file.core.windows.net", accountName); +FileServiceClient fileServiceClient = new FileServiceClientBuilder().endpoint(fileServiceURL) + .credential(sasToken).buildClient(); +``` + +### Share +The share resource includes metadata and properties for that share. It allows the opertions of creating, creating snapshot, deleting shares, getting share properties, setting metadata, getting and setting ACL (Access policy). +Once you have the SASToken, you can construct the file service client with `${accountName}`, `${shareName}`, `${sasToken}` + +``` +String shareURL = String.format("https://%s.file.core.windows.net", accountName); +ShareClient shareClient = new ShareClientBuilder().endpoint(shareURL) + .credential(sasToken).shareName(shareName).buildClient(); +``` + +### Directory + The directory resource includes the properties for that directory. It allows the operations of creating, listing, deleting directories or subdirectories or files, getting properties, setting metadata, listing and force closing the handles. + Once you have the SASToken, you can construct the file service client with `${accountName}`, `${shareName}`, `${directoryPath}`, `${sasToken}` + + ``` + String directoryURL = String.format("https://%s.file.core.windows.net/%s%s", accountName, shareName, directoryPath, sasToken); + DirectoryClient directoryClient = new DirectoryClientBuilder().endpoint(directoryURL) + .credential(sasToken).shareName(shareName).directoryName(directoryPath).buildClient(); + ``` +### File + The file resource includes the properties for that file. It allows the operations of creating, uploading, copying, downloading, deleting files or range of the files, getting properties, setting metadata, listing and force closing the handles. + Once you have the SASToken, you can construct the file service client with `${accountName}`, `${shareName}`, `${directoryPath}`, `${fileName}`, `${sasToken}` + + ``` + String fileURL = String.format("https://%s.file.core.windows.net", accountName); + FileClient fileClient = new FileClientBuilder().endpoint(fileURL) + .credential(sasToken).shareName(shareName).filePath(directoryPath + "/" + fileName).buildClient(); + ``` + +## Examples + +The following sections provide several code snippets covering some of the most common Configuration Service tasks, including: +- [Create a Share](#Create-a-share) +- [Create a snapshot on Share](#Create-a-snapshot-on-share) +- [Create a Directory](#Create-a-directory) +- [Create a Subdirectory](#Create-a-subdirectory) +- [Create a File](#Create-a-file) +- [List all Shares](#List-all-shares) +- [List all Subdirectories and Files](#List-all-subdirectories-and-files) +- [List all ranges on file](#List-all-ranges-on-file) +- [Delete a Share](#Delete-a-share) +- [Delete a Directory](#Delete-a-directory) +- [Delete a Subdirectory](#Delete-a-subdirectory) +- [Delete a File](#Delete-a-file) +- [Copy a File](#Copy-a-file) +- [Abort copy a File](#Abort-copy-a-file) +- [Upload data to Storage File](#Upload-data-to-storage) +- [Upload file to Storage File](#Upload-file-to-storage) +- [Download data from Storage File](#Download-data-from-storage) +- [Download file from Storage File](#Download-file-from-storage) +- [Get a File Service property](#Get-a-file-service-property) +- [Set a File Service property](#set-a-file-service-property) +- [Set a Share metadata](#Set-a-share-metadata) +- [Get a Share access policy](#Get-a-share-access-policy) +- [Set a Share access policy](#Set-a-share-access-policy) +- [Get handles on Directory and File](#Get-handles-on-directory-file) +- [Force close handles on handle id](#Force-close-handles-on-handle-id) +- [Set quota on Share](#Set-quota-on-share) +- [Set file httpHeaders](#Set-file-httpheaders) + +### Create a share +Create a share in the Storage Account. Throws StorageErrorException If the share fails to be created. +Taking a FileServiceClient in KeyConcept, [`${fileServiceClient}`](#File-services) . + +```Java +String shareName = "testshare"; +fileServiceClient.createShare(shareName); +``` + +### Create a snapshot on Share +Taking a FileServiceClient in KeyConcept, [`${fileServiceClient}`](#File-services) . + +```Java +String shareName = "testshare"; +ShareClient shareClient = fileServiceClient.getShareClient(shareName); +shareClient.createSnapshot(); +``` + +### Create a directory +Taking the [`${shareClient}](#Create-snapshot-on-share) initialized above, [`${shareClient}`](#Share) . + +```Java +String dirName = "testdir"; +shareClient.createDirectory(dirName); +``` + +### Create a subdirectory +Taking the directoryClient in KeyConcept, [`${directoryClient}`](#Directory) . + +```Java +String subDirName = "testsubdir"; +directoryClient.createSubDirectory(subDirName); +``` + +### Create a File +Taking the directoryClient in KeyConcept, [`${directoryClient}`](#Directory) . + +```Java +String fileName = "testfile"; +directoryClient.createFile(fileName); +``` + +### List all Shares +Taking the fileServiceClient in KeyConcept, [`${fileServiceClient}`](#File-services) + +```Java +fileServiceClient.listShares(); +``` + +### Create all subdirectories and files +Taking the directoryClient in KeyConcept, [`${directoryClient}`](#Directory) + +```Java +directoryClient.listFilesAndDirectories(); +``` + +### List all ranges on file +Taking the fileClient in KeyConcept, [`${fileClient}`](#File) + +```Java +fileClient.listRanges(); +``` + +### Delete a share +Taking the shareClient in KeyConcept, [`${shareClient}`](#Share) + +```Java +shareClient.delete(); +``` + +### Delete a directory +Taking the shareClient in KeyConcept, [`${shareClient}`](#Share) . + +```Java +String dirName = "testdir"; +shareClient.deleteDirectory(dirName) +``` + +### Delete a subdirectory +Taking the directoryClient in KeyConcept, [`${directoryClient}`](#Directory) . + +```Java +String subDirName = "testsubdir"; +directoryClient.deleteSubDirectory(subDirName) +``` + +### Delete a file +Taking the directoryClient in KeyConcept, [`${directoryClient}`](#Directory) . + +```Java +String fileName = "testfile"; +directoryClient.deleteFile(fileName) +``` + +### Copy a file +Taking the fileClient in KeyConcept, [`${fileClient}`](#File) with string of source URL. + +```Java +String sourceURL = "https://myaccount.file.core.windows.net/myshare/myfile"; +Response copyInfoResponse = fileClient.startCopy(sourceURL, null); +``` + +### Abort copy a file +Taking the fileClient in KeyConcept, [`${fileClient}`](#File) with the copy info response returned above `${copyId}=[copyInfoResponse](#Copy-a-file)`. + +```Java +String copyId = copyInfoResponse.value().copyId(); +fileClient.abortCopy(copyId); +``` + +### Upload data to storage +Taking the fileClient in KeyConcept, [`${fileClient}`](#File) with data of "default" . + +```Java +ByteBuf data = Unpooled.wrappedBuffer("default".getBytes(StandardCharsets.UTF_8)); +fileClient.upload(data, data.readableBytes()); +``` + +### Upload file to storage +Taking the fileClient in KeyConcept, [`${fileClient}`](#File) . +```Java +String filePath = "/mydir/myfile"; +fileClient.uploadFromFile(filePath); +``` + +### Download data from file range +Taking the fileClient in KeyConcept, [`${fileClient}`](#File) with the range from 1024 to 2048. +```Java +FileRange fileRange = new FileRange(1024, 2047); +fileClient.downloadWithProperties(fileRange, false); +``` + +### Download file from storage +Taking the fileClient in KeyConcept, [`${fileClient}`](#File) and download to the file of filePath. +```Java +String filePath = "/mydir/myfile"; +fileClient.downloadToFile(filePath); +``` + +### Get a file service properties +Taking a FileServiceClient in KeyConcept, [`${fileServiceClient}`](#File-services) . + +```Java +fileServiceClient.getProperties(); +``` + +### Set a file service properties +Taking a FileServiceClient in KeyConcept, [`${fileServiceClient}`](#File-services) . + +```Java +FileServiceProperties properties = fileServiceClient.getProperties().value(); + +properties.minuteMetrics().enabled(true); +properties.hourMetrics().enabled(true); + +VoidResponse response = fileServiceClient.setProperties(properties); +``` + +### Set a share metadata +Taking the shareClient in KeyConcept, [`${shareClient}`](#Share) . + +```Java +Map metadata = Collections.singletonMap("directory", "metadata"); +shareClient.setMetadata(metadata); +``` + +### Get a share access policy +Taking the shareClient in KeyConcept, [`${shareClient}`](#Share) + +```Java +shareClient.getAccessPolicy(); +``` + +### Set a share access policy +Taking the shareClient in KeyConcept, [`${shareClient}`](#Share) . + +```Java +AccessPolicy accessPolicy = new AccessPolicy().permission("r") + .start(OffsetDateTime.now(ZoneOffset.UTC)) + .expiry(OffsetDateTime.now(ZoneOffset.UTC).plusDays(10)); + +SignedIdentifier permission = new SignedIdentifier().id("mypolicy").accessPolicy(accessPolicy); +shareClient.setAccessPolicy(Collections.singletonList(permission)); +``` + +### Get handles on directory file +Taking the directoryClient in KeyConcept, [`${directoryClient}`](#Directory) + +```Java +Iterable handleItems = directoryClient.getHandles(null, true); +``` + +### Force close handles on handle id +Taking the directoryClient in KeyConcept, [`${directoryClient}`](#Directory) and the handle id returned above `${handleId}=[handleItems](#Get-handles-on-directory-file)` + +```Java +String handleId = result.iterator().next().handleId(); +directoryClient.forceCloseHandles(handleId); +``` + +### Set quota on share +Taking the shareClient in KeyConcept, [`${shareClient}`](#Share) . + +```Java +int quotaOnGB = 1; +shareClient.setQuota(quotaOnGB); +``` + +### Set file httpheaders +Taking the fileClient in KeyConcept, [`${fileClient}`](#File) . + +```Java +FileHTTPHeaders httpHeaders = new FileHTTPHeaders().fileContentType("text/plain"); +fileClient.setHttpHeaders(httpHeaders); +``` + +## Troubleshooting + +## General + +When you interact with file using this Java client library, errors returned by the service correspond to the same HTTP status codes returned for [REST API][storage_file_rest] requests. For example, if you try to retrieve a share that doesn't exist in your Storage Account, a `404` error is returned, indicating `Not Found`. + +## Next steps + +### More Samples +- [FileServiceSample](src/samples/java/file/FileServiceSample.java) +- [ShareSample](src/samples/java/file/ShareSample.java) +- [DirectorySample](src/samples/java/file/DirectorySample.java) +- [FileSample](src/samples/java/file/FileSample.java) +- [AsyncSample](src/samples/java/file/AsyncSample.java) + +[Quickstart: Create a Java Spring app with App Configuration](https://docs.microsoft.com/en-us/azure/azure-app-configuration/quickstart-java-spring-app) + +## Contributing +This project welcomes contributions and suggestions. Most contributions require you to agree to a +Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us +the rights to use your contribution. For details, visit https://cla.microsoft.com. + +When you submit a pull request, a CLA-bot will automatically determine whether you need to provide +a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions +provided by the bot. You will only need to do this once across all repos using our CLA. + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or +contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +If you would like to become an active contributor to this project please follow the instructions provided in [Microsoft Azure Projects Contribution Guidelines](http://azure.github.io/guidelines.html). + +1. Fork it +2. Create your feature branch (`git checkout -b my-new-feature`) +3. Commit your changes (`git commit -am 'Add some feature'`) +4. Push to the branch (`git push origin my-new-feature`) +5. Create new Pull Request + + +[source_code]: to-be-continue +[package]: to-be-continue +[api_documentation]: https://docs.microsoft.com/en-us/rest/api/storageservices/file-service-rest-api +[storage_docs]: https://docs.microsoft.com/en-us/azure/storage/files/storage-files-introduction +[jdk]: https://docs.microsoft.com/en-us/java/azure/java-supported-jdk-runtime?view=azure-java-stable +[maven]: https://maven.apache.org/ +[azure_subscription]: https://azure.microsoft.com/en-us/free/ +[storage_account]: https://docs.microsoft.com/en-us/azure/storage/common/storage-quickstart-create-account?tabs=azure-portal +[azure_cli]: https://docs.microsoft.com/cli/azure +[sas_token]: https://docs.microsoft.com/en-us/azure/storage/common/storage-dotnet-shared-access-signature-part-1 +[RFC_URL_1]: https://www.ietf.org/rfc/rfc2616.txt +[RFL_URL_2]: https://www.ietf.org/rfc/rfc3987.txt +[C_identifiers]: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/ +[storage_file_rest]: https://docs.microsoft.com/en-us/rest/api/storageservices/file-service-error-codes diff --git a/storage/client/file/pom.xml b/storage/client/file/pom.xml new file mode 100644 index 0000000000000..5355d42110408 --- /dev/null +++ b/storage/client/file/pom.xml @@ -0,0 +1,75 @@ + + + + com.azure + azure-client-sdk-parent + 1.1.0 + ../../../pom.client.xml + + + 4.0.0 + + com.azure + azure-storage-file + 12.0.0-preview.1 + + azure-storage-file + https://github.com/Azure/azure-sdk-for-java + + + + azure-java-build-docs + ${site.url}/site/${project.artifactId} + + + + + scm:git:https://github.com/Azure/azure-sdk-for-java + scm:git:git@github.com:Azure/azure-sdk-for-java.git + HEAD + + + + + com.azure + azure-core + 1.0.0-preview.2 + + + + + com.google.code.findbugs + jsr305 + 3.0.2 + provided + + + + com.azure + azure-core-test + 1.0.0-preview.2 + test + + + com.azure + azure-identity + 1.0.0-preview.1 + test + + + junit + junit + test + + + io.projectreactor + reactor-test + test + + + + diff --git a/storage/client/file/src/main/java/com/azure/storage/common/credentials/SharedKeyCredential.java b/storage/client/file/src/main/java/com/azure/storage/common/credentials/SharedKeyCredential.java index 19b2b4c307221..6e72a9c3d2fe5 100644 --- a/storage/client/file/src/main/java/com/azure/storage/common/credentials/SharedKeyCredential.java +++ b/storage/client/file/src/main/java/com/azure/storage/common/credentials/SharedKeyCredential.java @@ -4,6 +4,7 @@ package com.azure.storage.common.credentials; import com.azure.core.implementation.util.ImplUtils; +import com.azure.core.util.logging.ClientLogger; import io.netty.handler.codec.http.QueryStringDecoder; import javax.crypto.Mac; @@ -26,11 +27,12 @@ * SharedKey credential policy that is put into a header to authorize requests. */ public final class SharedKeyCredential { + private final ClientLogger logger = new ClientLogger(SASTokenCredential.class); private static final String AUTHORIZATION_HEADER_FORMAT = "SharedKey %s:%s"; // Pieces of the connection string that are needed. - private static final String ACCOUNT_NAME = "AccountName".toLowerCase(); - private static final String ACCOUNT_KEY = "AccountKey".toLowerCase(); + private static final String ACCOUNT_NAME = "accountname"; + private static final String ACCOUNT_KEY = "accountkey"; private final String accountName; private final byte[] accountKey; @@ -59,7 +61,7 @@ public static SharedKeyCredential fromConnectionString(String connectionString) HashMap connectionStringPieces = new HashMap<>(); for (String connectionStringPiece : connectionString.split(";")) { String[] kvp = connectionStringPiece.split("=", 2); - connectionStringPieces.put(kvp[0].toLowerCase(), kvp[1]); + connectionStringPieces.put(kvp[0].toLowerCase(Locale.ROOT), kvp[1]); } String accountName = connectionStringPieces.get(ACCOUNT_NAME); @@ -98,9 +100,13 @@ public String generateAuthorizationHeader(URL requestURL, String httpMethod, Map * * @param stringToSign The UTF-8-encoded string to sign. * @return A {@code String} that contains the HMAC-SHA256-encoded signature. - * @throws InvalidKeyException If the accountKey is not a valid Base64-encoded string. + * @throws RuntimeException for one of the following cases: + *

    + *
  • If the HMAC-SHA256 signature for {@code sharedKeyCredentials} fails to generate.
  • + *
  • If the an invalid key has been given to the client.
  • + *
*/ - public String computeHmac256(final String stringToSign) throws InvalidKeyException { + public String computeHmac256(final String stringToSign) { try { /* We must get a new instance of the Mac calculator for each signature calculated because the instances are @@ -111,8 +117,14 @@ public String computeHmac256(final String stringToSign) throws InvalidKeyExcepti hmacSha256.init(new SecretKeySpec(this.accountKey, "HmacSHA256")); byte[] utf8Bytes = stringToSign.getBytes(StandardCharsets.UTF_8); return Base64.getEncoder().encodeToString(hmacSha256.doFinal(utf8Bytes)); - } catch (final NoSuchAlgorithmException e) { - throw new Error(e); + } catch (final NoSuchAlgorithmException e) { + String errorMsg = "There is no such algorithm. Error Details: " + e.getMessage(); + logger.asWarning().log(errorMsg); + throw new RuntimeException(errorMsg); + } catch (InvalidKeyException e) { + String errorMsg = "Please double check the account key. Error details: " + e.getMessage(); + logger.asWarning().log(errorMsg); + throw new RuntimeException(errorMsg); } } @@ -121,20 +133,20 @@ private String buildStringToSign(URL requestURL, String httpMethod, MapSample Code

+ *

For more samples, please see the sample file

+ */ +public final class StorageException extends HttpResponseException { + private static final String ERROR_CODE = "x-ms-error-code"; + private final String message; + + StorageException(StorageErrorException e, String responseBody) { + super(e.getMessage(), e.response(), e); + this.message = responseBody; + } + + /** + * @return The error code returned by the service. + */ + public StorageErrorCode errorCode() { + return StorageErrorCode.fromString(super.response().headers().value(ERROR_CODE)); + } + + /** + * @return The message returned by the service. + */ + public String message() { + return this.message; + } + + /** + * @return The status code on the response. + */ + public int statusCode() { + return super.response().statusCode(); + } +} diff --git a/storage/client/file/src/main/java/com/azure/storage/common/policy/RequestRetryOptions.java b/storage/client/file/src/main/java/com/azure/storage/common/policy/RequestRetryOptions.java index edeaf6484ea6a..86e7d0ef35f37 100644 --- a/storage/client/file/src/main/java/com/azure/storage/common/policy/RequestRetryOptions.java +++ b/storage/client/file/src/main/java/com/azure/storage/common/policy/RequestRetryOptions.java @@ -64,6 +64,11 @@ public RequestRetryOptions() { *

Sample Code

* *

For more samples, please see the samples file

+ * @throws IllegalArgumentException If one of the following case exists: + *
    + *
  • There is only one null value for retryDelay and maxRetryDelay.
  • + *
  • Unrecognized retry policy type.
  • + *
*/ public RequestRetryOptions(RetryPolicyType retryPolicyType, Integer maxTries, Integer tryTimeout, Long retryDelayInMs, Long maxRetryDelayInMs, String secondaryHost) { diff --git a/storage/client/file/src/main/java/com/azure/storage/common/policy/RequestRetryPolicy.java b/storage/client/file/src/main/java/com/azure/storage/common/policy/RequestRetryPolicy.java index b862b76928af0..774a44999ce76 100644 --- a/storage/client/file/src/main/java/com/azure/storage/common/policy/RequestRetryPolicy.java +++ b/storage/client/file/src/main/java/com/azure/storage/common/policy/RequestRetryPolicy.java @@ -30,6 +30,11 @@ public final class RequestRetryPolicy implements HttpPipelinePolicy { private final RequestRetryOptions requestRetryOptions; + /** + * Constructs the policy using the retry options. + * + * @param requestRetryOptions Retry options for the policy. + */ public RequestRetryPolicy(RequestRetryOptions requestRetryOptions) { this.requestRetryOptions = requestRetryOptions; } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/DirectoryAsyncClient.java b/storage/client/file/src/main/java/com/azure/storage/file/DirectoryAsyncClient.java index 0669d18eb92f9..c792bdd89d94c 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/DirectoryAsyncClient.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/DirectoryAsyncClient.java @@ -3,77 +3,490 @@ package com.azure.storage.file; +import com.azure.core.http.HttpPipeline; import com.azure.core.http.rest.Response; +import com.azure.core.http.rest.SimpleResponse; import com.azure.core.http.rest.VoidResponse; +import com.azure.core.util.Context; +import com.azure.storage.common.credentials.SASTokenCredential; +import com.azure.storage.common.credentials.SharedKeyCredential; +import com.azure.storage.file.implementation.AzureFileStorageBuilder; +import com.azure.storage.file.implementation.AzureFileStorageImpl; import com.azure.storage.file.models.DirectoryInfo; import com.azure.storage.file.models.DirectoryProperties; +import com.azure.storage.file.models.DirectorySetMetadataInfo; +import com.azure.storage.file.models.DirectorysCreateResponse; +import com.azure.storage.file.models.DirectorysForceCloseHandlesResponse; +import com.azure.storage.file.models.DirectorysGetPropertiesResponse; +import com.azure.storage.file.models.DirectorysListFilesAndDirectoriesSegmentResponse; +import com.azure.storage.file.models.DirectorysListHandlesResponse; +import com.azure.storage.file.models.DirectorysSetMetadataResponse; import com.azure.storage.file.models.FileHTTPHeaders; import com.azure.storage.file.models.FileRef; import com.azure.storage.file.models.HandleItem; +import com.azure.storage.file.models.StorageErrorException; +import java.net.MalformedURLException; +import java.net.URL; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import java.util.Map; - +/** + * This class provides a client that contains all the operations for interacting with directory in Azure Storage File Service. + * Operations allowed by the client are creating, deleting and listing subdirectory and file, retrieving properties, setting metadata + * and list or force close handles of the directory or file. + * + *

Instantiating an Asynchronous Directory Client

+ * + * {@codesnippet com.azure.storage.file.directoryAsyncClient.instantiation} + * + *

View {@link DirectoryClientBuilder this} for additional ways to construct the client.

+ * + * @see DirectoryClientBuilder + * @see DirectoryClient + * @see SharedKeyCredential + * @see SASTokenCredential + */ public class DirectoryAsyncClient { + private final AzureFileStorageImpl azureFileStorageClient; + private final String shareName; + private final String directoryPath; + private final String snapshot; + + /** + * Creates a DirectoryAsyncClient that sends requests to the storage directory at {@link AzureFileStorageImpl#url() endpoint}. + * Each service call goes through the {@link HttpPipeline pipeline} in the {@code client}. + * @param azureFileStorageClient Client that interacts with the service interfaces + * @param shareName Name of the share + * @param directoryPath Name of the directory + * @param snapshot The snapshot of the share + */ + DirectoryAsyncClient(AzureFileStorageImpl azureFileStorageClient, String shareName, String directoryPath, String snapshot) { + this.shareName = shareName; + this.directoryPath = directoryPath; + this.snapshot = snapshot; + this.azureFileStorageClient = azureFileStorageClient; + } - DirectoryAsyncClient() { - throw new UnsupportedOperationException(); + /** + * Creates a DirectoryAsyncClient that sends requests to this directory at {@code endpoint}. + * Each service call goes through the {@code httpPipeline}. + * @param endpoint URL for the Storage File service + * @param httpPipeline HttpPipeline that HTTP requests and response flow through + * @param shareName Name of the share + * @param directoryPath Name of the directory + * @param snapshot Optional snapshot of the share + */ + DirectoryAsyncClient(URL endpoint, HttpPipeline httpPipeline, String shareName, String directoryPath, String snapshot) { + this.shareName = shareName; + this.directoryPath = directoryPath; + this.snapshot = snapshot; + this.azureFileStorageClient = new AzureFileStorageBuilder().pipeline(httpPipeline) + .url(endpoint.toString()) + .build(); } - public static DirectoryClientBuilder asyncBuilder() { - throw new UnsupportedOperationException(); + /** + * Get the url of the storage directory client. + * @return the URL of the storage directory client + * @throws RuntimeException If the directory is using a malformed URL. + */ + public URL getDirectoryUrl() { + try { + return new URL(azureFileStorageClient.url()); + } catch (MalformedURLException e) { + throw new RuntimeException(String.format("Invalid URL on %s: %s" + getClass().getSimpleName(), + azureFileStorageClient.url()), e); + } } - public FileAsyncClient getFileClient(String name) { - throw new UnsupportedOperationException(); + /** + * Constructs a FileAsyncClient that interacts with the specified file. + * + *

If the file doesn't exist in this directory {@link FileAsyncClient#create(long)} create} in the client will + * need to be called before interaction with the file can happen.

+ * + * @param fileName Name of the file + * @return a FileAsyncClient that interacts with the specified share + */ + public FileAsyncClient getFileClient(String fileName) { + String filePath = directoryPath + "/" + fileName; + return new FileAsyncClient(azureFileStorageClient, shareName, filePath, null); } - public DirectoryAsyncClient getDirectoryClient(String directoryName) { - throw new UnsupportedOperationException(); + /** + * Constructs a DirectoryAsyncClient that interacts with the specified directory. + * + *

If the file doesn't exist in this directory {@link DirectoryAsyncClient#create()} create} in the client will + * need to be called before interaction with the directory can happen.

+ * + * @param subDirectoryName Name of the directory + * @return a DirectoryAsyncClient that interacts with the specified directory + */ + public DirectoryAsyncClient getSubDirectoryClient(String subDirectoryName) { + String directoryPath = this.directoryPath + "/" + subDirectoryName; + return new DirectoryAsyncClient(azureFileStorageClient, shareName, directoryPath, snapshot); } + /** + * Creates this directory in the file share and returns a response of {@link DirectoryInfo} to interact with it. + * + *

Code Samples

+ * + *

Create the directory

+ * + * {@codesnippet com.azure.storage.file.directoryAsyncClient.create} + * + * @return A response containing the directory info and the status of creating the directory. + * @throws StorageErrorException If the directory has already existed, the parent directory does not exist or directory name is an invalid resource name. + */ + public Mono> create() { + return create(null); + } + + /** + * Creates a directory in the file share and returns a response of DirectoryInfo to interact with it. + * + *

Code Samples

+ * + *

Create the directory

+ * + * {@codesnippet com.azure.storage.file.directoryAsyncClient.create#map} + * + * @param metadata Optional metadata to associate with the directory + * @return A response containing the directory info and the status of creating the directory. + * @throws StorageErrorException If the directory has already existed, the parent directory does not exist or directory name is an invalid resource name. + */ public Mono> create(Map metadata) { - throw new UnsupportedOperationException(); + return azureFileStorageClient.directorys().createWithRestResponseAsync(shareName, directoryPath, null, metadata, Context.NONE) + .map(this::createWithRestResponse); } + /** + * Deletes the directory in the file share. + * + *

Code Samples

+ * + *

Delete the directory

+ * + * {@codesnippet com.azure.storage.file.directoryClient.delete} + * + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the share doesn't exist + */ public Mono delete() { - throw new UnsupportedOperationException(); + return azureFileStorageClient.directorys().deleteWithRestResponseAsync(shareName, directoryPath, Context.NONE).map(VoidResponse::new) + .map(VoidResponse::new); + } + + /** + * Retrieves the properties of this directory. + * The properties includes directory metadata, last modified date, is server encrypted, and eTag. + * + *

Code Samples

+ * + *

Retrieve directory properties

+ * + * {@codesnippet com.azure.storage.file.directoryAsyncClient.getProperties} + * + * @return Storage directory properties + */ + public Mono> getProperties() { + return azureFileStorageClient.directorys().getPropertiesWithRestResponseAsync(shareName, directoryPath, snapshot, null, Context.NONE) + .map(this::getPropertiesResponse); } - public Mono> getProperties(String shareSnapshot) { - throw new UnsupportedOperationException(); + /** + * Sets the user-defined metadata to associate to the directory. + * + *

If {@code null} is passed for the metadata it will clear the metadata associated to the directory.

+ * + *

Code Samples

+ * + *

Set the metadata to "directory:updatedMetadata"

+ * + * {@codesnippet com.azure.storage.file.directoryAsyncClient.setMetadata#map} + * + *

Clear the metadata of the directory

+ * + * {@codesnippet com.azure.storage.file.directoryAsyncClient.setMetadata#map.clearMetadata} + * + * @param metadata Optional metadata to set on the directory, if null is passed the metadata for the directory is cleared + * @return information about the directory + * @throws StorageErrorException If the directory doesn't exist or the metadata contains invalid keys + */ + public Mono> setMetadata(Map metadata) { + return azureFileStorageClient.directorys().setMetadataWithRestResponseAsync(shareName, directoryPath, null, metadata, Context.NONE) + .map(this::setMetadataResponse); } - public Mono> setMetadata(Map metadata) { - throw new UnsupportedOperationException(); + /** + * Lists all sub-directories and files in this directory without their prefix or maxResult. + * + *

Code Samples

+ * + *

List all sub-directories and files in the account

+ * + * {@codesnippet com.azure.storage.file.directoryClient.listFilesAndDirectories} + * + * @return {@link FileRef File info} in the storage directory + */ + public Flux listFilesAndDirectories() { + return listFilesAndDirectories(null, null); } - public Flux listFilesAndDirectories(String prefix, int maxResults, String shareSnapshot) { - throw new UnsupportedOperationException(); + /** + * Lists all sub-directories and files in this directory with their prefix or snapshots. + * + *

Code Samples

+ * + *

List all sub-directories with "subdir" prefix and return 10 results in the account

+ * + * {@codesnippet com.azure.storage.file.directoryAsyncClient.listFilesAndDirectories#string-integer} + * + * @param prefix Optional prefix which filters the results to return only files and directories whose name begins with. + * @param maxResults Optional maximum number of files and/or directories to return per page. + * If the request does not specify maxresults or specifies a value greater than 5,000, the server will return up to 5,000 items. + * @return {@link FileRef File info} in this directory with prefix and max number of return results. + */ + public Flux listFilesAndDirectories(String prefix, Integer maxResults) { + return azureFileStorageClient.directorys().listFilesAndDirectoriesSegmentWithRestResponseAsync(shareName, directoryPath, prefix, snapshot, null, maxResults, null, Context.NONE) + .flatMapMany(response -> nextPageForFileAndDirecotries(response, prefix, maxResults)); } - public Flux getHandles(int maxResult, boolean recursive) { - throw new UnsupportedOperationException(); + /** + * List of open handles on a directory or a file. + * + *

Code Samples

+ * + *

Get 10 handles with recursive call.

+ * + * {@codesnippet com.azure.storage.file.directoryAsyncClient.getHandles} + * + * @param maxResult Optional maximum number of results will return per page + * @param recursive Specifies operation should apply to the directory specified in the URI, its files, its subdirectories and their files. + * @return {@link HandleItem handles} in the directory that satisfy the requirements + */ + public Flux getHandles(Integer maxResult, boolean recursive) { + return azureFileStorageClient.directorys().listHandlesWithRestResponseAsync(shareName, directoryPath, null, maxResult, null, snapshot, recursive, Context.NONE) + .flatMapMany(response -> nextPageForHandles(response, maxResult, recursive)); } + /** + * Closes a handle or handles opened on a directory or a file at the service. It is intended to be used alongside {@link DirectoryAsyncClient#getHandles(Integer, boolean)} . + * TODO: Will change the return type to how many handles have been closed. Implement one more API to force close all handles. + * TODO: @see Github Issue 4525 + * + *

Code Samples

+ * + *

Force close handles with handles returned by get handles in recursive.

+ * + * {@codesnippet com.azure.storage.file.directoryAsyncClient.forceCloseHandles} + * + * @param handleId Specifies the handle ID to be closed. Use an asterisk ('*') as a wildcard string to specify all handles. + * @param recursive A boolean value that specifies if the operation should also apply to the files and subdirectories of the directory specified in the URI. + * @return The counts of number of handles closed + */ public Flux forceCloseHandles(String handleId, boolean recursive) { - throw new UnsupportedOperationException(); + return azureFileStorageClient.directorys().forceCloseHandlesWithRestResponseAsync(shareName, directoryPath, handleId, null, null, snapshot, recursive, Context.NONE) + .flatMapMany(response -> nextPageForForceCloseHandles(response, handleId, recursive)); } - public Mono> createSubDirectory(String directoryName, Map metadata) { - throw new UnsupportedOperationException(); + /** + * Creates a subdirectory under current directory with specific name and returns a response of DirectoryAsyncClient to interact with it. + * + *

Code Samples

+ * + *

Create the sub directory "subdir"

+ * + * {@codesnippet com.azure.storage.file.directoryAsyncClient.createSubDirectory#string} + * + * @param subDirectoryName Name of the subdirectory + * @return A response containing the subdirectory client and the status of creating the directory. + * @throws StorageErrorException If the subdirectory has already existed, the parent directory does not exist or directory is an invalid resource name. + */ + public Mono> createSubDirectory(String subDirectoryName) { + return createSubDirectory(subDirectoryName, null); } - public Mono deleteSubDirectory(String directoryName) { - throw new UnsupportedOperationException(); + /** + * Creates a subdirectory under current directory with specific name , metadata and returns a response of DirectoryAsyncClient to interact with it. + * + *

Code Samples

+ * + *

Create the subdirectory named "subdir", with metadata

+ * + * {@codesnippet com.azure.storage.file.directoryClient.createSubDirectory#string-map} + * + * @param subDirectoryName Name of the subdirectory + * @param metadata Optional metadata to associate with the subdirectory + * @return A response containing the subdirectory client and the status of creating the directory. + * @throws StorageErrorException If the directory has already existed, the parent directory does not exist or subdirectory is an invalid resource name. + */ + public Mono> createSubDirectory(String subDirectoryName, Map metadata) { + DirectoryAsyncClient createSubClient = getSubDirectoryClient(subDirectoryName); + return createSubClient.create(metadata) + .map(response -> new SimpleResponse<>(response, createSubClient)); } - public Mono> createFile(String fileName, long maxSize, FileHTTPHeaders httpHeaders, Map meatadata) { - throw new UnsupportedOperationException(); + /** + * Deletes the subdirectory with specific name in this directory. + * + *

Code Samples

+ * + *

Delete the subdirectory named "subdir"

+ * + * {@codesnippet com.azure.storage.file.directoryAsyncClient.deleteSubDirectory#string} + * + * @param subDirectoryName Name of the subdirectory + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the subdirectory doesn't exist, the parent directory does not exist or subdirectory name is an invalid resource name. + */ + public Mono deleteSubDirectory(String subDirectoryName) { + DirectoryAsyncClient deleteSubClient = getSubDirectoryClient(subDirectoryName); + return deleteSubClient.delete().map(VoidResponse::new); } + /** + * Creates a file in this directory with specific name, max number of results and returns a response of DirectoryInfo to interact with it. + * + *

Code Samples

+ * + *

Create 1k file with named "myFile"

+ * + * {@codesnippet com.azure.storage.file.directoryAsyncClient.createFile#string-long} + * + * @param fileName Name of the file + * @param maxSize Size of the file + * @return A response containing the FileAsyncClient and the status of creating the directory. + * @throws StorageErrorException If the file has already existed, the parent directory does not exist or file name is an invalid resource name. + */ + public Mono> createFile(String fileName, long maxSize) { + return createFile(fileName, maxSize, null, null); + } + + /** + * Creates a file in this directory with specific name and returns a response of DirectoryInfo to interact with it. + * + *

Code Samples

+ * + *

Create the file named "myFile"

+ * + * {@codesnippet com.azure.storage.file.directoryAsyncClient.createFile#string-long-fileHTTPHeaders-map} + * + * @param fileName Name of the file + * @param maxSize Max size of the file + * @param httpHeaders the Http headers set to the file + * @param metadata Optional name-value pairs associated with the file as metadata. Metadata names must adhere to the naming rules. + * @return A response containing the directory info and the status of creating the directory. + * @throws StorageErrorException If the directory has already existed, the parent directory does not exist or file name is an invalid resource name. + */ + public Mono> createFile(String fileName, long maxSize, FileHTTPHeaders httpHeaders, Map metadata) { + FileAsyncClient fileAsyncClient = getFileClient(fileName); + return fileAsyncClient.create(maxSize, httpHeaders, metadata).map(response -> new SimpleResponse<>(response, fileAsyncClient)); + } + + /** + * Deletes the file with specific name in this directory. + * + *

Code Samples

+ * + *

Delete the file "filetest"

+ * + * {@codesnippet com.azure.storage.file.directoryAsyncClient.deleteFile#string} + * + * @param fileName Name of the file + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the directory doesn't exist or the file doesn't exist or file name is an invalid resource name. + */ public Mono deleteFile(String fileName) { - throw new UnsupportedOperationException(); + FileAsyncClient fileAsyncClient = getFileClient(fileName); + return fileAsyncClient.delete().map(VoidResponse::new); + } + + /** + * Get snapshot id which attached to {@link DirectoryAsyncClient}. + * Return {@code null} if no snapshot id attached. + * + *

Code Samples

+ * + *

Get the share snapshot id.

+ * + * {@codesnippet com.azure.storage.file.directoryAsyncClient.getShareSnapshotId} + * + * @return The snapshot id which is a unique {@code DateTime} value that identifies the share snapshot to its base share. + */ + public String getShareSnapshotId() { + return this.snapshot; + } + + private Response createWithRestResponse(final DirectorysCreateResponse response) { + String eTag = response.deserializedHeaders().eTag(); + OffsetDateTime lastModified = response.deserializedHeaders().lastModified(); + DirectoryInfo directoryInfo = new DirectoryInfo(eTag, lastModified); + return new SimpleResponse<>(response, directoryInfo); + + } + + private Response getPropertiesResponse(DirectorysGetPropertiesResponse response) { + Map metadata = response.deserializedHeaders().metadata(); + String eTag = response.deserializedHeaders().eTag(); + OffsetDateTime offsetDateTime = response.deserializedHeaders().lastModified(); + boolean isServerEncrypted = response.deserializedHeaders().isServerEncrypted(); + + DirectoryProperties directoryProperties = new DirectoryProperties(metadata, eTag, offsetDateTime, isServerEncrypted); + return new SimpleResponse<>(response, directoryProperties); + } + + private Response setMetadataResponse(final DirectorysSetMetadataResponse response) { + String eTag = response.deserializedHeaders().eTag(); + boolean isServerEncrypted = response.deserializedHeaders().isServerEncrypted(); + + DirectorySetMetadataInfo directorySetMetadataInfo = new DirectorySetMetadataInfo(eTag, isServerEncrypted); + return new SimpleResponse<>(response, directorySetMetadataInfo); + } + + private Flux nextPageForFileAndDirecotries(final DirectorysListFilesAndDirectoriesSegmentResponse response, final String prefix, final Integer maxResult) { + List fileRefs = convertResponseAndGetNumOfResults(response); + + if (response.value().nextMarker() == null) { + return Flux.fromIterable(fileRefs); + } + Mono listResponse = azureFileStorageClient.directorys().listFilesAndDirectoriesSegmentWithRestResponseAsync(shareName, directoryPath, prefix, snapshot, response.value().nextMarker(), maxResult, null, Context.NONE); + Flux fileRefPublisher = listResponse.flatMapMany(newResponse -> nextPageForFileAndDirecotries(newResponse, prefix, maxResult)); + return Flux.fromIterable(fileRefs).concatWith(fileRefPublisher); + } + + private Flux nextPageForHandles(DirectorysListHandlesResponse response, Integer maxResult, boolean recursive) { + List handleItems = response.value().handleList(); + + if (response.value().nextMarker() == null) { + return Flux.fromIterable(handleItems); + } + Mono listResponse = azureFileStorageClient.directorys().listHandlesWithRestResponseAsync(shareName, directoryPath, response.value().nextMarker(), maxResult, null, snapshot, recursive, Context.NONE); + Flux fileRefPublisher = listResponse.flatMapMany(newResponse -> nextPageForHandles(newResponse, maxResult, recursive)); + return Flux.fromIterable(handleItems).concatWith(fileRefPublisher); + } + + private Flux nextPageForForceCloseHandles(DirectorysForceCloseHandlesResponse response, String handleId, boolean recursive) { + List handleCount = Arrays.asList(response.deserializedHeaders().numberOfHandlesClosed()); + + if (response.deserializedHeaders().marker() == null) { + return Flux.fromIterable(handleCount); + } + Mono listResponse = azureFileStorageClient.directorys().forceCloseHandlesWithRestResponseAsync(shareName, directoryPath, handleId, null, response.deserializedHeaders().marker(), snapshot, recursive, Context.NONE); + Flux fileRefPublisher = listResponse.flatMapMany(newResponse -> nextPageForForceCloseHandles(newResponse, handleId, recursive)); + return Flux.fromIterable(handleCount).concatWith(fileRefPublisher); + } + + private List convertResponseAndGetNumOfResults(DirectorysListFilesAndDirectoriesSegmentResponse response) { + List fileRefs = new ArrayList<>(); + response.value().segment().directoryItems().forEach(directoryItem -> fileRefs.add(new FileRef(directoryItem.name(), true, null))); + response.value().segment().fileItems().forEach(fileItem -> fileRefs.add(new FileRef(fileItem.name(), false, fileItem.properties()))); + return fileRefs; } } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/DirectoryClient.java b/storage/client/file/src/main/java/com/azure/storage/file/DirectoryClient.java index 22d4063b0f410..44c84a0dade28 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/DirectoryClient.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/DirectoryClient.java @@ -4,78 +4,363 @@ package com.azure.storage.file; import com.azure.core.http.rest.Response; +import com.azure.core.http.rest.SimpleResponse; import com.azure.core.http.rest.VoidResponse; +import com.azure.storage.common.credentials.SASTokenCredential; +import com.azure.storage.common.credentials.SharedKeyCredential; import com.azure.storage.file.models.DirectoryInfo; import com.azure.storage.file.models.DirectoryProperties; +import com.azure.storage.file.models.DirectorySetMetadataInfo; import com.azure.storage.file.models.FileHTTPHeaders; import com.azure.storage.file.models.FileRef; import com.azure.storage.file.models.HandleItem; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - +import com.azure.storage.file.models.StorageErrorException; +import java.net.URL; import java.util.Map; +/** + * This class provides a client that contains all the operations for interacting with directory in Azure Storage File Service. + * Operations allowed by the client are creating, deleting and listing subdirectory and file, retrieving properties, , setting metadata + * and list or force close handles of the directory or file. + * + *

Instantiating an Synchronous Directory Client

+ * + * {@codesnippet com.azure.storage.file.directoryClient.instantiation} + * + *

View {@link DirectoryClientBuilder this} for additional ways to construct the client.

+ * + * @see DirectoryClientBuilder + * @see DirectoryClient + * @see SharedKeyCredential + * @see SASTokenCredential + */ public class DirectoryClient { - private final DirectoryAsyncClient client; + private final DirectoryAsyncClient directoryAsyncClient; + + /** + * Creates a DirectoryClient that wraps a DirectoryAsyncClient and blocks requests. + * + * @param directoryAsyncClient DirectoryAsyncClient that is used to send requests + */ + DirectoryClient(DirectoryAsyncClient directoryAsyncClient) { + this.directoryAsyncClient = directoryAsyncClient; + } + + /** + * Get the url of the storage directory client. + * @return the URL of the storage directory client. + * @throws RuntimeException If the directory is using a malformed URL. + */ + public URL getDirectoryUrl() { + return directoryAsyncClient.getDirectoryUrl(); + } + + /** + * Constructs a FileClient that interacts with the specified file. + * + *

If the file doesn't exist in this directory {@link FileClient#create(long)} create} in the client will + * need to be called before interaction with the file can happen.

+ * + * @param fileName Name of the file + * @return a FileClient that interacts with the specified share + */ + public FileClient getFileClient(String fileName) { + return new FileClient(directoryAsyncClient.getFileClient(fileName)); + } + + /** + * Constructs a DirectoryClient that interacts with the specified directory. + * + *

If the file doesn't exist in this directory {@link DirectoryClient#create()} create} in the client will + * need to be called before interaction with the directory can happen.

+ * + * @param subDirectoryName Name of the directory + * @return a DirectoryClient that interacts with the specified directory + */ + public DirectoryClient getSubDirectoryClient(String subDirectoryName) { + return new DirectoryClient(directoryAsyncClient.getSubDirectoryClient(subDirectoryName)); + } + + /** + * Creates a directory in the file share and returns a response of {@link DirectoryInfo} to interact with it. + * + *

Code Samples

+ * + *

Create the directory

+ * + * {@codesnippet com.azure.storage.file.directoryClient.createDirectory} + * + * @return A response containing the directory info and the status of creating the directory. + * @throws StorageErrorException If the directory has already existed, the parent directory does not exist or directory name is an invalid resource name. + */ + public Response create() { + return create(null); + } - DirectoryClient() { - throw new UnsupportedOperationException(); + /** + * Creates a directory in the file share and returns a response of DirectoryInfo to interact with it. + * + *

Code Samples

+ * + *

Create the directory

+ * + * {@codesnippet com.azure.storage.file.directoryClient.create#map} + * + * @param metadata Optional metadata to associate with the directory. + * @return A response containing the directory info and the status of creating the directory. + * @throws StorageErrorException If the directory has already existed, the parent directory does not exist or directory name is an invalid resource name. + */ + public Response create(Map metadata) { + return directoryAsyncClient.create(metadata).block(); } - public static DirectoryClientBuilder syncBuilder() { - throw new UnsupportedOperationException(); + /** + * Deletes the directory in the file share. The directory must be empty before it can be deleted. + * + *

Code Samples

+ * + *

Delete the directory

+ * + * {@codesnippet com.azure.storage.file.directoryClient.delete} + * + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the share doesn't exist + */ + public VoidResponse delete() { + return directoryAsyncClient.delete().block(); } - public FileClient getFileClient(String name) { - throw new UnsupportedOperationException(); + /** + * Retrieves the properties of this directory. + * The properties includes directory metadata, last modified date, is server encrypted, and eTag. + * + *

Code Samples

+ * + *

Retrieve directory properties

+ * + * {@codesnippet com.azure.storage.file.directoryClient.getProperties} + * + * @return Storage directory properties + */ + public Response getProperties() { + return directoryAsyncClient.getProperties().block(); } - public DirectoryClient getDirectoryClient(String directoryName) { - throw new UnsupportedOperationException(); + /** + * Sets the user-defined metadata to associate to the directory. + * + *

If {@code null} is passed for the metadata it will clear the metadata associated to the directory.

+ * + *

Code Samples

+ * + *

Set the metadata to "directory:updatedMetadata"

+ * + * {@codesnippet com.azure.storage.file.directoryClient.setMetadata#map} + * + *

Clear the metadata of the directory

+ * + * {@codesnippet com.azure.storage.file.directoryClient.setMetadata#map.clearMetadata} + * + * @param metadata Optional metadata to set on the directory, if null is passed the metadata for the directory is cleared + * @return information about the directory + * @throws StorageErrorException If the directory doesn't exist or the metadata contains invalid keys + */ + public Response setMetadata(Map metadata) { + return directoryAsyncClient.setMetadata(metadata).block(); } - public Mono> create(Map metadata) { - throw new UnsupportedOperationException(); + /** + * Lists all sub-directories and files in this directory without their prefix or maxResult. + * + *

Code Samples

+ * + *

List all sub-directories and files in the account

+ * + * {@codesnippet com.azure.storage.file.directoryClient.listFilesAndDirectories} + * + * @return {@link FileRef File info} in the storage directory + */ + public Iterable listFilesAndDirectories() { + return listFilesAndDirectories(null, null); } - public Mono delete() { - throw new UnsupportedOperationException(); + /** + * Lists all sub-directories and files in this directory with their prefix or snapshots. + * + *

Code Samples

+ * + *

List all sub-directories and files in this directory with "subdir" prefix and return 10 results in the account

+ * + * {@codesnippet com.azure.storage.file.directoryClient.listFilesAndDirectories#string-integer} + * + * @param prefix Optional prefix which filters the results to return only files and directories whose name begins with. + * @param maxResults Optional maximum number of files and/or directories to return per page. + * If the request does not specify maxresults or specifies a value greater than 5,000, the server will return up to 5,000 items. + * @return {@link FileRef File info} in this directory with prefix and max number of return results. + */ + public Iterable listFilesAndDirectories(String prefix, Integer maxResults) { + return directoryAsyncClient.listFilesAndDirectories(prefix, maxResults).toIterable(); } - public Mono> getProperties(String shareSnapshot) { - throw new UnsupportedOperationException(); + /** + * List of open handles on a directory or a file. + * + *

Code Samples

+ * + *

Get 10 handles with recursive call.

+ * + * {@codesnippet com.azure.storage.file.directoryClient.getHandles} + * + * @param maxResult Optional maximum number of results will return per page + * @param recursive Specifies operation should apply to the directory specified in the URI, its files, its subdirectories and their files. + * @return {@link HandleItem handles} in the directory that satisfy the requirements + */ + public Iterable getHandles(Integer maxResult, boolean recursive) { + return directoryAsyncClient.getHandles(maxResult, recursive).collectList().block(); } - public Mono> setMetadata(Map metadata) { - throw new UnsupportedOperationException(); + /** + * Closes a handle or handles opened on a directory or a file at the service. It is intended to be used alongside {@link DirectoryClient#getHandles(Integer, boolean)} . + * TODO: Will change the return type to how many handles have been closed. Implement one more API to force close all handles. + * TODO: @see Github Issue 4525 + * + *

Code Samples

+ * + *

Force close handles with handles returned by get handles in recursive.

+ * + * {@codesnippet com.azure.storage.file.directoryClient.forceCloseHandles} + * + * @param handleId Specifies the handle ID to be closed. Use an asterisk ('*') as a wildcard string to specify all handles. + * @param recursive A boolean value that specifies if the operation should also apply to the files and subdirectories of the directory specified in the URI. + * @return The counts of number of handles closed. + */ + public Iterable forceCloseHandles(String handleId, boolean recursive) { + return directoryAsyncClient.forceCloseHandles(handleId, recursive).collectList().block(); } - public Flux listFilesAndDirectories(String prefix, int maxResults, String shareSnapshot) { - throw new UnsupportedOperationException(); + /** + * Creates a subdirectory under current directory with specific name and returns a response of DirectoryClient to interact with it. + * + *

Code Samples

+ * + *

Create the sub directory "subdir"

+ * + * {@codesnippet com.azure.storage.file.directoryClient.createSubDirectory#string} + * + * @param subDirectoryName Name of the subdirectory + * @return A response containing the subdirectory client and the status of creating the directory. + * @throws StorageErrorException If the subdirectory has already existed, the parent directory does not exist or directory is an invalid resource name. + */ + public Response createSubDirectory(String subDirectoryName) { + return createSubDirectory(subDirectoryName, null); } - public Flux getHandles(int maxResult, boolean recursive) { - throw new UnsupportedOperationException(); + /** + * Creates a subdirectory under current directory with specific name , metadata and returns a response of DirectoryClient to interact with it. + * + *

Code Samples

+ * + *

Create the subdirectory named "subdir", with metadata

+ * + * {@codesnippet com.azure.storage.file.directoryClient.createSubDirectory#string-map} + * + * @param subDirectoryName Name of the subdirectory + * @param metadata Optional metadata to associate with the subdirectory + * @return A response containing the subdirectory client and the status of creating the directory. + * @throws StorageErrorException If the directory has already existed, the parent directory does not exist or subdirectory is an invalid resource name. + */ + public Response createSubDirectory(String subDirectoryName, Map metadata) { + DirectoryClient directoryClient = getSubDirectoryClient(subDirectoryName); + return new SimpleResponse<>(directoryClient.create(metadata), directoryClient); } - public Flux forceCloseHandles(String handleId, boolean recursive) { - throw new UnsupportedOperationException(); + /** + * Deletes the subdirectory with specific name in this directory. The directory must be empty before it can be deleted. + * + *

Code Samples

+ * + *

Delete the subdirectory named "subdir"

+ * + * {@codesnippet com.azure.storage.file.directoryClient.deleteSubDirectory#string} + * + * @param subDirectoryName Name of the subdirectory + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the subdirectory doesn't exist, the parent directory does not exist or subdirectory name is an invalid resource name. + */ + public VoidResponse deleteSubDirectory(String subDirectoryName) { + return directoryAsyncClient.deleteSubDirectory(subDirectoryName).block(); } - public Mono> createSubDirectory(String directoryName, Map metadata) { - throw new UnsupportedOperationException(); + /** + * Creates a file in this directory with specific name, max number of results and returns a response of DirectoryInfo to interact with it. + * + *

Code Samples

+ * + *

Create 1k file with named "myFile"

+ * + * {@codesnippet com.azure.storage.file.directoryClient.createFile#string-long} + * + * @param fileName Name of the file + * @param maxSize Size of the file + * @return A response containing the FileClient and the status of creating the directory. + * @throws StorageErrorException If the file has already existed, the parent directory does not exist or file name is an invalid resource name. + */ + public Response createFile(String fileName, long maxSize) { + return createFile(fileName, maxSize, null, null); } - public Mono deleteSubDirectory(String directoryName) { - throw new UnsupportedOperationException(); + /** + * Creates a file in this directory with specific name and returns a response of DirectoryInfo to interact with it. + * + *

Code Samples

+ * + *

Create the file named "myFile"

+ * + * {@codesnippet com.azure.storage.file.directoryClient.createFile#string-long-fileHTTPHeaders-map} + * + * @param fileName Name of the file + * @param maxSize Max size of the file + * @param httpHeaders the Http headers set to the file + * @param metadata Optional name-value pairs associated with the file as metadata. Metadata names must adhere to the naming rules. + * @return A response containing the directory info and the status of creating the directory. + * @throws StorageErrorException If the directory has already existed, the parent directory does not exist or file name is an invalid resource name. + */ + public Response createFile(String fileName, long maxSize, FileHTTPHeaders httpHeaders, Map metadata) { + return directoryAsyncClient.createFile(fileName, maxSize, httpHeaders, metadata) + .map(response -> new SimpleResponse<>(response, new FileClient(response.value()))).block(); } - public Mono> createFile(String fileName, long maxSize, FileHTTPHeaders httpHeaders, Map meatadata) { - throw new UnsupportedOperationException(); + /** + * Deletes the file with specific name in this directory. + * + *

Code Samples

+ * + *

Delete the file "filetest"

+ * + * {@codesnippet com.azure.storage.file.directoryClient.deleteFile#string} + * + * @param fileName Name of the file + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the directory doesn't exist or the file doesn't exist or file name is an invalid resource name. + */ + public VoidResponse deleteFile(String fileName) { + return directoryAsyncClient.deleteFile(fileName).block(); } - public Mono deleteFile(String fileName) { - throw new UnsupportedOperationException(); + /** + * Get snapshot id which attached to {@link DirectoryClient}. + * Return {@code null} if no snapshot id attached. + * + *

Code Samples

+ * + *

Get the share snapshot id.

+ * + * {@codesnippet com.azure.storage.file.directoryClient.getShareSnapshotId} + * + * @return The snapshot id which is a unique {@code DateTime} value that identifies the share snapshot to its base share. + */ + public String getShareSnapshotId() { + return directoryAsyncClient.getShareSnapshotId(); } } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/DirectoryClientBuilder.java b/storage/client/file/src/main/java/com/azure/storage/file/DirectoryClientBuilder.java index 7bbd3cb138d2e..e5b7129cc046e 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/DirectoryClientBuilder.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/DirectoryClientBuilder.java @@ -3,7 +3,357 @@ package com.azure.storage.file; +import com.azure.core.http.HttpClient; +import com.azure.core.http.HttpPipeline; +import com.azure.core.http.policy.AddDatePolicy; +import com.azure.core.http.policy.HttpLogDetailLevel; +import com.azure.core.http.policy.HttpLoggingPolicy; +import com.azure.core.http.policy.HttpPipelinePolicy; +import com.azure.core.http.policy.RequestIdPolicy; +import com.azure.core.http.policy.RetryPolicy; +import com.azure.core.http.policy.UserAgentPolicy; +import com.azure.core.implementation.http.policy.spi.HttpPolicyProviders; +import com.azure.core.util.configuration.Configuration; +import com.azure.core.util.configuration.ConfigurationManager; +import com.azure.storage.common.credentials.SASTokenCredential; +import com.azure.storage.common.credentials.SharedKeyCredential; +import com.azure.storage.common.policy.SASTokenCredentialPolicy; +import com.azure.storage.common.policy.SharedKeyCredentialPolicy; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; + +/** + * This class provides a fluent builder API to help aid the configuration and instantiation of the {@link DirectoryClient FileClients} + * and {@link DirectoryAsyncClient FileAsyncClients}, calling {@link DirectoryClientBuilder#buildClient() buildClient} + * constructs an instance of FileClient and calling {@link DirectoryClientBuilder#buildAsyncClient() buildAsyncClient} + * constructs an instance of FileAsyncClient. + * + *

The client needs the endpoint of the Azure Storage File service, name of the share, and authorization credential. + * {@link DirectoryClientBuilder#endpoint(String) endpoint} gives the builder the endpoint and may give the builder the + * {@link DirectoryClientBuilder#shareName(String)}, {@link DirectoryClientBuilder#directoryPath(String)} and a {@link SASTokenCredential} that authorizes the client.

+ * + *

Instantiating a synchronous Directory Client with SAS token

+ * {@codesnippet com.azure.storage.file.directoryClient.instantiation.sastoken} + * + *

Instantiating an Asynchronous Directory Client with SAS token

+ * {@codesnippet com.azure.storage.file.directoryClient.instantiation.sastoken} + * + *

If the {@code endpoint} doesn't contain the query parameters to construct a {@code SASTokenCredential} they may + * be set using {@link DirectoryClientBuilder#credential(SASTokenCredential) credential}.

+ * + * {@codesnippet com.azure.storage.file.directoryClient.instantiation.credential} + * + * {@codesnippet com.azure.storage.file.directoryAsyncClient.instantiation.credential} + * + *

Another way to authenticate the client is using a {@link SharedKeyCredential}. To create a SharedKeyCredential + * a connection string from the Storage File service must be used. Set the SharedKeyCredential with + * {@link DirectoryClientBuilder#connectionString(String) connectionString}. If the builder has both a SASTokenCredential and + * SharedKeyCredential the SharedKeyCredential will be preferred when authorizing requests sent to the service.

+ * + *

Instantiating a synchronous Directory Client with connection string.

+ * {@codesnippet com.azure.storage.file.directoryClient.instantiation.connectionstring} + * + *

Instantiating an Asynchronous Directory Client with connection string.

+ * {@codesnippet com.azure.storage.file.directoryAsyncClient.instantiation.connectionstring} + * + * @see DirectoryClient + * @see DirectoryAsyncClient + * @see SASTokenCredential + * @see SharedKeyCredential + */ public class DirectoryClientBuilder { + private static final String ACCOUNT_NAME = "accountname"; + private final List policies; + private final RetryPolicy retryPolicy; + + private HttpLogDetailLevel logLevel; + private Configuration configuration; + private URL endpoint; + private String shareName; + private String directoryPath; + private SASTokenCredential sasTokenCredential; + private SharedKeyCredential sharedKeyCredential; + private HttpClient httpClient; + private HttpPipeline pipeline; + private String snapshot; + + /** + * Creates a builder instance that is able to configure and construct {@link DirectoryClient DirectoryClients} + * and {@link DirectoryAsyncClient DirectoryAsyncClients}. + */ + public DirectoryClientBuilder() { + retryPolicy = new RetryPolicy(); + logLevel = HttpLogDetailLevel.NONE; + policies = new ArrayList<>(); + + configuration = ConfigurationManager.getConfiguration(); + } + + /** + * Creates a {@link DirectoryAsyncClient} based on options set in the builder. Every time {@code buildAsyncClient()} is + * called a new instance of {@link DirectoryAsyncClient} is created. + * + *

+ * If {@link DirectoryClientBuilder#pipeline(HttpPipeline) pipeline} is set, then the {@code pipeline} and + * {@link DirectoryClientBuilder#endpoint(String) endpoint} are used to create the + * {@link DirectoryAsyncClient client}. All other builder settings are ignored. + *

+ * + * @return A ShareAsyncClient with the options set from the builder. + * @throws NullPointerException If {@code shareName} is {@code null} or {@code shareName} is {@code null}. + * @throws IllegalArgumentException If neither a {@link SharedKeyCredential} or {@link SASTokenCredential} has been set. + */ + public DirectoryAsyncClient buildAsyncClient() { + Objects.requireNonNull(shareName); + Objects.requireNonNull(directoryPath); + + if (pipeline != null) { + return new DirectoryAsyncClient(endpoint, pipeline, shareName, directoryPath, snapshot); + } + + if (sasTokenCredential == null && sharedKeyCredential == null) { + throw new IllegalArgumentException("Credentials are required for authorization"); + } + + // Closest to API goes first, closest to wire goes last. + final List policies = new ArrayList<>(); + + policies.add(new UserAgentPolicy(FileConfiguration.NAME, FileConfiguration.VERSION, configuration)); + policies.add(new RequestIdPolicy()); + policies.add(new AddDatePolicy()); + + if (sharedKeyCredential != null) { + policies.add(new SharedKeyCredentialPolicy(sharedKeyCredential)); + } else { + policies.add(new SASTokenCredentialPolicy(sasTokenCredential)); + } + + HttpPolicyProviders.addBeforeRetryPolicies(policies); + + policies.add(retryPolicy); + + policies.addAll(this.policies); + HttpPolicyProviders.addAfterRetryPolicies(policies); + policies.add(new HttpLoggingPolicy(logLevel)); + + HttpPipeline pipeline = HttpPipeline.builder() + .policies(policies.toArray(new HttpPipelinePolicy[0])) + .httpClient(httpClient) + .build(); + + return new DirectoryAsyncClient(endpoint, pipeline, shareName, directoryPath, snapshot); + } + + /** + * Creates a {@link DirectoryClient} based on options set in the builder. Every time {@code buildClient()} is + * called a new instance of {@link DirectoryClient} is created. + * + *

+ * If {@link DirectoryClientBuilder#pipeline(HttpPipeline) pipeline} is set, then the {@code pipeline} and + * {@link DirectoryClientBuilder#endpoint(String) endpoint} are used to create the + * {@link DirectoryClient client}. All other builder settings are ignored. + *

+ * + * @return A DirectoryClient with the options set from the builder. + * @throws NullPointerException If {@code endpoint}, {@code shareName} or {@code directoryPath} is {@code null}. + * @throws IllegalArgumentException If neither a {@link SharedKeyCredential} or {@link SASTokenCredential} has been set. + */ + public DirectoryClient buildClient() { + return new DirectoryClient(this.buildAsyncClient()); + } + + /** + * Sets the endpoint for the Azure Storage File instance that the client will interact with. + * + *

The first path segment, if the endpoint contains path segments, will be assumed to be the name of the share + * that the client will interact with. Rest of the path segments should be the path of the directory.

+ * + *

Query parameters of the endpoint will be parsed using {@link SASTokenCredential#fromQuery(String)} in an + * attempt to generate a {@link SASTokenCredential} to authenticate requests sent to the service.

+ * + * @param endpoint The URL of the Azure Storage File instance to send service requests to and receive responses from. + * @return the updated DirectoryClientBuilder object + * @throws IllegalArgumentException If {@code endpoint} is {@code null} or is an invalid URL + */ + public DirectoryClientBuilder endpoint(String endpoint) { + Objects.requireNonNull(endpoint); + try { + URL fullURL = new URL(endpoint); + this.endpoint = new URL(fullURL.getProtocol() + "://" + fullURL.getHost()); + String[] pathSegments = fullURL.getPath().split("/"); + int length = pathSegments.length; + this.shareName = length >= 2 ? pathSegments[1] : this.shareName; + this.directoryPath = length >= 3 ? pathSegments[2] : this.directoryPath; + + // Attempt to get the SAS token from the URL passed + this.sasTokenCredential = SASTokenCredential.fromQuery(fullURL.getQuery()); + if (this.sasTokenCredential != null) { + this.sharedKeyCredential = null; + } + } catch (MalformedURLException ex) { + throw new IllegalArgumentException("The Azure Storage Directory endpoint url is malformed."); + } + + return this; + } + + /** + * Sets the {@link SASTokenCredential} used to authenticate requests sent to the File service. + * + * @param credential SAS token credential generated from the Storage account that authorizes requests + * @return the updated DirectoryClientBuilder object + * @throws NullPointerException If {@code credential} is {@code null}. + */ + public DirectoryClientBuilder credential(SASTokenCredential credential) { + this.sasTokenCredential = Objects.requireNonNull(credential); + this.sharedKeyCredential = null; + return this; + } + + /** + * Sets the {@link SharedKeyCredential} used to authenticate requests sent to the File service. + * + * @param credential Shared key credential generated from the Storage account that authorizes requests + * @return the updated ShareClientBuilder object + * @throws NullPointerException If {@code credential} is {@code null}. + */ + public DirectoryClientBuilder credential(SharedKeyCredential credential) { + this.sharedKeyCredential = Objects.requireNonNull(credential); + this.sasTokenCredential = null; + return this; + } + + /** + * Creates a {@link SharedKeyCredential} from the {@code connectionString} used to authenticate requests sent to the + * File service. + * + * @param connectionString Connection string from the Access Keys section in the Storage account + * @return the updated DirectoryClientBuilder object + * @throws NullPointerException If {@code connectionString} is {@code null}. + */ + public DirectoryClientBuilder connectionString(String connectionString) { + Objects.requireNonNull(connectionString); + this.sharedKeyCredential = SharedKeyCredential.fromConnectionString(connectionString); + getEndPointFromConnectionString(connectionString); + return this; + } + + private void getEndPointFromConnectionString(String connectionString) { + Map connectionStringPieces = new HashMap<>(); + for (String connectionStringPiece : connectionString.split(";")) { + String[] kvp = connectionStringPiece.split("=", 2); + connectionStringPieces.put(kvp[0].toLowerCase(Locale.ROOT), kvp[1]); + } + String accountName = connectionStringPieces.get(ACCOUNT_NAME); + try { + this.endpoint = new URL(String.format("https://%s.file.core.windows.net", accountName)); + } catch (MalformedURLException e) { + throw new IllegalArgumentException(String.format("There is no valid endpoint for the connection string. " + + "Connection String: %s", connectionString)); + } + } + + /** + * Sets the share that the constructed clients will interact with + * + * @param shareName Name of the share + * @return the updated DirectoryClientBuilder object + * @throws NullPointerException If {@code shareName} is {@code null}. + */ + public DirectoryClientBuilder shareName(String shareName) { + this.shareName = shareName; + return this; + } + + /** + * Sets the directory that the constructed clients will interact with + * + * @param directoryPath Path to the directory + * @return the updated DirectoryClientBuilder object + * @throws NullPointerException If {@code directoryPath} is {@code null}. + */ + public DirectoryClientBuilder directoryPath(String directoryPath) { + this.directoryPath = directoryPath; + return this; + } + + /** + * Sets the HTTP client to use for sending and receiving requests to and from the service. + * + * @param httpClient The HTTP client to use for requests. + * @return The updated DirectoryClientBuilder object. + * @throws NullPointerException If {@code httpClient} is {@code null}. + */ + public DirectoryClientBuilder httpClient(HttpClient httpClient) { + this.httpClient = httpClient; + return this; + } + + /** + * Adds a policy to the set of existing policies that are executed after the {@link RetryPolicy}. + * + * @param pipelinePolicy The retry policy for service requests. + * @return The updated DirectoryClientBuilder object. + * @throws NullPointerException If {@code pipelinePolicy} is {@code null}. + */ + public DirectoryClientBuilder addPolicy(HttpPipelinePolicy pipelinePolicy) { + this.policies.add(Objects.requireNonNull(pipelinePolicy)); + return this; + } + + /** + * Sets the logging level for HTTP requests and responses. + * + * @param logLevel The amount of logging output when sending and receiving HTTP requests/responses. + * @return The updated DirectoryClientBuilder object. + */ + public DirectoryClientBuilder httpLogDetailLevel(HttpLogDetailLevel logLevel) { + this.logLevel = logLevel; + return this; + } + + /** + * Sets the HTTP pipeline to use for the service client. + * + *

If {@code pipeline} is set, all other settings are ignored, aside from {@link DirectoryClientBuilder#endpoint(String) endpoint}, + * {@link DirectoryClientBuilder#shareName(String) shareName} @{link DirectoryClientBuilder#directoryPath(String) filePath}, and {@link DirectoryClientBuilder#snapshot(String) snaphotShot} + * when building clients.

+ * + * @param pipeline The HTTP pipeline to use for sending service requests and receiving responses. + * @return The updated DirectoryClientBuilder object. + * @throws NullPointerException If {@code pipeline} is {@code null}. + */ + public DirectoryClientBuilder pipeline(HttpPipeline pipeline) { + this.pipeline = Objects.requireNonNull(pipeline); + return this; + } + + /** + * Sets the configuration object used to retrieve environment configuration values used to buildClient the client with + * when they are not set in the builder, defaults to Configuration.NONE + * @param configuration configuration store + * @return the updated DirectoryClientBuilder object + */ + public DirectoryClientBuilder configuration(Configuration configuration) { + this.configuration = configuration; + return this; + } -// connectionString, shareName, directoryName, FileClientOptions, SharedKeyCredential, + /** + * Sets the share snapshot that the constructed clients will interact with. This snapshot must be linked to the share + * that has been specified in the builder. + * + * @param snapshot Identifier of the share snapshot + * @return the updated DirectoryClientBuilder object. + */ + public DirectoryClientBuilder snapshot(String snapshot) { + this.snapshot = snapshot; + return this; + } } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/FileAsyncClient.java b/storage/client/file/src/main/java/com/azure/storage/file/FileAsyncClient.java index 0297818f4650b..2dd054d060a2e 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/FileAsyncClient.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/FileAsyncClient.java @@ -3,77 +3,734 @@ package com.azure.storage.file; +import com.azure.core.http.HttpPipeline; import com.azure.core.http.rest.Response; +import com.azure.core.http.rest.SimpleResponse; import com.azure.core.http.rest.VoidResponse; +import com.azure.core.implementation.util.FluxUtil; +import com.azure.core.util.Context; +import com.azure.core.util.logging.ClientLogger; +import com.azure.storage.common.credentials.SASTokenCredential; +import com.azure.storage.common.credentials.SharedKeyCredential; +import com.azure.storage.file.implementation.AzureFileStorageBuilder; +import com.azure.storage.file.implementation.AzureFileStorageImpl; +import com.azure.storage.file.models.CopyStatusType; import com.azure.storage.file.models.FileCopyInfo; import com.azure.storage.file.models.FileDownloadInfo; +import com.azure.storage.file.models.FileGetPropertiesHeaders; import com.azure.storage.file.models.FileHTTPHeaders; import com.azure.storage.file.models.FileInfo; +import com.azure.storage.file.models.FileMetadataInfo; import com.azure.storage.file.models.FileProperties; -import com.azure.storage.file.models.FileRangeInfo; +import com.azure.storage.file.models.FileRange; import com.azure.storage.file.models.FileRangeWriteType; import com.azure.storage.file.models.FileUploadInfo; +import com.azure.storage.file.models.FileUploadRangeHeaders; +import com.azure.storage.file.models.FilesCreateResponse; +import com.azure.storage.file.models.FilesDownloadResponse; +import com.azure.storage.file.models.FilesForceCloseHandlesResponse; +import com.azure.storage.file.models.FilesGetPropertiesResponse; +import com.azure.storage.file.models.FilesGetRangeListResponse; +import com.azure.storage.file.models.FilesListHandlesResponse; +import com.azure.storage.file.models.FilesSetHTTPHeadersResponse; +import com.azure.storage.file.models.FilesSetMetadataResponse; +import com.azure.storage.file.models.FilesStartCopyResponse; +import com.azure.storage.file.models.FilesUploadRangeResponse; import com.azure.storage.file.models.HandleItem; +import com.azure.storage.file.models.StorageErrorException; import io.netty.buffer.ByteBuf; +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.channels.AsynchronousFileChannel; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.time.Duration; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeoutException; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; -import java.util.Map; - +/** + * This class provides a client that contains all the operations for interacting with file in Azure Storage File Service. + * Operations allowed by the client are creating, copying, uploading, downloading, deleting and listing on a file, retrieving properties, setting metadata + * and list or force close handles of the file. + * + *

Instantiating an Asynchronous File Client

+ * + * {@codesnippet com.azure.storage.file.fileAsyncClient.instantiation} + * + *

View {@link FileClientBuilder this} for additional ways to construct the client.

+ * + * @see FileClientBuilder + * @see FileClient + * @see SharedKeyCredential + * @see SASTokenCredential + */ public class FileAsyncClient { - FileAsyncClient() { - throw new UnsupportedOperationException(); + private final ClientLogger logger = new ClientLogger(FileAsyncClient.class); + private static final long FILE_DEFAULT_BLOCK_SIZE = 4 * 1024 * 1024L; + private static final long DOWNLOAD_UPLOAD_CHUNK_TIMEOUT = 300; + + private final AzureFileStorageImpl azureFileStorageClient; + private final String shareName; + private final String filePath; + private final String snapshot; + + /** + * Creates a FileAsyncClient that sends requests to the storage file at {@link AzureFileStorageImpl#url() endpoint}. + * Each service call goes through the {@link HttpPipeline pipeline} in the {@code client}. + * @param azureFileStorageClient Client that interacts with the service interfaces + * @param shareName Name of the share + * @param filePath Path to the file + * @param snapshot The snapshot of the share + */ + FileAsyncClient(AzureFileStorageImpl azureFileStorageClient, String shareName, String filePath, String snapshot) { + this.shareName = shareName; + this.filePath = filePath; + this.snapshot = snapshot; + this.azureFileStorageClient = azureFileStorageClient; + } + + /** + * Creates a FileAsyncClient that sends requests to the storage account at {@code endpoint}. + * Each service call goes through the {@code httpPipeline}. + * @param endpoint URL for the Storage File service + * @param httpPipeline HttpPipeline that HTTP requests and response flow through + * @param shareName Name of the share + * @param filePath Path to the file + * @param snapshot Optional snapshot of the share + */ + FileAsyncClient(URL endpoint, HttpPipeline httpPipeline, String shareName, String filePath, String snapshot) { + this.shareName = shareName; + this.filePath = filePath; + this.snapshot = snapshot; + this.azureFileStorageClient = new AzureFileStorageBuilder().pipeline(httpPipeline) + .url(endpoint.toString()) + .build(); } - public static FileClientBuilder asyncBuilder() { - throw new UnsupportedOperationException(); + /** + * Get the url of the storage file client. + * @return the URL of the storage file client + * @throws RuntimeException If the file is using a malformed URL. + */ + public URL getFileUrl() { + try { + return new URL(azureFileStorageClient.url()); + } catch (MalformedURLException e) { + throw new RuntimeException(String.format("Invalid URL on %s: %s" + getClass().getSimpleName(), + azureFileStorageClient.url()), e); + } } + /** + * Creates a file in the storage account and returns a response of {@link FileInfo} to interact with it. + * + *

Code Samples

+ * + *

Create the file with size 1KB.

+ * + * {@codesnippet com.azure.storage.file.fileClient.create} + * + * @param maxSize The maximum size in bytes for the file, up to 1 TiB. + * @return A response containing the file info and the status of creating the file. + * @throws StorageErrorException If the file has already existed, the parent directory does not exist or fileName is an invalid resource name. + */ + public Mono> create(long maxSize) { + return create(maxSize, null, null); + } + + /** + * Creates a file in the storage account and returns a response of FileInfo to interact with it. + * + *

Code Samples

+ * + *

Create the file with length of 1024 bytes, some headers and metadata.

+ * + * {@codesnippet com.azure.storage.file.fileAsyncClient.create#long-filehttpheaders-map} + * + * @param maxSize The maximum size in bytes for the file, up to 1 TiB. + * @param httpHeaders Additional parameters for the operation. + * @param metadata Optional name-value pairs associated with the file as metadata. Metadata names must adhere to the naming rules. + * @see C# identifiers + * @return A response containing the directory info and the status of creating the directory. + * @throws StorageErrorException If the directory has already existed, the parent directory does not exist or directory is an invalid resource name. + */ public Mono> create(long maxSize, FileHTTPHeaders httpHeaders, Map metadata) { - throw new UnsupportedOperationException(); + return azureFileStorageClient.files().createWithRestResponseAsync(shareName, filePath, maxSize, null, metadata, httpHeaders, Context.NONE) + .map(this::createFileInfoResponse); } + /** + * Copies a blob or file to a destination file within the storage account. + * + *

Code Samples

+ * + *

Copy file from source url to the {@code filePath}

+ * + * {@codesnippet com.azure.storage.file.fileAsyncClient.startCopy#string-map} + * + * @param sourceUrl Specifies the URL of the source file or blob, up to 2 KB in length. + * @param metadata Optional name-value pairs associated with the file as metadata. Metadata names must adhere to the naming rules. + * * @see C# identifiers + * @return A response containing the file copy info and the status of copying the file. + */ public Mono> startCopy(String sourceUrl, Map metadata) { - throw new UnsupportedOperationException(); + return azureFileStorageClient.files().startCopyWithRestResponseAsync(shareName, filePath, sourceUrl, null, metadata, Context.NONE) + .map(this::startCopyResponse); } + /** + * Aborts a pending Copy File operation, and leaves a destination file with zero length and full metadata. + * + *

Code Samples

+ * + *

Abort copy file from copy id("someCopyId")

+ * + * {@codesnippet com.azure.storage.file.fileAsyncClient.abortCopy#string} + * + * @param copyId Specifies the copy id which has copying pending status associate with it. + * @return A response containing the status of aborting copy the file. + */ public Mono abortCopy(String copyId) { - throw new UnsupportedOperationException(); + return azureFileStorageClient.files().abortCopyWithRestResponseAsync(shareName, filePath, copyId, Context.NONE) + .map(VoidResponse::new); + } + + /** + * Downloads a file from the system, including its metadata and properties + * + *

Code Samples

+ * + *

Download the file to current folder.

+ * + * {@codesnippet com.azure.storage.file.fileAsyncClient.downloadToFile#string} + * + * @param downloadFilePath The path where store the downloaded file + * @return An empty response. + */ + public Mono downloadToFile(String downloadFilePath) { + return downloadToFile(downloadFilePath, null); + } + + /** + * Downloads a file from the system, including its metadata and properties + * + *

Code Samples

+ * + *

Download the file from 1024 to 2048 bytes to current folder.

+ * + * {@codesnippet com.azure.storage.file.fileAsyncClient.downloadToFile#string-filerange} + * + * @param downloadFilePath The path where store the downloaded file + * @param range Optional byte range which returns file data only from the specified range. + * @return An empty response. + */ + public Mono downloadToFile(String downloadFilePath, FileRange range) { + AsynchronousFileChannel channel = channelSetup(downloadFilePath); + return sliceFileRange(range) + .flatMap(chunk -> downloadWithProperties(chunk, false) + .map(dar -> dar.value().body()) + .subscribeOn(Schedulers.elastic()) + .flatMap(fbb -> FluxUtil.bytebufStreamToFile(fbb, channel, chunk.start() - (range == null ? 0 : range.start())) + .subscribeOn(Schedulers.elastic()) + .timeout(Duration.ofSeconds(DOWNLOAD_UPLOAD_CHUNK_TIMEOUT)) + .retry(3, throwable -> throwable instanceof IOException || throwable instanceof TimeoutException))) + .then() + .doOnTerminate(() -> channelCleanUp(channel)); + } + + private AsynchronousFileChannel channelSetup(String filePath) { + try { + return AsynchronousFileChannel.open(Paths.get(filePath), StandardOpenOption.READ, StandardOpenOption.WRITE); + } catch (IOException e) { + throw new UncheckedIOException(e); + } } - public Mono> downloadWithProperties(long offset, long length, boolean rangeGetContentMD5) { - throw new UnsupportedOperationException(); + private void channelCleanUp(AsynchronousFileChannel channel) { + try { + channel.close(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } } + private Flux sliceFileRange(FileRange fileRange) { + long offset = fileRange == null ? 0L : fileRange.start(); + Mono end; + if (fileRange != null) { + end = Mono.just(fileRange.end()); + } else { + end = Mono.empty(); + } + end = end.switchIfEmpty(getProperties().map(rb -> rb.value().contentLength())); + return end + .map(e -> { + List chunks = new ArrayList<>(); + for (long pos = offset; pos < e; pos += FILE_DEFAULT_BLOCK_SIZE) { + long count = FILE_DEFAULT_BLOCK_SIZE; + if (pos + count > e) { + count = e - pos; + } + chunks.add(new FileRange(pos, pos + count - 1)); + } + return chunks; + }) + .flatMapMany(Flux::fromIterable); + } + + /** + * Downloads a file from the system, including its metadata and properties + * + *

Code Samples

+ * + *

Download the file with its metadata and properties.

+ * + * {@codesnippet com.azure.storage.file.fileAsyncClient.downloadWithProperties} + * + * @return A response that only contains headers and response status code + */ + public Mono> downloadWithProperties() { + return downloadWithProperties(null, null); + } + + /** + * Downloads a file from the system, including its metadata and properties + * + *

Code Samples

+ * + *

Download the file from 1024 to 2048 bytes with its metadata and properties and without the contentMD5.

+ * + * {@codesnippet com.azure.storage.file.fileAsyncClient.downloadWithProperties#filerange-boolean} + * + * @param range Optional byte range which returns file data only from the specified range. + * @param rangeGetContentMD5 Optional boolean which the service returns the MD5 hash for the range when it sets to true, as long as the range is less than or equal to 4 MB in size. + * @return A response that only contains headers and response status code + */ + public Mono> downloadWithProperties(FileRange range, Boolean rangeGetContentMD5) { + String rangeString = range == null ? null : range.toString(); + return azureFileStorageClient.files().downloadWithRestResponseAsync(shareName, filePath, null, rangeString, rangeGetContentMD5, Context.NONE) + .map(this::downloadWithPropertiesResponse); + } + + /** + * Deletes the file associate with the client. + * + *

Code Samples

+ * + *

Delete the file

+ * + * {@codesnippet com.azure.storage.file.fileAsyncClient.delete} + * + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the directory doesn't exist or the file doesn't exist. + */ public Mono delete() { - throw new UnsupportedOperationException(); + return azureFileStorageClient.files().deleteWithRestResponseAsync(shareName, filePath, Context.NONE) + .map(VoidResponse::new); } - public Mono> getProperties(String shareSnapshot) { - throw new UnsupportedOperationException(); + /** + * Retrieves the properties of the storage account's file. + * The properties includes file metadata, last modified date, is server encrypted, and eTag. + * + *

Code Samples

+ * + *

Retrieve file properties

+ * + * {@codesnippet com.azure.storage.file.fileAsyncClient.getProperties} + * + * @return Storage file properties + */ + public Mono> getProperties() { + return azureFileStorageClient.files().getPropertiesWithRestResponseAsync(shareName, filePath, snapshot, null, Context.NONE) + .map(this::getPropertiesResponse); } + /** + * Sets the user-defined httpHeaders to associate to the file. + * + *

If {@code null} is passed for the httpHeaders it will clear the httpHeaders associated to the file.

+ * + *

Code Samples

+ * + *

Set the httpHeaders of contentType of "text/plain"

+ * + * {@codesnippet com.azure.storage.file.fileAsyncClient.setHttpHeaders#long-filehttpheaders} + * + *

Clear the metadata of the file

+ * + * {@codesnippet com.azure.storage.file.fileAsyncClient.setHttpHeaders#long-filehttpheaders.clearHttpHeaders} + * + * @param newFileSize New file size of the file + * @param httpHeaders Resizes a file to the specified size. If the specified byte value is less than the current size of the file, then all ranges above the specified byte value are cleared. + * @return Response of the information about the file + * @throws IllegalArgumentException thrown if parameters fail the validation. + */ public Mono> setHttpHeaders(long newFileSize, FileHTTPHeaders httpHeaders) { - throw new UnsupportedOperationException(); + return azureFileStorageClient.files().setHTTPHeadersWithRestResponseAsync(shareName, filePath, null, newFileSize, httpHeaders, Context.NONE) + .map(this::setHttpHeadersResponse); + } + + /** + * Sets the user-defined metadata to associate to the file. + * + *

If {@code null} is passed for the metadata it will clear the metadata associated to the file.

+ * + *

Code Samples

+ * + *

Set the metadata to "file:updatedMetadata"

+ * + * {@codesnippet com.azure.storage.file.fileAsyncClient.setMetadata#map} + * + *

Clear the metadata of the file

+ * + * {@codesnippet com.azure.storage.file.fileAsyncClient.setMetadata#map.clearMetadata} + * + * @param metadata Options.Metadata to set on the file, if null is passed the metadata for the file is cleared + * @return information about the file + * @throws StorageErrorException If the file doesn't exist or the metadata contains invalid keys + */ + public Mono> setMetadata(Map metadata) { + return azureFileStorageClient.files().setMetadataWithRestResponseAsync(shareName, filePath, null, metadata, Context.NONE) + .map(this::setMeatadataResponse); + } + + /** + * Uploads a range of bytes to the beginning of a file in storage file service. Upload operations performs an in-place write on the specified file. + * + *

Code Samples

+ * + *

Upload "default" to the file.

+ * + * {@codesnippet com.azure.storage.file.fileAsyncClient.upload#flux-long} + * + * @param data The data which will upload to the storage file. + * @param length Specifies the number of bytes being transmitted in the request body. When the FileRangeWriteType is set to clear, the value of this header must be set to zero.. + * @return A response that only contains headers and response status code + * @throws StorageErrorException If you attempt to upload a range that is larger than 4 MB, the service returns status code 413 (Request Entity Too Large) + */ + public Mono> upload(Flux data, long length) { + FileRange range = new FileRange(0, length - 1); + return azureFileStorageClient.files().uploadRangeWithRestResponseAsync(shareName, filePath, range.toString(), FileRangeWriteType.UPDATE, length, data, null, null, Context.NONE) + .map(this::uploadResponse); + } + + /** + * Uploads a range of bytes to specific of a file in storage file service. Upload operations performs an in-place write on the specified file. + * + *

Code Samples

+ * + *

Upload the file from 1024 to 2048 bytes with its metadata and properties and without the contentMD5.

+ * + * {@codesnippet com.azure.storage.file.fileAsyncClient.upload#bytebuf-long-int-filerangewritetype} + * + * @param data The data which will upload to the storage file. + * @param offset Optional starting point of the upload range. It will start from the beginning if it is {@code null} + * @param length Specifies the number of bytes being transmitted in the request body. When the FileRangeWriteType is set to clear, the value of this header must be set to zero. + * @param type You may specify one of the following options: + *
    + *
  • Update: Writes the bytes specified by the request body into the specified range.
  • + *
  • Clear: Clears the specified range and releases the space used in storage for that range. To clear a range, set the Content-Length header to zero.
  • + *
      + * @return A response that only contains headers and response status code + * @throws StorageErrorException If you attempt to upload a range that is larger than 4 MB, the service returns status code 413 (Request Entity Too Large) + */ + public Mono> upload(Flux data, long length, long offset, FileRangeWriteType type) { + FileRange range = new FileRange(offset, offset + length - 1); + return azureFileStorageClient.files().uploadRangeWithRestResponseAsync(shareName, filePath, range.toString(), type, length, data, null, null, Context.NONE) + .map(this::uploadResponse); + } + + /** + * Uploads file to storage file service. + * + *

      Code Samples

      + * + *

      Upload the file from the source file path.

      + * + * {@codesnippet com.azure.storage.file.fileAsyncClient.uploadFromFile#string} + * + * @param uploadFilePath The path where store the source file to upload + * @return An empty response. + */ + public Mono uploadFromFile(String uploadFilePath) { + return uploadFromFile(uploadFilePath, FileRangeWriteType.UPDATE); } - public Mono> setMeatadata(Map meatadata) { - throw new UnsupportedOperationException(); + /** + * Uploads file to storage file service. + * + *

      Code Samples

      + * + *

      Upload the file from the source file path.

      + * + * (@codesnippet com.azure.storage.file.fileAsyncClient.uploadFromFile#string-filerangewritetype} + * + * @param uploadFilePath The path where store the source file to upload + * @param type You may specify one of the following options: + *
        + *
      • Update: Writes the bytes specified by the request body into the specified range.
      • + *
      • Clear: Clears the specified range and releases the space used in storage for that range. To clear a range, set the Content-Length header to zero.
      • + *
          + * + * @return An empty response. + * @throws UncheckedIOException If an I/O error occurs. + */ + public Mono uploadFromFile(String uploadFilePath, FileRangeWriteType type) { + AsynchronousFileChannel channel = channelSetup(uploadFilePath); + return Flux.fromIterable(sliceFile(uploadFilePath)) + .flatMap(chunk -> { + return upload(FluxUtil.byteBufStreamFromFile(channel, chunk.start(), chunk.end() - chunk.start() + 1), chunk.end() - chunk.start() + 1, chunk.start(), type) + .timeout(Duration.ofSeconds(DOWNLOAD_UPLOAD_CHUNK_TIMEOUT)) + .retry(3, throwable -> throwable instanceof IOException || throwable instanceof TimeoutException); + }) + .then() + .doOnTerminate(() -> channelCleanUp(channel)); } - public Mono> upload(FileRangeWriteType type, long offset, long length, Flux data) { - throw new UnsupportedOperationException(); + private List sliceFile(String path) { + File file = new File(path); + assert file.exists(); + List ranges = new ArrayList<>(); + for (long pos = 0; pos < file.length(); pos += FILE_DEFAULT_BLOCK_SIZE) { + long count = FILE_DEFAULT_BLOCK_SIZE; + if (pos + count > file.length()) { + count = file.length() - pos; + } + ranges.add(new FileRange(pos, pos + count - 1)); + } + return ranges; } - public Flux listRanges(long offset, long length, String shareSnapshot) { - throw new UnsupportedOperationException(); + /** + * List of valid ranges for a file. + * + *

          Code Samples

          + * + *

          List all ranges for the file client.

          + * + * {@codesnippet com.azure.storage.file.fileAsyncClient.listRanges} + * + * @return {@link FileRange ranges} in the files. + */ + public Flux listRanges() { + return azureFileStorageClient.files().getRangeListWithRestResponseAsync(shareName, filePath, snapshot, null, null, Context.NONE) + .flatMapMany(this::convertListRangesResponseToFileRangeInfo); } - public Flux listHandles(int maxResults) { - throw new UnsupportedOperationException(); + /** + * List of valid ranges for a file. + * + *

          Code Samples

          + * + *

          List all ranges within the file range from 1KB to 2KB.

          + * + * {@codesnippet com.azure.storage.file.fileAsyncClient.listRanges#filerange} + * + * @param range Optional byte range which returns file data only from the specified range. + * @return {@link FileRange ranges} in the files that satisfy the requirements + */ + public Flux listRanges(FileRange range) { + String rangeString = range == null ? null : range.toString(); + return azureFileStorageClient.files().getRangeListWithRestResponseAsync(shareName, filePath, snapshot, null, rangeString, Context.NONE) + .flatMapMany(this::convertListRangesResponseToFileRangeInfo); } + /** + * List of open handles on a file. + * + *

          Code Samples

          + * + *

          List all handles for the file client.

          + * + * {@codesnippet com.azure.storage.file.fileAsyncClient.listHandles} + * + * @return {@link HandleItem handles} in the files that satisfy the requirements + */ + public Flux listHandles() { + return listHandles(null); + } + + /** + * List of open handles on a file. + * + *

          Code Samples

          + * + *

          List 10 handles for the file client.

          + * + * {@codesnippet com.azure.storage.file.fileAsyncClient.listHandles#integer} + * + * @param maxResults Optional maximum number of results will return per page + * @return {@link HandleItem handles} in the file that satisfy the requirements + */ + public Flux listHandles(Integer maxResults) { + return azureFileStorageClient.files().listHandlesWithRestResponseAsync(shareName, filePath, null, maxResults, null, snapshot, Context.NONE) + .flatMapMany(response -> nextPageForHandles(response, maxResults)); + } + + /** + * Closes a handle or handles opened on a file at the service. It is intended to be used alongside {@link FileAsyncClient#listHandles()} (Integer)} . + * TODO: Will change the return type to how many handles have been closed. Implement one more API to force close all handles. + * TODO: @see Github Issue 4525 + * + *

          Code Samples

          + * + *

          Force close handles with handles returned by list handles in recursive.

          + * + * {@codesnippet com.azure.storage.file.fileAsyncClient.forceCloseHandles#string} + * + * @param handleId Specifies the handle ID to be closed. Use an asterisk ('*') as a wildcard string to specify all handles. + * @return The counts of number of handles closed + */ public Flux forceCloseHandles(String handleId) { - throw new UnsupportedOperationException(); + return azureFileStorageClient.files().forceCloseHandlesWithRestResponseAsync(shareName, filePath, handleId, null, null, snapshot, Context.NONE) + .flatMapMany(response -> nextPageForForceCloseHandles(response, handleId)); + } + + /** + * Get snapshot id which attached to {@link FileAsyncClient}. + * Return {@code null} if no snapshot id attached. + * + *

          Code Samples

          + * + *

          Get the share snapshot id.

          + * + * {@codesnippet com.azure.storage.file.fileAsyncClient.getShareSnapshotId} + * + * @return The snapshot id which is a unique {@code DateTime} value that identifies the share snapshot to its base share. + */ + public String getShareSnapshotId() { + return this.snapshot; + } + + private Flux nextPageForForceCloseHandles(final FilesForceCloseHandlesResponse response, final String handleId) { + List handleCount = Arrays.asList(response.deserializedHeaders().numberOfHandlesClosed()); + + if (response.deserializedHeaders().marker() == null) { + return Flux.fromIterable(handleCount); + } + Mono listResponse = azureFileStorageClient.files().forceCloseHandlesWithRestResponseAsync(shareName, filePath, handleId, null, response.deserializedHeaders().marker(), snapshot, Context.NONE); + Flux fileRefPublisher = listResponse.flatMapMany(newResponse -> nextPageForForceCloseHandles(newResponse, handleId)); + return Flux.fromIterable(handleCount).concatWith(fileRefPublisher); + } + + private Flux nextPageForHandles(final FilesListHandlesResponse response, final Integer maxResults) { + List handleItems = response.value().handleList(); + + if (response.value().nextMarker() == null) { + return Flux.fromIterable(handleItems); + } + + Mono listResponse = azureFileStorageClient.files().listHandlesWithRestResponseAsync(shareName, filePath, response.value().nextMarker(), maxResults, null, snapshot, Context.NONE); + Flux fileRefPublisher = listResponse.flatMapMany(newResponse -> nextPageForHandles(newResponse, maxResults)); + return Flux.fromIterable(handleItems).concatWith(fileRefPublisher); + } + + private Response createFileInfoResponse(final FilesCreateResponse response) { + String eTag = response.deserializedHeaders().eTag(); + OffsetDateTime lastModified = response.deserializedHeaders().lastModified(); + boolean isServerEncrypted = response.deserializedHeaders().isServerEncrypted(); + FileInfo fileInfo = new FileInfo(eTag, lastModified, isServerEncrypted); + return new SimpleResponse<>(response, fileInfo); + } + + private Response startCopyResponse(final FilesStartCopyResponse response) { + String eTag = response.deserializedHeaders().eTag(); + OffsetDateTime lastModified = response.deserializedHeaders().lastModified(); + String copyId = response.deserializedHeaders().copyId(); + CopyStatusType copyStatus = response.deserializedHeaders().copyStatus(); + FileCopyInfo fileCopyInfo = new FileCopyInfo(eTag, lastModified, copyId, copyStatus); + return new SimpleResponse<>(response, fileCopyInfo); + } + + private Response setHttpHeadersResponse(final FilesSetHTTPHeadersResponse response) { + String eTag = response.deserializedHeaders().eTag(); + OffsetDateTime lastModified = response.deserializedHeaders().lastModified(); + boolean isServerEncrypted = response.deserializedHeaders().isServerEncrypted(); + FileInfo fileInfo = new FileInfo(eTag, lastModified, isServerEncrypted); + return new SimpleResponse<>(response, fileInfo); + } + private Response downloadWithPropertiesResponse(final FilesDownloadResponse response) { + String eTag = response.deserializedHeaders().eTag(); + OffsetDateTime lastModified = response.deserializedHeaders().lastModified(); + Map metadata = response.deserializedHeaders().metadata(); + Long contentLength = response.deserializedHeaders().contentLength(); + String contentType = response.deserializedHeaders().contentType(); + String contentRange = response.deserializedHeaders().contentRange(); + Flux body = response.value(); + FileDownloadInfo fileDownloadInfo = new FileDownloadInfo(eTag, lastModified, metadata, contentLength, contentType, contentRange, body); + return new SimpleResponse<>(response, fileDownloadInfo); + } + + private Response getPropertiesResponse(final FilesGetPropertiesResponse response) { + FileGetPropertiesHeaders headers = response.deserializedHeaders(); + String eTag = headers.eTag(); + OffsetDateTime lastModified = headers.lastModified(); + Map metadata = headers.metadata(); + String fileType = headers.fileType(); + Long contentLength = headers.contentLength(); + String contentType = headers.contentType(); + byte[] contentMD5; + try { + contentMD5 = headers.contentMD5(); + } catch (NullPointerException e) { + contentMD5 = null; + } + String contentEncoding = headers.contentEncoding(); + String cacheControl = headers.cacheControl(); + String contentDisposition = headers.contentDisposition(); + OffsetDateTime copyCompletionTime = headers.copyCompletionTime(); + String copyStatusDescription = headers.copyStatusDescription(); + String copyId = headers.copyId(); + String copyProgress = headers.copyProgress(); + String copySource = headers.copySource(); + CopyStatusType copyStatus = headers.copyStatus(); + Boolean isServerEncrpted = headers.isServerEncrypted(); + FileProperties fileProperties = new FileProperties(eTag, lastModified, metadata, fileType, contentLength, + contentType, contentMD5, contentEncoding, cacheControl, contentDisposition, copyCompletionTime, copyStatusDescription, + copyId, copyProgress, copySource, copyStatus, isServerEncrpted); + return new SimpleResponse<>(response, fileProperties); + } + + private Response uploadResponse(final FilesUploadRangeResponse response) { + FileUploadRangeHeaders headers = response.deserializedHeaders(); + String eTag = headers.eTag(); + OffsetDateTime lastModified = headers.lastModified(); + byte[] contentMD5; + try { + contentMD5 = headers.contentMD5(); + } catch (NullPointerException e) { + contentMD5 = null; + } + Boolean isServerEncrypted = headers.isServerEncrypted(); + FileUploadInfo fileUploadInfo = new FileUploadInfo(eTag, lastModified, contentMD5, isServerEncrypted); + return new SimpleResponse<>(response, fileUploadInfo); + } + + private Response setMeatadataResponse(final FilesSetMetadataResponse response) { + String eTag = response.deserializedHeaders().eTag(); + boolean isServerEncrypted = response.deserializedHeaders().isServerEncrypted(); + FileMetadataInfo fileMetadataInfo = new FileMetadataInfo(eTag, isServerEncrypted); + return new SimpleResponse<>(response, fileMetadataInfo); + } + + private Flux convertListRangesResponseToFileRangeInfo(FilesGetRangeListResponse response) { + List fileRanges = new ArrayList<>(); + response.value().forEach(range -> { + long start = range.start(); + long end = range.end(); + fileRanges.add(new FileRange(start, end)); + }); + return Flux.fromIterable(fileRanges); } } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/FileClient.java b/storage/client/file/src/main/java/com/azure/storage/file/FileClient.java index 3d6bacb4ff3ea..4736673bee43e 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/FileClient.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/FileClient.java @@ -5,78 +5,444 @@ import com.azure.core.http.rest.Response; import com.azure.core.http.rest.VoidResponse; +import com.azure.storage.common.credentials.SASTokenCredential; +import com.azure.storage.common.credentials.SharedKeyCredential; import com.azure.storage.file.models.FileCopyInfo; import com.azure.storage.file.models.FileDownloadInfo; import com.azure.storage.file.models.FileHTTPHeaders; import com.azure.storage.file.models.FileInfo; +import com.azure.storage.file.models.FileMetadataInfo; import com.azure.storage.file.models.FileProperties; -import com.azure.storage.file.models.FileRangeInfo; +import com.azure.storage.file.models.FileRange; import com.azure.storage.file.models.FileRangeWriteType; import com.azure.storage.file.models.FileUploadInfo; import com.azure.storage.file.models.HandleItem; +import com.azure.storage.file.models.StorageErrorException; import io.netty.buffer.ByteBuf; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - +import java.net.URL; import java.util.Map; +import reactor.core.publisher.Flux; +/** + * This class provides a client that contains all the operations for interacting files under Azure Storage File Service. + * Operations allowed by the client are creating, uploading, copying, listing, downloading, and deleting files. + * + *

          Instantiating a synchronous File Client

          + * + * {@codesnippet com.azure.storage.file.fileClient.instantiation} + * + *

          View {@link FileClientBuilder this} for additional ways to construct the client.

          + * + * @see FileClientBuilder + * @see FileAsyncClient + * @see SharedKeyCredential + * @see SASTokenCredential + */ public class FileClient { - private final FileAsyncClient client; + private final FileAsyncClient fileAsyncClient; + + /** + * Creates a FileClient that wraps a FileAsyncClient and blocks requests. + * + * @param fileAsyncClient FileAsyncClient that is used to send requests + */ + FileClient(FileAsyncClient fileAsyncClient) { + this.fileAsyncClient = fileAsyncClient; + } + + /** + * Get the url of the storage file client. + * @return the URL of the storage file client. + * @throws RuntimeException If the file is using a malformed URL. + */ + public URL getFileUrl() { + return fileAsyncClient.getFileUrl(); + } + + /** + * Creates a file in the storage account and returns a response of {@link FileInfo} to interact with it. + * + *

          Code Samples

          + * + *

          Create the file with length of 1024 bytes, some headers and metadata.

          + * + * {@codesnippet com.azure.storage.file.fileClient.create} + * + * @param maxSize The maximum size in bytes for the file, up to 1 TiB. + * @return A response containing the file info and the status of creating the file. + * @throws StorageErrorException If the file has already existed, the parent directory does not exist or fileName is an invalid resource name. + */ + public Response create(long maxSize) { + return fileAsyncClient.create(maxSize).block(); + } + + /** + * Creates a file in the storage account and returns a response of FileInfo to interact with it. + * + *

          Code Samples

          + * + *

          Create the file with length of 1024 bytes, some headers and metadata.

          + * + * {@codesnippet com.azure.storage.file.fileClient.create#long-filehttpheaders-map} + * + * @param maxSize The maximum size in bytes for the file, up to 1 TiB. + * @param httpHeaders Additional parameters for the operation. + * @param metadata Optional name-value pairs associated with the file as metadata. Metadata names must adhere to the naming rules. + * @see C# identifiers + * @return A response containing the directory info and the status of creating the directory. + * @throws StorageErrorException If the directory has already existed, the parent directory does not exist or directory is an invalid resource name. + */ + public Response create(long maxSize, FileHTTPHeaders httpHeaders, Map metadata) { + return fileAsyncClient.create(maxSize, httpHeaders, metadata).block(); + } + + /** + * Copies a blob or file to a destination file within the storage account. + * + *

          Code Samples

          + * + *

          Copy file from source getDirectoryUrl to the {@code filePath}

          + * + * {@codesnippet com.azure.storage.file.fileClient.startCopy#string-map} + * + * @param sourceUrl Specifies the URL of the source file or blob, up to 2 KB in length. + * @param metadata Optional name-value pairs associated with the file as metadata. Metadata names must adhere to the naming rules. + * @see C# identifiers + * @return A response containing the file copy info and the status of copying the file. + */ + public Response startCopy(String sourceUrl, Map metadata) { + return fileAsyncClient.startCopy(sourceUrl, metadata).block(); + } + + /** + * Aborts a pending Copy File operation, and leaves a destination file with zero length and full metadata. + * + *

          Code Samples

          + * + *

          Abort copy file from copy id("someCopyId")

          + * + * {@codesnippet com.azure.storage.file.fileClient.abortCopy#string} + * + * @param copyId Specifies the copy id which has copying pending status associate with it. + * @return A response containing the status of aborting copy the file. + */ + public VoidResponse abortCopy(String copyId) { + return fileAsyncClient.abortCopy(copyId).block(); + } + + /** + * Downloads a file from the system, including its metadata and properties + * + *

          Code Samples

          + * + *

          Download the file to current folder.

          + * + * {@codesnippet com.azure.storage.file.fileClient.downloadToFile#string} + * + * @param downloadFilePath The path where store the downloaded file + */ + public void downloadToFile(String downloadFilePath) { + downloadToFile(downloadFilePath, null); + } + + /** + * Downloads a file from the system, including its metadata and properties + * + *

          Code Samples

          + * + *

          Download the file from 1024 to 2048 bytes to current folder.

          + * + * {@codesnippet com.azure.storage.file.fileClient.downloadToFile#string-filerange} + * + * @param downloadFilePath The path where store the downloaded file + * @param range Optional byte range which returns file data only from the specified range. + */ + public void downloadToFile(String downloadFilePath, FileRange range) { + fileAsyncClient.downloadToFile(downloadFilePath, range).block(); + } + + /** + * Downloads a file from the system, including its metadata and properties + * + *

          Code Samples

          + * + *

          Download the file with its metadata and properties.

          + * + * {@codesnippet com.azure.storage.file.fileClient.downloadWithProperties} + * + * @return A response that only contains headers and response status code + */ + public Response downloadWithProperties() { + return fileAsyncClient.downloadWithProperties(null, null).block(); + } + + /** + * Downloads a file from the system, including its metadata and properties + * + *

          Code Samples

          + * + *

          Download the file from 1024 to 2048 bytes with its metadata and properties and without the contentMD5.

          + * + * {@codesnippet com.azure.storage.file.fileClient.downloadWithProperties#filerange-boolean} + * + * @param range Optional byte range which returns file data only from the specified range. + * @param rangeGetContentMD5 Optional boolean which the service returns the MD5 hash for the range when it sets to true, as long as the range is less than or equal to 4 MB in size. + * @return A response that only contains headers and response status code + */ + public Response downloadWithProperties(FileRange range, Boolean rangeGetContentMD5) { + return fileAsyncClient.downloadWithProperties(range, rangeGetContentMD5).block(); + } - FileClient() { - throw new UnsupportedOperationException(); + /** + * Deletes the file associate with the client. + * + *

          Code Samples

          + * + *

          Delete the file

          + * + * {@codesnippet com.azure.storage.file.fileClient.delete} + * + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the directory doesn't exist or the file doesn't exist. + */ + public VoidResponse delete() { + return fileAsyncClient.delete().block(); } - public static FileClientBuilder syncBuilder() { - throw new UnsupportedOperationException(); + /** + * Retrieves the properties of the storage account's file. + * The properties includes file metadata, last modified date, is server encrypted, and eTag. + * + *

          Code Samples

          + * + *

          Retrieve file properties

          + * + * {@codesnippet com.azure.storage.file.fileClient.getProperties} + * + * @return Storage file properties + */ + public Response getProperties() { + return fileAsyncClient.getProperties().block(); } - public Mono> create(long maxSize, FileHTTPHeaders httpHeaders, Map metadata) { - throw new UnsupportedOperationException(); + /** + * Sets the user-defined httpHeaders to associate to the file. + * + *

          If {@code null} is passed for the httpHeaders it will clear the httpHeaders associated to the file.

          + * + *

          Code Samples

          + * + *

          Set the httpHeaders of contentType of "text/plain"

          + * + * {@codesnippet com.azure.storage.file.fileClient.setHttpHeaders#long-filehttpheaders} + * + *

          Clear the httpHeaders of the file

          + * + * {@codesnippet com.azure.storage.file.fileClient.setHttpHeaders#long-filehttpheaders.clearHttpHeaders} + * + * @param newFileSize New file size of the file + * @param httpHeaders Resizes a file to the specified size. If the specified byte value is less than the current size of the file, then all ranges above the specified byte value are cleared. + * @return Response of the information about the file + * @throws IllegalArgumentException thrown if parameters fail the validation. + */ + public Response setHttpHeaders(long newFileSize, FileHTTPHeaders httpHeaders) { + return fileAsyncClient.setHttpHeaders(newFileSize, httpHeaders).block(); } - public Mono> startCopy(String sourceUrl, Map metadata) { - throw new UnsupportedOperationException(); + /** + * Sets the user-defined metadata to associate to the file. + * + *

          If {@code null} is passed for the metadata it will clear the metadata associated to the file.

          + * + *

          Code Samples

          + * + *

          Set the metadata to "file:updatedMetadata"

          + * + * {@codesnippet com.azure.storage.file.fileClient.setMetadata#map} + * + *

          Clear the metadata of the file

          + * + * {@codesnippet com.azure.storage.file.fileClient.setMetadata#map.clearMetadata} + * + * @param metadata Options.Metadata to set on the file, if null is passed the metadata for the file is cleared + * @return information about the file + * @throws StorageErrorException If the file doesn't exist or the metadata contains invalid keys + */ + public Response setMetadata(Map metadata) { + return fileAsyncClient.setMetadata(metadata).block(); } - public Mono abortCopy(String copyId) { - throw new UnsupportedOperationException(); + /** + * Uploads a range of bytes to the beginning of a file in storage file service. Upload operations performs an in-place write on the specified file. + * + *

          Code Samples

          + * + *

          Upload "default" to the file.

          + * + * {@codesnippet com.azure.storage.file.fileClient.upload#flux-long} + * + * @param data The data which will upload to the storage file. + * @param length Specifies the number of bytes being transmitted in the request body. When the FileRangeWriteType is set to clear, the value of this header must be set to zero.. + * @return A response that only contains headers and response status code + * @throws StorageErrorException If you attempt to upload a range that is larger than 4 MB, the service returns status code 413 (Request Entity Too Large) + */ + public Response upload(ByteBuf data, long length) { + return fileAsyncClient.upload(Flux.just(data), length).block(); } - public Mono> downloadWithProperties(long offset, long length, boolean rangeGetContentMD5) { - throw new UnsupportedOperationException(); + /** + * Uploads a range of bytes to specific of a file in storage file service. Upload operations performs an in-place write on the specified file. + * + *

          Code Samples

          + * + *

          Upload the file from 1024 to 2048 bytes with its metadata and properties and without the contentMD5.

          + * + * {@codesnippet com.azure.storage.file.fileClient.upload#bytebuf-long-int-filerangewritetype} + * + * @param data The data which will upload to the storage file. + * @param offset Optional starting point of the upload range. It will start from the beginning if it is {@code null} + * @param length Specifies the number of bytes being transmitted in the request body. When the FileRangeWriteType is set to clear, the value of this header must be set to zero. + * @param type You may specify one of the following options: + *
            + *
          • Update: Writes the bytes specified by the request body into the specified range.
          • + *
          • Clear: Clears the specified range and releases the space used in storage for that range. To clear a range, set the Content-Length header to zero.
          • + *
              + * @return A response that only contains headers and response status code + * @throws StorageErrorException If you attempt to upload a range that is larger than 4 MB, the service returns status code 413 (Request Entity Too Large) + */ + public Response upload(ByteBuf data, long length, int offset, FileRangeWriteType type) { + return fileAsyncClient.upload(Flux.just(data), length, offset, type).block(); } - public Mono delete() { - throw new UnsupportedOperationException(); + /** + * Uploads file to storage file service. + * + *

              Code Samples

              + * + *

              Upload the file from the source file path.

              + * + * {@codesnippet com.azure.storage.file.fileClient.uploadFromFile#string} + * + * @param uploadFilePath The path where store the source file to upload + */ + public void uploadFromFile(String uploadFilePath) { + uploadFromFile(uploadFilePath, FileRangeWriteType.UPDATE); } - public Mono> getProperties(String shareSnapshot) { - throw new UnsupportedOperationException(); + /** + * Uploads file to storage file service. + * + *

              Code Samples

              + * + *

              Upload the file from the source file path.

              + * + * {@codesnippet com.azure.storage.file.fileClient.uploadFromFile#string-filerangewritetype} + * + * @param uploadFilePath The path where store the source file to upload + * @param type You may specify one of the following options: + *
                + *
              • Update: Writes the bytes specified by the request body into the specified range.
              • + *
              • Clear: Clears the specified range and releases the space used in storage for that range. To clear a range, set the Content-Length header to zero.
              • + *
                  + */ + public void uploadFromFile(String uploadFilePath, FileRangeWriteType type) { + fileAsyncClient.uploadFromFile(uploadFilePath, type).block(); } - public Mono> setHttpHeaders(long newFileSize, FileHTTPHeaders httpHeaders) { - throw new UnsupportedOperationException(); + /** + * List of valid ranges for a file. + * + *

                  Code Samples

                  + * + *

                  List all ranges for the file client.

                  + * + * {@codesnippet com.azure.storage.file.fileClient.listRanges} + * + * @return {@link FileRange ranges} in the files. + */ + public Iterable listRanges() { + return fileAsyncClient.listRanges(null).toIterable(); } - public Mono> setMeatadata(Map meatadata) { - throw new UnsupportedOperationException(); + /** + * List of valid ranges for a file. + * + *

                  Code Samples

                  + * + *

                  List all ranges within the file range from 1KB to 2KB.

                  + * + * {@codesnippet com.azure.storage.file.fileClient.listRanges#filerange} + * + * @param range Optional byte range which returns file data only from the specified range. + * @return {@link FileRange ranges} in the files that satisfy the requirements + */ + public Iterable listRanges(FileRange range) { + return fileAsyncClient.listRanges(range).toIterable(); } - public Mono> upload(FileRangeWriteType type, long offset, long length, Flux data) { - throw new UnsupportedOperationException(); + /** + * List of open handles on a file. + * + *

                  Code Samples

                  + * + *

                  List all handles for the file client.

                  + * + * {@codesnippet com.azure.storage.file.fileClient.listHandles} + * + * @return {@link HandleItem handles} in the files that satisfy the requirements + */ + public Iterable listHandles() { + return listHandles(null); } - public Flux listRanges(long offset, long length, String shareSnapshot) { - throw new UnsupportedOperationException(); + /** + * List of open handles on a file. + * + *

                  Code Samples

                  + * + *

                  List 10 handles for the file client.

                  + * + * {@codesnippet com.azure.storage.file.fileClient.listHandles#integer} + * + * @param maxResults Optional max number of results returned per page + * @return {@link HandleItem handles} in the file that satisfy the requirements + */ + public Iterable listHandles(Integer maxResults) { + return fileAsyncClient.listHandles(maxResults).toIterable(); } - public Flux listHandles(int maxResults) { - throw new UnsupportedOperationException(); + /** + * Closes a handle or handles opened on a file at the service. It is intended to be used alongside {@link FileClient#listHandles()} (Integer)} . + * TODO: Will change the return type to how many handles have been closed. Implement one more API to force close all handles. + * TODO: @see Github Issue 4525 + * + *

                  Code Samples

                  + * + *

                  Force close handles with handles returned by list handles in recursive.

                  + * + * {@codesnippet com.azure.storage.file.fileClient.forceCloseHandles#string} + * + * @param handleId Specifies the handle ID to be closed. Use an asterisk ('*') as a wildcard string to specify all handles. + * @return The counts of number of handles closed + */ + public Iterable forceCloseHandles(String handleId) { + return fileAsyncClient.forceCloseHandles(handleId).toIterable(); } - public Flux forceCloseHandles(String handleId) { - throw new UnsupportedOperationException(); + /** + * Get snapshot id which attached to {@link FileClient}. + * Return {@code null} if no snapshot id attached. + * + *

                  Code Samples

                  + * + *

                  Get the share snapshot id.

                  + * + * {@codesnippet com.azure.storage.file.fileClient.getShareSnapshotId} + * + * @return The snapshot id which is a unique {@code DateTime} value that identifies the share snapshot to its base share. + */ + public String getShareSnapshotId() { + return fileAsyncClient.getShareSnapshotId(); } } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/FileClientBuilder.java b/storage/client/file/src/main/java/com/azure/storage/file/FileClientBuilder.java index 610466b2aed52..4c130cd9dfd80 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/FileClientBuilder.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/FileClientBuilder.java @@ -3,7 +3,361 @@ package com.azure.storage.file; +import com.azure.core.http.HttpClient; +import com.azure.core.http.HttpPipeline; +import com.azure.core.http.policy.AddDatePolicy; +import com.azure.core.http.policy.HttpLogDetailLevel; +import com.azure.core.http.policy.HttpLoggingPolicy; +import com.azure.core.http.policy.HttpPipelinePolicy; +import com.azure.core.http.policy.RequestIdPolicy; +import com.azure.core.http.policy.RetryPolicy; +import com.azure.core.http.policy.UserAgentPolicy; +import com.azure.core.implementation.http.policy.spi.HttpPolicyProviders; +import com.azure.core.util.configuration.Configuration; +import com.azure.core.util.configuration.ConfigurationManager; +import com.azure.storage.common.credentials.SASTokenCredential; +import com.azure.storage.common.credentials.SharedKeyCredential; +import com.azure.storage.common.policy.SASTokenCredentialPolicy; +import com.azure.storage.common.policy.SharedKeyCredentialPolicy; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; + +/** + * This class provides a fluent builder API to help aid the configuration and instantiation of the {@link FileClient FileClients} + * and {@link FileAsyncClient FileAsyncClients}, calling {@link FileClientBuilder#buildClient() buildClient} + * constructs an instance of FileClient and calling {@link FileClientBuilder#buildAsyncClient() buildAsyncClient} + * constructs an instance of FileAsyncClient. + * + *

                  The client needs the endpoint of the Azure Storage File service, name of the share, and authorization credential. + * {@link FileClientBuilder#endpoint(String) endpoint} gives the builder the endpoint and may give the builder the + * {@link FileClientBuilder#shareName(String)}, {@link FileClientBuilder#filePath(String)} and a {@link SASTokenCredential} that authorizes the client.

                  + * + *

                  Instantiating a synchronous File Client with SAS token

                  + * {@codesnippet com.azure.storage.file.fileClient.instantiation.sastoken} + * + *

                  Instantiating an Asynchronous File Client with SAS token

                  + * {@codesnippet com.azure.storage.file.directoryClient.instantiation.sastoken} + * + *

                  If the {@code endpoint} doesn't contain the query parameters to construct a {@code SASTokenCredential} they may + * be set using {@link FileClientBuilder#credential(SASTokenCredential) credential}.

                  + * + * {@codesnippet com.azure.storage.file.fileClient.instantiation.credential} + * + * {@codesnippet com.azure.storage.file.fileAsyncClient.instantiation.credential} + * + *

                  Another way to authenticate the client is using a {@link SharedKeyCredential}. To create a SharedKeyCredential + * a connection string from the Storage File service must be used. Set the SharedKeyCredential with + * {@link FileClientBuilder#connectionString(String) connectionString}. If the builder has both a SASTokenCredential and + * SharedKeyCredential the SharedKeyCredential will be preferred when authorizing requests sent to the service.

                  + * + *

                  Instantiating a synchronous File Client with connection string.

                  + * {@codesnippet com.azure.storage.file.directoryClient.instantiation.connectionstring} + * + *

                  Instantiating an Asynchronous File Client with connection string.

                  + * {@codesnippet com.azure.storage.file.directoryAsyncClient.instantiation.connectionstring} + * + * @see FileClient + * @see FileAsyncClient + * @see SASTokenCredential + * @see SharedKeyCredential + */ public class FileClientBuilder { + private static final String ACCOUNT_NAME = "accountname"; + private final List policies; + private final RetryPolicy retryPolicy; + + private HttpLogDetailLevel logLevel; + private Configuration configuration; + private URL endpoint; + private String shareName; + private String filePath; + private SASTokenCredential sasTokenCredential; + private SharedKeyCredential sharedKeyCredential; + private HttpClient httpClient; + private HttpPipeline pipeline; + private String snapshot; + + /** + * Creates a builder instance that is able to configure and construct {@link FileClient FileClients} + * and {@link FileAsyncClient FileAsyncClients}. + */ + public FileClientBuilder() { + retryPolicy = new RetryPolicy(); + logLevel = HttpLogDetailLevel.NONE; + policies = new ArrayList<>(); + + configuration = ConfigurationManager.getConfiguration(); + } + + /** + * Creates a {@link FileAsyncClient} based on options set in the builder. Every time {@code buildAsyncClient()} is + * called a new instance of {@link FileAsyncClient} is created. + * + *

                  + * If {@link FileClientBuilder#pipeline(HttpPipeline) pipeline} is set, then the {@code pipeline} and + * {@link FileClientBuilder#endpoint(String) endpoint} are used to create the + * {@link FileAsyncClient client}. All other builder settings are ignored. + *

                  + * + * @return A ShareAsyncClient with the options set from the builder. + * @throws NullPointerException If {@code shareName} is {@code null} or the (@code filePath) is {@code null}. + * @throws IllegalArgumentException If neither a {@link SharedKeyCredential} or {@link SASTokenCredential} has been set. + */ + public FileAsyncClient buildAsyncClient() { + Objects.requireNonNull(shareName); + Objects.requireNonNull(filePath); + + if (pipeline != null) { + return new FileAsyncClient(endpoint, pipeline, shareName, filePath, snapshot); + } + + if (sasTokenCredential == null && sharedKeyCredential == null) { + throw new IllegalArgumentException("Credentials are required for authorization"); + } + + // Closest to API goes first, closest to wire goes last. + final List policies = new ArrayList<>(); + + policies.add(new UserAgentPolicy(FileConfiguration.NAME, FileConfiguration.VERSION, configuration)); + policies.add(new RequestIdPolicy()); + policies.add(new AddDatePolicy()); + + if (sharedKeyCredential != null) { + policies.add(new SharedKeyCredentialPolicy(sharedKeyCredential)); + } else { + policies.add(new SASTokenCredentialPolicy(sasTokenCredential)); + } + + HttpPolicyProviders.addBeforeRetryPolicies(policies); + + policies.add(retryPolicy); + + policies.addAll(this.policies); + HttpPolicyProviders.addAfterRetryPolicies(policies); + policies.add(new HttpLoggingPolicy(logLevel)); + + HttpPipeline pipeline = HttpPipeline.builder() + .policies(policies.toArray(new HttpPipelinePolicy[0])) + .httpClient(httpClient) + .build(); + + return new FileAsyncClient(endpoint, pipeline, shareName, filePath, snapshot); + } + + /** + * Creates a {@link FileClient} based on options set in the builder. Every time {@code buildClient()} is + * called a new instance of {@link FileClient} is created. + * + *

                  + * If {@link FileClientBuilder#pipeline(HttpPipeline) pipeline} is set, then the {@code pipeline} and + * {@link FileClientBuilder#endpoint(String) endpoint} are used to create the + * {@link FileClient client}. All other builder settings are ignored. + *

                  + * + * @return A FileClient with the options set from the builder. + * @throws NullPointerException If {@code endpoint}, {@code shareName} or {@code filePath} is {@code null}. + * @throws IllegalStateException If neither a {@link SharedKeyCredential} or {@link SASTokenCredential} has been set. + */ + public FileClient buildClient() { + return new FileClient(this.buildAsyncClient()); + } + + /** + * Sets the endpoint for the Azure Storage File instance that the client will interact with. + * + *

                  The first path segment, if the endpoint contains path segments, will be assumed to be the name of the share + * that the client will interact with. Rest of the path segments should be the path of the file. + * It mush end up with the file name if more segments exist.

                  + * + *

                  Query parameters of the endpoint will be parsed using {@link SASTokenCredential#fromQuery(String)} in an + * attempt to generate a {@link SASTokenCredential} to authenticate requests sent to the service.

                  + * + * @param endpoint The URL of the Azure Storage File instance to send service requests to and receive responses from. + * @return the updated FileClientBuilder object + * @throws IllegalArgumentException If {@code endpoint} is {@code null} or is an invalid URL + */ + public FileClientBuilder endpoint(String endpoint) { + try { + URL fullURL = new URL(endpoint); + this.endpoint = new URL(fullURL.getProtocol() + "://" + fullURL.getHost()); + + // Attempt to get the share name and file path from the URL passed + String[] pathSegments = fullURL.getPath().split("/"); + int length = pathSegments.length; + this.shareName = length >= 2 ? pathSegments[1] : this.shareName; + String[] filePathParams = length >= 3 ? Arrays.copyOfRange(pathSegments, 2, length) : null; + this.filePath = filePathParams != null ? String.join("/", filePathParams) : this.filePath; + + // Attempt to get the SAS token from the URL passed + this.sasTokenCredential = SASTokenCredential.fromQuery(fullURL.getQuery()); + if (this.sasTokenCredential != null) { + this.sharedKeyCredential = null; + } + } catch (MalformedURLException ex) { + throw new IllegalArgumentException("The Azure Storage File endpoint url is malformed."); + } + + return this; + } + + /** + * Sets the {@link SASTokenCredential} used to authenticate requests sent to the File service. + * + * @param credential SAS token credential generated from the Storage account that authorizes requests + * @return the updated FileClientBuilder object + * @throws NullPointerException If {@code credential} is {@code null}. + */ + public FileClientBuilder credential(SASTokenCredential credential) { + this.sasTokenCredential = Objects.requireNonNull(credential); + this.sharedKeyCredential = null; + return this; + } + + /** + * Sets the {@link SharedKeyCredential} used to authenticate requests sent to the File service. + * + * @param credential Shared key credential generated from the Storage account that authorizes requests + * @return the updated ShareClientBuilder object + * @throws NullPointerException If {@code credential} is {@code null}. + */ + public FileClientBuilder credential(SharedKeyCredential credential) { + this.sharedKeyCredential = Objects.requireNonNull(credential); + this.sasTokenCredential = null; + return this; + } + /** + * Creates a {@link SharedKeyCredential} from the {@code connectionString} used to authenticate requests sent to the + * File service. + * + * @param connectionString Connection string from the Access Keys section in the Storage account + * @return the updated FileClientBuilder object + * @throws NullPointerException If {@code connectionString} is {@code null}. + */ + public FileClientBuilder connectionString(String connectionString) { + Objects.requireNonNull(connectionString); + this.sharedKeyCredential = SharedKeyCredential.fromConnectionString(connectionString); + getEndPointFromConnectionString(connectionString); + return this; + } + + private void getEndPointFromConnectionString(String connectionString) { + Map connectionStringPieces = new HashMap<>(); + for (String connectionStringPiece : connectionString.split(";")) { + String[] kvp = connectionStringPiece.split("=", 2); + connectionStringPieces.put(kvp[0].toLowerCase(Locale.ROOT), kvp[1]); + } + String accountName = connectionStringPieces.get(ACCOUNT_NAME); + try { + this.endpoint = new URL(String.format("https://%s.file.core.windows.net", accountName)); + } catch (MalformedURLException e) { + throw new IllegalArgumentException(String.format("There is no valid endpoint for the connection string. " + + "Connection String: %s", connectionString)); + } + } + + /** + * Sets the share that the constructed clients will interact with + * + * @param shareName Name of the share + * @return the updated FileClientBuilder object + * @throws NullPointerException If {@code shareName} is {@code null}. + */ + public FileClientBuilder shareName(String shareName) { + this.shareName = shareName; + return this; + } + + /** + * Sets the file that the constructed clients will interact with + * + * @param filePath Path of the file, mush end up with the file name. + * @return the updated FileClientBuilder object + * @throws NullPointerException If {@code filePath} is {@code null}. + */ + public FileClientBuilder filePath(String filePath) { + this.filePath = filePath; + return this; + } + + /** + * Sets the HTTP client to use for sending and receiving requests to and from the service. + * + * @param httpClient The HTTP client to use for requests. + * @return The updated FileClientBuilder object. + * @throws NullPointerException If {@code httpClient} is {@code null}. + */ + public FileClientBuilder httpClient(HttpClient httpClient) { + this.httpClient = httpClient; + return this; + } + + /** + * Adds a policy to the set of existing policies that are executed after the {@link RetryPolicy}. + * + * @param pipelinePolicy The retry policy for service requests. + * @return The updated FileClientBuilder object. + * @throws NullPointerException If {@code pipelinePolicy} is {@code null}. + */ + public FileClientBuilder addPolicy(HttpPipelinePolicy pipelinePolicy) { + this.policies.add(pipelinePolicy); + return this; + } + + /** + * Sets the logging level for HTTP requests and responses. + * + * @param logLevel The amount of logging output when sending and receiving HTTP requests/responses. + * @return The updated FileClientBuilder object. + */ + public FileClientBuilder httpLogDetailLevel(HttpLogDetailLevel logLevel) { + this.logLevel = logLevel; + return this; + } + + /** + * Sets the HTTP pipeline to use for the service client. + * + *

                  If {@code pipeline} is set, all other settings are ignored, aside from {@link FileClientBuilder#endpoint(String) endpoint}, + * {@link FileClientBuilder#shareName(String) shareName} @{link FileClientBuilder#filePath(String) filePath}, and {@link FileClientBuilder#snapshot(String) snaphotShot} + * when building clients.

                  + * + * @param pipeline The HTTP pipeline to use for sending service requests and receiving responses. + * @return The updated FileClientBuilder object. + * @throws NullPointerException If {@code pipeline} is {@code null}. + */ + public FileClientBuilder pipeline(HttpPipeline pipeline) { + this.pipeline = Objects.requireNonNull(pipeline); + return this; + } + + /** + * Sets the configuration object used to retrieve environment configuration values used to buildClient the client with + * when they are not set in the builder, defaults to Configuration.NONE + * @param configuration configuration store + * @return the updated FileClientBuilder object + */ + public FileClientBuilder configuration(Configuration configuration) { + this.configuration = configuration; + return this; + } - // fileUri, SharedKeyCredential, FileClientOptions ,withSnapshot + /** + * Sets the snapshot that the constructed clients will interact with. This snapshot must be linked to the share + * that has been specified in the builder. + * + * @param snapshot Identifier of the snapshot + * @return the updated FileClientBuilder object + * @throws NullPointerException If {@code snapshot} is {@code null}. + */ + public FileClientBuilder snapshot(String snapshot) { + this.snapshot = snapshot; + return this; + } } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/FileConfiguration.java b/storage/client/file/src/main/java/com/azure/storage/file/FileConfiguration.java new file mode 100644 index 0000000000000..02d964e002412 --- /dev/null +++ b/storage/client/file/src/main/java/com/azure/storage/file/FileConfiguration.java @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.file; + +/* + * Gets the SDK information for this library component. + */ +class FileConfiguration { + //TODO: Eventually remove these hardcoded strings with https://github.com/Azure/azure-sdk-for-java/issues/3141 + static final String NAME = "azure-storage-file"; + static final String VERSION = "12.0.0-preview.1"; +} diff --git a/storage/client/file/src/main/java/com/azure/storage/file/FileServiceAsyncClient.java b/storage/client/file/src/main/java/com/azure/storage/file/FileServiceAsyncClient.java index 9c66716ca61c0..3c13197f7d477 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/FileServiceAsyncClient.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/FileServiceAsyncClient.java @@ -3,51 +3,327 @@ package com.azure.storage.file; +import com.azure.core.http.HttpPipeline; import com.azure.core.http.rest.Response; +import com.azure.core.http.rest.SimpleResponse; import com.azure.core.http.rest.VoidResponse; +import com.azure.core.implementation.util.ImplUtils; +import com.azure.core.util.Context; +import com.azure.storage.common.credentials.SASTokenCredential; +import com.azure.storage.common.credentials.SharedKeyCredential; +import com.azure.storage.file.implementation.AzureFileStorageBuilder; +import com.azure.storage.file.implementation.AzureFileStorageImpl; +import com.azure.storage.file.models.CorsRule; +import com.azure.storage.file.models.DeleteSnapshotsOptionType; import com.azure.storage.file.models.FileServiceProperties; +import com.azure.storage.file.models.ListSharesIncludeType; import com.azure.storage.file.models.ListSharesOptions; +import com.azure.storage.file.models.ListSharesResponse; +import com.azure.storage.file.models.ServicesListSharesSegmentResponse; import com.azure.storage.file.models.ShareItem; +import com.azure.storage.file.models.StorageErrorException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import java.util.Map; - -public class FileServiceAsyncClient { +/** + * This class provides a azureFileStorageClient that contains all the operations for interacting with a file account in Azure Storage. + * Operations allowed by the azureFileStorageClient are creating, listing, and deleting shares and retrieving and updating properties + * of the account. + * + *

                  Instantiating an Asynchronous File Service Client

                  + * + * {@codesnippet com.azure.storage.file.fileServiceAsyncClient.instantiation} + * + *

                  View {@link FileServiceClientBuilder this} for additional ways to construct the azureFileStorageClient.

                  + * + * @see FileServiceClientBuilder + * @see FileServiceClient + * @see SharedKeyCredential + * @see SASTokenCredential + */ +public final class FileServiceAsyncClient { + private final AzureFileStorageImpl azureFileStorageClient; - FileServiceAsyncClient() { - throw new UnsupportedOperationException(); + /** + * Creates a FileServiceClient that sends requests to the storage account at {@code endpoint}. + * Each service call goes through the {@code httpPipeline}. + * + * @param endpoint URL for the Storage File service + * @param httpPipeline HttpPipeline that the HTTP requests and responses flow through + */ + FileServiceAsyncClient(URL endpoint, HttpPipeline httpPipeline) { + this.azureFileStorageClient = new AzureFileStorageBuilder().pipeline(httpPipeline) + .url(endpoint.toString()) + .build(); } - public static FileServiceClientBuilder asyncBuilder() { - throw new UnsupportedOperationException(); + /** + * Get the url of the storage file service client. + * @return the url of the Storage File service. + * @throws RuntimeException If the file service is using a malformed URL. + */ + public URL getFileServiceUrl() { + try { + return new URL(azureFileStorageClient.url()); + } catch (MalformedURLException e) { + throw new RuntimeException(String.format("Invalid URL on %s: %s" + getClass().getSimpleName(), + azureFileStorageClient.url()), e); + } } - public String url() { - throw new UnsupportedOperationException(); + /** + * Constructs a ShareAsyncClient that interacts with the specified share. + * + *

                  If the share doesn't exist in the storage account {@link ShareAsyncClient#create() create} in the azureFileStorageClient will + * need to be called before interaction with the share can happen.

                  + * + * @param shareName Name of the share + * @return a ShareAsyncClient that interacts with the specified share + */ + public ShareAsyncClient getShareAsyncClient(String shareName) { + return new ShareAsyncClient(azureFileStorageClient, shareName); } - public ShareAsyncClient getShareClient(String shareName) { - throw new UnsupportedOperationException(); + /** + * Lists all shares in the storage account without their metadata or snapshots. + * + *

                  Code Samples

                  + * + *

                  List all shares in the account

                  + * + * {@codesnippet com.azure.storage.file.fileServiceAsyncClient.listShares} + * + * @return {@link ShareItem Shares} in the storage account without their metadata or snapshots + */ + public Flux listShares() { + return listShares(null); } + /** + * Lists the shares in the Storage account that pass the options filter. + * + *

                  Set starts with name filter using {@link ListSharesOptions#prefix(String) prefix} to filter shares that are + * listed.

                  + * + *

                  Pass true to {@link ListSharesOptions#includeMetadata(boolean) includeMetadata} to have metadata returned for + * the shares.

                  + * + *

                  Pass true to {@link ListSharesOptions#includeSnapshots(boolean) includeSnapshots} to have snapshots of the + * shares listed.

                  + * + *

                  Code Samples

                  + * + *

                  List all shares that begin with "azure"

                  + * + * {@codesnippet com.azure.storage.file.fileServiceAsyncClient.listShares#ListSharesOptions.prefix} + * + *

                  List all shares including their snapshots and metadata

                  + * + * {@codesnippet com.azure.storage.file.fileServiceAsyncClient.listShares#ListSharesOptions.metadata.snapshot} + * + * @param options Options for listing shares + * @return {@link ShareItem Shares} in the storage account that satisfy the filter requirements + */ public Flux listShares(ListSharesOptions options) { - throw new UnsupportedOperationException(); + return listShares(null, options); } + /** + * Lists the shares in the storage account that pass the filter starting at the specified marker. + * + * @param marker Starting point to list the shares + * @param options Options for listing shares + * @return {@link ShareItem Shares} in the storage account that satisfy the filter requirements + */ + private Flux listShares(String marker, ListSharesOptions options) { + String prefix = null; + Integer maxResults = null; + List include = new ArrayList<>(); + + if (options != null) { + prefix = options.prefix(); + maxResults = options.maxResults(); + + if (options.includeMetadata()) { + include.add(ListSharesIncludeType.fromString(ListSharesIncludeType.METADATA.toString())); + } + + if (options.includeSnapshots()) { + include.add(ListSharesIncludeType.fromString(ListSharesIncludeType.SNAPSHOTS.toString())); + } + } + + return azureFileStorageClient.services().listSharesSegmentWithRestResponseAsync(prefix, marker, maxResults, include, null, Context.NONE) + .flatMapMany(response -> Flux.fromIterable(response.value().shareItems())); + } + + /* + * Helper function used to auto-enumerate through paged responses + */ + private Flux listShares(ServicesListSharesSegmentResponse response, List include, Context context) { + ListSharesResponse value = response.value(); + Mono result = azureFileStorageClient.services() + .listSharesSegmentWithRestResponseAsync(value.prefix(), value.marker(), value.maxResults(), include, null, context); + + return result.flatMapMany(r -> extractAndFetchShares(r, include, context)); + } + + /* + * Helper function used to auto-enumerate through paged responses + */ + private Publisher extractAndFetchShares(ServicesListSharesSegmentResponse response, List include, Context context) { + String nextPageLink = response.value().nextMarker(); + if (ImplUtils.isNullOrEmpty(nextPageLink)) { + return Flux.fromIterable(response.value().shareItems()); + } + + return Flux.fromIterable(response.value().shareItems()).concatWith(listShares(response, include, context)); + } + + /** + * Retrieves the properties of the storage account's File service. The properties range from storage analytics and + * metrics to CORS (Cross-Origin Resource Sharing). + * + *

                  Code Samples

                  + * + *

                  Retrieve File service properties

                  + * + * {@codesnippet com.azure.storage.file.fileServiceAsyncClient.getProperties} + * + * @return Storage account File service properties + */ public Mono> getProperties() { - throw new UnsupportedOperationException(); + return azureFileStorageClient.services().getPropertiesWithRestResponseAsync(Context.NONE) + .map(response -> new SimpleResponse<>(response, response.value())); + } + + /** + * Sets the properties for the storage account's File service. The properties range from storage analytics and + * metric to CORS (Cross-Origin Resource Sharing). + * + * To maintain the CORS in the Queue service pass a {@code null} value for {@link FileServiceProperties#cors() CORS}. + * To disable all CORS in the Queue service pass an empty list for {@link FileServiceProperties#cors() CORS}. + * + *

                  Code Sample

                  + * + *

                  Clear CORS in the File service

                  + * + * {@codesnippet com.azure.storage.file.fileServiceAsyncClient.setProperties#fileServiceProperties.clearCORS} + * + *

                  Enable Minute and Hour Metrics

                  + * + * {@codesnippet com.azure.storage.file.fileServiceAsyncClient.setProperties#fileServiceProperties} + * + * @param properties Storage account File service properties + * @return A response that only contains headers and response status code + * @throws StorageErrorException When one of the following is true + *
                    + *
                  • A CORS rule is missing one of its fields
                  • + *
                  • More than five CORS rules will exist for the Queue service
                  • + *
                  • Size of all CORS rules exceeds 2KB
                  • + *
                  • + * Length of {@link CorsRule#allowedHeaders() allowed headers}, {@link CorsRule#exposedHeaders() exposed headers}, + * or {@link CorsRule#allowedOrigins() allowed origins} exceeds 256 characters. + *
                  • + *
                  • {@link CorsRule#allowedMethods() Allowed methods} isn't DELETE, GET, HEAD, MERGE, POST, OPTIONS, or PUT
                  • + *
                  + */ + public Mono setProperties(FileServiceProperties properties) { + return azureFileStorageClient.services().setPropertiesWithRestResponseAsync(properties, Context.NONE) + .map(VoidResponse::new); } - public Mono setProperties() { - throw new UnsupportedOperationException(); + /** + * Creates a share in the storage account with the specified name and returns a ShareAsyncClient to interact with it. + * + *

                  Code Samples

                  + * + *

                  Create the share "test"

                  + * + * {@codesnippet com.azure.storage.file.fileServiceAsyncClient.createShare#string} + * + * @param shareName Name of the share + * @return A response containing the ShareAsyncClient and the status of creating the share. + * @throws StorageErrorException If a share with the same name already exists + */ + public Mono> createShare(String shareName) { + return createShare(shareName, null, null); } - public Mono> createShare(String shareName, Map metadata, int quotaInGB) { - throw new UnsupportedOperationException(); + /** + * Creates a share in the storage account with the specified name, metadata, and quota and returns a ShareAsyncClient to + * interact with it. + * + *

                  Code Samples

                  + * + *

                  Create the share "test" with metadata "share:metadata"

                  + * + * {@codesnippet com.azure.storage.file.fileServiceAsyncClient.createShare#string-map-integer.metadata} + * + *

                  Create the share "test" with a quota of 10 GB

                  + * + * {@codesnippet com.azure.storage.file.fileServiceAsyncClient.createShare#string-map-integer.quota} + * + * @param shareName Name of the share + * @param metadata Optional metadata to associate with the share + * @param quotaInGB Optional maximum size the share is allowed to grow to in GB. This must be greater than 0 and + * less than or equal to 5120. The default value is 5120. + * @return A response containing the ShareAsyncClient and the status of creating the share. + * @throws StorageErrorException If a share with the same name already exists or {@code quotaInGB} is outside the + * allowed range. + */ + public Mono> createShare(String shareName, Map metadata, Integer quotaInGB) { + ShareAsyncClient shareAsyncClient = new ShareAsyncClient(azureFileStorageClient, shareName); + + return shareAsyncClient.create(metadata, quotaInGB) + .map(response -> new SimpleResponse<>(response, shareAsyncClient)); } - public Mono deleteShare(String shareName, String shareSnapshot) { - throw new UnsupportedOperationException(); + /** + * Deletes the share in the storage account with the given name. + * + *

                  Code Samples

                  + * + *

                  Delete the share "test"

                  + * + * {@codesnippet com.azure.storage.file.fileServiceAsyncClient.deleteShare#string} + * + * @param shareName Name of the share + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the share doesn't exist + */ + public Mono deleteShare(String shareName) { + return deleteShare(shareName, null); } + + /** + * Deletes the specific snapshot of the share in the storage account with the given name. Snapshot are identified + * by the time they were created. + * + *

                  Code Samples

                  + * + *

                  Delete the snapshot of share "test" that was created at current time.

                  + * + * {@codesnippet com.azure.storage.file.fileServiceAsyncClient.deleteShare#string-string} + * + * @param shareName Name of the share + * @param snapshot Identifier of the snapshot + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the share doesn't exist or the snapshot doesn't exist + */ + public Mono deleteShare(String shareName, String snapshot) { + DeleteSnapshotsOptionType deleteSnapshots = null; + if (ImplUtils.isNullOrEmpty(snapshot)) { + deleteSnapshots = DeleteSnapshotsOptionType.fromString(DeleteSnapshotsOptionType.INCLUDE.toString()); + } + return azureFileStorageClient.shares().deleteWithRestResponseAsync(shareName, snapshot, null, deleteSnapshots, Context.NONE) + .map(VoidResponse::new); + } + } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/FileServiceClient.java b/storage/client/file/src/main/java/com/azure/storage/file/FileServiceClient.java index fd808bf1d6fde..2dd4656926e15 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/FileServiceClient.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/FileServiceClient.java @@ -1,52 +1,242 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + package com.azure.storage.file; import com.azure.core.http.rest.Response; +import com.azure.core.http.rest.SimpleResponse; import com.azure.core.http.rest.VoidResponse; +import com.azure.storage.common.credentials.SASTokenCredential; +import com.azure.storage.common.credentials.SharedKeyCredential; +import com.azure.storage.file.models.CorsRule; import com.azure.storage.file.models.FileServiceProperties; import com.azure.storage.file.models.ListSharesOptions; import com.azure.storage.file.models.ShareItem; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - +import com.azure.storage.file.models.StorageErrorException; +import java.net.URL; import java.util.Map; -public class FileServiceClient { +/** + * This class provides a fileServiceAsyncClient that contains all the operations for interacting with a file account in Azure Storage. + * Operations allowed by the fileServiceAsyncClient are creating, listing, and deleting shares and retrieving and updating properties + * of the account. + * + *

                  Instantiating a Synchronous File Service Client

                  + * + * {@codesnippet com.azure.storage.file.fileServiceClient.instantiation} + * + *

                  View {@link FileServiceClientBuilder this} for additional ways to construct the fileServiceAsyncClient.

                  + * + * @see FileServiceClientBuilder + * @see FileServiceAsyncClient + * @see SharedKeyCredential + * @see SASTokenCredential + */ +public final class FileServiceClient { + private final FileServiceAsyncClient fileServiceAsyncClient; - private final FileServiceAsyncClient client; + /** + * Creates a FileServiceClient that wraps a FileServiceAsyncClient and blocks requests. + * + * @param client FileServiceAsyncClient that is used to send requests + */ + FileServiceClient(FileServiceAsyncClient client) { + this.fileServiceAsyncClient = client; + } - FileServiceClient() { - throw new UnsupportedOperationException(); + /** + * Get the url of the storage file service client. + * @return the url of the Storage File service. + * @throws RuntimeException If the directory is using a malformed URL. + */ + public URL getFileServiceUrl() { + return fileServiceAsyncClient.getFileServiceUrl(); } - public static FileServiceClientBuilder syncBuilder() { - throw new UnsupportedOperationException(); + /** + * Constructs a ShareClient that interacts with the specified share. + * + *

                  If the share doesn't exist in the storage account {@link ShareClient#create() create} in the fileServiceAsyncClient will + * need to be called before interaction with the share can happen.

                  + * + * @param shareName Name of the share + * @return a ShareClient that interacts with the specified share + */ + public ShareClient getShareClient(String shareName) { + return new ShareClient(fileServiceAsyncClient.getShareAsyncClient(shareName)); } - public String url() { - throw new UnsupportedOperationException(); + /** + * Lists all shares in the storage account without their metadata or snapshots. + * + *

                  Code Samples

                  + * + *

                  List all shares in the account

                  + * + * {@codesnippet com.azure.storage.file.fileServiceClient.listShares} + * + * @return {@link ShareItem Shares} in the storage account without their metadata or snapshots + */ + public Iterable listShares() { + return listShares(null); } - public ShareClient getShareClient(String shareName) { - throw new UnsupportedOperationException(); + /** + * Lists the shares in the Storage account that pass the options filter. + * + *

                  Set starts with name filter using {@link ListSharesOptions#prefix(String) prefix} to filter shares that are + * listed.

                  + * + *

                  Pass true to {@link ListSharesOptions#includeMetadata(boolean) includeMetadata} to have metadata returned for + * the shares.

                  + * + *

                  Pass true to {@link ListSharesOptions#includeSnapshots(boolean) includeSnapshots} to have snapshots of the + * shares listed.

                  + * + *

                  Code Samples

                  + * + *

                  List all shares that begin with "azure"

                  + * + * {@codesnippet com.azure.storage.file.fileServiceClient.listShares#ListSharesOptions.prefix} + * + *

                  List all shares including their snapshots and metadata

                  + * + * {@codesnippet com.azure.storage.file.fileServiceClient.listShares#ListSharesOptions.metadata.snapshot} + * + * @param options Options for listing shares + * @return {@link ShareItem Shares} in the storage account that satisfy the filter requirements + */ + public Iterable listShares(ListSharesOptions options) { + return fileServiceAsyncClient.listShares(options).toIterable(); + } + + /** + * Retrieves the properties of the storage account's File service. The properties range from storage analytics and + * metrics to CORS (Cross-Origin Resource Sharing). + * + *

                  Code Samples

                  + * + *

                  Retrieve File service properties

                  + * + * {@codesnippet com.azure.storage.file.fileServiceClient.getProperties} + * + * @return Storage account File service properties + */ + public Response getProperties() { + return fileServiceAsyncClient.getProperties().block(); } - public Flux listShares(ListSharesOptions options) { - throw new UnsupportedOperationException(); + /** + * Sets the properties for the storage account's File service. The properties range from storage analytics and + * metric to CORS (Cross-Origin Resource Sharing). + * + * To maintain the CORS in the Queue service pass a {@code null} value for {@link FileServiceProperties#cors() CORS}. + * To disable all CORS in the Queue service pass an empty list for {@link FileServiceProperties#cors() CORS}. + * + *

                  Code Sample

                  + * + *

                  Clear CORS in the File service

                  + * + * {@codesnippet com.azure.storage.file.fileServiceClient.setProperties#fileServiceProperties.clearCORS} + * + *

                  Enable Minute and Hour Metrics

                  + * + * {@codesnippet com.azure.storage.file.fileServiceClient.setProperties#fileServiceProperties} + * + * @param properties Storage account File service properties + * @return A response that only contains headers and response status code + * @throws StorageErrorException When one of the following is true + *
                    + *
                  • A CORS rule is missing one of its fields
                  • + *
                  • More than five CORS rules will exist for the Queue service
                  • + *
                  • Size of all CORS rules exceeds 2KB
                  • + *
                  • + * Length of {@link CorsRule#allowedHeaders() allowed headers}, {@link CorsRule#exposedHeaders() exposed headers}, + * or {@link CorsRule#allowedOrigins() allowed origins} exceeds 256 characters. + *
                  • + *
                  • {@link CorsRule#allowedMethods() Allowed methods} isn't DELETE, GET, HEAD, MERGE, POST, OPTIONS, or PUT
                  • + *
                  + */ + public VoidResponse setProperties(FileServiceProperties properties) { + return fileServiceAsyncClient.setProperties(properties).block(); } - public Mono> getProperties() { - throw new UnsupportedOperationException(); + /** + * Creates a share in the storage account with the specified name and returns a ShareClient to interact with it. + * + *

                  Code Samples

                  + * + *

                  Create the share with share name of "myshare"

                  + * {@codesnippet com.azure.storage.file.fileServiceClient.createShare#string} + * + * @param shareName Name of the share + * @return A response containing the ShareClient and the status of creating the share. + * @throws StorageErrorException If a share with the same name already exists + */ + public Response createShare(String shareName) { + return createShare(shareName, null, null); } - public Mono setProperties() { - throw new UnsupportedOperationException(); + /** + * Creates a share in the storage account with the specified name and metadata and returns a ShareClient to + * interact with it. + * + *

                  Code Samples

                  + * + *

                  Create the share "test" with metadata "share:metadata"

                  + * + * {@codesnippet com.azure.storage.file.fileServiceClient.createShare#string-map-integer.metadata} + * + *

                  Create the share "test" with a quota of 10 GB

                  + * + * {@codesnippet com.azure.storage.file.fileServiceClient.createShare#string-map-integer.quota} + * + * @param shareName Name of the share + * @param metadata Optional metadata to associate with the share + * @param quotaInGB Optional maximum size the share is allowed to grow to in GB. This must be greater than 0 and + * less than or equal to 5120. The default value is 5120. + * @return A response containing the ShareClient and the status of creating the share. + * @throws StorageErrorException If a share with the same name already exists or {@code quotaInGB} is outside the + * allowed range. + */ + public Response createShare(String shareName, Map metadata, Integer quotaInGB) { + ShareClient shareClient = getShareClient(shareName); + return new SimpleResponse<>(shareClient.create(metadata, quotaInGB), shareClient); } - public Mono> createShare(String shareName, Map metadata, int quotaInGB) { - throw new UnsupportedOperationException(); + /** + * Deletes the share in the storage account with the given name + * + *

                  Code Samples

                  + * + *

                  Delete the share "test"

                  + * + * {@codesnippet com.azure.storage.file.fileServiceClient.deleteShare#string} + * + * @param shareName Name of the share + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the share doesn't exist + */ + public VoidResponse deleteShare(String shareName) { + return deleteShare(shareName, null); } - public Mono deleteShare(String shareName, String shareSnapshot) { - throw new UnsupportedOperationException(); + /** + * Deletes the specific snapshot of the share in the storage account with the given name. Snapshot are identified + * by the time they were created. + * + *

                  Code Samples

                  + * + *

                  Delete the snapshot of share "test" that was created at current time.

                  + * + * {@codesnippet com.azure.storage.file.fileServiceClient.deleteShare#string-string} + * + * @param shareName Name of the share + * @param snapshot Identifier of the snapshot + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the share doesn't exist or the snapshot doesn't exist + */ + public VoidResponse deleteShare(String shareName, String snapshot) { + return fileServiceAsyncClient.deleteShare(shareName, snapshot).block(); } } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/FileServiceClientBuilder.java b/storage/client/file/src/main/java/com/azure/storage/file/FileServiceClientBuilder.java index 01148e8af57f5..5a6393ffffdec 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/FileServiceClientBuilder.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/FileServiceClientBuilder.java @@ -3,7 +3,309 @@ package com.azure.storage.file; -public class FileServiceClientBuilder { +import com.azure.core.http.HttpClient; +import com.azure.core.http.HttpPipeline; +import com.azure.core.http.policy.AddDatePolicy; +import com.azure.core.http.policy.HttpLogDetailLevel; +import com.azure.core.http.policy.HttpLoggingPolicy; +import com.azure.core.http.policy.HttpPipelinePolicy; +import com.azure.core.http.policy.RequestIdPolicy; +import com.azure.core.http.policy.RetryPolicy; +import com.azure.core.http.policy.UserAgentPolicy; +import com.azure.core.implementation.http.policy.spi.HttpPolicyProviders; +import com.azure.core.util.configuration.Configuration; +import com.azure.core.util.configuration.ConfigurationManager; +import com.azure.storage.common.credentials.SASTokenCredential; +import com.azure.storage.common.credentials.SharedKeyCredential; +import com.azure.storage.common.policy.SASTokenCredentialPolicy; +import com.azure.storage.common.policy.SharedKeyCredentialPolicy; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; - // connection string, FileClientOptions, Uri, SharedKeyCredential +/** + * This class provides a fluent builder API to help aid the configuration and instantiation of the {@link FileServiceClient FileServiceClients} + * and {@link FileServiceAsyncClient FileServiceAsyncClients}, calling {@link FileServiceClientBuilder#buildClient() buildClient} + * constructs an instance of FileServiceClient and calling {@link FileServiceClientBuilder#buildAsyncClient() buildAsyncClient} + * constructs an instance of FileServiceAsyncClient. + * + *

                  The client needs the endpoint of the Azure Storage File service and authorization credential. + * {@link FileServiceClientBuilder#endpoint(String) endpoint} gives the builder the endpoint and may give the builder a + * {@link SASTokenCredential} that authorizes the client.

                  + * + *

                  Instantiating a synchronous FileService Client with SAS token

                  + * {@codesnippet com.azure.storage.file.fileServiceClient.instantiation.sastoken} + * + *

                  Instantiating an Asynchronous FileService Client with SAS token

                  + * {@codesnippet com.azure.storage.file.fileServiceAsyncClient.instantiation.sastoken} + * + *

                  If the {@code endpoint} doesn't contain the query parameters to construct a {@code SASTokenCredential} they may + * be set using {@link FileServiceClientBuilder#credential(SASTokenCredential) credential}.

                  + * + * {@codesnippet com.azure.storage.file.fileServiceClient.instantiation.credential} + * + * {@codesnippet com.azure.storage.file.fileServiceAsyncClient.instantiation.credential} + * + *

                  Another way to authenticate the client is using a {@link SharedKeyCredential}. To create a SharedKeyCredential + * a connection string from the Storage File service must be used. Set the SharedKeyCredential with + * {@link FileServiceClientBuilder#connectionString(String) connectionString}. If the builder has both a SASTokenCredential and + * SharedKeyCredential the SharedKeyCredential will be preferred when authorizing requests sent to the service.

                  + * + *

                  Instantiating a synchronous FileService Client with connection string.

                  + * {@codesnippet com.azure.storage.file.fileServiceClient.instantiation.connectionstring} + * + *

                  Instantiating an Asynchronous FileService Client with connection string.

                  + * {@codesnippet com.azure.storage.file.fileServiceAsyncClient.instantiation.connectionstring} + * + * @see FileServiceClient + * @see FileServiceAsyncClient + * @see SASTokenCredential + * @see SharedKeyCredential + */ +public final class FileServiceClientBuilder { + private static final String ACCOUNT_NAME = "accountname"; + private final List policies; + + private URL endpoint; + private SASTokenCredential sasTokenCredential; + private SharedKeyCredential sharedKeyCredential; + private HttpClient httpClient; + private HttpPipeline pipeline; + private HttpLogDetailLevel logLevel; + private RetryPolicy retryPolicy; + private Configuration configuration; + + /** + * Creates a builder instance that is able to configure and construct {@link FileServiceClient FileServiceClients} + * and {@link FileServiceAsyncClient FileServiceAsyncClients}. + */ + public FileServiceClientBuilder() { + retryPolicy = new RetryPolicy(); + logLevel = HttpLogDetailLevel.NONE; + policies = new ArrayList<>(); + configuration = ConfigurationManager.getConfiguration(); + } + + /** + * Creates a {@link FileServiceAsyncClient} based on options set in the builder. Every time {@code buildAsyncClient()} is + * called a new instance of {@link FileServiceAsyncClient} is created. + * + *

                  + * If {@link FileServiceClientBuilder#pipeline(HttpPipeline) pipeline} is set, then the {@code pipeline} and + * {@link FileServiceClientBuilder#endpoint(String) endpoint} are used to create the + * {@link FileServiceAsyncClient client}. All other builder settings are ignored. + *

                  + * + * @return A FileServiceAsyncClient with the options set from the builder. + * @throws IllegalArgumentException If neither a {@link SharedKeyCredential} or {@link SASTokenCredential} has been set. + */ + public FileServiceAsyncClient buildAsyncClient() { + if (pipeline != null) { + return new FileServiceAsyncClient(endpoint, pipeline); + } + + if (sasTokenCredential == null && sharedKeyCredential == null) { + throw new IllegalArgumentException("Credentials are required for authorization"); + } + + // Closest to API goes first, closest to wire goes last. + final List policies = new ArrayList<>(); + + policies.add(new UserAgentPolicy(FileConfiguration.NAME, FileConfiguration.VERSION, configuration)); + policies.add(new RequestIdPolicy()); + policies.add(new AddDatePolicy()); + + if (sharedKeyCredential != null) { + policies.add(new SharedKeyCredentialPolicy(sharedKeyCredential)); + } else { + policies.add(new SASTokenCredentialPolicy(sasTokenCredential)); + } + + HttpPolicyProviders.addBeforeRetryPolicies(policies); + + policies.add(retryPolicy); + + policies.addAll(this.policies); + HttpPolicyProviders.addAfterRetryPolicies(policies); + policies.add(new HttpLoggingPolicy(logLevel)); + + HttpPipeline pipeline = HttpPipeline.builder() + .policies(policies.toArray(new HttpPipelinePolicy[0])) + .httpClient(httpClient) + .build(); + + return new FileServiceAsyncClient(endpoint, pipeline); + } + + /** + * Creates a {@link FileServiceClient} based on options set in the builder. Every time {@code buildClient()} is + * called a new instance of {@link FileServiceClient} is created. + * + *

                  + * If {@link FileServiceClientBuilder#pipeline(HttpPipeline) pipeline} is set, then the {@code pipeline} and + * {@link FileServiceClientBuilder#endpoint(String) endpoint} are used to create the + * {@link FileServiceClient client}. All other builder settings are ignored. + *

                  + * + * @return A FileServiceClient with the options set from the builder. + * @throws NullPointerException If {@code endpoint} is {@code null}. + * @throws IllegalStateException If neither a {@link SharedKeyCredential} or {@link SASTokenCredential} has been set. + */ + public FileServiceClient buildClient() { + return new FileServiceClient(buildAsyncClient()); + } + + /** + * Sets the endpoint for the Azure Storage File instance that the client will interact with. + * + *

                  Query parameters of the endpoint will be parsed using {@link SASTokenCredential#fromQuery(String) fromQuery} in an + * attempt to generate a {@link SASTokenCredential} to authenticate requests sent to the service.

                  + * + * @param endpoint The URL of the Azure Storage File instance to send service requests to and receive responses from. + * @return the updated FileServiceClientBuilder object + * @throws IllegalArgumentException If {@code endpoint} isn't a proper URL + */ + public FileServiceClientBuilder endpoint(String endpoint) { + try { + URL fullURL = new URL(endpoint); + this.endpoint = new URL(fullURL.getProtocol() + "://" + fullURL.getHost()); + + // Attempt to get the SAS token from the URL passed + this.sasTokenCredential = SASTokenCredential.fromQuery(fullURL.getQuery()); + if (this.sasTokenCredential != null) { + this.sharedKeyCredential = null; + } + } catch (MalformedURLException ex) { + throw new IllegalArgumentException("The Azure Storage File Service endpoint url is malformed."); + } + + return this; + } + + /** + * Sets the {@link SASTokenCredential} used to authenticate requests sent to the Queue service. + * + * @param credential SAS token credential generated from the Storage account that authorizes requests + * @return the updated FileServiceClientBuilder object + * @throws NullPointerException If {@code credential} is {@code null}. + */ + public FileServiceClientBuilder credential(SASTokenCredential credential) { + this.sasTokenCredential = Objects.requireNonNull(credential); + this.sharedKeyCredential = null; + return this; + } + + /** + * Sets the {@link SharedKeyCredential} used to authenticate requests sent to the Queue service. + * + * @param credential Shared key credential generated from the Storage account that authorizes requests + * @return the updated FileServiceClientBuilder object + * @throws NullPointerException If {@code credential} is {@code null}. + */ + public FileServiceClientBuilder credential(SharedKeyCredential credential) { + this.sharedKeyCredential = Objects.requireNonNull(credential); + this.sasTokenCredential = null; + return this; + } + + /** + * Creates a {@link SharedKeyCredential} from the {@code connectionString} used to authenticate requests sent to the + * File service. + * + * @param connectionString Connection string from the Access Keys section in the Storage account + * @return the updated FileServiceClientBuilder object + * @throws NullPointerException If {@code connectionString} is {@code null}. + */ + public FileServiceClientBuilder connectionString(String connectionString) { + Objects.requireNonNull(connectionString); + this.sharedKeyCredential = SharedKeyCredential.fromConnectionString(connectionString); + getEndPointFromConnectionString(connectionString); + return this; + } + + private void getEndPointFromConnectionString(String connectionString) { + Map connectionStringPieces = new HashMap<>(); + for (String connectionStringPiece : connectionString.split(";")) { + String[] kvp = connectionStringPiece.split("=", 2); + connectionStringPieces.put(kvp[0].toLowerCase(Locale.ROOT), kvp[1]); + } + String accountName = connectionStringPieces.get(ACCOUNT_NAME); + try { + this.endpoint = new URL(String.format("https://%s.file.core.windows.net", accountName)); + } catch (MalformedURLException e) { + throw new IllegalArgumentException(String.format("There is no valid endpoint for the connection string. " + + "Connection String: %s", connectionString)); + } + } + + /** + * Sets the HTTP client to use for sending and receiving requests to and from the service. + * + * @param httpClient The HTTP client to use for requests. + * @return The updated FileServiceClientBuilder object. + * @throws NullPointerException If {@code httpClient} is {@code null}. + */ + public FileServiceClientBuilder httpClient(HttpClient httpClient) { + this.httpClient = Objects.requireNonNull(httpClient); + return this; + } + + /** + * Adds a policy to the set of existing policies that are executed after the {@link RetryPolicy}. + * + * @param pipelinePolicy The retry policy for service requests. + * @return The updated FileServiceClientBuilder object. + * @throws NullPointerException If {@code pipelinePolicy} is {@code null}. + */ + public FileServiceClientBuilder addPolicy(HttpPipelinePolicy pipelinePolicy) { + Objects.requireNonNull(pipelinePolicy); + this.policies.add(pipelinePolicy); + return this; + } + + /** + * Sets the logging level for HTTP requests and responses. + * + * @param logLevel The amount of logging output when sending and receiving HTTP requests/responses. + * @return The updated FileServiceClientBuilder object. + */ + public FileServiceClientBuilder httpLogDetailLevel(HttpLogDetailLevel logLevel) { + this.logLevel = logLevel; + return this; + } + + /** + * Sets the HTTP pipeline to use for the service client. + * + * If {@code pipeline} is set, all other settings are ignored, aside from {@link FileServiceClientBuilder#endpoint(String) endpoint} + * when building clients. + * + * @param pipeline The HTTP pipeline to use for sending service requests and receiving responses. + * @return The updated FileServiceClientBuilder object. + * @throws NullPointerException If {@code pipeline} is {@code null}. + */ + public FileServiceClientBuilder pipeline(HttpPipeline pipeline) { + this.pipeline = Objects.requireNonNull(pipeline); + return this; + } + + /** + * Sets the configuration store that is used during construction of the service client. + * + * The default configuration store is a clone of the {@link ConfigurationManager#getConfiguration() global + * configuration store}, use {@link Configuration#NONE} to bypass using configuration settings during construction. + * + * @param configuration The configuration store used to + * @return The updated FileServiceClientBuilder object. + * @throws NullPointerException If {@code configuration} is {@code null}. + */ + public FileServiceClientBuilder configuration(Configuration configuration) { + this.configuration = Objects.requireNonNull(configuration); + return this; + } } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/ShareAsyncClient.java b/storage/client/file/src/main/java/com/azure/storage/file/ShareAsyncClient.java index d6d1326ba9545..5b95227e1941d 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/ShareAsyncClient.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/ShareAsyncClient.java @@ -3,81 +3,436 @@ package com.azure.storage.file; +import com.azure.core.http.HttpPipeline; import com.azure.core.http.rest.Response; +import com.azure.core.http.rest.SimpleResponse; import com.azure.core.http.rest.VoidResponse; +import com.azure.core.implementation.DateTimeRfc1123; +import com.azure.core.util.Context; +import com.azure.storage.common.credentials.SASTokenCredential; +import com.azure.storage.common.credentials.SharedKeyCredential; +import com.azure.storage.file.implementation.AzureFileStorageBuilder; +import com.azure.storage.file.implementation.AzureFileStorageImpl; +import com.azure.storage.file.models.ShareCreateSnapshotHeaders; +import com.azure.storage.file.models.ShareGetPropertiesHeaders; import com.azure.storage.file.models.ShareInfo; import com.azure.storage.file.models.ShareProperties; import com.azure.storage.file.models.ShareSnapshotInfo; import com.azure.storage.file.models.ShareStatistics; +import com.azure.storage.file.models.SharesCreateSnapshotResponse; +import com.azure.storage.file.models.SharesGetPropertiesResponse; +import com.azure.storage.file.models.SharesGetStatisticsResponse; import com.azure.storage.file.models.SignedIdentifier; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - +import com.azure.storage.file.models.StorageErrorException; +import java.net.MalformedURLException; +import java.net.URL; +import java.time.OffsetDateTime; import java.util.List; import java.util.Map; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +/** + * This class provides a azureFileStorageClient that contains all the operations for interacting with a share in Azure Storage Share. + * Operations allowed by the azureFileStorageClient are creating and deleting the share, creating snapshots for the share, creating and + * deleting directories in the share and retrieving and updating properties metadata and access policies of the share. + * + *

                  Instantiating an Asynchronous Share Client

                  + * + * {@codesnippet com.azure.storage.file.shareAsyncClient.instantiation} + * + *

                  View {@link ShareClientBuilder this} for additional ways to construct the azureFileStorageClient.

                  + * + * @see ShareClientBuilder + * @see ShareClient + * @see SharedKeyCredential + * @see SASTokenCredential + */ public class ShareAsyncClient { + private final AzureFileStorageImpl azureFileStorageClient; + private final String shareName; + private final String snapshot; + + /** + * Creates a ShareAsyncClient that sends requests to the storage share at {@link AzureFileStorageImpl#url() endpoint}. + * Each service call goes through the {@link HttpPipeline pipeline} in the {@code azureFileStorageClient}. + * + * @param client Client that interacts with the service interfaces + * @param shareName Name of the share + */ + ShareAsyncClient(AzureFileStorageImpl client, String shareName) { + this.shareName = shareName; + this.snapshot = null; - ShareAsyncClient() { - throw new UnsupportedOperationException(); + this.azureFileStorageClient = client; } - public static ShareClientBuilder asyncBuilder() { - throw new UnsupportedOperationException(); + /** + * Creates a ShareAsyncClient that sends requests to the storage share at {@code endpoint}. + * Each service call goes through the {@code httpPipeline}. + * + * @param endpoint URL for the Storage File service + * @param httpPipeline HttpPipeline that the HTTP requests and response flow through + * @param shareName Name of the share + * @param snapshot Optional specific snapshot of the share + */ + ShareAsyncClient(URL endpoint, HttpPipeline httpPipeline, String shareName, String snapshot) { + this.shareName = shareName; + this.snapshot = snapshot; + + this.azureFileStorageClient = new AzureFileStorageBuilder().pipeline(httpPipeline) + .url(endpoint.toString()) + .build(); } - public String url() { - throw new UnsupportedOperationException(); + /** + * Get the url of the storage share client. + * @return the url of the Storage Share. + * @throws RuntimeException If the share is using a malformed URL. + */ + public URL getShareUrl() { + try { + return new URL(azureFileStorageClient.url()); + } catch (MalformedURLException e) { + throw new RuntimeException(String.format("Invalid URL on %s: %s" + getClass().getSimpleName(), + azureFileStorageClient.url()), e); + } } + + /** + * Constructs a {@link DirectoryAsyncClient} that interacts with the root directory in the share. + * + *

                  If the directory doesn't exist in the share {@link DirectoryAsyncClient#create(Map) create} in the azureFileStorageClient will + * need to be called before interaction with the directory can happen.

                  + * + * @return a {@link DirectoryAsyncClient} that interacts with the root directory in the share + */ public DirectoryAsyncClient getRootDirectoryClient() { - throw new UnsupportedOperationException(); + return getDirectoryClient(""); } + /** + * Constructs a {@link DirectoryAsyncClient} that interacts with the specified directory. + * + *

                  If the directory doesn't exist in the share {@link DirectoryAsyncClient#create(Map) create} in the azureFileStorageClient will + * need to be called before interaction with the directory can happen.

                  + * + * @param directoryName Name of the directory + * @return a {@link DirectoryAsyncClient} that interacts with the directory in the share + */ public DirectoryAsyncClient getDirectoryClient(String directoryName) { - throw new UnsupportedOperationException(); + return new DirectoryAsyncClient(azureFileStorageClient, shareName, directoryName, snapshot); } - public Mono> create(Map metadata, int quotaInGB) { - throw new UnsupportedOperationException(); + /** + * Creates the share in the storage account. + * + *

                  Code Samples

                  + * + *

                  Create the share

                  + * + * {@codesnippet com.azure.storage.file.shareAsyncClient.create} + * + * @return A response containing information about the share and the status its creation. + * @throws StorageErrorException If the share already exists with different metadata + */ + public Mono> create() { + return create(null, null); } + /** + * Creates the share in the storage account with the specified metadata and quota. + * + *

                  Code Samples

                  + * + *

                  Create the share with metadata "share:metadata"

                  + * + * {@codesnippet com.azure.storage.file.shareAsyncClient.create#map-integer.metadata} + * + *

                  Create the share with a quota of 10 GB

                  + * + * {@codesnippet com.azure.storage.file.shareAsyncClient.create#map-integer.quota} + * + * @param metadata Optional metadata to associate with the share + * @param quotaInGB Optional maximum size the share is allowed to grow to in GB. This must be greater than 0 and + * less than or equal to 5120. The default value is 5120. + * @return A response containing information about the share and the status its creation. + * @throws StorageErrorException If the share already exists with different metadata or {@code quotaInGB} is outside the + * allowed range. + */ + public Mono> create(Map metadata, Integer quotaInGB) { + return azureFileStorageClient.shares().createWithRestResponseAsync(shareName, null, metadata, quotaInGB, Context.NONE) + .map(this::mapToShareInfoResponse); + } + + /** + * Creates a snapshot of the share with the same metadata associated to the share at the time of creation. + * + *

                  Code Samples

                  + * + *

                  Create a snapshot

                  + * + * {@codesnippet com.azure.storage.file.shareAsyncClient.createSnapshot} + * + * @return A response containing information about the snapshot of share. + * @throws StorageErrorException If the share doesn't exist, there are 200 snapshots of the share, or a snapshot is + * in progress for the share + */ + public Mono> createSnapshot() { + return createSnapshot(null); + } + + /** + * Creates a snapshot of the share with the metadata that was passed associated to the snapshot. + * + *

                  Code Samples

                  + * + *

                  Create a snapshot with metadata "snapshot:metadata"

                  + * + * {@codesnippet com.azure.storage.file.shareAsyncClient.createSnapshot#map} + * + * @param metadata Optional metadata to associate with the snapshot. If {@code null} the metadata of the share + * will be copied to the snapshot. + * @return A response containing information about the snapshot of share. + * @throws StorageErrorException If the share doesn't exist, there are 200 snapshots of the share, or a snapshot is + * in progress for the share + */ public Mono> createSnapshot(Map metadata) { - throw new UnsupportedOperationException(); + return azureFileStorageClient.shares().createSnapshotWithRestResponseAsync(shareName, null, metadata, Context.NONE) + .map(this::mapCreateSnapshotResponse); } - public Mono delete(String shareSnapshot) { - throw new UnsupportedOperationException(); + /** + * Deletes the share in the storage account + * + *

                  Code Samples

                  + * + *

                  Delete the share

                  + * + * {@codesnippet com.azure.storage.file.shareAsyncClient.delete} + * + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the share doesn't exist + */ + public Mono delete() { + return azureFileStorageClient.shares().deleteWithRestResponseAsync(shareName, snapshot, null, null, Context.NONE) + .map(VoidResponse::new); } - public Mono> getProperties(String shareSnapshot) { - throw new UnsupportedOperationException(); + /** + * Retrieves the properties of the share, these include the metadata associated to it and the quota that the share + * is restricted to. + * + *

                  Code Samples

                  + * + *

                  Retrieve the share properties

                  + * + * {@codesnippet com.azure.storage.file.shareAsyncClient.getProperties} + * + * @return the properties of the share + * @throws StorageErrorException If the share doesn't exist + */ + public Mono> getProperties() { + return azureFileStorageClient.shares().getPropertiesWithRestResponseAsync(shareName, snapshot, null, Context.NONE) + .map(this::mapGetPropertiesResponse); } + /** + * Sets the maximum size in GB that the share is allowed to grow. + * + *

                  Code Samples

                  + * + *

                  Set the quota to 1024 GB

                  + * + * {@codesnippet com.azure.storage.file.shareAsyncClient.setQuota} + * + * @param quotaInGB Size in GB to limit the share's growth. The quota in GB must be between 1 and 5120. + * @return information about the share + * @throws StorageErrorException If the share doesn't exist or {@code quotaInGB} is outside the allowed bounds + */ public Mono> setQuota(int quotaInGB) { - throw new UnsupportedOperationException(); + return azureFileStorageClient.shares().setQuotaWithRestResponseAsync(shareName, null, quotaInGB, Context.NONE) + .map(this::mapToShareInfoResponse); } + /** + * Sets the user-defined metadata to associate to the share. + * + *

                  If {@code null} is passed for the metadata it will clear the metadata associated to the share.

                  + * + *

                  Code Samples

                  + * + *

                  Set the metadata to "share:updatedMetadata"

                  + * + * {@codesnippet com.azure.storage.file.shareAsyncClient.setMetadata#map} + * + *

                  Clear the metadata of the share

                  + * + * {@codesnippet com.azure.storage.file.shareAsyncClient.clearMetadata#map} + * + * @param metadata Metadata to set on the share, if null is passed the metadata for the share is cleared + * @return information about the share + * @throws StorageErrorException If the share doesn't exist or the metadata contains invalid keys + */ public Mono> setMetadata(Map metadata) { - throw new UnsupportedOperationException(); + return azureFileStorageClient.shares().setMetadataWithRestResponseAsync(shareName, null, metadata, Context.NONE) + .map(this::mapToShareInfoResponse); } - public Flux listAccessPolicy() { - throw new UnsupportedOperationException(); + /** + * Retrieves stored access policies specified for the share. + * + *

                  Code Samples

                  + * + *

                  List the stored access policies

                  + * + * {@codesnippet com.azure.storage.file.shareAsyncClient.getAccessPolicy} + * + * @return The stored access policies specified on the queue. + * @throws StorageErrorException If the share doesn't exist + */ + public Flux getAccessPolicy() { + return azureFileStorageClient.shares().getAccessPolicyWithRestResponseAsync(shareName, Context.NONE) + .flatMapMany(response -> Flux.fromIterable(response.value())); } + + /** + * Sets stored access policies for the share. + * + *

                  Code Samples

                  + * + *

                  Set a read only stored access policy

                  + * + * {@codesnippet com.azure.storage.file.shareAsyncClient.setAccessPolicy} + * + * @param permissions Access policies to set on the queue + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the share doesn't exist, a stored access policy doesn't have all fields filled out, + * or the share will have more than five policies. + */ public Mono> setAccessPolicy(List permissions) { - throw new UnsupportedOperationException(); + return azureFileStorageClient.shares().setAccessPolicyWithRestResponseAsync(shareName, permissions, null, Context.NONE) + .map(this::mapToShareInfoResponse); } + /** + * Retrieves storage statistics about the share. + * + *

                  Code Samples

                  + * + *

                  Retrieve the storage statistics

                  + * + * {@codesnippet com.azure.storage.file.shareAsyncClient.getStatistics} + * + * @return the storage statistics of the share + */ public Mono> getStatistics() { - throw new UnsupportedOperationException(); + return azureFileStorageClient.shares().getStatisticsWithRestResponseAsync(shareName, Context.NONE) + .map(this::mapGetStatisticsResponse); + } + + /** + * Creates the directory in the share with the given name. + * + *

                  Code Samples

                  + * + *

                  Create the directory "mydirectory"

                  + * + * {@codesnippet com.azure.storage.file.shareAsyncClient.createDirectory#string} + * * + * @param directoryName Name of the directory + * @return A response containing a {@link DirectoryAsyncClient} to interact with the created directory and the + * status of its creation. + * @throws StorageErrorException If the share doesn't exist, the directory already exists or is in the process of + * being deleted, or the parent directory for the new directory doesn't exist + */ + public Mono> createDirectory(String directoryName) { + return createDirectory(directoryName, null); } + /** + * Creates the directory in the share with the given name and associates the passed metadata to it. + * + *

                  Code Samples

                  + * + *

                  Create the directory "documents" with metadata "directory:metadata"

                  + * + * {@codesnippet com.azure.storage.file.shareAsyncClient.createDirectory#string-map} + * + * @param directoryName Name of the directory + * @param metadata Optional metadata to associate with the directory + * @return A response containing a {@link DirectoryAsyncClient} to interact with the created directory and the + * status of its creation. + * @throws StorageErrorException If the share doesn't exist, the directory already exists or is in the process of + * being deleted, the parent directory for the new directory doesn't exist, or the metadata is using an illegal + * key name + */ public Mono> createDirectory(String directoryName, Map metadata) { - throw new UnsupportedOperationException(); + DirectoryAsyncClient directoryAsyncClient = getDirectoryClient(directoryName); + return directoryAsyncClient.create(metadata).map(response -> new SimpleResponse<>(response, directoryAsyncClient)); } + /** + * Deletes the specified directory in the share. + * + *

                  Code Samples

                  + * + *

                  Delete the directory "empty"

                  + * + * {@codesnippet com.azure.storage.file.shareAsyncClient.deleteDirectory#string} + * + * @param directoryName Name of the directory + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the share doesn't exist or the directory isn't empty + */ public Mono deleteDirectory(String directoryName) { - throw new UnsupportedOperationException(); + return getDirectoryClient(directoryName).delete().map(VoidResponse::new); + } + + /** + * Get snapshot id which attached to {@link ShareAsyncClient}. + * Return {@code null} if no snapshot id attached. + * + *

                  Code Samples

                  + * + *

                  Get the share snapshot id.

                  + * + * {@codesnippet com.azure.storage.file.fileAsyncClient.getShareSnapshotId} + * + * @return The snapshot id which is a unique {@code DateTime} value that identifies the share snapshot to its base share. + */ + public String getSnapshotId() { + return this.snapshot; + } + + private Response mapToShareInfoResponse(Response response) { + String eTag = response.headers().value("ETag"); + OffsetDateTime lastModified = new DateTimeRfc1123(response.headers().value("Last-Modified")).dateTime(); + + return new SimpleResponse<>(response.request(), response.statusCode(), response.headers(), new ShareInfo(eTag, lastModified)); + } + + private Response mapCreateSnapshotResponse(SharesCreateSnapshotResponse response) { + ShareCreateSnapshotHeaders headers = response.deserializedHeaders(); + ShareSnapshotInfo snapshotInfo = new ShareSnapshotInfo(headers.snapshot(), headers.eTag(), headers.lastModified()); + + return new SimpleResponse<>(response.request(), response.statusCode(), response.headers(), snapshotInfo); + } + + private Response mapGetPropertiesResponse(SharesGetPropertiesResponse response) { + ShareGetPropertiesHeaders headers = response.deserializedHeaders(); + ShareProperties shareProperties = new ShareProperties().quota(headers.quota()) + .etag(headers.eTag()) + .lastModified(headers.lastModified()) + .metadata(headers.metadata()); + + return new SimpleResponse<>(response.request(), response.statusCode(), response.headers(), shareProperties); + } + + private Response mapGetStatisticsResponse(SharesGetStatisticsResponse response) { + ShareStatistics shareStatistics = new ShareStatistics(response.value().shareUsageBytes() / 1024); + + return new SimpleResponse<>(response.request(), response.statusCode(), response.headers(), shareStatistics); } } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/ShareClient.java b/storage/client/file/src/main/java/com/azure/storage/file/ShareClient.java index 17ad31b4aa504..2d5c1859eee25 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/ShareClient.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/ShareClient.java @@ -1,82 +1,349 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + package com.azure.storage.file; import com.azure.core.http.rest.Response; +import com.azure.core.http.rest.SimpleResponse; import com.azure.core.http.rest.VoidResponse; +import com.azure.storage.common.credentials.SASTokenCredential; +import com.azure.storage.common.credentials.SharedKeyCredential; import com.azure.storage.file.models.ShareInfo; import com.azure.storage.file.models.ShareProperties; import com.azure.storage.file.models.ShareSnapshotInfo; import com.azure.storage.file.models.ShareStatistics; import com.azure.storage.file.models.SignedIdentifier; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - +import com.azure.storage.file.models.StorageErrorException; +import java.net.URL; import java.util.List; import java.util.Map; +/** + * This class provides a client that contains all the operations for interacting with a share in Azure Storage Share. + * Operations allowed by the client are creating and deleting the share, creating snapshots for the share, creating and + * deleting directories in the share and retrieving and updating properties metadata and access policies of the share. + * + *

                  Instantiating a Synchronous Share Client

                  + * + * {@codesnippet com.azure.storage.file.shareClient.instantiation} + * + *

                  View {@link ShareClientBuilder this} for additional ways to construct the client.

                  + * + * @see ShareClientBuilder + * @see ShareAsyncClient + * @see SharedKeyCredential + * @see SASTokenCredential + */ public class ShareClient { - private final ShareAsyncClient client; - ShareClient() { - throw new UnsupportedOperationException(); + ShareClient(ShareAsyncClient client) { + this.client = client; } - public static ShareClientBuilder syncBuilder() { - throw new UnsupportedOperationException(); - } - - public String url() { - throw new UnsupportedOperationException(); + /** + * Get the url of the storage share client. + * @return the url of the Storage Share. + * @throws RuntimeException If the share is using a malformed URL. + */ + public URL getShareUrl() { + return client.getShareUrl(); } + /** + * Constructs a {@link DirectoryClient} that interacts with the root directory in the share. + * + *

                  If the directory doesn't exist in the share {@link DirectoryClient#create(Map) create} in the client will + * need to be called before interaction with the directory can happen.

                  + * + * @return a {@link DirectoryClient} that interacts with the root directory in the share + */ public DirectoryClient getRootDirectoryClient() { - throw new UnsupportedOperationException(); + return getDirectoryClient(""); } + /** + * Constructs a {@link DirectoryClient} that interacts with the specified directory. + * + *

                  If the directory doesn't exist in the share {@link DirectoryClient#create(Map) create} in the client will + * need to be called before interaction with the directory can happen.

                  + * + * @param directoryName Name of the directory + * @return a {@link DirectoryClient} that interacts with the directory in the share + */ public DirectoryClient getDirectoryClient(String directoryName) { - throw new UnsupportedOperationException(); + return new DirectoryClient(client.getDirectoryClient(directoryName)); + } + + /** + * Creates the share in the storage account. + * + *

                  Code Samples

                  + * + *

                  Create the share

                  + * + * {@codesnippet com.azure.storage.file.shareClient.create} + * + * @return A response containing information about the share and the status its creation. + * @throws StorageErrorException If the share already exists with different metadata + */ + public Response create() { + return create(null, null); } - public Mono> create(Map metadata, int quotaInGB) { - throw new UnsupportedOperationException(); + /** + * Creates the share in the storage account with the specified metadata and quota. + * + *

                  Code Samples

                  + * + *

                  Create the share with metadata "share:metadata"

                  + * + * {@codesnippet com.azure.storage.file.shareClient.create#map-integer.metadata} + * + *

                  Create the share with a quota of 10 GB

                  + * + * {@codesnippet com.azure.storage.file.shareClient.create#map-integer.quota} + * + * @param metadata Optional metadata to associate with the share + * @param quotaInGB Optional maximum size the share is allowed to grow to in GB. This must be greater than 0 and + * less than or equal to 5120. The default value is 5120. + * @return A response containing information about the share and the status its creation. + * @throws StorageErrorException If the share already exists with different metadata or {@code quotaInGB} is outside the + * allowed range. + */ + public Response create(Map metadata, Integer quotaInGB) { + return client.create(metadata, quotaInGB).block(); } - public Mono> createSnapshot(Map metadata) { - throw new UnsupportedOperationException(); + /** + * Creates a snapshot of the share with the same metadata associated to the share at the time of creation. + * + *

                  Code Samples

                  + * + *

                  Create a snapshot

                  + * + * {@codesnippet com.azure.storage.file.shareClient.createSnapshot} + * + * @return A response containing information about the snapshot of share. + * @throws StorageErrorException If the share doesn't exist, there are 200 snapshots of the share, or a snapshot is + * in progress for the share + */ + public Response createSnapshot() { + return createSnapshot(null); } - public Mono delete(String shareSnapshot) { - throw new UnsupportedOperationException(); + /** + * Creates a snapshot of the share with the metadata that was passed associated to the snapshot. + * + *

                  Code Samples

                  + * + *

                  Create a snapshot with metadata "snapshot:metadata"

                  + * + * {@codesnippet com.azure.storage.file.shareClient.createSnapshot#map} + * + * @param metadata Optional metadata to associate with the snapshot. If {@code null} the metadata of the share + * will be copied to the snapshot. + * @return A response containing information about the snapshot of share. + * @throws StorageErrorException If the share doesn't exist, there are 200 snapshots of the share, or a snapshot is + * in progress for the share + */ + public Response createSnapshot(Map metadata) { + return client.createSnapshot(metadata).block(); } - public Mono> getProperties(String shareSnapshot) { - throw new UnsupportedOperationException(); + /** + * Deletes the share in the storage account + * + *

                  Code Samples

                  + * + *

                  Delete the share

                  + * + * {@codesnippet com.azure.storage.file.shareClient.delete} + * + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the share doesn't exist + */ + public VoidResponse delete() { + return client.delete().block(); } - public Mono> setQuota(int quotaInGB) { - throw new UnsupportedOperationException(); + /** + * Retrieves the properties of the share, these include the metadata associated to it and the quota that the share + * is restricted to. + * + *

                  Code Samples

                  + * + *

                  Retrieve the share properties

                  + * + * {@codesnippet com.azure.storage.file.shareClient.getProperties} + * + * @return the properties of the share + * @throws StorageErrorException If the share doesn't exist + */ + public Response getProperties() { + return client.getProperties().block(); } - public Mono> setMetadata(Map metadata) { - throw new UnsupportedOperationException(); + /** + * Sets the maximum size in GB that the share is allowed to grow. + * + *

                  Code Samples

                  + * + *

                  Set the quota to 1024 GB

                  + * + * {@codesnippet com.azure.storage.file.shareClient.setQuota} + * + * @param quotaInGB Size in GB to limit the share's growth. The quota in GB must be between 1 and 5120. + * @return information about the share + * @throws StorageErrorException If the share doesn't exist or {@code quotaInGB} is outside the allowed bounds + */ + public Response setQuota(int quotaInGB) { + return client.setQuota(quotaInGB).block(); } - public Flux listAccessPolicy() { - throw new UnsupportedOperationException(); + /** + * Sets the user-defined metadata to associate to the share. + * + *

                  If {@code null} is passed for the metadata it will clear the metadata associated to the share.

                  + * + *

                  Code Samples

                  + * + *

                  Set the metadata to "share:updatedMetadata"

                  + * + * {@codesnippet com.azure.storage.file.shareClient.setMetadata#map} + * + *

                  Clear the metadata of the share

                  + * + * {@codesnippet com.azure.storage.file.shareClient.clearMetadata#map} + * + * @param metadata Metadata to set on the share, if null is passed the metadata for the share is cleared + * @return information about the share + * @throws StorageErrorException If the share doesn't exist or the metadata contains invalid keys + */ + public Response setMetadata(Map metadata) { + return client.setMetadata(metadata).block(); } - public Mono> setAccessPolicy(List permissions) { - throw new UnsupportedOperationException(); + + /** + * Retrieves stored access policies specified for the share. + * + *

                  Code Samples

                  + * + *

                  List the stored access policies

                  + * + * {@codesnippet com.azure.storage.file.shareClient.getAccessPolicy} + * + * @return The stored access policies specified on the queue. + * @throws StorageErrorException If the share doesn't exist + */ + public Iterable getAccessPolicy() { + return client.getAccessPolicy().toIterable(); + } + + /** + * Sets stored access policies for the share. + * + *

                  Code Samples

                  + * + *

                  Set a read only stored access policy

                  + * + * {@codesnippet com.azure.storage.file.shareClient.setAccessPolicy} + * + * @param permissions Access policies to set on the queue + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the share doesn't exist, a stored access policy doesn't have all fields filled out, + * or the share will have more than five policies. + */ + public Response setAccessPolicy(List permissions) { + return client.setAccessPolicy(permissions).block(); + } + + /** + * Retrieves storage statistics about the share. + * + *

                  Code Samples

                  + * + *

                  Retrieve the storage statistics

                  + * + * {@codesnippet com.azure.storage.file.shareClient.getStatistics} + * + * @return the storage statistics of the share + */ + public Response getStatistics() { + return client.getStatistics().block(); + } + + /** + * Creates the directory in the share with the given name. + * + *

                  Code Samples

                  + * + *

                  Create the directory "documents"

                  + * + * {@codesnippet com.azure.storage.file.shareClient.createDirectory#string} + * + * @param directoryName Name of the directory + * @return A response containing a {@link DirectoryClient} to interact with the created directory and the + * status of its creation. + * @throws StorageErrorException If the share doesn't exist, the directory already exists or is in the process of + * being deleted, or the parent directory for the new directory doesn't exist + */ + public Response createDirectory(String directoryName) { + return createDirectory(directoryName, null); } - public Mono> getStatistics() { - throw new UnsupportedOperationException(); + /** + * Creates the directory in the share with the given name and associates the passed metadata to it. + * + *

                  Code Samples

                  + * + *

                  Create the directory "documents" with metadata "directory:metadata"

                  + * + * {@codesnippet com.azure.storage.file.shareClient.createDirectory#string-map} + * + * @param directoryName Name of the directory + * @param metadata Optional metadata to associate with the directory + * @return A response containing a {@link DirectoryAsyncClient} to interact with the created directory and the + * status of its creation. + * @throws StorageErrorException If the share doesn't exist, the directory already exists or is in the process of + * being deleted, the parent directory for the new directory doesn't exist, or the metadata is using an illegal + * key name + */ + public Response createDirectory(String directoryName, Map metadata) { + DirectoryClient directoryClient = getDirectoryClient(directoryName); + return new SimpleResponse<>(directoryClient.create(metadata), directoryClient); } - public Mono> createDirectory(String directoryName, Map metadata) { - throw new UnsupportedOperationException(); + /** + * Deletes the specified directory in the share. + * + *

                  Code Samples

                  + * + *

                  Delete the directory "empty"

                  + * + * {@codesnippet com.azure.storage.file.shareClient.deleteDirectory#string} + * + * @param directoryName Name of the directory + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the share doesn't exist or the directory isn't empty + */ + public VoidResponse deleteDirectory(String directoryName) { + return client.deleteDirectory(directoryName).block(); } - public Mono deleteDirectory(String directoryName) { - throw new UnsupportedOperationException(); + /** + * Get snapshot id which attached to {@link ShareClient}. + * Return {@code null} if no snapshot id attached. + * + *

                  Code Samples

                  + * + *

                  Get the share snapshot id.

                  + * + * {@codesnippet com.azure.storage.file.shareClient.getSnapshotId} + * + * @return The snapshot id which is a unique {@code DateTime} value that identifies the share snapshot to its base share. + */ + public String getSnapshotId() { + return client.getSnapshotId(); } } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/ShareClientBuilder.java b/storage/client/file/src/main/java/com/azure/storage/file/ShareClientBuilder.java index 5ab4e37ddcd3e..ec6d06ddee182 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/ShareClientBuilder.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/ShareClientBuilder.java @@ -3,7 +3,343 @@ package com.azure.storage.file; +import com.azure.core.http.HttpClient; +import com.azure.core.http.HttpPipeline; +import com.azure.core.http.policy.AddDatePolicy; +import com.azure.core.http.policy.HttpLogDetailLevel; +import com.azure.core.http.policy.HttpLoggingPolicy; +import com.azure.core.http.policy.HttpPipelinePolicy; +import com.azure.core.http.policy.RequestIdPolicy; +import com.azure.core.http.policy.RetryPolicy; +import com.azure.core.http.policy.UserAgentPolicy; +import com.azure.core.implementation.http.policy.spi.HttpPolicyProviders; +import com.azure.core.util.configuration.Configuration; +import com.azure.core.util.configuration.ConfigurationManager; +import com.azure.storage.common.credentials.SASTokenCredential; +import com.azure.storage.common.credentials.SharedKeyCredential; +import com.azure.storage.common.policy.SASTokenCredentialPolicy; +import com.azure.storage.common.policy.SharedKeyCredentialPolicy; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; + +/** + * This class provides a fluent builder API to help aid the configuration and instantiation of the {@link ShareClient ShareClients} + * and {@link ShareAsyncClient SahreAsyncClients}, calling {@link ShareClientBuilder#buildClient() buildClient} + * constructs an instance of ShareClient and calling {@link ShareClientBuilder#buildAsyncClient() buildAsyncClient} + * constructs an instance of SahreAsyncClient. + * + *

                  The client needs the endpoint of the Azure Storage File service, name of the share, and authorization credential. + * {@link ShareClientBuilder#endpoint(String) endpoint} gives the builder the endpoint and may give the builder the + * {@link ShareClientBuilder#shareName(String) shareName} and a {@link SASTokenCredential} that authorizes the client.

                  + * + *

                  Instantiating a synchronous Share Client with SAS token

                  + * {@codesnippet com.azure.storage.file.shareClient.instantiation.sastoken} + * + *

                  Instantiating an Asynchronous Share Client with SAS token

                  + * {@codesnippet com.azure.storage.file.shareAsyncClient.instantiation.sastoken} + * + *

                  If the {@code endpoint} doesn't contain the query parameters to construct a {@code SASTokenCredential} they may + * be set using {@link ShareClientBuilder#credential(SASTokenCredential) credential}.

                  + * + * {@codesnippet com.azure.storage.file.shareClient.instantiation.credential} + * + * {@codesnippet com.azure.storage.file.shareAsyncClient.instantiation.credential} + * + *

                  Another way to authenticate the client is using a {@link SharedKeyCredential}. To create a SharedKeyCredential + * a connection string from the Storage File service must be used. Set the SharedKeyCredential with + * {@link ShareClientBuilder#connectionString(String) connectionString}. If the builder has both a SASTokenCredential and + * SharedKeyCredential the SharedKeyCredential will be preferred when authorizing requests sent to the service.

                  + * + *

                  Instantiating a synchronous Share Client with connection string.

                  + * {@codesnippet com.azure.storage.file.shareClient.instantiation.connectionstring} + * + *

                  Instantiating an Asynchronous Share Client with connection string.

                  + * {@codesnippet com.azure.storage.file.shareAsyncClient.instantiation.connectionstring} + * + * @see ShareClient + * @see ShareAsyncClient + * @see SASTokenCredential + * @see SharedKeyCredential + */ public class ShareClientBuilder { + private static final String ACCOUNT_NAME = "accountname"; + private final List policies; + + private URL endpoint; + private SASTokenCredential sasTokenCredential; + private SharedKeyCredential sharedKeyCredential; + private String shareName; + private String snapshot; + private HttpClient httpClient; + private HttpPipeline pipeline; + private HttpLogDetailLevel logLevel; + private RetryPolicy retryPolicy; + private Configuration configuration; + + /** + * Creates a builder instance that is able to configure and construct {@link ShareClient ShareClients} + * and {@link ShareAsyncClient ShareAsyncClients}. + */ + public ShareClientBuilder() { + retryPolicy = new RetryPolicy(); + logLevel = HttpLogDetailLevel.NONE; + policies = new ArrayList<>(); + configuration = ConfigurationManager.getConfiguration(); + } + + /** + * Creates a {@link ShareAsyncClient} based on options set in the builder. Every time {@code buildAsyncClient()} is + * called a new instance of {@link ShareAsyncClient} is created. + * + *

                  + * If {@link ShareClientBuilder#pipeline(HttpPipeline) pipeline} is set, then the {@code pipeline} and + * {@link ShareClientBuilder#endpoint(String) endpoint} are used to create the + * {@link ShareAsyncClient client}. All other builder settings are ignored. + *

                  + * + * @return A ShareAsyncClient with the options set from the builder. + * @throws NullPointerException If {@code shareName} is {@code null}. + * @throws IllegalArgumentException If neither a {@link SharedKeyCredential} or {@link SASTokenCredential} has been set. + */ + public ShareAsyncClient buildAsyncClient() { + Objects.requireNonNull(shareName); + + if (pipeline != null) { + return new ShareAsyncClient(endpoint, pipeline, shareName, snapshot); + } + + if (sasTokenCredential == null && sharedKeyCredential == null) { + throw new IllegalArgumentException("Credentials are required for authorization"); + } + + // Closest to API goes first, closest to wire goes last. + final List policies = new ArrayList<>(); + + policies.add(new UserAgentPolicy(FileConfiguration.NAME, FileConfiguration.VERSION, configuration)); + policies.add(new RequestIdPolicy()); + policies.add(new AddDatePolicy()); + + if (sharedKeyCredential != null) { + policies.add(new SharedKeyCredentialPolicy(sharedKeyCredential)); + } else { + policies.add(new SASTokenCredentialPolicy(sasTokenCredential)); + } + + HttpPolicyProviders.addBeforeRetryPolicies(policies); + + policies.add(retryPolicy); + + policies.addAll(this.policies); + HttpPolicyProviders.addAfterRetryPolicies(policies); + policies.add(new HttpLoggingPolicy(logLevel)); + + HttpPipeline pipeline = HttpPipeline.builder() + .policies(policies.toArray(new HttpPipelinePolicy[0])) + .httpClient(httpClient) + .build(); + + return new ShareAsyncClient(endpoint, pipeline, shareName, snapshot); + } + + /** + * Creates a {@link ShareClient} based on options set in the builder. Every time {@code buildClient()} is + * called a new instance of {@link ShareClient} is created. + * + *

                  + * If {@link ShareClientBuilder#pipeline(HttpPipeline) pipeline} is set, then the {@code pipeline} and + * {@link ShareClientBuilder#endpoint(String) endpoint} are used to create the + * {@link ShareClient client}. All other builder settings are ignored. + *

                  + * + * @return A ShareClient with the options set from the builder. + * @throws NullPointerException If {@code endpoint} or {@code shareName} is {@code null}. + * @throws IllegalStateException If neither a {@link SharedKeyCredential} or {@link SASTokenCredential} has been set. + */ + public ShareClient buildClient() { + return new ShareClient(buildAsyncClient()); + } + + /** + * Sets the endpoint for the Azure Storage File instance that the client will interact with. + * + *

                  The first path segment, if the endpoint contains path segments, will be assumed to be the name of the share + * that the client will interact with.

                  + * + *

                  Query parameters of the endpoint will be parsed using {@link SASTokenCredential#fromQuery(String)} in an + * attempt to generate a {@link SASTokenCredential} to authenticate requests sent to the service.

                  + * + * @param endpoint The URL of the Azure Storage File instance to send service requests to and receive responses from. + * @return the updated ShareClientBuilder object + * @throws IllegalArgumentException If {@code endpoint} is {@code null} or is an invalid URL + */ + public ShareClientBuilder endpoint(String endpoint) { + try { + URL fullURL = new URL(endpoint); + this.endpoint = new URL(fullURL.getProtocol() + "://" + fullURL.getHost()); + + // Attempt to get the SAS token from the URL passed + this.sasTokenCredential = SASTokenCredential.fromQuery(fullURL.getQuery()); + if (this.sasTokenCredential != null) { + this.sharedKeyCredential = null; + } + } catch (MalformedURLException ex) { + throw new IllegalArgumentException("The Azure Storage File Service endpoint url is malformed."); + } + + return this; + } + + /** + * Sets the {@link SASTokenCredential} used to authenticate requests sent to the File service. + * + * @param credential SAS token credential generated from the Storage account that authorizes requests + * @return the updated ShareClientBuilder object + * @throws NullPointerException If {@code credential} is {@code null}. + */ + public ShareClientBuilder credential(SASTokenCredential credential) { + this.sasTokenCredential = Objects.requireNonNull(credential); + this.sharedKeyCredential = null; + return this; + } + + /** + * Sets the {@link SharedKeyCredential} used to authenticate requests sent to the File service. + * + * @param credential Shared key credential generated from the Storage account that authorizes requests + * @return the updated ShareClientBuilder object + * @throws NullPointerException If {@code credential} is {@code null}. + */ + public ShareClientBuilder credential(SharedKeyCredential credential) { + this.sharedKeyCredential = Objects.requireNonNull(credential); + this.sasTokenCredential = null; + return this; + } + + /** + * Creates a {@link SharedKeyCredential} from the {@code connectionString} used to authenticate requests sent to the + * File service. + * + * @param connectionString Connection string from the Access Keys section in the Storage account + * @return the updated ShareClientBuilder object + * @throws NullPointerException If {@code connectionString} is {@code null}. + */ + public ShareClientBuilder connectionString(String connectionString) { + Objects.requireNonNull(connectionString); + this.sharedKeyCredential = SharedKeyCredential.fromConnectionString(connectionString); + getEndPointFromConnectionString(connectionString); + return this; + } + + private void getEndPointFromConnectionString(String connectionString) { + Map connectionStringPieces = new HashMap<>(); + for (String connectionStringPiece : connectionString.split(";")) { + String[] kvp = connectionStringPiece.split("=", 2); + connectionStringPieces.put(kvp[0].toLowerCase(Locale.ROOT), kvp[1]); + } + String accountName = connectionStringPieces.get(ACCOUNT_NAME); + try { + this.endpoint = new URL(String.format("https://%s.file.core.windows.net", accountName)); + } catch (MalformedURLException e) { + throw new IllegalArgumentException(String.format("There is no valid endpoint for the connection string. " + + "Connection String: %s", connectionString)); + } + } + + /** + * Sets the share that the constructed clients will interact with + * + * @param shareName Name of the share + * @return the updated ShareClientBuilder object + * @throws NullPointerException If {@code shareName} is {@code null}. + */ + public ShareClientBuilder shareName(String shareName) { + this.shareName = Objects.requireNonNull(shareName); + return this; + } + + /** + * Sets the snapshot that the constructed clients will interact with. This snapshot must be linked to the share + * that has been specified in the builder. + * + * @param snapshot Identifier of the snapshot + * @return the updated ShareClientBuilder object + * @throws NullPointerException If {@code snapshot} is {@code null}. + */ + public ShareClientBuilder snapshot(String snapshot) { + this.snapshot = Objects.requireNonNull(snapshot); + return this; + } + + /** + * Sets the HTTP client to use for sending and receiving requests to and from the service. + * + * @param httpClient The HTTP client to use for requests. + * @return The updated ShareClientBuilder object. + * @throws NullPointerException If {@code httpClient} is {@code null}. + */ + public ShareClientBuilder httpClient(HttpClient httpClient) { + this.httpClient = Objects.requireNonNull(httpClient); + return this; + } + + /** + * Adds a policy to the set of existing policies that are executed after the {@link RetryPolicy}. + * + * @param pipelinePolicy The retry policy for service requests. + * @return The updated ShareClientBuilder object. + * @throws NullPointerException If {@code pipelinePolicy} is {@code null}. + */ + public ShareClientBuilder addPolicy(HttpPipelinePolicy pipelinePolicy) { + Objects.requireNonNull(pipelinePolicy); + this.policies.add(pipelinePolicy); + return this; + } + + /** + * Sets the logging level for HTTP requests and responses. + * + * @param logLevel The amount of logging output when sending and receiving HTTP requests/responses. + * @return The updated ShareClientBuilder object. + */ + public ShareClientBuilder httpLogDetailLevel(HttpLogDetailLevel logLevel) { + this.logLevel = logLevel; + return this; + } + + /** + * Sets the HTTP pipeline to use for the service client. + * + *

                  If {@code pipeline} is set, all other settings are ignored, aside from {@link ShareClientBuilder#endpoint(String) endpoint}, + * {@link ShareClientBuilder#shareName(String) shareName}, and {@link ShareClientBuilder#snapshot(String) snaphotShot} + * when building clients.

                  + * + * @param pipeline The HTTP pipeline to use for sending service requests and receiving responses. + * @return The updated ShareClientBuilder object. + * @throws NullPointerException If {@code pipeline} is {@code null}. + */ + public ShareClientBuilder pipeline(HttpPipeline pipeline) { + this.pipeline = Objects.requireNonNull(pipeline); + return this; + } - // connectionString, shareName, FileClientOptions, withSnapshot, SharedKeyCredential, + /** + * Sets the configuration store that is used during construction of the service client. + * + * The default configuration store is a clone of the {@link ConfigurationManager#getConfiguration() global + * configuration store}, use {@link Configuration#NONE} to bypass using configuration settings during construction. + * + * @param configuration The configuration store used to + * @return The updated ShareClientBuilder object. + * @throws NullPointerException If {@code configuration} is {@code null}. + */ + public ShareClientBuilder configuration(Configuration configuration) { + this.configuration = Objects.requireNonNull(configuration); + return this; + } } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/implementation/AzureFileStorageBuilder.java b/storage/client/file/src/main/java/com/azure/storage/file/implementation/AzureFileStorageBuilder.java index 6dced97e93c08..53903ed5f4c8d 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/implementation/AzureFileStorageBuilder.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/implementation/AzureFileStorageBuilder.java @@ -72,9 +72,9 @@ public AzureFileStorageImpl build() { this.pipeline = RestProxy.createDefaultPipeline(); } AzureFileStorageImpl client = new AzureFileStorageImpl(pipeline); - if (this.version != null) { - client.version(this.version); - } + + client.version(this.version); + if (this.url != null) { client.url(this.url); } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/implementation/DirectorysImpl.java b/storage/client/file/src/main/java/com/azure/storage/file/implementation/DirectorysImpl.java index 4e3f44c781ef9..b2afc6d343585 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/implementation/DirectorysImpl.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/implementation/DirectorysImpl.java @@ -11,6 +11,7 @@ import com.azure.core.annotations.Host; import com.azure.core.annotations.HostParam; import com.azure.core.annotations.PUT; +import com.azure.core.annotations.PathParam; import com.azure.core.annotations.QueryParam; import com.azure.core.annotations.Service; import com.azure.core.annotations.UnexpectedResponseExceptionType; @@ -23,9 +24,9 @@ import com.azure.storage.file.models.DirectorysListFilesAndDirectoriesSegmentResponse; import com.azure.storage.file.models.DirectorysListHandlesResponse; import com.azure.storage.file.models.DirectorysSetMetadataResponse; +import com.azure.storage.file.models.FilesDeleteResponse; import com.azure.storage.file.models.StorageErrorException; import reactor.core.publisher.Mono; - import java.util.Map; /** @@ -49,7 +50,7 @@ public final class DirectorysImpl { * @param client the instance of the service client containing this operation class. */ public DirectorysImpl(AzureFileStorageImpl client) { - this.service = RestProxy.create(DirectorysService.class, client); + this.service = RestProxy.create(DirectorysService.class, client.httpPipeline()); this.client = client; } @@ -58,164 +59,235 @@ public DirectorysImpl(AzureFileStorageImpl client) { * proxy service to perform REST calls. */ @Host("{url}") - @Service("Storage Files Directory") + @Service("Storage File Directories") private interface DirectorysService { @PUT("{shareName}/{directory}") @ExpectedResponses({201}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono create(@HostParam("url") String url, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-meta-") Map metadata, @HeaderParam("x-ms-version") String version, @QueryParam("restype") String restype, Context context); + Mono create(@HostParam("url") String url, @PathParam("shareName") String shareName, @PathParam("directory") String directoryName, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-meta-") Map metadata, @HeaderParam("x-ms-version") String version, @QueryParam("restype") String restype, Context context); @GET("{shareName}/{directory}") @ExpectedResponses({200}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono getProperties(@HostParam("url") String url, @QueryParam("sharesnapshot") String sharesnapshot, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @QueryParam("restype") String restype, Context context); + Mono getProperties(@HostParam("url") String url, @PathParam("shareName") String shareName, @PathParam("directory") String directoryName, @QueryParam("sharesnapshot") String sharesnapshot, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @QueryParam("restype") String restype, Context context); @DELETE("{shareName}/{directory}") @ExpectedResponses({202}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono delete(@HostParam("url") String url, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @QueryParam("restype") String restype, Context context); + Mono delete(@HostParam("url") String url, @PathParam("shareName") String shareName, @PathParam("directory") String directoryName, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @QueryParam("restype") String restype, Context context); + + @DELETE("{shareName}/{directory}/{fileName}") + @ExpectedResponses({202}) + @UnexpectedResponseExceptionType(StorageErrorException.class) + Mono delete(@HostParam("url") String url, @PathParam("shareName") String shareName, @PathParam("directory") String directoryName, @PathParam("fileName") String fileName, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @QueryParam("restype") String restype, Context context); @PUT("{shareName}/{directory}") @ExpectedResponses({200}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono setMetadata(@HostParam("url") String url, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-meta-") Map metadata, @HeaderParam("x-ms-version") String version, @QueryParam("restype") String restype, @QueryParam("comp") String comp, Context context); + Mono setMetadata(@HostParam("url") String url, @PathParam("shareName") String shareName, @PathParam("directory") String directoryName, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-meta-") Map metadata, @HeaderParam("x-ms-version") String version, @QueryParam("restype") String restype, @QueryParam("comp") String comp, Context context); @GET("{shareName}/{directory}") @ExpectedResponses({200}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono listFilesAndDirectoriesSegment(@HostParam("url") String url, @QueryParam("prefix") String prefix, @QueryParam("sharesnapshot") String sharesnapshot, @QueryParam("marker") String marker, @QueryParam("maxresults") Integer maxresults, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @QueryParam("restype") String restype, @QueryParam("comp") String comp, Context context); + Mono listFilesAndDirectoriesSegment(@HostParam("url") String url, @PathParam("shareName") String shareName, @PathParam("directory") String directoryName, @QueryParam("prefix") String prefix, @QueryParam("sharesnapshot") String sharesnapshot, @QueryParam("marker") String marker, @QueryParam("maxresults") Integer maxresults, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @QueryParam("restype") String restype, @QueryParam("comp") String comp, Context context); @GET("{shareName}/{directory}") @ExpectedResponses({200}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono listHandles(@HostParam("url") String url, @QueryParam("marker") String marker, @QueryParam("maxresults") Integer maxresults, @QueryParam("timeout") Integer timeout, @QueryParam("sharesnapshot") String sharesnapshot, @HeaderParam("x-ms-recursive") Boolean recursive, @HeaderParam("x-ms-version") String version, @QueryParam("comp") String comp, Context context); + Mono listHandles(@HostParam("url") String url, @PathParam("shareName") String shareName, @PathParam("directory") String directoryName, @QueryParam("marker") String marker, @QueryParam("maxresults") Integer maxresults, @QueryParam("timeout") Integer timeout, @QueryParam("sharesnapshot") String sharesnapshot, @HeaderParam("x-ms-recursive") Boolean recursive, @HeaderParam("x-ms-version") String version, @QueryParam("comp") String comp, Context context); @PUT("{shareName}/{directory}") @ExpectedResponses({200}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono forceCloseHandles(@HostParam("url") String url, @QueryParam("timeout") Integer timeout, @QueryParam("marker") String marker, @QueryParam("sharesnapshot") String sharesnapshot, @HeaderParam("x-ms-handle-id") String handleId, @HeaderParam("x-ms-recursive") Boolean recursive, @HeaderParam("x-ms-version") String version, @QueryParam("comp") String comp, Context context); + Mono forceCloseHandles(@HostParam("url") String url, @PathParam("shareName") String shareName, @PathParam("directory") String directoryName, @QueryParam("timeout") Integer timeout, @QueryParam("marker") String marker, @QueryParam("sharesnapshot") String sharesnapshot, @HeaderParam("x-ms-handle-id") String handleId, @HeaderParam("x-ms-recursive") Boolean recursive, @HeaderParam("x-ms-version") String version, @QueryParam("comp") String comp, Context context); } /** * Creates a new directory under the specified share or parent directory. * + * @param shareName Name of the share. + * @param directoryName Path to the directory. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono createWithRestResponseAsync(Context context) { + public Mono createWithRestResponseAsync(String shareName, String directoryName, Context context) { final Integer timeout = null; final Map metadata = null; final String restype = "directory"; - return service.create(this.client.url(), timeout, metadata, this.client.version(), restype, context); + return service.create(this.client.url(), shareName, directoryName, timeout, metadata, this.client.version(), restype, context); } /** * Creates a new directory under the specified share or parent directory. * + * @param shareName Name of the share. + * @param directoryName Path to the directory. * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. * @param metadata A name-value pair to associate with a file storage object. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono createWithRestResponseAsync(Integer timeout, Map metadata, Context context) { + public Mono createWithRestResponseAsync(String shareName, String directoryName, Integer timeout, Map metadata, Context context) { final String restype = "directory"; - return service.create(this.client.url(), timeout, metadata, this.client.version(), restype, context); + return service.create(this.client.url(), shareName, directoryName, timeout, metadata, this.client.version(), restype, context); + } + + /** + * Creates a new directory under the specified share or parent directory. + * + * @param shareName Name of the share. + * @param directoryName Path to the directory. + * @param metadata A name-value pair to associate with a file storage object. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @return a Mono which performs the network request upon subscription. + */ + public Mono createSubDirectoryWithRestResponseAsync(String shareName, String directoryName, Map metadata, Context context) { + final Integer timeout = null; + final String restype = "directory"; + return service.create(this.client.url(), shareName, directoryName, timeout, metadata, this.client.version(), restype, context); } /** * Returns all system properties for the specified directory, and can also be used to check the existence of a directory. The data returned does not include the files in the directory or any subdirectories. * + * @param shareName Name of the share. + * @param directoryName Path to the directory. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono getPropertiesWithRestResponseAsync(Context context) { + public Mono getPropertiesWithRestResponseAsync(String shareName, String directoryName, Context context) { final String sharesnapshot = null; final Integer timeout = null; final String restype = "directory"; - return service.getProperties(this.client.url(), sharesnapshot, timeout, this.client.version(), restype, context); + return service.getProperties(this.client.url(), shareName, directoryName, sharesnapshot, timeout, this.client.version(), restype, context); } /** * Returns all system properties for the specified directory, and can also be used to check the existence of a directory. The data returned does not include the files in the directory or any subdirectories. * + * @param shareName Name of the share. + * @param directoryName Path to the directory. * @param sharesnapshot The snapshot parameter is an opaque DateTime value that, when present, specifies the share snapshot to query. * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono getPropertiesWithRestResponseAsync(String sharesnapshot, Integer timeout, Context context) { + public Mono getPropertiesWithRestResponseAsync(String shareName, String directoryName, String sharesnapshot, Integer timeout, Context context) { final String restype = "directory"; - return service.getProperties(this.client.url(), sharesnapshot, timeout, this.client.version(), restype, context); + return service.getProperties(this.client.url(), shareName, directoryName, sharesnapshot, timeout, this.client.version(), restype, context); } /** * Removes the specified empty directory. Note that the directory must be empty before it can be deleted. * + * @param shareName Name of the share. + * @param directoryName Path to the directory. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono deleteWithRestResponseAsync(Context context) { + public Mono deleteWithRestResponseAsync(String shareName, String directoryName, Context context) { final Integer timeout = null; final String restype = "directory"; - return service.delete(this.client.url(), timeout, this.client.version(), restype, context); + return service.delete(this.client.url(), shareName, directoryName, timeout, this.client.version(), restype, context); + } + + /** + * Removes the specified empty directory. Note that the directory must be empty before it can be deleted. + * + * @param shareName Name of the share. + * @param directoryName Path to the directory. + * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @return a Mono which performs the network request upon subscription. + */ + public Mono deleteWithRestResponseAsync(String shareName, String directoryName, Integer timeout, Context context) { + final String restype = "directory"; + return service.delete(this.client.url(), shareName, directoryName, timeout, this.client.version(), restype, context); } + /** * Removes the specified empty directory. Note that the directory must be empty before it can be deleted. * + * @param shareName Name of the share. + * @param directoryName Path to the directory. + * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @return a Mono which performs the network request upon subscription. + */ + public Mono deleteSubWithRestResponseAsync(String shareName, String directoryName, Integer timeout, Context context) { + final String restype = "directory"; + return service.delete(this.client.url(), shareName, directoryName, timeout, this.client.version(), restype, context); + } + + /** + * Removes the specified file. + * + * @param shareName Name of the share. + * @param directoryName Path to the directory. + * @param fileName Name of the file. * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono deleteWithRestResponseAsync(Integer timeout, Context context) { + public Mono deleteFileWithRestResponseAsync(String shareName, String directoryName, String fileName, Integer timeout, Context context) { final String restype = "directory"; - return service.delete(this.client.url(), timeout, this.client.version(), restype, context); + return service.delete(this.client.url(), shareName, directoryName, fileName, timeout, this.client.version(), restype, context); } /** * Updates user defined metadata for the specified directory. * + * @param shareName Name of the share. + * @param directoryName Path to the directory. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono setMetadataWithRestResponseAsync(Context context) { + public Mono setMetadataWithRestResponseAsync(String shareName, String directoryName, Context context) { final Integer timeout = null; final Map metadata = null; final String restype = "directory"; final String comp = "metadata"; - return service.setMetadata(this.client.url(), timeout, metadata, this.client.version(), restype, comp, context); + return service.setMetadata(this.client.url(), shareName, directoryName, timeout, metadata, this.client.version(), restype, comp, context); } /** * Updates user defined metadata for the specified directory. * + * @param shareName Name of the share. + * @param directoryName Path to the directory. * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. * @param metadata A name-value pair to associate with a file storage object. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono setMetadataWithRestResponseAsync(Integer timeout, Map metadata, Context context) { + public Mono setMetadataWithRestResponseAsync(String shareName, String directoryName, Integer timeout, Map metadata, Context context) { final String restype = "directory"; final String comp = "metadata"; - return service.setMetadata(this.client.url(), timeout, metadata, this.client.version(), restype, comp, context); + return service.setMetadata(this.client.url(), shareName, directoryName, timeout, metadata, this.client.version(), restype, comp, context); } /** * Returns a list of files or directories under the specified share or directory. It lists the contents only for a single level of the directory hierarchy. * + * @param shareName Name of the share. + * @param directoryName Path to the directory. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono listFilesAndDirectoriesSegmentWithRestResponseAsync(Context context) { + public Mono listFilesAndDirectoriesSegmentWithRestResponseAsync(String shareName, String directoryName, Context context) { final String prefix = null; final String sharesnapshot = null; final String marker = null; @@ -223,12 +295,14 @@ public Mono listFilesAndDirect final Integer timeout = null; final String restype = "directory"; final String comp = "list"; - return service.listFilesAndDirectoriesSegment(this.client.url(), prefix, sharesnapshot, marker, maxresults, timeout, this.client.version(), restype, comp, context); + return service.listFilesAndDirectoriesSegment(this.client.url(), shareName, directoryName, prefix, sharesnapshot, marker, maxresults, timeout, this.client.version(), restype, comp, context); } /** * Returns a list of files or directories under the specified share or directory. It lists the contents only for a single level of the directory hierarchy. * + * @param shareName Name of the share. + * @param directoryName Path to the directory. * @param prefix Filters the results to return only entries whose name begins with the specified prefix. * @param sharesnapshot The snapshot parameter is an opaque DateTime value that, when present, specifies the share snapshot to query. * @param marker A string value that identifies the portion of the list to be returned with the next list operation. The operation returns a marker value within the response body if the list returned was not complete. The marker value may then be used in a subsequent call to request the next set of list items. The marker value is opaque to the client. @@ -238,32 +312,36 @@ public Mono listFilesAndDirect * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono listFilesAndDirectoriesSegmentWithRestResponseAsync(String prefix, String sharesnapshot, String marker, Integer maxresults, Integer timeout, Context context) { + public Mono listFilesAndDirectoriesSegmentWithRestResponseAsync(String shareName, String directoryName, String prefix, String sharesnapshot, String marker, Integer maxresults, Integer timeout, Context context) { final String restype = "directory"; final String comp = "list"; - return service.listFilesAndDirectoriesSegment(this.client.url(), prefix, sharesnapshot, marker, maxresults, timeout, this.client.version(), restype, comp, context); + return service.listFilesAndDirectoriesSegment(this.client.url(), shareName, directoryName, prefix, sharesnapshot, marker, maxresults, timeout, this.client.version(), restype, comp, context); } /** * Lists handles for directory. * + * @param shareName Name of the share. + * @param directoryName Path to the directory. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono listHandlesWithRestResponseAsync(Context context) { + public Mono listHandlesWithRestResponseAsync(String shareName, String directoryName, Context context) { final String marker = null; final Integer maxresults = null; final Integer timeout = null; final String sharesnapshot = null; final Boolean recursive = null; final String comp = "listhandles"; - return service.listHandles(this.client.url(), marker, maxresults, timeout, sharesnapshot, recursive, this.client.version(), comp, context); + return service.listHandles(this.client.url(), shareName, directoryName, marker, maxresults, timeout, sharesnapshot, recursive, this.client.version(), comp, context); } /** * Lists handles for directory. * + * @param shareName Name of the share. + * @param directoryName Path to the directory. * @param marker A string value that identifies the portion of the list to be returned with the next list operation. The operation returns a marker value within the response body if the list returned was not complete. The marker value may then be used in a subsequent call to request the next set of list items. The marker value is opaque to the client. * @param maxresults Specifies the maximum number of entries to return. If the request does not specify maxresults, or specifies a value greater than 5,000, the server will return up to 5,000 items. * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. @@ -273,31 +351,35 @@ public Mono listHandlesWithRestResponseAsync(Cont * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono listHandlesWithRestResponseAsync(String marker, Integer maxresults, Integer timeout, String sharesnapshot, Boolean recursive, Context context) { + public Mono listHandlesWithRestResponseAsync(String shareName, String directoryName, String marker, Integer maxresults, Integer timeout, String sharesnapshot, Boolean recursive, Context context) { final String comp = "listhandles"; - return service.listHandles(this.client.url(), marker, maxresults, timeout, sharesnapshot, recursive, this.client.version(), comp, context); + return service.listHandles(this.client.url(), shareName, directoryName, marker, maxresults, timeout, sharesnapshot, recursive, this.client.version(), comp, context); } /** * Closes all handles open for given directory. * + * @param shareName Name of the share. + * @param directoryName Path to the directory. * @param handleId Specifies handle ID opened on the file or directory to be closed. Asterix (‘*’) is a wildcard that specifies all handles. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono forceCloseHandlesWithRestResponseAsync(String handleId, Context context) { + public Mono forceCloseHandlesWithRestResponseAsync(String shareName, String directoryName, String handleId, Context context) { final Integer timeout = null; final String marker = null; final String sharesnapshot = null; final Boolean recursive = null; final String comp = "forceclosehandles"; - return service.forceCloseHandles(this.client.url(), timeout, marker, sharesnapshot, handleId, recursive, this.client.version(), comp, context); + return service.forceCloseHandles(this.client.url(), shareName, directoryName, timeout, marker, sharesnapshot, handleId, recursive, this.client.version(), comp, context); } /** * Closes all handles open for given directory. * + * @param shareName Name of the share. + * @param directoryName Path to the directory. * @param handleId Specifies handle ID opened on the file or directory to be closed. Asterix (‘*’) is a wildcard that specifies all handles. * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. * @param marker A string value that identifies the portion of the list to be returned with the next list operation. The operation returns a marker value within the response body if the list returned was not complete. The marker value may then be used in a subsequent call to request the next set of list items. The marker value is opaque to the client. @@ -307,8 +389,8 @@ public Mono forceCloseHandlesWithRestRespon * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono forceCloseHandlesWithRestResponseAsync(String handleId, Integer timeout, String marker, String sharesnapshot, Boolean recursive, Context context) { + public Mono forceCloseHandlesWithRestResponseAsync(String shareName, String directoryName, String handleId, Integer timeout, String marker, String sharesnapshot, Boolean recursive, Context context) { final String comp = "forceclosehandles"; - return service.forceCloseHandles(this.client.url(), timeout, marker, sharesnapshot, handleId, recursive, this.client.version(), comp, context); + return service.forceCloseHandles(this.client.url(), shareName, directoryName, timeout, marker, sharesnapshot, handleId, recursive, this.client.version(), comp, context); } } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/implementation/FilesImpl.java b/storage/client/file/src/main/java/com/azure/storage/file/implementation/FilesImpl.java index ee44ffe4f9830..ad5783b2d1d68 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/implementation/FilesImpl.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/implementation/FilesImpl.java @@ -13,6 +13,7 @@ import com.azure.core.annotations.Host; import com.azure.core.annotations.HostParam; import com.azure.core.annotations.PUT; +import com.azure.core.annotations.PathParam; import com.azure.core.annotations.QueryParam; import com.azure.core.annotations.Service; import com.azure.core.annotations.UnexpectedResponseExceptionType; @@ -61,7 +62,7 @@ public final class FilesImpl { * @param client the instance of the service client containing this operation class. */ public FilesImpl(AzureFileStorageImpl client) { - this.service = RestProxy.create(FilesService.class, client); + this.service = RestProxy.create(FilesService.class, client.httpPipeline()); this.client = client; } @@ -70,77 +71,80 @@ public FilesImpl(AzureFileStorageImpl client) { * proxy service to perform REST calls. */ @Host("{url}") - @Service("Storage Files File") + @Service("Storage File Files") private interface FilesService { - @PUT("{shareName}/{directory}/{fileName}") + @PUT("{shareName}/{filePath}") @ExpectedResponses({201}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono create(@HostParam("url") String url, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-content-length") long fileContentLength, @HeaderParam("x-ms-type") String fileTypeConstant, @HeaderParam("x-ms-meta-") Map metadata, @HeaderParam("x-ms-content-type") String fileContentType, @HeaderParam("x-ms-content-encoding") String fileContentEncoding, @HeaderParam("x-ms-content-language") String fileContentLanguage, @HeaderParam("x-ms-cache-control") String fileCacheControl, @HeaderParam("x-ms-content-md5") String fileContentMD5, @HeaderParam("x-ms-content-disposition") String fileContentDisposition, Context context); + Mono create(@HostParam("url") String url, @PathParam("shareName") String shareName, @PathParam("filePath") String filePath, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-content-length") long fileContentLength, @HeaderParam("x-ms-type") String fileTypeConstant, @HeaderParam("x-ms-meta-") Map metadata, @HeaderParam("x-ms-content-type") String fileContentType, @HeaderParam("x-ms-content-encoding") String fileContentEncoding, @HeaderParam("x-ms-content-language") String fileContentLanguage, @HeaderParam("x-ms-cache-control") String fileCacheControl, @HeaderParam("x-ms-content-md5") String fileContentMD5, @HeaderParam("x-ms-content-disposition") String fileContentDisposition, Context context); - @GET("{shareName}/{directory}/{fileName}") + @GET("{shareName}/{filePath}") @ExpectedResponses({200, 206}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono download(@HostParam("url") String url, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-range") String range, @HeaderParam("x-ms-range-get-content-md5") Boolean rangeGetContentMD5, Context context); + Mono download(@HostParam("url") String url, @PathParam("shareName") String shareName, @PathParam("filePath") String filePath, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-range") String range, @HeaderParam("x-ms-range-get-content-md5") Boolean rangeGetContentMD5, Context context); - @HEAD("{shareName}/{directory}/{fileName}") + @HEAD("{shareName}/{filePath}") @ExpectedResponses({200}) - Mono getProperties(@HostParam("url") String url, @QueryParam("sharesnapshot") String sharesnapshot, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, Context context); + @UnexpectedResponseExceptionType(StorageErrorException.class) + Mono getProperties(@HostParam("url") String url, @PathParam("shareName") String shareName, @PathParam("filePath") String filePath, @QueryParam("sharesnapshot") String sharesnapshot, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, Context context); - @DELETE("{shareName}/{directory}/{fileName}") + @DELETE("{shareName}/{filePath}") @ExpectedResponses({202}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono delete(@HostParam("url") String url, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, Context context); + Mono delete(@HostParam("url") String url, @PathParam("shareName") String shareName, @PathParam("filePath") String filePath, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, Context context); - @PUT("{shareName}/{directory}/{fileName}") + @PUT("{shareName}/{filePath}") @ExpectedResponses({200}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono setHTTPHeaders(@HostParam("url") String url, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-content-length") Long fileContentLength, @QueryParam("comp") String comp, @HeaderParam("x-ms-content-type") String fileContentType, @HeaderParam("x-ms-content-encoding") String fileContentEncoding, @HeaderParam("x-ms-content-language") String fileContentLanguage, @HeaderParam("x-ms-cache-control") String fileCacheControl, @HeaderParam("x-ms-content-md5") String fileContentMD5, @HeaderParam("x-ms-content-disposition") String fileContentDisposition, Context context); + Mono setHTTPHeaders(@HostParam("url") String url, @PathParam("shareName") String shareName, @PathParam("filePath") String filePath, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-content-length") Long fileContentLength, @QueryParam("comp") String comp, @HeaderParam("x-ms-content-type") String fileContentType, @HeaderParam("x-ms-content-encoding") String fileContentEncoding, @HeaderParam("x-ms-content-language") String fileContentLanguage, @HeaderParam("x-ms-cache-control") String fileCacheControl, @HeaderParam("x-ms-content-md5") String fileContentMD5, @HeaderParam("x-ms-content-disposition") String fileContentDisposition, Context context); - @PUT("{shareName}/{directory}/{fileName}") + @PUT("{shareName}/{filePath}") @ExpectedResponses({200}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono setMetadata(@HostParam("url") String url, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-meta-") Map metadata, @HeaderParam("x-ms-version") String version, @QueryParam("comp") String comp, Context context); + Mono setMetadata(@HostParam("url") String url, @PathParam("shareName") String shareName, @PathParam("filePath") String filePath, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-meta-") Map metadata, @HeaderParam("x-ms-version") String version, @QueryParam("comp") String comp, Context context); - @PUT("{shareName}/{directory}/{fileName}") + @PUT("{shareName}/{filePath}") @ExpectedResponses({201}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono uploadRange(@HostParam("url") String url, @BodyParam("application/octet-stream") Flux optionalbody, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-range") String range, @HeaderParam("x-ms-write") FileRangeWriteType fileRangeWrite, @HeaderParam("Content-Length") long contentLength, @HeaderParam("Content-MD5") String contentMD5, @HeaderParam("x-ms-version") String version, @QueryParam("comp") String comp, Context context); + Mono uploadRange(@HostParam("url") String url, @PathParam("shareName") String shareName, @PathParam("filePath") String filePath, @BodyParam("application/octet-stream") Flux optionalbody, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-range") String range, @HeaderParam("x-ms-write") FileRangeWriteType fileRangeWrite, @HeaderParam("Content-Length") long contentLength, @HeaderParam("Content-MD5") String contentMD5, @HeaderParam("x-ms-version") String version, @QueryParam("comp") String comp, Context context); - @GET("{shareName}/{directory}/{fileName}") + @GET("{shareName}/{filePath}") @ExpectedResponses({200}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono getRangeList(@HostParam("url") String url, @QueryParam("sharesnapshot") String sharesnapshot, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-range") String range, @QueryParam("comp") String comp, Context context); + Mono getRangeList(@HostParam("url") String url, @PathParam("shareName") String shareName, @PathParam("filePath") String filePath, @QueryParam("sharesnapshot") String sharesnapshot, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-range") String range, @QueryParam("comp") String comp, Context context); - @PUT("{shareName}/{directory}/{fileName}") + @PUT("{shareName}/{filePath}") @ExpectedResponses({202}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono startCopy(@HostParam("url") String url, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-meta-") Map metadata, @HeaderParam("x-ms-copy-source") String copySource, Context context); + Mono startCopy(@HostParam("url") String url, @PathParam("shareName") String shareName, @PathParam("filePath") String filePath, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-meta-") Map metadata, @HeaderParam("x-ms-copy-source") String copySource, Context context); - @PUT("{shareName}/{directory}/{fileName}") + @PUT("{shareName}/{filePath}") @ExpectedResponses({204}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono abortCopy(@HostParam("url") String url, @QueryParam("copyid") String copyId, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-copy-action") String copyActionAbortConstant, @HeaderParam("x-ms-version") String version, @QueryParam("comp") String comp, Context context); + Mono abortCopy(@HostParam("url") String url, @PathParam("shareName") String shareName, @PathParam("filePath") String filePath, @QueryParam("copyid") String copyId, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-copy-action") String copyActionAbortConstant, @HeaderParam("x-ms-version") String version, @QueryParam("comp") String comp, Context context); - @GET("{shareName}/{directory}/{fileName}") + @GET("{shareName}/{filePath}") @ExpectedResponses({200}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono listHandles(@HostParam("url") String url, @QueryParam("marker") String marker, @QueryParam("maxresults") Integer maxresults, @QueryParam("timeout") Integer timeout, @QueryParam("sharesnapshot") String sharesnapshot, @HeaderParam("x-ms-version") String version, @QueryParam("comp") String comp, Context context); + Mono listHandles(@HostParam("url") String url, @PathParam("shareName") String shareName, @PathParam("filePath") String filePath, @QueryParam("marker") String marker, @QueryParam("maxresults") Integer maxresults, @QueryParam("timeout") Integer timeout, @QueryParam("sharesnapshot") String sharesnapshot, @HeaderParam("x-ms-version") String version, @QueryParam("comp") String comp, Context context); - @PUT("{shareName}/{directory}/{fileName}") + @PUT("{shareName}/{filePath}") @ExpectedResponses({200}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono forceCloseHandles(@HostParam("url") String url, @QueryParam("timeout") Integer timeout, @QueryParam("marker") String marker, @QueryParam("sharesnapshot") String sharesnapshot, @HeaderParam("x-ms-handle-id") String handleId, @HeaderParam("x-ms-version") String version, @QueryParam("comp") String comp, Context context); + Mono forceCloseHandles(@HostParam("url") String url, @PathParam("shareName") String shareName, @PathParam("filePath") String filePath, @QueryParam("timeout") Integer timeout, @QueryParam("marker") String marker, @QueryParam("sharesnapshot") String sharesnapshot, @HeaderParam("x-ms-handle-id") String handleId, @HeaderParam("x-ms-version") String version, @QueryParam("comp") String comp, Context context); } /** * Creates a new file or replaces a file. Note it only initializes the file with no content. * + * @param shareName Name of the share. + * @param filePath Path to the file. * @param fileContentLength Specifies the maximum size for the file, up to 1 TB. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono createWithRestResponseAsync(long fileContentLength, Context context) { + public Mono createWithRestResponseAsync(String shareName, String filePath, long fileContentLength, Context context) { final Integer timeout = null; final String fileTypeConstant = "file"; final Map metadata = null; @@ -150,12 +154,14 @@ public Mono createWithRestResponseAsync(long fileContentLen final String fileCacheControl = null; final String fileContentDisposition = null; String fileContentMD5Converted = null; - return service.create(this.client.url(), timeout, this.client.version(), fileContentLength, fileTypeConstant, metadata, fileContentType, fileContentEncoding, fileContentLanguage, fileCacheControl, fileContentMD5Converted, fileContentDisposition, context); + return service.create(this.client.url(), shareName, filePath, timeout, this.client.version(), fileContentLength, fileTypeConstant, metadata, fileContentType, fileContentEncoding, fileContentLanguage, fileCacheControl, fileContentMD5Converted, fileContentDisposition, context); } /** * Creates a new file or replaces a file. Note it only initializes the file with no content. * + * @param shareName Name of the share. + * @param filePath Path to the file. * @param fileContentLength Specifies the maximum size for the file, up to 1 TB. * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. * @param metadata A name-value pair to associate with a file storage object. @@ -164,7 +170,7 @@ public Mono createWithRestResponseAsync(long fileContentLen * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono createWithRestResponseAsync(long fileContentLength, Integer timeout, Map metadata, FileHTTPHeaders fileHTTPHeaders, Context context) { + public Mono createWithRestResponseAsync(String shareName, String filePath, long fileContentLength, Integer timeout, Map metadata, FileHTTPHeaders fileHTTPHeaders, Context context) { final String fileTypeConstant = "file"; String fileContentType = null; if (fileHTTPHeaders != null) { @@ -191,26 +197,30 @@ public Mono createWithRestResponseAsync(long fileContentLen fileContentDisposition = fileHTTPHeaders.fileContentDisposition(); } String fileContentMD5Converted = Base64Util.encodeToString(fileContentMD5); - return service.create(this.client.url(), timeout, this.client.version(), fileContentLength, fileTypeConstant, metadata, fileContentType, fileContentEncoding, fileContentLanguage, fileCacheControl, fileContentMD5Converted, fileContentDisposition, context); + return service.create(this.client.url(), shareName, filePath, timeout, this.client.version(), fileContentLength, fileTypeConstant, metadata, fileContentType, fileContentEncoding, fileContentLanguage, fileCacheControl, fileContentMD5Converted, fileContentDisposition, context); } /** * Reads or downloads a file from the system, including its metadata and properties. * + * @param shareName Name of the share. + * @param filePath Path to the file. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono downloadWithRestResponseAsync(Context context) { + public Mono downloadWithRestResponseAsync(String shareName, String filePath, Context context) { final Integer timeout = null; final String range = null; final Boolean rangeGetContentMD5 = null; - return service.download(this.client.url(), timeout, this.client.version(), range, rangeGetContentMD5, context); + return service.download(this.client.url(), shareName, filePath, timeout, this.client.version(), range, rangeGetContentMD5, context); } /** * Reads or downloads a file from the system, including its metadata and properties. * + * @param shareName Name of the share. + * @param filePath Path to the file. * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. * @param range Return file data only from the specified byte range. * @param rangeGetContentMD5 When this header is set to true and specified together with the Range header, the service returns the MD5 hash for the range, as long as the range is less than or equal to 4 MB in size. @@ -218,68 +228,78 @@ public Mono downloadWithRestResponseAsync(Context context * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono downloadWithRestResponseAsync(Integer timeout, String range, Boolean rangeGetContentMD5, Context context) { - return service.download(this.client.url(), timeout, this.client.version(), range, rangeGetContentMD5, context); + public Mono downloadWithRestResponseAsync(String shareName, String filePath, Integer timeout, String range, Boolean rangeGetContentMD5, Context context) { + return service.download(this.client.url(), shareName, filePath, timeout, this.client.version(), range, rangeGetContentMD5, context); } /** * Returns all user-defined metadata, standard HTTP properties, and system properties for the file. It does not return the content of the file. * + * @param shareName Name of the share. + * @param filePath Path to the file. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono getPropertiesWithRestResponseAsync(Context context) { + public Mono getPropertiesWithRestResponseAsync(String shareName, String filePath, Context context) { final String sharesnapshot = null; final Integer timeout = null; - return service.getProperties(this.client.url(), sharesnapshot, timeout, this.client.version(), context); + return service.getProperties(this.client.url(), shareName, filePath, sharesnapshot, timeout, this.client.version(), context); } /** * Returns all user-defined metadata, standard HTTP properties, and system properties for the file. It does not return the content of the file. * + * @param shareName Name of the share. + * @param filePath Path to the file. * @param sharesnapshot The snapshot parameter is an opaque DateTime value that, when present, specifies the share snapshot to query. * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono getPropertiesWithRestResponseAsync(String sharesnapshot, Integer timeout, Context context) { - return service.getProperties(this.client.url(), sharesnapshot, timeout, this.client.version(), context); + public Mono getPropertiesWithRestResponseAsync(String shareName, String filePath, String sharesnapshot, Integer timeout, Context context) { + return service.getProperties(this.client.url(), shareName, filePath, sharesnapshot, timeout, this.client.version(), context); } /** * removes the file from the storage account. * + * @param shareName Name of the share. + * @param filePath Path to the file. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono deleteWithRestResponseAsync(Context context) { + public Mono deleteWithRestResponseAsync(String shareName, String filePath, Context context) { final Integer timeout = null; - return service.delete(this.client.url(), timeout, this.client.version(), context); + return service.delete(this.client.url(), shareName, filePath, timeout, this.client.version(), context); } /** * removes the file from the storage account. * + * @param shareName Name of the share. + * @param filePath Path to the file. * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono deleteWithRestResponseAsync(Integer timeout, Context context) { - return service.delete(this.client.url(), timeout, this.client.version(), context); + public Mono deleteWithRestResponseAsync(String shareName, String filePath, Integer timeout, Context context) { + return service.delete(this.client.url(), shareName, filePath, timeout, this.client.version(), context); } /** * Sets HTTP headers on the file. * + * @param shareName Name of the share. + * @param filePath Path to the file. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono setHTTPHeadersWithRestResponseAsync(Context context) { + public Mono setHTTPHeadersWithRestResponseAsync(String shareName, String filePath, Context context) { final Integer timeout = null; final Long fileContentLength = null; final String comp = "properties"; @@ -289,12 +309,14 @@ public Mono setHTTPHeadersWithRestResponseAsync(Con final String fileCacheControl = null; final String fileContentDisposition = null; String fileContentMD5Converted = null; - return service.setHTTPHeaders(this.client.url(), timeout, this.client.version(), fileContentLength, comp, fileContentType, fileContentEncoding, fileContentLanguage, fileCacheControl, fileContentMD5Converted, fileContentDisposition, context); + return service.setHTTPHeaders(this.client.url(), shareName, filePath, timeout, this.client.version(), fileContentLength, comp, fileContentType, fileContentEncoding, fileContentLanguage, fileCacheControl, fileContentMD5Converted, fileContentDisposition, context); } /** * Sets HTTP headers on the file. * + * @param shareName Name of the share. + * @param filePath Path to the file. * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. * @param fileContentLength Resizes a file to the specified size. If the specified byte value is less than the current size of the file, then all ranges above the specified byte value are cleared. * @param fileHTTPHeaders Additional parameters for the operation. @@ -302,7 +324,7 @@ public Mono setHTTPHeadersWithRestResponseAsync(Con * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono setHTTPHeadersWithRestResponseAsync(Integer timeout, Long fileContentLength, FileHTTPHeaders fileHTTPHeaders, Context context) { + public Mono setHTTPHeadersWithRestResponseAsync(String shareName, String filePath, Integer timeout, Long fileContentLength, FileHTTPHeaders fileHTTPHeaders, Context context) { final String comp = "properties"; String fileContentType = null; if (fileHTTPHeaders != null) { @@ -329,40 +351,46 @@ public Mono setHTTPHeadersWithRestResponseAsync(Int fileContentDisposition = fileHTTPHeaders.fileContentDisposition(); } String fileContentMD5Converted = Base64Util.encodeToString(fileContentMD5); - return service.setHTTPHeaders(this.client.url(), timeout, this.client.version(), fileContentLength, comp, fileContentType, fileContentEncoding, fileContentLanguage, fileCacheControl, fileContentMD5Converted, fileContentDisposition, context); + return service.setHTTPHeaders(this.client.url(), shareName, filePath, timeout, this.client.version(), fileContentLength, comp, fileContentType, fileContentEncoding, fileContentLanguage, fileCacheControl, fileContentMD5Converted, fileContentDisposition, context); } /** * Updates user-defined metadata for the specified file. * + * @param shareName Name of the share. + * @param filePath Path to the file. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono setMetadataWithRestResponseAsync(Context context) { + public Mono setMetadataWithRestResponseAsync(String shareName, String filePath, Context context) { final Integer timeout = null; final Map metadata = null; final String comp = "metadata"; - return service.setMetadata(this.client.url(), timeout, metadata, this.client.version(), comp, context); + return service.setMetadata(this.client.url(), shareName, filePath, timeout, metadata, this.client.version(), comp, context); } /** * Updates user-defined metadata for the specified file. * + * @param shareName Name of the share. + * @param filePath Path to the file. * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. * @param metadata A name-value pair to associate with a file storage object. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono setMetadataWithRestResponseAsync(Integer timeout, Map metadata, Context context) { + public Mono setMetadataWithRestResponseAsync(String shareName, String filePath, Integer timeout, Map metadata, Context context) { final String comp = "metadata"; - return service.setMetadata(this.client.url(), timeout, metadata, this.client.version(), comp, context); + return service.setMetadata(this.client.url(), shareName, filePath, timeout, metadata, this.client.version(), comp, context); } /** * Upload a range of bytes to a file. * + * @param shareName Name of the share. + * @param filePath Path to the file. * @param range Specifies the range of bytes to be written. Both the start and end of the range must be specified. For an update operation, the range can be up to 4 MB in size. For a clear operation, the range can be up to the value of the file's full size. The File service accepts only a single byte range for the Range and 'x-ms-range' headers, and the byte range must be specified in the following format: bytes=startByte-endByte. * @param fileRangeWrite Specify one of the following options: - Update: Writes the bytes specified by the request body into the specified range. The Range and Content-Length headers must match to perform the update. - Clear: Clears the specified range and releases the space used in storage for that range. To clear a range, set the Content-Length header to zero, and set the Range header to a value that indicates the range to clear, up to maximum file size. Possible values include: 'update', 'clear'. * @param contentLength Specifies the number of bytes being transmitted in the request body. When the x-ms-write header is set to clear, the value of this header must be set to zero. @@ -370,17 +398,19 @@ public Mono setMetadataWithRestResponseAsync(Integer t * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono uploadRangeWithRestResponseAsync(String range, FileRangeWriteType fileRangeWrite, long contentLength, Context context) { + public Mono uploadRangeWithRestResponseAsync(String shareName, String filePath, String range, FileRangeWriteType fileRangeWrite, long contentLength, Context context) { final Flux optionalbody = null; final Integer timeout = null; final String comp = "range"; String contentMD5Converted = null; - return service.uploadRange(this.client.url(), optionalbody, timeout, range, fileRangeWrite, contentLength, contentMD5Converted, this.client.version(), comp, context); + return service.uploadRange(this.client.url(), shareName, filePath, optionalbody, timeout, range, fileRangeWrite, contentLength, contentMD5Converted, this.client.version(), comp, context); } /** * Upload a range of bytes to a file. * + * @param shareName Name of the share. + * @param filePath Path to the file. * @param range Specifies the range of bytes to be written. Both the start and end of the range must be specified. For an update operation, the range can be up to 4 MB in size. For a clear operation, the range can be up to the value of the file's full size. The File service accepts only a single byte range for the Range and 'x-ms-range' headers, and the byte range must be specified in the following format: bytes=startByte-endByte. * @param fileRangeWrite Specify one of the following options: - Update: Writes the bytes specified by the request body into the specified range. The Range and Content-Length headers must match to perform the update. - Clear: Clears the specified range and releases the space used in storage for that range. To clear a range, set the Content-Length header to zero, and set the Range header to a value that indicates the range to clear, up to maximum file size. Possible values include: 'update', 'clear'. * @param contentLength Specifies the number of bytes being transmitted in the request body. When the x-ms-write header is set to clear, the value of this header must be set to zero. @@ -391,30 +421,34 @@ public Mono uploadRangeWithRestResponseAsync(String ra * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono uploadRangeWithRestResponseAsync(String range, FileRangeWriteType fileRangeWrite, long contentLength, Flux optionalbody, Integer timeout, byte[] contentMD5, Context context) { + public Mono uploadRangeWithRestResponseAsync(String shareName, String filePath, String range, FileRangeWriteType fileRangeWrite, long contentLength, Flux optionalbody, Integer timeout, byte[] contentMD5, Context context) { final String comp = "range"; String contentMD5Converted = Base64Util.encodeToString(contentMD5); - return service.uploadRange(this.client.url(), optionalbody, timeout, range, fileRangeWrite, contentLength, contentMD5Converted, this.client.version(), comp, context); + return service.uploadRange(this.client.url(), shareName, filePath, optionalbody, timeout, range, fileRangeWrite, contentLength, contentMD5Converted, this.client.version(), comp, context); } /** * Returns the list of valid ranges for a file. * + * @param shareName Name of the share. + * @param filePath Path to the file. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono getRangeListWithRestResponseAsync(Context context) { + public Mono getRangeListWithRestResponseAsync(String shareName, String filePath, Context context) { final String sharesnapshot = null; final Integer timeout = null; final String range = null; final String comp = "rangelist"; - return service.getRangeList(this.client.url(), sharesnapshot, timeout, this.client.version(), range, comp, context); + return service.getRangeList(this.client.url(), shareName, filePath, sharesnapshot, timeout, this.client.version(), range, comp, context); } /** * Returns the list of valid ranges for a file. * + * @param shareName Name of the share. + * @param filePath Path to the file. * @param sharesnapshot The snapshot parameter is an opaque DateTime value that, when present, specifies the share snapshot to query. * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. * @param range Specifies the range of bytes over which to list ranges, inclusively. @@ -422,28 +456,32 @@ public Mono getRangeListWithRestResponseAsync(Context * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono getRangeListWithRestResponseAsync(String sharesnapshot, Integer timeout, String range, Context context) { + public Mono getRangeListWithRestResponseAsync(String shareName, String filePath, String sharesnapshot, Integer timeout, String range, Context context) { final String comp = "rangelist"; - return service.getRangeList(this.client.url(), sharesnapshot, timeout, this.client.version(), range, comp, context); + return service.getRangeList(this.client.url(), shareName, filePath, sharesnapshot, timeout, this.client.version(), range, comp, context); } /** * Copies a blob or file to a destination file within the storage account. * + * @param shareName Name of the share. + * @param filePath Path to the file. * @param copySource Specifies the URL of the source file or blob, up to 2 KB in length. To copy a file to another file within the same storage account, you may use Shared Key to authenticate the source file. If you are copying a file from another storage account, or if you are copying a blob from the same storage account or another storage account, then you must authenticate the source file or blob using a shared access signature. If the source is a public blob, no authentication is required to perform the copy operation. A file in a share snapshot can also be specified as a copy source. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono startCopyWithRestResponseAsync(String copySource, Context context) { + public Mono startCopyWithRestResponseAsync(String shareName, String filePath, String copySource, Context context) { final Integer timeout = null; final Map metadata = null; - return service.startCopy(this.client.url(), timeout, this.client.version(), metadata, copySource, context); + return service.startCopy(this.client.url(), shareName, filePath, timeout, this.client.version(), metadata, copySource, context); } /** * Copies a blob or file to a destination file within the storage account. * + * @param shareName Name of the share. + * @param filePath Path to the file. * @param copySource Specifies the URL of the source file or blob, up to 2 KB in length. To copy a file to another file within the same storage account, you may use Shared Key to authenticate the source file. If you are copying a file from another storage account, or if you are copying a blob from the same storage account or another storage account, then you must authenticate the source file or blob using a shared access signature. If the source is a public blob, no authentication is required to perform the copy operation. A file in a share snapshot can also be specified as a copy source. * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. * @param metadata A name-value pair to associate with a file storage object. @@ -451,59 +489,67 @@ public Mono startCopyWithRestResponseAsync(String copySo * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono startCopyWithRestResponseAsync(String copySource, Integer timeout, Map metadata, Context context) { - return service.startCopy(this.client.url(), timeout, this.client.version(), metadata, copySource, context); + public Mono startCopyWithRestResponseAsync(String shareName, String filePath, String copySource, Integer timeout, Map metadata, Context context) { + return service.startCopy(this.client.url(), shareName, filePath, timeout, this.client.version(), metadata, copySource, context); } /** * Aborts a pending Copy File operation, and leaves a destination file with zero length and full metadata. * + * @param shareName Name of the share. + * @param filePath Path to the file. * @param copyId The copy identifier provided in the x-ms-copy-id header of the original Copy File operation. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono abortCopyWithRestResponseAsync(String copyId, Context context) { + public Mono abortCopyWithRestResponseAsync(String shareName, String filePath, String copyId, Context context) { final Integer timeout = null; final String copyActionAbortConstant = "abort"; final String comp = "copy"; - return service.abortCopy(this.client.url(), copyId, timeout, copyActionAbortConstant, this.client.version(), comp, context); + return service.abortCopy(this.client.url(), shareName, filePath, copyId, timeout, copyActionAbortConstant, this.client.version(), comp, context); } /** * Aborts a pending Copy File operation, and leaves a destination file with zero length and full metadata. * + * @param shareName Name of the share. + * @param filePath Path to the file. * @param copyId The copy identifier provided in the x-ms-copy-id header of the original Copy File operation. * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono abortCopyWithRestResponseAsync(String copyId, Integer timeout, Context context) { + public Mono abortCopyWithRestResponseAsync(String shareName, String filePath, String copyId, Integer timeout, Context context) { final String copyActionAbortConstant = "abort"; final String comp = "copy"; - return service.abortCopy(this.client.url(), copyId, timeout, copyActionAbortConstant, this.client.version(), comp, context); + return service.abortCopy(this.client.url(), shareName, filePath, copyId, timeout, copyActionAbortConstant, this.client.version(), comp, context); } /** * Lists handles for file. * + * @param shareName Name of the share. + * @param filePath Path to the file. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono listHandlesWithRestResponseAsync(Context context) { + public Mono listHandlesWithRestResponseAsync(String shareName, String filePath, Context context) { final String marker = null; final Integer maxresults = null; final Integer timeout = null; final String sharesnapshot = null; final String comp = "listhandles"; - return service.listHandles(this.client.url(), marker, maxresults, timeout, sharesnapshot, this.client.version(), comp, context); + return service.listHandles(this.client.url(), shareName, filePath, marker, maxresults, timeout, sharesnapshot, this.client.version(), comp, context); } /** * Lists handles for file. * + * @param shareName Name of the share. + * @param filePath Path to the file. * @param marker A string value that identifies the portion of the list to be returned with the next list operation. The operation returns a marker value within the response body if the list returned was not complete. The marker value may then be used in a subsequent call to request the next set of list items. The marker value is opaque to the client. * @param maxresults Specifies the maximum number of entries to return. If the request does not specify maxresults, or specifies a value greater than 5,000, the server will return up to 5,000 items. * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. @@ -512,30 +558,34 @@ public Mono listHandlesWithRestResponseAsync(Context c * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono listHandlesWithRestResponseAsync(String marker, Integer maxresults, Integer timeout, String sharesnapshot, Context context) { + public Mono listHandlesWithRestResponseAsync(String shareName, String filePath, String marker, Integer maxresults, Integer timeout, String sharesnapshot, Context context) { final String comp = "listhandles"; - return service.listHandles(this.client.url(), marker, maxresults, timeout, sharesnapshot, this.client.version(), comp, context); + return service.listHandles(this.client.url(), shareName, filePath, marker, maxresults, timeout, sharesnapshot, this.client.version(), comp, context); } /** * Closes all handles open for given file. * + * @param shareName Name of the share. + * @param filePath Path to the file. * @param handleId Specifies handle ID opened on the file or directory to be closed. Asterix (‘*’) is a wildcard that specifies all handles. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono forceCloseHandlesWithRestResponseAsync(String handleId, Context context) { + public Mono forceCloseHandlesWithRestResponseAsync(String shareName, String filePath, String handleId, Context context) { final Integer timeout = null; final String marker = null; final String sharesnapshot = null; final String comp = "forceclosehandles"; - return service.forceCloseHandles(this.client.url(), timeout, marker, sharesnapshot, handleId, this.client.version(), comp, context); + return service.forceCloseHandles(this.client.url(), shareName, filePath, timeout, marker, sharesnapshot, handleId, this.client.version(), comp, context); } /** * Closes all handles open for given file. * + * @param shareName Name of the share. + * @param filePath Path to the file. * @param handleId Specifies handle ID opened on the file or directory to be closed. Asterix (‘*’) is a wildcard that specifies all handles. * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. * @param marker A string value that identifies the portion of the list to be returned with the next list operation. The operation returns a marker value within the response body if the list returned was not complete. The marker value may then be used in a subsequent call to request the next set of list items. The marker value is opaque to the client. @@ -544,8 +594,8 @@ public Mono forceCloseHandlesWithRestResponseAsy * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono forceCloseHandlesWithRestResponseAsync(String handleId, Integer timeout, String marker, String sharesnapshot, Context context) { + public Mono forceCloseHandlesWithRestResponseAsync(String shareName, String filePath, String handleId, Integer timeout, String marker, String sharesnapshot, Context context) { final String comp = "forceclosehandles"; - return service.forceCloseHandles(this.client.url(), timeout, marker, sharesnapshot, handleId, this.client.version(), comp, context); + return service.forceCloseHandles(this.client.url(), shareName, filePath, timeout, marker, sharesnapshot, handleId, this.client.version(), comp, context); } } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/implementation/ServicesImpl.java b/storage/client/file/src/main/java/com/azure/storage/file/implementation/ServicesImpl.java index 5fcf68a794327..d2fc21cb7b595 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/implementation/ServicesImpl.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/implementation/ServicesImpl.java @@ -18,14 +18,15 @@ import com.azure.core.implementation.RestProxy; import com.azure.core.implementation.serializer.jackson.JacksonAdapter; import com.azure.core.util.Context; +import com.azure.storage.file.models.FileServiceProperties; import com.azure.storage.file.models.ListSharesIncludeType; import com.azure.storage.file.models.ServicesGetPropertiesResponse; import com.azure.storage.file.models.ServicesListSharesSegmentResponse; import com.azure.storage.file.models.ServicesSetPropertiesResponse; import com.azure.storage.file.models.StorageErrorException; -import com.azure.storage.file.models.StorageServiceProperties; import reactor.core.publisher.Mono; + import java.util.List; /** @@ -49,7 +50,7 @@ public final class ServicesImpl { * @param client the instance of the service client containing this operation class. */ public ServicesImpl(AzureFileStorageImpl client) { - this.service = RestProxy.create(ServicesService.class, client); + this.service = RestProxy.create(ServicesService.class, client.httpPipeline()); this.client = client; } @@ -58,12 +59,12 @@ public ServicesImpl(AzureFileStorageImpl client) { * proxy service to perform REST calls. */ @Host("{url}") - @Service("Storage Files Service") + @Service("Storage File Services") private interface ServicesService { @PUT("") @ExpectedResponses({202}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono setProperties(@HostParam("url") String url, @BodyParam("application/xml; charset=utf-8") StorageServiceProperties storageServiceProperties, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @QueryParam("restype") String restype, @QueryParam("comp") String comp, Context context); + Mono setProperties(@HostParam("url") String url, @BodyParam("application/xml; charset=utf-8") FileServiceProperties fileServiceProperties, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @QueryParam("restype") String restype, @QueryParam("comp") String comp, Context context); @GET("") @ExpectedResponses({200}) @@ -79,31 +80,31 @@ private interface ServicesService { /** * Sets properties for a storage account's File service endpoint, including properties for Storage Analytics metrics and CORS (Cross-Origin Resource Sharing) rules. * - * @param storageServiceProperties The StorageService properties. + * @param fileServiceProperties The FileService properties. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono setPropertiesWithRestResponseAsync(StorageServiceProperties storageServiceProperties, Context context) { + public Mono setPropertiesWithRestResponseAsync(FileServiceProperties fileServiceProperties, Context context) { final Integer timeout = null; final String restype = "service"; final String comp = "properties"; - return service.setProperties(this.client.url(), storageServiceProperties, timeout, this.client.version(), restype, comp, context); + return service.setProperties(this.client.url(), fileServiceProperties, timeout, this.client.version(), restype, comp, context); } /** * Sets properties for a storage account's File service endpoint, including properties for Storage Analytics metrics and CORS (Cross-Origin Resource Sharing) rules. * - * @param storageServiceProperties The StorageService properties. + * @param fileServiceProperties The FileService properties. * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono setPropertiesWithRestResponseAsync(StorageServiceProperties storageServiceProperties, Integer timeout, Context context) { + public Mono setPropertiesWithRestResponseAsync(FileServiceProperties fileServiceProperties, Integer timeout, Context context) { final String restype = "service"; final String comp = "properties"; - return service.setProperties(this.client.url(), storageServiceProperties, timeout, this.client.version(), restype, comp, context); + return service.setProperties(this.client.url(), fileServiceProperties, timeout, this.client.version(), restype, comp, context); } /** diff --git a/storage/client/file/src/main/java/com/azure/storage/file/implementation/SharesImpl.java b/storage/client/file/src/main/java/com/azure/storage/file/implementation/SharesImpl.java index bdc5fbadff999..befdb6335b90d 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/implementation/SharesImpl.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/implementation/SharesImpl.java @@ -12,6 +12,7 @@ import com.azure.core.annotations.Host; import com.azure.core.annotations.HostParam; import com.azure.core.annotations.PUT; +import com.azure.core.annotations.PathParam; import com.azure.core.annotations.QueryParam; import com.azure.core.annotations.Service; import com.azure.core.annotations.UnexpectedResponseExceptionType; @@ -29,9 +30,8 @@ import com.azure.storage.file.models.SharesSetQuotaResponse; import com.azure.storage.file.models.SignedIdentifier; import com.azure.storage.file.models.StorageErrorException; -import reactor.core.publisher.Mono; - import java.util.List; +import reactor.core.publisher.Mono; import java.util.Map; /** @@ -64,72 +64,74 @@ public SharesImpl(AzureFileStorageImpl client) { * proxy service to perform REST calls. */ @Host("{url}") - @Service("Storage Files Shares") + @Service("Storage File Shares") private interface SharesService { @PUT("{shareName}") @ExpectedResponses({201}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono create(@HostParam("url") String url, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-meta-") Map metadata, @HeaderParam("x-ms-share-quota") Integer quota, @HeaderParam("x-ms-version") String version, @QueryParam("restype") String restype, Context context); + Mono create(@HostParam("url") String url, @PathParam ("shareName") String shareName, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-meta-") Map metadata, @HeaderParam("x-ms-share-quota") Integer quota, @HeaderParam("x-ms-version") String version, @QueryParam("restype") String restype, Context context); @GET("{shareName}") @ExpectedResponses({200}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono getProperties(@HostParam("url") String url, @QueryParam("sharesnapshot") String sharesnapshot, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @QueryParam("restype") String restype, Context context); + Mono getProperties(@HostParam("url") String url, @PathParam ("shareName") String shareName, @QueryParam("sharesnapshot") String sharesnapshot, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @QueryParam("restype") String restype, Context context); @DELETE("{shareName}") @ExpectedResponses({202}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono delete(@HostParam("url") String url, @QueryParam("sharesnapshot") String sharesnapshot, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-delete-snapshots") DeleteSnapshotsOptionType deleteSnapshots, @QueryParam("restype") String restype, Context context); + Mono delete(@HostParam("url") String url, @PathParam ("shareName") String shareName, @QueryParam("sharesnapshot") String sharesnapshot, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-delete-snapshots") DeleteSnapshotsOptionType deleteSnapshots, @QueryParam("restype") String restype, Context context); @PUT("{shareName}") @ExpectedResponses({201}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono createSnapshot(@HostParam("url") String url, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-meta-") Map metadata, @HeaderParam("x-ms-version") String version, @QueryParam("restype") String restype, @QueryParam("comp") String comp, Context context); + Mono createSnapshot(@HostParam("url") String url, @PathParam ("shareName") String shareName, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-meta-") Map metadata, @HeaderParam("x-ms-version") String version, @QueryParam("restype") String restype, @QueryParam("comp") String comp, Context context); @PUT("{shareName}") @ExpectedResponses({200}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono setQuota(@HostParam("url") String url, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-share-quota") Integer quota, @QueryParam("restype") String restype, @QueryParam("comp") String comp, Context context); + Mono setQuota(@HostParam("url") String url, @PathParam ("shareName") String shareName, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-share-quota") Integer quota, @QueryParam("restype") String restype, @QueryParam("comp") String comp, Context context); @PUT("{shareName}") @ExpectedResponses({200}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono setMetadata(@HostParam("url") String url, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-meta-") Map metadata, @HeaderParam("x-ms-version") String version, @QueryParam("restype") String restype, @QueryParam("comp") String comp, Context context); + Mono setMetadata(@HostParam("url") String url, @PathParam ("shareName") String shareName, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-meta-") Map metadata, @HeaderParam("x-ms-version") String version, @QueryParam("restype") String restype, @QueryParam("comp") String comp, Context context); @GET("{shareName}") @ExpectedResponses({200}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono getAccessPolicy(@HostParam("url") String url, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @QueryParam("restype") String restype, @QueryParam("comp") String comp, Context context); + Mono getAccessPolicy(@HostParam("url") String url, @PathParam ("shareName") String shareName, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @QueryParam("restype") String restype, @QueryParam("comp") String comp, Context context); @PUT("{shareName}") @ExpectedResponses({200}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono setAccessPolicy(@HostParam("url") String url, @BodyParam("application/xml; charset=utf-8") SignedIdentifiersWrapper shareAcl, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @QueryParam("restype") String restype, @QueryParam("comp") String comp, Context context); + Mono setAccessPolicy(@HostParam("url") String url, @PathParam ("shareName") String shareName, @BodyParam("application/xml; charset=utf-8") SignedIdentifiersWrapper shareAcl, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @QueryParam("restype") String restype, @QueryParam("comp") String comp, Context context); @GET("{shareName}") @ExpectedResponses({200}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono getStatistics(@HostParam("url") String url, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @QueryParam("restype") String restype, @QueryParam("comp") String comp, Context context); + Mono getStatistics(@HostParam("url") String url, @PathParam ("shareName") String shareName, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @QueryParam("restype") String restype, @QueryParam("comp") String comp, Context context); } /** * Creates a new share under the specified account. If the share with the same name already exists, the operation fails. * + * @param shareName The name of the share to create. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono createWithRestResponseAsync(Context context) { + public Mono createWithRestResponseAsync(String shareName, Context context) { final Integer timeout = null; final Map metadata = null; final Integer quota = null; final String restype = "share"; - return service.create(this.client.url(), timeout, metadata, quota, this.client.version(), restype, context); + return service.create(this.client.url(), shareName, timeout, metadata, quota, this.client.version(), restype, context); } /** * Creates a new share under the specified account. If the share with the same name already exists, the operation fails. * + * @param shareName The name of the share to create. * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. * @param metadata A name-value pair to associate with a file storage object. * @param quota Specifies the maximum size of the share, in gigabytes. @@ -137,57 +139,61 @@ public Mono createWithRestResponseAsync(Context context) { * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono createWithRestResponseAsync(Integer timeout, Map metadata, Integer quota, Context context) { + public Mono createWithRestResponseAsync(String shareName, Integer timeout, Map metadata, Integer quota, Context context) { final String restype = "share"; - return service.create(this.client.url(), timeout, metadata, quota, this.client.version(), restype, context); + return service.create(this.client.url(), shareName, timeout, metadata, quota, this.client.version(), restype, context); } /** * Returns all user-defined metadata and system properties for the specified share or share snapshot. The data returned does not include the share's list of files. * + * @param shareName The name of the share that is having its properties retrieved. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono getPropertiesWithRestResponseAsync(Context context) { + public Mono getPropertiesWithRestResponseAsync(String shareName, Context context) { final String sharesnapshot = null; final Integer timeout = null; final String restype = "share"; - return service.getProperties(this.client.url(), sharesnapshot, timeout, this.client.version(), restype, context); + return service.getProperties(this.client.url(), shareName, sharesnapshot, timeout, this.client.version(), restype, context); } /** * Returns all user-defined metadata and system properties for the specified share or share snapshot. The data returned does not include the share's list of files. * + * @param shareName The name of the share that is having its properties retrieved. * @param sharesnapshot The snapshot parameter is an opaque DateTime value that, when present, specifies the share snapshot to query. * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono getPropertiesWithRestResponseAsync(String sharesnapshot, Integer timeout, Context context) { + public Mono getPropertiesWithRestResponseAsync(String shareName, String sharesnapshot, Integer timeout, Context context) { final String restype = "share"; - return service.getProperties(this.client.url(), sharesnapshot, timeout, this.client.version(), restype, context); + return service.getProperties(this.client.url(), shareName, sharesnapshot, timeout, this.client.version(), restype, context); } /** * Operation marks the specified share or share snapshot for deletion. The share or share snapshot and any files contained within it are later deleted during garbage collection. * + * @param shareName The name of the share to delete. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono deleteWithRestResponseAsync(Context context) { + public Mono deleteWithRestResponseAsync(String shareName, Context context) { final String sharesnapshot = null; final Integer timeout = null; final DeleteSnapshotsOptionType deleteSnapshots = null; final String restype = "share"; - return service.delete(this.client.url(), sharesnapshot, timeout, this.client.version(), deleteSnapshots, restype, context); + return service.delete(this.client.url(), shareName, sharesnapshot, timeout, this.client.version(), deleteSnapshots, restype, context); } /** * Operation marks the specified share or share snapshot for deletion. The share or share snapshot and any files contained within it are later deleted during garbage collection. * + * @param shareName The name of the share to delete. * @param sharesnapshot The snapshot parameter is an opaque DateTime value that, when present, specifies the share snapshot to query. * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. * @param deleteSnapshots Specifies the option include to delete the base share and all of its snapshots. Possible values include: 'include'. @@ -195,185 +201,197 @@ public Mono deleteWithRestResponseAsync(Context context) { * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono deleteWithRestResponseAsync(String sharesnapshot, Integer timeout, DeleteSnapshotsOptionType deleteSnapshots, Context context) { + public Mono deleteWithRestResponseAsync(String shareName, String sharesnapshot, Integer timeout, DeleteSnapshotsOptionType deleteSnapshots, Context context) { final String restype = "share"; - return service.delete(this.client.url(), sharesnapshot, timeout, this.client.version(), deleteSnapshots, restype, context); + return service.delete(this.client.url(), shareName, sharesnapshot, timeout, this.client.version(), deleteSnapshots, restype, context); } /** * Creates a read-only snapshot of a share. * + * @param shareName The name of the share that is having a snapshot created. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono createSnapshotWithRestResponseAsync(Context context) { + public Mono createSnapshotWithRestResponseAsync(String shareName, Context context) { final Integer timeout = null; final Map metadata = null; final String restype = "share"; final String comp = "snapshot"; - return service.createSnapshot(this.client.url(), timeout, metadata, this.client.version(), restype, comp, context); + return service.createSnapshot(this.client.url(), shareName, timeout, metadata, this.client.version(), restype, comp, context); } /** * Creates a read-only snapshot of a share. * + * @param shareName The name of the share that is having a snapshot created. * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. * @param metadata A name-value pair to associate with a file storage object. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono createSnapshotWithRestResponseAsync(Integer timeout, Map metadata, Context context) { + public Mono createSnapshotWithRestResponseAsync(String shareName, Integer timeout, Map metadata, Context context) { final String restype = "share"; final String comp = "snapshot"; - return service.createSnapshot(this.client.url(), timeout, metadata, this.client.version(), restype, comp, context); + return service.createSnapshot(this.client.url(), shareName, timeout, metadata, this.client.version(), restype, comp, context); } /** * Sets quota for the specified share. * + * @param shareName The name of the share that is having its quota set. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono setQuotaWithRestResponseAsync(Context context) { + public Mono setQuotaWithRestResponseAsync(String shareName, Context context) { final Integer timeout = null; final Integer quota = null; final String restype = "share"; final String comp = "properties"; - return service.setQuota(this.client.url(), timeout, this.client.version(), quota, restype, comp, context); + return service.setQuota(this.client.url(), shareName, timeout, this.client.version(), quota, restype, comp, context); } /** * Sets quota for the specified share. * + * @param shareName The name of the share that is having its quota set. * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. * @param quota Specifies the maximum size of the share, in gigabytes. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono setQuotaWithRestResponseAsync(Integer timeout, Integer quota, Context context) { + public Mono setQuotaWithRestResponseAsync(String shareName, Integer timeout, Integer quota, Context context) { final String restype = "share"; final String comp = "properties"; - return service.setQuota(this.client.url(), timeout, this.client.version(), quota, restype, comp, context); + return service.setQuota(this.client.url(), shareName, timeout, this.client.version(), quota, restype, comp, context); } /** * Sets one or more user-defined name-value pairs for the specified share. * + * @param shareName The name of the share having its metadata set. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono setMetadataWithRestResponseAsync(Context context) { + public Mono setMetadataWithRestResponseAsync(String shareName, Context context) { final Integer timeout = null; final Map metadata = null; final String restype = "share"; final String comp = "metadata"; - return service.setMetadata(this.client.url(), timeout, metadata, this.client.version(), restype, comp, context); + return service.setMetadata(this.client.url(), shareName, timeout, metadata, this.client.version(), restype, comp, context); } /** * Sets one or more user-defined name-value pairs for the specified share. * + * @param shareName The name of the share having its metadata set. * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. * @param metadata A name-value pair to associate with a file storage object. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono setMetadataWithRestResponseAsync(Integer timeout, Map metadata, Context context) { + public Mono setMetadataWithRestResponseAsync(String shareName, Integer timeout, Map metadata, Context context) { final String restype = "share"; final String comp = "metadata"; - return service.setMetadata(this.client.url(), timeout, metadata, this.client.version(), restype, comp, context); + return service.setMetadata(this.client.url(), shareName, timeout, metadata, this.client.version(), restype, comp, context); } /** * Returns information about stored access policies specified on the share. * + * @param shareName The name of the share having its access policy retrieved. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono getAccessPolicyWithRestResponseAsync(Context context) { + public Mono getAccessPolicyWithRestResponseAsync(String shareName, Context context) { final Integer timeout = null; final String restype = "share"; final String comp = "acl"; - return service.getAccessPolicy(this.client.url(), timeout, this.client.version(), restype, comp, context); + return service.getAccessPolicy(this.client.url(), shareName, timeout, this.client.version(), restype, comp, context); } /** * Returns information about stored access policies specified on the share. * + * @param shareName The name of the share having its access policy retrieved. * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono getAccessPolicyWithRestResponseAsync(Integer timeout, Context context) { + public Mono getAccessPolicyWithRestResponseAsync(String shareName, Integer timeout, Context context) { final String restype = "share"; final String comp = "acl"; - return service.getAccessPolicy(this.client.url(), timeout, this.client.version(), restype, comp, context); + return service.getAccessPolicy(this.client.url(), shareName, timeout, this.client.version(), restype, comp, context); } /** * Sets a stored access policy for use with shared access signatures. * + * @param shareName The name of the share having its access policy set. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono setAccessPolicyWithRestResponseAsync(Context context) { + public Mono setAccessPolicyWithRestResponseAsync(String shareName, Context context) { final Integer timeout = null; final String restype = "share"; final String comp = "acl"; SignedIdentifiersWrapper shareAclConverted = new SignedIdentifiersWrapper(null); - return service.setAccessPolicy(this.client.url(), shareAclConverted, timeout, this.client.version(), restype, comp, context); + return service.setAccessPolicy(this.client.url(), shareName, shareAclConverted, timeout, this.client.version(), restype, comp, context); } /** * Sets a stored access policy for use with shared access signatures. * + * @param shareName The name of the share having its access policy set. * @param shareAcl The ACL for the share. * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono setAccessPolicyWithRestResponseAsync(List shareAcl, Integer timeout, Context context) { + public Mono setAccessPolicyWithRestResponseAsync(String shareName, List shareAcl, Integer timeout, Context context) { final String restype = "share"; final String comp = "acl"; SignedIdentifiersWrapper shareAclConverted = new SignedIdentifiersWrapper(shareAcl); - return service.setAccessPolicy(this.client.url(), shareAclConverted, timeout, this.client.version(), restype, comp, context); + return service.setAccessPolicy(this.client.url(), shareName, shareAclConverted, timeout, this.client.version(), restype, comp, context); } /** * Retrieves statistics related to the share. * + * @param shareName The name of the share having its statistics retrieved. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono getStatisticsWithRestResponseAsync(Context context) { + public Mono getStatisticsWithRestResponseAsync(String shareName, Context context) { final Integer timeout = null; final String restype = "share"; final String comp = "stats"; - return service.getStatistics(this.client.url(), timeout, this.client.version(), restype, comp, context); + return service.getStatistics(this.client.url(), shareName, timeout, this.client.version(), restype, comp, context); } /** * Retrieves statistics related to the share. * + * @param shareName The name of the share having its statistics retrieved. * @param timeout The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono getStatisticsWithRestResponseAsync(Integer timeout, Context context) { + public Mono getStatisticsWithRestResponseAsync(String shareName, Integer timeout, Context context) { final String restype = "share"; final String comp = "stats"; - return service.getStatistics(this.client.url(), timeout, this.client.version(), restype, comp, context); + return service.getStatistics(this.client.url(), shareName, timeout, this.client.version(), restype, comp, context); } } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/models/DeleteSnapshotsOptionType.java b/storage/client/file/src/main/java/com/azure/storage/file/models/DeleteSnapshotsOptionType.java index 080e8961a2710..002eb887ac448 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/models/DeleteSnapshotsOptionType.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/models/DeleteSnapshotsOptionType.java @@ -14,12 +14,7 @@ public enum DeleteSnapshotsOptionType { /** * Enum value include. */ - INCLUDE("include"), - - /** - * Enum value only. - */ - ONLY("only"); + INCLUDE("include"); /** * The actual serialized value for a DeleteSnapshotsOptionType instance. diff --git a/storage/client/file/src/main/java/com/azure/storage/file/models/DirectoryInfo.java b/storage/client/file/src/main/java/com/azure/storage/file/models/DirectoryInfo.java index bd529f2538e2f..d651cbdb0e89a 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/models/DirectoryInfo.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/models/DirectoryInfo.java @@ -3,5 +3,37 @@ package com.azure.storage.file.models; +import java.time.OffsetDateTime; + +/** + * Contains information about a Directory in the storage File service. + */ public final class DirectoryInfo { + private final String eTag; + private final OffsetDateTime lastModified; + + /** + * Creates an instance of information about a specific Directory. + * + * @param eTag Entity tag that corresponds to the directory. + * @param lastModified Last time the directory was modified. + */ + public DirectoryInfo(final String eTag, final OffsetDateTime lastModified) { + this.eTag = eTag; + this.lastModified = lastModified; + } + + /** + * @return The entity tag that corresponds to the directory. + */ + public String eTag() { + return eTag; + } + + /** + * @return The last time the share was modified. + */ + public OffsetDateTime lastModified() { + return lastModified; + } } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/models/DirectoryProperties.java b/storage/client/file/src/main/java/com/azure/storage/file/models/DirectoryProperties.java index b140fd4a481eb..5ce45d09456b0 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/models/DirectoryProperties.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/models/DirectoryProperties.java @@ -3,5 +3,58 @@ package com.azure.storage.file.models; +import java.time.OffsetDateTime; +import java.util.Map; + +/** + * Contains properties information about a Directory in the storage File service. + */ public final class DirectoryProperties { + private final Map metadata; + private final String eTag; + private final OffsetDateTime lastModified; + private final boolean isServerEncrypted; + + /** + * Creates an instance of properties information about a specific Directory. + * + * @param metadata A set of name-value pairs that contain metadata for the directory. + * @param eTag Entity tag that corresponds to the directory. + * @param lastModified Last time the directory was modified. + * @param isServerEncrypted The value of this header is set to true if the directory metadata is completely encrypted using the specified algorithm. Otherwise, the value is set to false. + */ + public DirectoryProperties(final Map metadata, final String eTag, final OffsetDateTime lastModified, final boolean isServerEncrypted) { + this.metadata = metadata; + this.eTag = eTag; + this.lastModified = lastModified; + this.isServerEncrypted = isServerEncrypted; + } + + /** + * @return A set of name-value pairs that contain metadata for the directory. + */ + public Map metadata() { + return metadata; + } + + /** + * @return Entity tag that corresponds to the directory. + */ + public String eTag() { + return eTag; + } + + /** + * @return Entity tag that corresponds to the directory. + */ + public OffsetDateTime lastModified() { + return lastModified; + } + + /** + * @return The value of this header is true if the directory metadata is completely encrypted using the specified algorithm. Otherwise, the value is false. + */ + public boolean isServerEncrypted() { + return isServerEncrypted; + } } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/models/DirectorySetMetadataInfo.java b/storage/client/file/src/main/java/com/azure/storage/file/models/DirectorySetMetadataInfo.java new file mode 100644 index 0000000000000..2e51fdbbd45aa --- /dev/null +++ b/storage/client/file/src/main/java/com/azure/storage/file/models/DirectorySetMetadataInfo.java @@ -0,0 +1,34 @@ +package com.azure.storage.file.models; + +/** + * Contains metadata information about a Directory in the storage File service. + */ +public class DirectorySetMetadataInfo { + private final String eTag; + private final boolean isServerEncrypted; + + /** + * Creates an instance of information about a specific Directory. + * + * @param eTag Entity tag that corresponds to the share + * @param isServerEncrypted The value of this header is set to true if the directory metadata is completely encrypted using the specified algorithm. Otherwise, the value is set to false. + */ + public DirectorySetMetadataInfo(final String eTag, final boolean isServerEncrypted) { + this.eTag = eTag; + this.isServerEncrypted = isServerEncrypted; + } + + /** + * @return The entity tag that corresponds to the directory. + */ + public String eTag() { + return eTag; + } + + /** + * @return The value of this header is true if the directory metadata is completely encrypted using the specified algorithm. Otherwise, the value is false. + */ + public boolean isServerEncrypted() { + return isServerEncrypted; + } +} diff --git a/storage/client/file/src/main/java/com/azure/storage/file/models/FileCopyInfo.java b/storage/client/file/src/main/java/com/azure/storage/file/models/FileCopyInfo.java index fe4498b81c3da..f76a2096de9dd 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/models/FileCopyInfo.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/models/FileCopyInfo.java @@ -3,5 +3,63 @@ package com.azure.storage.file.models; +import java.time.OffsetDateTime; + +/** + * Contains copy information about a File in the storage File service. + */ public final class FileCopyInfo { + private final String eTag; + private final OffsetDateTime lastModified; + private final String copyId; + private final CopyStatusType copyStatus; + + /** + * Creates an instance of copy information about a specific File. + * + * @param eTag Entity tag that corresponds to the directory. + * @param lastModified Last time the directory was modified. + * @param copyId String identifier for this copy operation. + * @param copyStatus State of the copy operation with these values: + *
                    + *
                  • success: the copy completed successfully.
                  • + *
                  • pending: the copy is still in progress.
                  • + *
                  + */ + public FileCopyInfo(final String eTag, final OffsetDateTime lastModified, final String copyId, final CopyStatusType copyStatus) { + this.eTag = eTag; + this.lastModified = lastModified; + this.copyId = copyId; + this.copyStatus = copyStatus; + } + + /** + * @return Entity tag that corresponds to the directory. + */ + public String eTag() { + return eTag; + } + + /** + * @return Last time the directory was modified. + */ + public OffsetDateTime lastModified() { + return lastModified; + } + + /** + * @return String identifier for this copy operation. + */ + public String copyId() { + return copyId; + } + + /** + * @return State of the copy operation with these values: + * - success: the copy completed successfully. + * - pending: the copy is still in progress. + */ + public CopyStatusType copyStatus() { + return copyStatus; + } } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/models/FileDownloadHeaders.java b/storage/client/file/src/main/java/com/azure/storage/file/models/FileDownloadHeaders.java index df05458677164..76e60103ac0b2 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/models/FileDownloadHeaders.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/models/FileDownloadHeaders.java @@ -6,6 +6,7 @@ import com.azure.core.annotations.HeaderCollection; import com.azure.core.implementation.DateTimeRfc1123; +import com.azure.core.implementation.util.ImplUtils; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; import java.time.OffsetDateTime; @@ -345,7 +346,7 @@ public FileDownloadHeaders eTag(String eTag) { * @return the contentMD5 value. */ public byte[] contentMD5() { - return Arrays.copyOf(this.contentMD5, this.contentMD5.length); + return ImplUtils.clone(this.contentMD5); } /** @@ -362,7 +363,7 @@ public byte[] contentMD5() { * @return the FileDownloadHeaders object itself. */ public FileDownloadHeaders contentMD5(byte[] contentMD5) { - this.contentMD5 = Arrays.copyOf(contentMD5, contentMD5.length); + this.contentMD5 = ImplUtils.clone(contentMD5); return this; } @@ -714,7 +715,7 @@ public FileDownloadHeaders copyStatus(CopyStatusType copyStatus) { * @return the fileContentMD5 value. */ public byte[] fileContentMD5() { - return Arrays.copyOf(this.fileContentMD5, this.fileContentMD5.length); + return ImplUtils.clone(this.fileContentMD5); } /** diff --git a/storage/client/file/src/main/java/com/azure/storage/file/models/FileDownloadInfo.java b/storage/client/file/src/main/java/com/azure/storage/file/models/FileDownloadInfo.java index 694fe762c8131..74ce72c7315e4 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/models/FileDownloadInfo.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/models/FileDownloadInfo.java @@ -3,5 +3,90 @@ package com.azure.storage.file.models; +import io.netty.buffer.ByteBuf; +import java.time.OffsetDateTime; +import java.util.Map; +import reactor.core.publisher.Flux; + +/** + * Contains download information about a File in the storage File service. + */ public final class FileDownloadInfo { + private final String eTag; + private final OffsetDateTime lastModified; + private final Map metadata; + private final Long contentLength; + private final String contentType; + private final String contentRange; + private final Flux body; + + /** + * Creates an instance of download information about a specific File. + * + * @param eTag Entity tag that corresponds to the directory. + * @param lastModified Last time the directory was modified. + * @param metadata A set of name-value pairs associated with this file as user-defined metadata. + * @param contentLength The number of bytes present in the response body. + * @param contentType The content type specified for the file. The default content type is application/octet-stream. + * @param contentRange Indicates the range of bytes returned if the client requested a subset of the file by setting the Range request header. + * @param body The download request body. + */ + public FileDownloadInfo(final String eTag, final OffsetDateTime lastModified, final Map metadata, final Long contentLength, final String contentType, final String contentRange, final Flux body) { + this.eTag = eTag; + this.lastModified = lastModified; + this.metadata = metadata; + this.contentLength = contentLength; + this.contentType = contentType; + this.contentRange = contentRange; + this.body = body; + } + + /** + * @return Entity tag that corresponds to the directory. + */ + public String eTag() { + return eTag; + } + + /** + * @return Last time the directory was modified. + */ + public OffsetDateTime lastModified() { + return lastModified; + } + + /** + * @return A set of name-value pairs associated with this file as user-defined metadata. + */ + public Map metadata() { + return metadata; + } + + /** + * @return The number of bytes present in the response body. + */ + public Long contentLength() { + return contentLength; + } + + /** + * @return The content type specified for the file. The default content type is application/octet-stream. + */ + public String contentType() { + return contentType; + } + + /** + * @return The range of bytes returned if the client requested a subset of the file by setting the Range request header. + */ + public String contentRange() { + return contentRange; + } + + /** + * @return The download request body. + */ + public Flux body() { + return body; + } } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/models/FileGetPropertiesHeaders.java b/storage/client/file/src/main/java/com/azure/storage/file/models/FileGetPropertiesHeaders.java index ff397f6cd7ce1..727b476b60394 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/models/FileGetPropertiesHeaders.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/models/FileGetPropertiesHeaders.java @@ -6,6 +6,7 @@ import com.azure.core.annotations.HeaderCollection; import com.azure.core.implementation.DateTimeRfc1123; +import com.azure.core.implementation.util.ImplUtils; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; import java.time.OffsetDateTime; @@ -323,7 +324,7 @@ public FileGetPropertiesHeaders eTag(String eTag) { * @return the contentMD5 value. */ public byte[] contentMD5() { - return Arrays.copyOf(this.contentMD5, this.contentMD5.length); + return ImplUtils.clone(this.contentMD5); } /** @@ -335,7 +336,7 @@ public byte[] contentMD5() { * @return the FileGetPropertiesHeaders object itself. */ public FileGetPropertiesHeaders contentMD5(byte[] contentMD5) { - this.contentMD5 = Arrays.copyOf(contentMD5, contentMD5.length); + this.contentMD5 = ImplUtils.clone(contentMD5); return this; } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/models/FileHTTPHeaders.java b/storage/client/file/src/main/java/com/azure/storage/file/models/FileHTTPHeaders.java index 01c69b3a9f299..7555663560b59 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/models/FileHTTPHeaders.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/models/FileHTTPHeaders.java @@ -4,6 +4,7 @@ package com.azure.storage.file.models; +import com.azure.core.implementation.util.ImplUtils; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; import java.util.Arrays; @@ -146,7 +147,7 @@ public FileHTTPHeaders fileCacheControl(String fileCacheControl) { * @return the fileContentMD5 value. */ public byte[] fileContentMD5() { - return Arrays.copyOf(this.fileContentMD5, this.fileContentMD5.length); + return ImplUtils.clone(this.fileContentMD5); } /** @@ -156,7 +157,7 @@ public byte[] fileContentMD5() { * @return the FileHTTPHeaders object itself. */ public FileHTTPHeaders fileContentMD5(byte[] fileContentMD5) { - this.fileContentMD5 = Arrays.copyOf(fileContentMD5, fileContentMD5.length); + this.fileContentMD5 = ImplUtils.clone(fileContentMD5); return this; } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/models/FileInfo.java b/storage/client/file/src/main/java/com/azure/storage/file/models/FileInfo.java index 47a634785e190..4f421f0b79348 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/models/FileInfo.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/models/FileInfo.java @@ -3,5 +3,47 @@ package com.azure.storage.file.models; +import java.time.OffsetDateTime; + +/** + * Contains information about a File in the storage File service. + */ public final class FileInfo { + private final String eTag; + private final OffsetDateTime lastModified; + private final Boolean isServerEncrypted; + + /** + * Creates an instance of information about a specific Directory. + * + * @param eTag Entity tag that corresponds to the directory. + * @param lastModified Last time the directory was modified. + * @param isServerEncrypted The value of this header is set to true if the directory metadata is completely encrypted using the specified algorithm. Otherwise, the value is set to false. + */ + public FileInfo(final String eTag, final OffsetDateTime lastModified, final Boolean isServerEncrypted) { + this.eTag = eTag; + this.lastModified = lastModified; + this.isServerEncrypted = isServerEncrypted; + } + + /** + * @return The entity tag that corresponds to the directory. + */ + public String eTag() { + return eTag; + } + + /** + * @return The last time the share was modified. + */ + public OffsetDateTime lastModified() { + return lastModified; + } + + /** + * @return The value of this header is true if the directory metadata is completely encrypted using the specified algorithm. Otherwise, the value is false. + */ + public Boolean isServerEncrypted() { + return isServerEncrypted; + } } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/models/FileMetadataInfo.java b/storage/client/file/src/main/java/com/azure/storage/file/models/FileMetadataInfo.java new file mode 100644 index 0000000000000..29b222b480758 --- /dev/null +++ b/storage/client/file/src/main/java/com/azure/storage/file/models/FileMetadataInfo.java @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.file.models; + +public class FileMetadataInfo { + private final String eTag; + private final Boolean isServerEncrypted; + + public FileMetadataInfo(final String eTag, final Boolean isServerEncrypted) { + this.eTag = eTag; + this.isServerEncrypted = isServerEncrypted; + } + + public String eTag() { + return eTag; + } + + public Boolean isServerEncrypted() { + return isServerEncrypted; + } +} diff --git a/storage/client/file/src/main/java/com/azure/storage/file/models/FileProperties.java b/storage/client/file/src/main/java/com/azure/storage/file/models/FileProperties.java index dd691c0b06871..670057a600b7b 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/models/FileProperties.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/models/FileProperties.java @@ -3,5 +3,200 @@ package com.azure.storage.file.models; +import com.azure.core.implementation.util.ImplUtils; +import java.time.OffsetDateTime; +import java.util.Map; + +/** + * Contains property information about a File in the storage File service. + */ public final class FileProperties { + private final String eTag; + private final OffsetDateTime lastModified; + private final Map metadata; + private final String fileType; + private final Long contentLength; + private final String contentType; + private final byte[] contentMD5; + private final String contentEncoding; + private final String cacheControl; + private final String contentDisposition; + private final OffsetDateTime copyCompletionTime; + private final String copyStatusDescription; + private final String copyId; + private final String copyProgress; + private final String copySource; + private final CopyStatusType copyStatus; + private final Boolean isServerEncrypted; + + /** + * Creates an instance of property information about a specific File. + * + * @param eTag Entity tag that corresponds to the directory. + * @param lastModified Last time the directory was modified. + * @param metadata A set of name-value pairs associated with this file as user-defined metadata. + * @param fileType Type of the file. + * @param contentLength The number of bytes present in the response body. + * @param contentType The content type specified for the file. The default content type is application/octet-stream. + * @param contentMD5 The MD5 hash of the file to check the message content integrity. + * @param contentEncoding This header returns the value that was specified for the Content-Encoding request header. + * @param cacheControl This header is returned if it was previously specified for the file. + * @param contentDisposition The value that was specified for the x-ms-content-disposition header and specifies how to process the response. + * @param copyCompletionTime Conclusion time of the last attempted Copy File operation where this file was the destination file. + * @param copyStatusDescription Appears when x-ms-copy-status is failed or pending. Describes cause of fatal or non-fatal copy operation failure. + * @param copyId String identifier for the last attempted Copy File operation where this file was the destination file. + * @param copyProgress 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. + * @param copySource 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. + * @param copyStatus State of the copy operation identified by x-ms-copy-id, with these values: + * - success: Copy completed successfully. + * - pending: Copy is in progress. Check x-ms-copy-status-description if intermittent, non-fatal errors impede copy progress but don't cause failure. + * - aborted: Copy was ended by Abort Copy File. + * - failed: Copy failed. See x-ms-copy-status-description for failure details. + * @param isServerEncrypted 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. + */ + public FileProperties(final String eTag, final OffsetDateTime lastModified, final Map metadata, + final String fileType, final Long contentLength, final String contentType, final byte[] contentMD5, + final String contentEncoding, final String cacheControl, final String contentDisposition, + final OffsetDateTime copyCompletionTime, final String copyStatusDescription, final String copyId, + final String copyProgress, final String copySource, final CopyStatusType copyStatus, final Boolean isServerEncrypted) { + this.eTag = eTag; + this.lastModified = lastModified; + this.metadata = metadata; + this.fileType = fileType; + this.contentLength = contentLength; + this.contentType = contentType; + this.contentMD5 = ImplUtils.clone(contentMD5); + this.contentEncoding = contentEncoding; + this.cacheControl = cacheControl; + this.contentDisposition = contentDisposition; + this.copyCompletionTime = copyCompletionTime; + this.copyStatusDescription = copyStatusDescription; + this.copyId = copyId; + this.copyProgress = copyProgress; + this.copySource = copySource; + this.copyStatus = copyStatus; + this.isServerEncrypted = isServerEncrypted; + } + + /** + * @return Entity tag that corresponds to the directory. + */ + public String eTag() { + return eTag; + } + + /** + * @return Last time the directory was modified. + */ + public OffsetDateTime lastModified() { + return lastModified; + } + + /** + * @return A set of name-value pairs associated with this file as user-defined metadata. + */ + public Map metadata() { + return metadata; + } + + /** + * @return The number of bytes present in the response body. + */ + public Long contentLength() { + return contentLength; + } + + /** + * @return The type of the file. + */ + public String fileType() { + return fileType; + } + /** + * @return The content type specified for the file. The default content type is application/octet-stream. + */ + public String contentType() { + return contentType; + } + + /** + * @return The MD5 hash of the file. + */ + public byte[] contentMD5() { + return ImplUtils.clone(contentMD5); + } + + /** + * @return The value that was specified for the Content-Encoding request header. + */ + public String contentEncoding() { + return contentEncoding; + } + + /** + * @return This header is returned if it was previously specified for the file. + */ + public String cacheControl() { + return cacheControl; + } + + /** + * @return The value that was specified for the x-ms-content-disposition header and specifies how to process the response. + */ + public String contentDisposition() { + return contentDisposition; + } + + /** + * @return Conclusion time of the last attempted Copy File operation where this file was the destination file. + */ + public OffsetDateTime copyCompletionTime() { + return copyCompletionTime; + } + + /** + * @return When x-ms-copy-status is failed or pending. Describes cause of fatal or non-fatal copy operation failure. + */ + public String copyStatusDescription() { + return copyStatusDescription; + } + + /** + * @return String identifier for the last attempted Copy File operation where this file was the destination file. + */ + public String copyId() { + return copyId; + } + + /** + * @return 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. + */ + public String copyProgress() { + return copyProgress; + } + + /** + * @return 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. + */ + public String copySource() { + return copySource; + } + + /** + * @return State of the copy operation identified by x-ms-copy-id, with these values: + * - success: Copy completed successfully. + * - pending: Copy is in progress. Check x-ms-copy-status-description if intermittent, non-fatal errors impede copy progress but don't cause failure. + * - aborted: Copy was ended by Abort Copy File. + * - failed: Copy failed. See x-ms-copy-status-description for failure details. + */ + public CopyStatusType copyStatus() { + return copyStatus; + } + + /** + * @return True if the file data and application metadata are completely encrypted using the specified algorithm. Otherwise, return false. + */ + public Boolean isServerEncrypted() { + return isServerEncrypted; + } } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/models/FileRange.java b/storage/client/file/src/main/java/com/azure/storage/file/models/FileRange.java new file mode 100644 index 0000000000000..9c264f3f4f8fd --- /dev/null +++ b/storage/client/file/src/main/java/com/azure/storage/file/models/FileRange.java @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.file.models; + +import java.util.Objects; + +/** + * The range of a file in the storage file service. + */ +public final class FileRange { + private final long start; + private final Long end; + + /** + * Create an instance of the range of a file. Both the start and end of the range must be specified. + * @param start Specifies the start of bytes to be written. + * @param end Specifies the end of bytes to be written + */ + public FileRange(final long start, final Long end) { + this.start = start; + this.end = end; + } + + /** + * @return The start of bytes to be written. + */ + public long start() { + return start; + } + + /** + * @return The end of bytes to be written. + */ + public Long end() { + return end; + } + + /** + * @return The string format of the FileRange written into request. + */ + @Override + public String toString() { + String endString = Objects.toString(end); + return "bytes=" + String.valueOf(start) + "-" + endString; + } +} diff --git a/storage/client/file/src/main/java/com/azure/storage/file/models/FileRef.java b/storage/client/file/src/main/java/com/azure/storage/file/models/FileRef.java index 0cd1261d2af97..8ed977bee61c9 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/models/FileRef.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/models/FileRef.java @@ -3,5 +3,47 @@ package com.azure.storage.file.models; +/** + * Contains file or directory reference information in the storage File service. + */ public final class FileRef { + private final String name; + private final boolean isDirectory; + private final FileProperty fileProperty; + + /** + * Creates an instance of file or directory reference information about a specific Share. + * + * @param name Name of the file or the directory. + * @param isDirectory A boolean set to true if the reference is a directory, false if the reference is a file. + * @param fileProperty Property of a file. Pass {@code null} if the reference is a directory. + */ + public FileRef(final String name, final boolean isDirectory, final FileProperty fileProperty) { + this.name = name; + this.isDirectory = isDirectory; + this.fileProperty = fileProperty; + } + + + /** + * @return Name of the file or the directory. + */ + public String name() { + return name; + } + + /** + * @return True if the reference is a directory, or false if the reference is a file. + */ + public boolean isDirectory() { + return isDirectory; + } + + /** + * @return Property of a file. Return {@code null} if the reference is a directory. + */ + public FileProperty fileProperties() { + return fileProperty; + } + } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/models/FileServiceProperties.java b/storage/client/file/src/main/java/com/azure/storage/file/models/FileServiceProperties.java index 8ad66286adba1..ef42bbb4becf8 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/models/FileServiceProperties.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/models/FileServiceProperties.java @@ -1,7 +1,115 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +// Code generated by Microsoft (R) AutoRest Code Generator. package com.azure.storage.file.models; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; +import java.util.ArrayList; +import java.util.List; + +/** + * Storage service properties. + */ +@JacksonXmlRootElement(localName = "StorageServiceProperties") public final class FileServiceProperties { + /* + * A summary of request statistics grouped by API in hourly aggregates for + * files. + */ + @JsonProperty(value = "HourMetrics") + private Metrics hourMetrics; + + /* + * A summary of request statistics grouped by API in minute aggregates for + * files. + */ + @JsonProperty(value = "MinuteMetrics") + private Metrics minuteMetrics; + + private static final class CorsWrapper { + @JacksonXmlProperty(localName = "CorsRule") + private final List items; + + @JsonCreator + private CorsWrapper(@JacksonXmlProperty(localName = "CorsRule") List items) { + this.items = items; + } + } + + /* + * The set of CORS rules. + */ + @JsonProperty(value = "Cors") + private CorsWrapper cors; + + /** + * Get the hourMetrics property: A summary of request statistics grouped by + * API in hourly aggregates for files. + * + * @return the hourMetrics value. + */ + public Metrics hourMetrics() { + return this.hourMetrics; + } + + /** + * Set the hourMetrics property: A summary of request statistics grouped by + * API in hourly aggregates for files. + * + * @param hourMetrics the hourMetrics value to set. + * @return the FileServiceProperties object itself. + */ + public FileServiceProperties hourMetrics(Metrics hourMetrics) { + this.hourMetrics = hourMetrics; + return this; + } + + /** + * Get the minuteMetrics property: A summary of request statistics grouped + * by API in minute aggregates for files. + * + * @return the minuteMetrics value. + */ + public Metrics minuteMetrics() { + return this.minuteMetrics; + } + + /** + * Set the minuteMetrics property: A summary of request statistics grouped + * by API in minute aggregates for files. + * + * @param minuteMetrics the minuteMetrics value to set. + * @return the FileServiceProperties object itself. + */ + public FileServiceProperties minuteMetrics(Metrics minuteMetrics) { + this.minuteMetrics = minuteMetrics; + return this; + } + + /** + * Get the cors property: The set of CORS rules. + * + * @return the cors value. + */ + public List cors() { + if (this.cors == null) { + this.cors = new CorsWrapper(new ArrayList()); + } + return this.cors.items; + } + + /** + * Set the cors property: The set of CORS rules. + * + * @param cors the cors value to set. + * @return the FileServiceProperties object itself. + */ + public FileServiceProperties cors(List cors) { + this.cors = new CorsWrapper(cors); + return this; + } } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/models/FileUploadInfo.java b/storage/client/file/src/main/java/com/azure/storage/file/models/FileUploadInfo.java index 644075f434f74..26ffc82c314bd 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/models/FileUploadInfo.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/models/FileUploadInfo.java @@ -3,5 +3,35 @@ package com.azure.storage.file.models; +import com.azure.core.implementation.util.ImplUtils; +import java.time.OffsetDateTime; + public final class FileUploadInfo { + private String eTag; + private OffsetDateTime lastModified; + private byte[] contentMD5; + private Boolean isServerEncrypted; + + public FileUploadInfo(final String eTag, final OffsetDateTime lastModified, final byte[] contentMD5, final Boolean isServerEncrypted) { + this.eTag = eTag; + this.lastModified = lastModified; + this.contentMD5 = ImplUtils.clone(contentMD5); + this.isServerEncrypted = isServerEncrypted; + } + + public String eTag() { + return eTag; + } + + public OffsetDateTime lastModified() { + return lastModified; + } + + public byte[] contentMD5() { + return ImplUtils.clone(contentMD5); + } + + public Boolean isServerEncrypted() { + return isServerEncrypted; + } } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/models/FileUploadRangeHeaders.java b/storage/client/file/src/main/java/com/azure/storage/file/models/FileUploadRangeHeaders.java index 686014ab74e59..68b8ed554de37 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/models/FileUploadRangeHeaders.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/models/FileUploadRangeHeaders.java @@ -5,6 +5,7 @@ package com.azure.storage.file.models; import com.azure.core.implementation.DateTimeRfc1123; +import com.azure.core.implementation.util.ImplUtils; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; import java.time.OffsetDateTime; @@ -138,7 +139,7 @@ public FileUploadRangeHeaders lastModified(OffsetDateTime lastModified) { * @return the contentMD5 value. */ public byte[] contentMD5() { - return Arrays.copyOf(this.contentMD5, this.contentMD5.length); + return ImplUtils.clone(this.contentMD5); } /** @@ -151,7 +152,7 @@ public byte[] contentMD5() { * @return the FileUploadRangeHeaders object itself. */ public FileUploadRangeHeaders contentMD5(byte[] contentMD5) { - this.contentMD5 = Arrays.copyOf(contentMD5, contentMD5.length); + this.contentMD5 = ImplUtils.clone(contentMD5); return this; } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/models/ListFilesAndDirectoriesSegmentResponse.java b/storage/client/file/src/main/java/com/azure/storage/file/models/ListFilesAndDirectoriesSegmentResponse.java index 1ba129b882372..19f443c9ba0f7 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/models/ListFilesAndDirectoriesSegmentResponse.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/models/ListFilesAndDirectoriesSegmentResponse.java @@ -26,7 +26,7 @@ public final class ListFilesAndDirectoriesSegmentResponse { private String shareName; /* - * The shareSnapshot property. + * The snapshot property. */ @JacksonXmlProperty(localName = "ShareSnapshot", isAttribute = true) private String shareSnapshot; @@ -108,18 +108,18 @@ public ListFilesAndDirectoriesSegmentResponse shareName(String shareName) { } /** - * Get the shareSnapshot property: The shareSnapshot property. + * Get the snapshot property: The snapshot property. * - * @return the shareSnapshot value. + * @return the snapshot value. */ public String shareSnapshot() { return this.shareSnapshot; } /** - * Set the shareSnapshot property: The shareSnapshot property. + * Set the snapshot property: The snapshot property. * - * @param shareSnapshot the shareSnapshot value to set. + * @param shareSnapshot the snapshot value to set. * @return the ListFilesAndDirectoriesSegmentResponse object itself. */ public ListFilesAndDirectoriesSegmentResponse shareSnapshot(String shareSnapshot) { diff --git a/storage/client/file/src/main/java/com/azure/storage/file/models/ListSharesOptions.java b/storage/client/file/src/main/java/com/azure/storage/file/models/ListSharesOptions.java index 612b95238ea2a..a75b4bab697f1 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/models/ListSharesOptions.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/models/ListSharesOptions.java @@ -3,5 +3,107 @@ package com.azure.storage.file.models; +/** + * A set of options for selecting shares from Storage File service. + * + *
                    + *
                  • + * Providing {@link ListSharesOptions#prefix(String) prefix} will filter selections to {@link ShareItem shares} + * that that begin with the prefix. + *
                  • + *
                  • + * Providing {@link ListSharesOptions#maxResults(Integer) maxResults} will limit the number of {@link ShareItem shares} + * returned in a single page. + *
                  • + *
                  • + * Setting {@link ListSharesOptions#includeMetadata(boolean) includeMetadata} to true will include the metadata + * of each {@link ShareItem share}, if false {@link ShareItem#metadata() metadata} for each share will be {@code null}. + *
                  • + *
                  • + * Setting {@link ListSharesOptions#includeSnapshots(boolean) includeSnapshots} to true will include snapshots + * of each {@link ShareItem share}, the snapshot will be included as separate items in the response and will be + * identifiable by {@link ShareItem#snapshot() snapshot} having a value. The base share will contain {@code null} + * for the snapshot. + *
                  • + *
                  + */ public final class ListSharesOptions { + private String prefix; + private Integer maxResults; + private boolean includeMetadata; + private boolean includeSnapshots; + + /** + * Sets the prefix that a share must match to be included in the listing. + * + * @param prefix The prefix that shares must start with to pass the filter + * @return An updated ListSharesOptions object + */ + public ListSharesOptions prefix(String prefix) { + this.prefix = prefix; + return this; + } + + /** + * @return the prefix that a share must match to be included in the listing + */ + public String prefix() { + return prefix; + } + + /** + * Sets the maximum number of shares to include in a single response. + * + * @param maxResults Maximum number of shares to include in a single response. This value must be between 1 and 5000. + * @return An updated ListSharesOptions object + */ + public ListSharesOptions maxResults(Integer maxResults) { + this.maxResults = maxResults; + return this; + } + + /** + * @return the maximum number of shares to inlcude in a single response + */ + public Integer maxResults() { + return maxResults; + } + + /** + * Sets the status of including share metadata when listing shares + * + * If listing snapshots as well this will also determine if the snapshots have their metadata included as well. + * + * @param includeMetadata Flag indicating if metadata should be including in the listing + * @return An updated ListSharesOptions object + */ + public ListSharesOptions includeMetadata(boolean includeMetadata) { + this.includeMetadata = includeMetadata; + return this; + } + + /** + * @return the status of include share metadata when listing shares + */ + public boolean includeMetadata() { + return includeMetadata; + } + + /** + * Sets the status of including share snapshots when listing shares + * + * @param includeSnapshots Flag indicating if snapshots should be included in the listing + * @return An updated ListSharesOptions object + */ + public ListSharesOptions includeSnapshots(boolean includeSnapshots) { + this.includeSnapshots = includeSnapshots; + return this; + } + + /** + * @return the status of including share snapshots when listing shares + */ + public boolean includeSnapshots() { + return includeSnapshots; + } } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/models/ServicesGetPropertiesResponse.java b/storage/client/file/src/main/java/com/azure/storage/file/models/ServicesGetPropertiesResponse.java index 267a198ab8b8d..92a15ab7d889e 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/models/ServicesGetPropertiesResponse.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/models/ServicesGetPropertiesResponse.java @@ -11,7 +11,7 @@ /** * Contains all response data for the getProperties operation. */ -public final class ServicesGetPropertiesResponse extends ResponseBase { +public final class ServicesGetPropertiesResponse extends ResponseBase { /** * Creates an instance of ServicesGetPropertiesResponse. * @@ -21,7 +21,7 @@ public final class ServicesGetPropertiesResponse extends ResponseBase metadata; + /** * Get the lastModified property: The lastModified property. * @@ -98,4 +104,13 @@ public ShareProperties quota(int quota) { this.quota = quota; return this; } + + public Map metadata() { + return metadata; + } + + public ShareProperties metadata(Map metadata) { + this.metadata = metadata; + return this; + } } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/models/ShareSnapshotInfo.java b/storage/client/file/src/main/java/com/azure/storage/file/models/ShareSnapshotInfo.java index 2a3e17d8ad01d..4e95b45182a23 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/models/ShareSnapshotInfo.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/models/ShareSnapshotInfo.java @@ -3,5 +3,49 @@ package com.azure.storage.file.models; -public final class ShareSnapshotInfo { +import java.time.OffsetDateTime; + +/** + * Contains information about a snapshot of a Share in the storage File service. + */ +public final class ShareSnapshotInfo { + private String snapshot; + private String eTag; + private OffsetDateTime lastModified; + + /** + * Creates an instance of snapshot information for a specific Share. + * + * @param snapshot Identifier for the snapshot + * @param eTag Entity tag that corresponds to the snapshot + * @param lastModified Last time the Share was modified if the snapshot was created without metadata, if the snapshot + * was created with metadata then it will be the time the snapshot was created + */ + public ShareSnapshotInfo(String snapshot, String eTag, OffsetDateTime lastModified) { + this.snapshot = snapshot; + this.eTag = eTag; + this.lastModified = lastModified; + } + + /** + * @return the identifier of the snapshot + */ + public String snapshot() { + return snapshot; + } + + /** + * @return the entity tag that corresponds to the snapshot + */ + public String eTag() { + return eTag; + } + + /** + * @return the last time the share was modified if the snapshot was created without metadata, otherwise this is the + * time that the snapshot was created. + */ + public OffsetDateTime lastModified() { + return lastModified; + } } diff --git a/storage/client/file/src/main/java/com/azure/storage/file/models/ShareStatistics.java b/storage/client/file/src/main/java/com/azure/storage/file/models/ShareStatistics.java index aef819551c53f..d10648dfb3dc8 100644 --- a/storage/client/file/src/main/java/com/azure/storage/file/models/ShareStatistics.java +++ b/storage/client/file/src/main/java/com/azure/storage/file/models/ShareStatistics.java @@ -3,5 +3,25 @@ package com.azure.storage.file.models; +/** + * Contains statistics about a Share in the storage File service. + */ public final class ShareStatistics { + private final int shareUsageInGB; + + /** + * Creates an instance of storage statistics for a Share. + * + * @param shareUsageInGB Size in GB of the Share + */ + public ShareStatistics(int shareUsageInGB) { + this.shareUsageInGB = shareUsageInGB; + } + + /** + * @return the size in GB of the Share + */ + public int getShareUsageInGB() { + return shareUsageInGB; + } } diff --git a/storage/client/file/src/samples/java/com/azure/file/AsyncSample.java b/storage/client/file/src/samples/java/com/azure/file/AsyncSample.java new file mode 100644 index 0000000000000..12a0ae4e36c88 --- /dev/null +++ b/storage/client/file/src/samples/java/com/azure/file/AsyncSample.java @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.file; + +import com.azure.core.util.configuration.ConfigurationManager; +import com.azure.storage.file.FileServiceAsyncClient; +import com.azure.storage.file.FileServiceClientBuilder; +import java.util.UUID; + +/** + * Sample demonstrates how to create, list and delete a share using the async file service client. + */ +public class AsyncSample { + private static final String ENDPOINT = ConfigurationManager.getConfiguration().get("AZURE_STORAGE_FILE_ENDPOINT"); + + // This is the helper method to generate random name. + private static String generateRandomName() { + return UUID.randomUUID().toString().substring(0, 8); + } + + /** + * The main method show how to make async cal for creating and listing shares. + * @param args No args needed for the main method + */ + public static void main(String[] args) { + // Create a file service client + FileServiceAsyncClient fileServiceAsyncClient = new FileServiceClientBuilder().endpoint(ENDPOINT) + .buildAsyncClient(); + // Create a share + String shareName = generateRandomName(); + fileServiceAsyncClient.createShare(shareName).subscribe( + response -> { + System.out.printf("Successfully created a share with status code: %d.", response.statusCode()); + }, + err -> { + System.out.println("Failed to create a share. Reasons: " + err.getMessage()); + }, + () -> { + System.out.println("Completed creating a share!"); + } + ); + + // List all shares and delete them. + fileServiceAsyncClient.listShares().subscribe( + shareItem -> { + System.out.println("There is a share named: " + shareItem.name()); + fileServiceAsyncClient.deleteShare(shareItem.name()).subscribe( + response -> { + System.out.printf("Successfully delete the share: %s.", shareItem.name()); + }, + err -> { + System.out.printf("Failed to delete the share: %s. Reasons: %s.", shareItem.name(), err.getMessage()); + }, + () -> { + System.out.println("Completed deleting the share."); + } + ); + }, + err -> { + System.out.println("Failed to list shares. Reasons: " + err.getMessage()); + }, + () -> { + System.out.println("Completed the listing and deleting the shares."); + } + ); + } +} diff --git a/storage/client/file/src/samples/java/com/azure/file/DirectorySample.java b/storage/client/file/src/samples/java/com/azure/file/DirectorySample.java new file mode 100644 index 0000000000000..0c3e7d768082d --- /dev/null +++ b/storage/client/file/src/samples/java/com/azure/file/DirectorySample.java @@ -0,0 +1,115 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.file; + +import com.azure.core.http.rest.Response; +import com.azure.core.util.configuration.ConfigurationManager; +import com.azure.storage.file.DirectoryClient; +import com.azure.storage.file.DirectoryClientBuilder; +import com.azure.storage.file.ShareClient; +import com.azure.storage.file.ShareClientBuilder; +import com.azure.storage.file.models.DirectoryProperties; +import com.azure.storage.file.models.StorageErrorException; +import java.util.UUID; + +/** + * Sample demonstrates how to create, list and delete directory and its subdirectories and files + * and how to get and set properties. + */ +public class DirectorySample { + private static final String ENDPOINT = ConfigurationManager.getConfiguration().get("AZURE_STORAGE_FILE_ENDPOINT"); + + // This is the helper method to generate random name. + private static String generateRandomName() { + return UUID.randomUUID().toString().substring(0, 8); + } + + /** + * The main method shows how to do the basic operation using directory sync client. + * @param args No args needed for the main method. + */ + public static void main(String[] args) { + String shareName = generateRandomName(); + ShareClient shareClient = new ShareClientBuilder().endpoint(ENDPOINT).shareName(shareName).buildClient(); + shareClient.create(); + // Build up a directory client + DirectoryClient directoryClient = new DirectoryClientBuilder().endpoint(ENDPOINT).shareName(generateRandomName()) + .shareName(shareName) + .directoryPath(generateRandomName()).buildClient(); + // Create a parent directory + try { + directoryClient.create(); + } catch (StorageErrorException e) { + System.out.println("Failed to create a directory. Reasons: " + e.getMessage()); + } + + // Create a child directory. + String childDirectoryName = generateRandomName(); + try { + directoryClient.createSubDirectory(childDirectoryName); + } catch (StorageErrorException e) { + System.out.println("Failed to create sub directory. Reasons: " + e.getMessage()); + } + + // Create a 1KB file under the child directory. + DirectoryClient childDirClient = null; + String fileName = generateRandomName(); + try { + childDirClient = directoryClient.getSubDirectoryClient(childDirectoryName); + childDirClient.createFile(fileName, 1024); + } catch (StorageErrorException e) { + System.out.println("Failed to create a file under the child directory. Reasons: " + e.getMessage()); + } + + // Delete the child directory. The operation will fail because storage service only allowed to delete the empty directory. + try { + childDirClient.delete(); + } catch (StorageErrorException e) { + System.out.println("This is expected as the child directory is not empty."); + } + + // List all the sub directories and files. + try { + directoryClient.listFilesAndDirectories().forEach( + fileRef -> { + System.out.printf("Is the resource a directory? %b. The resource name is: ", fileRef.isDirectory(), + fileRef.name()); + } + ); + } catch (StorageErrorException e) { + System.out.println("Failed to list all the subdirectories and files. Reasons: " + e.getMessage()); + } + + // Get the parent directory properties. + try { + Response propertiesResponse = directoryClient.getProperties(); + System.out.printf("This is the eTag %s of the directory: ", propertiesResponse.value().eTag()); + } catch (StorageErrorException e) { + System.out.println("Failed to get the properties of the parent directory"); + } + + // Delete the file. + try { + childDirClient.deleteFile(fileName); + } catch (StorageErrorException e) { + System.out.println("Failed to delete the file. Reasons: " + e.getMessage()); + } + + // Delete the child folder + try { + directoryClient.deleteSubDirectory(childDirectoryName); + } catch (StorageErrorException e) { + System.out.println("Failed to delete the child directory. Reasons: " + e.getMessage()); + } + + // Delete the parent folder + try { + directoryClient.delete(); + } catch (StorageErrorException e) { + System.out.println("Failed to delete the parent directory. Reasons: " + e.getMessage()); + } + + shareClient.delete(); + } + +} diff --git a/storage/client/file/src/samples/java/com/azure/file/FileSample.java b/storage/client/file/src/samples/java/com/azure/file/FileSample.java new file mode 100644 index 0000000000000..bff249f71e43c --- /dev/null +++ b/storage/client/file/src/samples/java/com/azure/file/FileSample.java @@ -0,0 +1,144 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.file; + +import com.azure.core.http.rest.Response; +import com.azure.core.util.configuration.ConfigurationManager; +import com.azure.storage.file.FileClient; +import com.azure.storage.file.FileClientBuilder; +import com.azure.storage.file.ShareClient; +import com.azure.storage.file.ShareClientBuilder; +import com.azure.storage.file.models.CopyStatusType; +import com.azure.storage.file.models.FileCopyInfo; +import com.azure.storage.file.models.FileProperties; +import com.azure.storage.file.models.StorageErrorException; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.UUID; + +/** + * Sample demonstrates how to create, copy and delete a file and how to get and set properties. + */ +public class FileSample { + private static final String ENDPOINT = ConfigurationManager.getConfiguration().get("AZURE_STORAGE_FILE_ENDPOINT"); + + // This is the helper method to generate random name. + private static String generateRandomName() { + return UUID.randomUUID().toString().substring(0, 8); + } + + /** + * The main method shows how to do the base operation using file sync client. + * @param args No args needed for the main method. + * @throws RuntimeException If error occurs when make storage API call. + * @throws Exception If there are any IO exception occurs. + */ + public static void main(String[] args) throws Exception { + String shareName = generateRandomName(); + ShareClient shareClient = new ShareClientBuilder().endpoint(ENDPOINT).shareName(shareName).buildClient(); + shareClient.create(); + String parentDirName = generateRandomName(); + shareClient.createDirectory(parentDirName); + + // Create a source file client + String srcFileName = generateRandomName(); + FileClient srcFileClient = new FileClientBuilder().endpoint(ENDPOINT).shareName(shareName) + .filePath(parentDirName + "/" + srcFileName).buildClient(); + + // Create a source file + try { + srcFileClient.create(1024); + } catch (StorageErrorException e) { + System.out.println("Failed to create source client. Reasons: " + e.getMessage()); + } + + // Upload some data bytes to the src file. + String dataText = "Hello, file client sample!"; + ByteBuf uploadData = Unpooled.wrappedBuffer(dataText.getBytes(StandardCharsets.UTF_8)); + try { + srcFileClient.upload(uploadData, uploadData.readableBytes()); + } catch (StorageErrorException e) { + System.out.println("Failed to upload the data. Reasons: " + e.getMessage()); + } + // Create a destination file client. + String destFileName = generateRandomName(); + FileClient destFileClient = new FileClientBuilder().endpoint(ENDPOINT).shareName(shareName) + .filePath(parentDirName + "/" + destFileName).buildClient(); + destFileClient.create(1024); + + // Copy the file from source file to destination file. + URL clientURL = null; + clientURL = srcFileClient.getFileUrl(); + + String sourceURL = clientURL.toString() + "/" + shareName + "/" + parentDirName + "/" + srcFileName; + + Response copyResponse = null; + try { + copyResponse = destFileClient.startCopy(sourceURL, null); + } catch (StorageErrorException e) { + throw new RuntimeException("Failed to start the copy of source file. Reasons: " + e.getMessage()); + } + + // Abort the copy if the status is pending. + if (copyResponse.value().copyStatus() == CopyStatusType.PENDING) { + try { + destFileClient.abortCopy(copyResponse.value().copyId()); + } catch (StorageErrorException e) { + System.out.println("Failed to abort the copy. Reasons: " + e.getMessage()); + } + } + + // Upload a local file to the storage. + URL fileFolder = FileSample.class.getClassLoader().getResource("."); + String uploadPath = fileFolder.getPath() + "testfiles/" + "uploadSample.txt"; + + try { + srcFileClient.uploadFromFile(uploadPath); + } catch (StorageErrorException e) { + System.out.println("Failed to upload file to storage. Reasons: " + e.getMessage()); + } + + // Download storage file to local file. + String downloadPath = fileFolder.getPath() + "testfiles/" + "downloadSample.txt"; + File downloadFile = new File(downloadPath); + try { + if (!Files.exists(downloadFile.toPath())) { + downloadFile.createNewFile(); + } + } catch (IOException e) { + throw new RuntimeException("Failed to create new upload file."); + } + try { + srcFileClient.downloadToFile(downloadPath); + } catch (StorageErrorException e) { + System.out.println("Failed to download file from storage. Reasons: " + e.getMessage()); + } + + if (!Files.exists(downloadFile.toPath())) { + downloadFile.delete(); + } + + // Get the file properties + try { + Response propertiesResponse = srcFileClient.getProperties(); + System.out.printf("This is the eTag: %s of the file. File type is : %s.", propertiesResponse.value().eTag(), propertiesResponse.value().fileType()); + } catch (StorageErrorException e) { + System.out.println("Failed to get file properties. Reasons: " + e.getMessage()); + } + + // Delete the source file. + try { + srcFileClient.delete(); + } catch (StorageErrorException e) { + System.out.println("Failed to delete the src file. Reasons: " + e.getMessage()); + } + + // Delete the share + shareClient.delete(); + } +} diff --git a/storage/client/file/src/samples/java/com/azure/file/FileServiceSample.java b/storage/client/file/src/samples/java/com/azure/file/FileServiceSample.java new file mode 100644 index 0000000000000..f5fad2f7035aa --- /dev/null +++ b/storage/client/file/src/samples/java/com/azure/file/FileServiceSample.java @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.file; + +import com.azure.core.http.rest.Response; +import com.azure.core.util.configuration.ConfigurationManager; +import com.azure.storage.file.FileServiceClient; +import com.azure.storage.file.FileServiceClientBuilder; +import com.azure.storage.file.models.FileServiceProperties; +import com.azure.storage.file.models.StorageErrorException; +import java.util.UUID; + +/** + * Sample demonstrates how to create, list and delete shares, and get and set properties. + */ +public class FileServiceSample { + private static final String CONNECTION_STRING = ConfigurationManager.getConfiguration().get("AZURE_STORAGE_CONNECTION_STRING"); + + // This is the helper method to generate random name. + private static String generateRandomName() { + return UUID.randomUUID().toString().substring(0, 8); + } + + /** + * The main method shows how to do the base operation using file service sync client. + * @param args No args needed for the main method + */ + public static void main(String[] args) { + // Create a file service client. + FileServiceClient fileServiceClient = new FileServiceClientBuilder() + .connectionString(CONNECTION_STRING).buildClient(); + + // Create 3 shares + for (int i = 0; i < 3; i++) { + try { + fileServiceClient.createShare(generateRandomName()); + } catch (StorageErrorException e) { + System.out.printf("Failed to create share %d. Reasons: %s", i, e.getMessage()); + } + } + + // Get properties from the file service + try { + Response response = fileServiceClient.getProperties(); + + System.out.printf("Hour metrics enabled: %b, Minute metrics enabled: %b\n", + response.value().hourMetrics(), response.value().minuteMetrics()); + } catch (StorageErrorException e) { + System.out.println("Failed to get the account properties. Reasons: " + e.getMessage()); + } + // List all shares under file service and delete them. + fileServiceClient.listShares().forEach( + shareItem -> { + try { + System.out.printf("This is the share name: %s in the file account.\n", shareItem.name()); + fileServiceClient.deleteShare(shareItem.name()); + System.out.println("The share has been deleted from the storage file account!"); + } catch (StorageErrorException e) { + System.out.println("Failed to delete the share. Reasons: " + e.getMessage()); + } + } + ); + + // Delete all shares under file service + + } +} diff --git a/storage/client/file/src/samples/java/com/azure/file/ShareSample.java b/storage/client/file/src/samples/java/com/azure/file/ShareSample.java new file mode 100644 index 0000000000000..1d6e9db8ebabb --- /dev/null +++ b/storage/client/file/src/samples/java/com/azure/file/ShareSample.java @@ -0,0 +1,134 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.file; + +import com.azure.core.http.rest.Response; +import com.azure.core.util.configuration.ConfigurationManager; +import com.azure.storage.file.ShareClient; +import com.azure.storage.file.ShareClientBuilder; +import com.azure.storage.file.models.ShareProperties; +import com.azure.storage.file.models.StorageErrorException; +import java.util.UUID; + +/** + * Sample demonstrates how to create, list and delete shares, and get and set properties. + */ +public class ShareSample { + private static final String ENDPOINT = ConfigurationManager.getConfiguration().get("AZURE_STORAGE_FILE_ENDPOINT"); + + // This is the helper method to generate random name. + private static String generateRandomName() { + return UUID.randomUUID().toString().substring(0, 8); + } + + /** + * The main method shows how to do the base operation using share sync client. + * @param args No args needed for the main method + */ + public static void main(String[] args) { + // Build a share client + String shareName = generateRandomName(); + ShareClient shareClient = new ShareClientBuilder().endpoint(ENDPOINT). + shareName(shareName).buildClient(); + + // Create first snapshot on share. + String shareSnapshot1 = null; + try { + shareSnapshot1 = shareClient.createSnapshot().value().snapshot(); + } catch (StorageErrorException e) { + System.out.println("Failed to create snapshot on share. Reasons: " + e.getMessage()); + } + + // Create a share using teh share client + try { + shareClient.create(); + } catch (StorageErrorException e) { + System.out.printf("Failed to create the share %s with share client. Reasons: %s\n", shareName, e.getMessage()); + } + // Create 3 directories using share client + for (int i = 0; i < 3; i++) { + try { + shareClient.createDirectory(generateRandomName()); + } catch (StorageErrorException e) { + System.out.println("Failed to create directory using the share client. Reasons: " + e.getMessage()); + } + } + + // Create another snapshot on share. + String shareSnapshot2 = null; + try { + shareSnapshot2 = shareClient.createSnapshot().value().snapshot(); + } catch (StorageErrorException e) { + System.out.println("Failed to create snapshot on share. Reasons: " + e.getMessage()); + } + + // Create another directory after having the snapshot + try { + shareClient.createDirectory(generateRandomName()); + } catch (StorageErrorException e) { + System.out.println("Failed to create directory using the share client. Reasons: " + e.getMessage()); + } + + // Get the properties of the share with first snapshot. + ShareClient shareClientWithSnapshot1 = null; + try { + shareClientWithSnapshot1 = new ShareClientBuilder().endpoint(ENDPOINT) + .shareName(shareName).snapshot(shareSnapshot1).buildClient(); + Response shareProperties1 = shareClientWithSnapshot1.getProperties(); + System.out.println("This is the first snapshot eTag: " + shareProperties1.value().etag()); + } catch (StorageErrorException e) { + System.out.println("Failed to get properties for the first share snapshot. Reasons: " + e.getMessage()); + } + + // Get the properties of the share with second snapshot. + ShareClient shareClientWithSnapshot2 = null; + try { + shareClientWithSnapshot2 = new ShareClientBuilder().endpoint(ENDPOINT) + .shareName(shareName).snapshot(shareSnapshot2).buildClient(); + Response shareProperties2 = shareClientWithSnapshot2.getProperties(); + System.out.println("This is the second snapshot eTag: " + shareProperties2.value().etag()); + } catch (StorageErrorException e) { + System.out.println("Failed to get properties for the second share snapshot. Reasons: " + e.getMessage()); + } + + // Get the root directory and list all directories. + try { + shareClient.getRootDirectoryClient().listFilesAndDirectories().forEach( + fileRef -> { + System.out.printf("Is the resource a file or directory?"); + } + ); + } catch (StorageErrorException e) { + System.out.println("Failed to delete the share. Reasons: " + e.getMessage()); + } + + // Delete the share snapshot 1 + try { + shareClientWithSnapshot1.delete(); + } catch (StorageErrorException e) { + System.out.println("Failed to delete the share snapshot 1. Reasons: " + e.getMessage()); + } + + // Check the delete share snapshot properties. + try { + shareClientWithSnapshot1.getProperties(); + } catch (StorageErrorException e) { + System.out.println("This is expected. The snapshot has been delete. Reasons: " + e.getMessage()); + } + + // Check the one not deleted. + try { + shareClientWithSnapshot2.getProperties(); + } catch (StorageErrorException e) { + System.out.println("Failed to get the properties of share snapshot 2. Reasons: " + e.getMessage()); + } + + // Delete the share. + try { + shareClient.delete(); + } catch (StorageErrorException e) { + System.out.println("Failed to delete the share. Reasons: " + e.getMessage()); + } + + } +} diff --git a/storage/client/file/src/samples/java/com/azure/storage/file/DirectoryJavaDocCodeSamples.java b/storage/client/file/src/samples/java/com/azure/storage/file/DirectoryJavaDocCodeSamples.java new file mode 100644 index 0000000000000..ff06ab021ca03 --- /dev/null +++ b/storage/client/file/src/samples/java/com/azure/storage/file/DirectoryJavaDocCodeSamples.java @@ -0,0 +1,586 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.file; + +import com.azure.core.http.rest.Response; +import com.azure.core.http.rest.VoidResponse; +import com.azure.storage.common.credentials.SASTokenCredential; +import com.azure.storage.common.credentials.SharedKeyCredential; +import com.azure.storage.file.models.DirectoryInfo; +import com.azure.storage.file.models.DirectoryProperties; +import com.azure.storage.file.models.DirectorySetMetadataInfo; +import com.azure.storage.file.models.FileHTTPHeaders; +import com.azure.storage.file.models.HandleItem; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.Collections; +import java.util.Map; + +/** + * Contains code snippets when generating javadocs through doclets for {@link DirectoryClient} and {@link DirectoryAsyncClient}. + */ +public class DirectoryJavaDocCodeSamples { + /** + * Generates code sample for {@link DirectoryClient} instantiation. + */ + public void initialization() { + // BEGIN: com.azure.storage.file.directoryClient.instantiation + DirectoryClient client = new DirectoryClientBuilder() + .connectionString("${connectionString}") + .endpoint("${endpoint}") + .buildClient(); + // END: com.azure.storage.file.directoryClient.instantiation + } + + /** + * Generates code sample for {@link DirectoryAsyncClient} instantiation. + */ + public void asyncInitialization() { + // BEGIN: com.azure.storage.file.directoryAsyncClient.instantiation + DirectoryAsyncClient client = new DirectoryClientBuilder() + .connectionString("${connectionString}") + .endpoint("${endpoint}") + .buildAsyncClient(); + // END: com.azure.storage.file.directoryAsyncClient.instantiation + } + + /** + * Generates code sample for creating a {@link DirectoryClient} with {@link SASTokenCredential} + * @return An instance of {@link DirectoryClient} + */ + public DirectoryClient createClientWithSASToken() { + // BEGIN: com.azure.storage.file.directoryClient.instantiation.sastoken + DirectoryClient directoryClient = new DirectoryClientBuilder() + .endpoint("https://${accountName}.file.core.windows.net?${SASToken}") + .shareName("myshare") + .directoryPath("mydirectory") + .buildClient(); + // END: com.azure.storage.file.directoryClient.instantiation.sastoken + return directoryClient; + } + + /** + * Generates code sample for creating a {@link DirectoryAsyncClient} with {@link SASTokenCredential} + * @return An instance of {@link DirectoryAsyncClient} + */ + public DirectoryAsyncClient createAsyncClientWithSASToken() { + // BEGIN: com.azure.storage.file.directoryAsyncClient.instantiation.sastoken + DirectoryAsyncClient directoryAsyncClient = new DirectoryClientBuilder() + .endpoint("https://{accountName}.file.core.windows.net?{SASToken}") + .shareName("myshare") + .directoryPath("mydirectory") + .buildAsyncClient(); + // END: com.azure.storage.file.directoryAsyncClient.instantiation.sastoken + return directoryAsyncClient; + } + + /** + * Generates code sample for creating a {@link DirectoryClient} with {@link SASTokenCredential} + * @return An instance of {@link DirectoryClient} + */ + public DirectoryClient createClientWithCredential() { + + // BEGIN: com.azure.storage.file.directoryClient.instantiation.credential + DirectoryClient directoryClient = new DirectoryClientBuilder() + .endpoint("https://${accountName}.file.core.windows.net") + .credential(SASTokenCredential.fromQuery("${SASTokenQueryParams}")) + .shareName("myshare") + .directoryPath("mydirectory") + .buildClient(); + // END: com.azure.storage.file.directoryClient.instantiation.credential + return directoryClient; + } + + /** + * Generates code sample for creating a {@link DirectoryAsyncClient} with {@link SASTokenCredential} + * @return An instance of {@link DirectoryAsyncClient} + */ + public DirectoryAsyncClient createAsyncClientWithCredential() { + // BEGIN: com.azure.storage.file.directoryAsyncClient.instantiation.credential + DirectoryAsyncClient direcotryAsyncClient = new DirectoryClientBuilder() + .endpoint("https://{accountName}.file.core.windows.net") + .credential(SASTokenCredential.fromQuery("${SASTokenQueryParams}")) + .shareName("myshare") + .directoryPath("mydirectory") + .buildAsyncClient(); + // END: com.azure.storage.file.directoryAsyncClient.instantiation.credential + return direcotryAsyncClient; + } + + /** + * Generates code sample for creating a {@link DirectoryClient} with {@code connectionString} which turns into {@link SharedKeyCredential} + * @return An instance of {@link DirectoryClient} + */ + public DirectoryClient createClientWithConnectionString() { + // BEGIN: com.azure.storage.file.directoryClient.instantiation.connectionstring + String connectionString = "DefaultEndpointsProtocol=https;AccountName={name};AccountKey={key}" + + ";EndpointSuffix={core.windows.net}"; + DirectoryClient directoryClient = new DirectoryClientBuilder() + .connectionString(connectionString).shareName("myshare").directoryPath("mydirectory") + .buildClient(); + // END: com.azure.storage.file.directoryClient.instantiation.connectionstring + return directoryClient; + } + + /** + * Generates code sample for creating a {@link DirectoryAsyncClient} with {@code connectionString} which turns into {@link SharedKeyCredential} + * @return An instance of {@link DirectoryAsyncClient} + */ + public DirectoryAsyncClient createAsyncClientWithConnectionString() { + // BEGIN: com.azure.storage.file.directoryAsyncClient.instantiation.connectionstring + String connectionString = "DefaultEndpointsProtocol=https;AccountName={name};AccountKey={key};" + + "EndpointSuffix={core.windows.net}"; + DirectoryAsyncClient directoryAsyncClient = new DirectoryClientBuilder() + .connectionString(connectionString).shareName("myshare").directoryPath("mydirectory") + .buildAsyncClient(); + // END: com.azure.storage.file.directoryAsyncClient.instantiation.connectionstring + return directoryAsyncClient; + } + + /** + * Generates a code sample for using {@link DirectoryClient#create()} + */ + public void createDirectory() { + DirectoryClient directoryClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryClient.createDirectory + Response response = directoryClient.create(); + System.out.println("Complete creating the directory with status code: " + response.statusCode()); + // END: com.azure.storage.file.directoryClient.createDirectory + } + + /** + * Generates a code sample for using {@link DirectoryAsyncClient#create} + */ + public void createDirectoryAsync() { + DirectoryAsyncClient directoryAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryAsyncClient.create + directoryAsyncClient.create().subscribe( + response -> { }, + error -> System.err.print(error.toString()), + () -> System.out.println("Complete creating the directory!") + ); + // END: com.azure.storage.file.directoryAsyncClient.create + } + + /** + * Generates a code sample for using {@link DirectoryClient#create(Map)} + */ + public void createDirectoryWithOverload() { + DirectoryClient directoryClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryClient.create#map + Response response = directoryClient.create(Collections.singletonMap("directory", "metadata")); + System.out.println("Complete creating the directory with status code: " + response.statusCode()); + // END: com.azure.storage.file.directoryClient.create#map + } + + /** + * Generates a code sample for using {@link DirectoryAsyncClient#create(Map)} + */ + public void createDirectoryAsyncWithOverload() { + DirectoryAsyncClient directoryAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryAsyncClient.create#map + directoryAsyncClient.create(Collections.singletonMap("directory", "metadata")).subscribe( + response -> System.out.printf("Creating the directory completed with status code %d", + response.statusCode()), + error -> System.err.print(error.toString()), + () -> System.out.println("Complete creating the directory!") + ); + // END: com.azure.storage.file.directoryAsyncClient.create#map + } + + /** + * Generates a code sample for using {@link DirectoryClient#createSubDirectory(String) + */ + public void createSubDirectory() { + DirectoryClient directoryClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryClient.createSubDirectory#string + Response response = directoryClient.createSubDirectory("subdir"); + System.out.println("Complete creating the subdirectory with status code " + response.statusCode()); + // END: com.azure.storage.file.directoryClient.createSubDirectory#string + } + + /** + * Generates a code sample for using {@link DirectoryAsyncClient#createSubDirectory(String)} + */ + public void createSubDirectoryAsync() { + DirectoryAsyncClient directoryAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryAsyncClient.createSubDirectory#string + directoryAsyncClient.createSubDirectory("subdir").subscribe( + response -> System.out.println("Successfully creating the subdirectory with status code: " + + response.statusCode()), + error -> System.err.println(error.toString()), + () -> System.out.println("Complete creating the sub directory.") + ); + // END: com.azure.storage.file.directoryAsyncClient.createSubDirectory#string + } + + /** + * Generates a code sample for using {@link DirectoryClient#createSubDirectory(String, Map)} + */ + public void createSubDirectoryMaxOverload() { + DirectoryClient directoryClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryClient.createSubDirectory#string-map + Response response = directoryClient.createSubDirectory("subdir", + Collections.singletonMap("directory", "metadata")); + System.out.printf("Creating the sub directory completed with status code %d", response.statusCode()); + // END: com.azure.storage.file.directoryClient.createSubDirectory#string-map + } + + /** + * Generates a code sample for using {@link DirectoryAsyncClient#createSubDirectory(String, Map)} + */ + public void createSubDirectoryAsyncMaxOverload() { + DirectoryAsyncClient directoryAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryAsyncClient.createSubDirectory#string-map + directoryAsyncClient.createSubDirectory("subdir", + Collections.singletonMap("directory", "metadata")) + .subscribe(response -> System.out.printf("Creating the subdirectory completed with status code %d", + response.statusCode())); + // END: com.azure.storage.file.directoryAsyncClient.createSubDirectory#string-map + } + + /** + * Generates a code sample for using {@link DirectoryClient#createFile(String, long)} + */ + public void createFile() { + DirectoryClient directoryClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryClient.createFile#string-long + Response response = directoryClient.createFile("myfile", 1024); + System.out.println("Complete creating the file with status code: " + response.statusCode()); + // END: com.azure.storage.file.directoryClient.createFile#string-long + } + + /** + * Generates a code sample for using {@link DirectoryAsyncClient#createFile(String, long)} + */ + public void createFileAsync() { + DirectoryAsyncClient directoryAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryAsyncClient.createFile#string-long + directoryAsyncClient.createFile("myfile", 1024).subscribe( + response -> { }, + error -> System.err.println(error.toString()), + () -> System.out.println("Complete creating the file.") + ); + // END: com.azure.storage.file.directoryAsyncClient.createFile#string-long + } + + /** + * Generates a code sample for using {@link DirectoryClient#createFile(String, long, FileHTTPHeaders, Map)} + */ + public void createFileMaxOverload() { + DirectoryClient directoryClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryClient.createFile#string-long-fileHTTPHeaders-map + FileHTTPHeaders httpHeaders = new FileHTTPHeaders().fileContentType("text/plain"); + Response response = directoryClient.createFile("myFile", 1024, + httpHeaders, Collections.singletonMap("directory", "metadata")); + System.out.println("Complete creating the file with status code: " + response.statusCode()); + // END: com.azure.storage.file.directoryClient.createFile#string-long-fileHTTPHeaders-map + } + + /** + * Generates a code sample for using {@link DirectoryAsyncClient#createFile(String, long, FileHTTPHeaders, Map)} + */ + public void createFileAsyncMaxOverload() { + DirectoryAsyncClient directoryAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryAsyncClient.createFile#string-long-fileHTTPHeaders-map + FileHTTPHeaders httpHeaders = new FileHTTPHeaders().fileContentType("text/plain"); + directoryAsyncClient.createFile("myFile", 1024, httpHeaders, + Collections.singletonMap("directory", "metadata")).subscribe( + response -> System.out.printf("Creating the file completed with status code %d", response.statusCode()), + error -> System.err.println(error.toString()), + () -> System.out.println("Complete creating the file.") + ); + // END: com.azure.storage.file.directoryAsyncClient.createFile#string-long-fileHTTPHeaders-map + } + + /** + * Generates a code sample for using {@link DirectoryClient#listFilesAndDirectories()} + */ + public void listDirectoriesAndFiles() { + DirectoryClient directoryClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryClient.listFilesAndDirectories + directoryClient.listFilesAndDirectories().forEach( + fileRef -> System.out.printf("Is the resource a directory? %b. The resource name is: %s.", + fileRef.isDirectory(), fileRef.name()) + ); + // END: com.azure.storage.file.directoryClient.listFilesAndDirectories + } + + /** + * Generates a code sample for using {@link DirectoryAsyncClient#listFilesAndDirectories()} + */ + public void listDirectoriesAndFilesAsync() { + DirectoryAsyncClient directoryAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryAsyncClient.listFilesAndDirectories + directoryAsyncClient.listFilesAndDirectories().subscribe( + fileRef -> System.out.printf("Is the resource a directory? %b. The resource name is: %s.", + fileRef.isDirectory(), fileRef.name()), + error -> System.err.println(error.toString()), + () -> System.out.println("Complete listing the directories and files.") + ); + // END: com.azure.storage.file.directoryAsyncClient.listFilesAndDirectories + } + + /** + * Generates a code sample for using {@link DirectoryClient#listFilesAndDirectories(String, Integer)} + */ + public void listDirectoriesAndFilesMaxOverload() { + DirectoryClient directoryClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryClient.listFilesAndDirectories#string-integer + directoryClient.listFilesAndDirectories("subdir", 10).forEach( + fileRef -> System.out.printf("Is the resource a directory? %b. The resource name is: %s.", + fileRef.isDirectory(), fileRef.name()) + ); + // END: com.azure.storage.file.directoryClient.listFilesAndDirectories#string-integer + } + + + /** + * Generates a code sample for using {@link DirectoryAsyncClient#listFilesAndDirectories(String, Integer)} + */ + public void listDirectoriesAndFilesAsyncMaxOverload() { + DirectoryAsyncClient directoryAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryAsyncClient.listFilesAndDirectories#string-integer + directoryAsyncClient.listFilesAndDirectories("subdir", 10).subscribe( + fileRef -> System.out.printf("Is the resource a directory? %b. The resource name is: %s.", + fileRef.isDirectory(), fileRef.name()), + error -> System.err.println(error.toString()), + () -> System.out.println("Complete listing the directories and files.") + ); + // END: com.azure.storage.file.directoryAsyncClient.listFilesAndDirectories#string-integer + } + + /** + * Generates a code sample for using {@link DirectoryClient#deleteFile(String)} ()} + */ + public void deleteFile() { + DirectoryClient directoryClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryClient.deleteFile#string + VoidResponse response = directoryClient.deleteFile("myfile"); + System.out.println("Complete deleting the file with status code: " + response.statusCode()); + // END: com.azure.storage.file.directoryClient.deleteFile#string + } + + /** + * Generates a code sample for using {@link DirectoryAsyncClient#deleteFile(String)} ()} + */ + public void deleteFileAsync() { + DirectoryAsyncClient directoryAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryAsyncClient.deleteFile#string + directoryAsyncClient.deleteFile("myfile").subscribe( + response -> { }, + error -> System.err.println(error.toString()), + () -> System.out.println("Complete deleting the file.") + ); + // END: com.azure.storage.file.directoryAsyncClient.deleteFile#string + } + + /** + * Generates a code sample for using {@link DirectoryClient#deleteSubDirectory(String)} + */ + public void deleteSubDirectory() { + DirectoryClient directoryClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryClient.deleteSubDirectory#string + VoidResponse response = directoryClient.deleteSubDirectory("mysubdirectory"); + System.out.println("Complete deleting the subdirectory with status code: " + response.statusCode()); + // END: com.azure.storage.file.directoryClient.deleteSubDirectory#string + } + + /** + * Generates a code sample for using {@link DirectoryAsyncClient#deleteSubDirectory(String)} ()} + */ + public void deleteSubDirectoryAsync() { + DirectoryAsyncClient directoryAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryAsyncClient.deleteSubDirectory#string + directoryAsyncClient.deleteSubDirectory("mysubdirectory").subscribe( + response -> { }, + error -> System.err.println(error.toString()), + () -> System.out.println("Complete deleting the subdirectory.") + ); + // END: com.azure.storage.file.directoryAsyncClient.deleteSubDirectory#string + } + + /** + * Generates a code sample for using {@link DirectoryClient#delete()} ()} + */ + public void deleteDirectory() { + DirectoryClient directoryClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryClient.delete + VoidResponse response = directoryClient.delete(); + System.out.println("Complete deleting the file with status code: " + response.statusCode()); + // END: com.azure.storage.file.directoryClient.delete + } + + /** + * Generates a code sample for using {@link DirectoryAsyncClient#delete()} + */ + public void deleteDirectoryAsync() { + DirectoryAsyncClient directoryAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryAsyncClient.delete + directoryAsyncClient.delete().subscribe( + response -> { }, + error -> System.err.println(error.toString()), + () -> System.out.println("Complete deleting the file.") + ); + // END: com.azure.storage.file.directoryAsyncClient.delete + } + + /** + * Generates a code sample for using {@link DirectoryClient#getProperties()} + */ + public void getProperties() { + DirectoryClient directoryClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryClient.getProperties + Response response = directoryClient.getProperties(); + System.out.printf("Directory latest modified date is %s.", response.value().lastModified()); + // END: com.azure.storage.file.directoryClient.getProperties + } + + /** + * Generates a code sample for using {@link DirectoryAsyncClient#getProperties()} + */ + public void getPropertiesAsync() { + DirectoryAsyncClient directoryAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryAsyncClient.getProperties + directoryAsyncClient.getProperties() + .subscribe(response -> { + DirectoryProperties properties = response.value(); + System.out.printf("Directory latest modified date is %s.", properties.lastModified()); + }); + // END: com.azure.storage.file.directoryAsyncClient.getProperties + } + + /** + * Generates a code sample for using {@link DirectoryClient#setMetadata(Map)} + */ + public void setMetadata() { + DirectoryClient directoryClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryClient.setMetadata#map + Response response = + directoryClient.setMetadata(Collections.singletonMap("directory", "updatedMetadata")); + System.out.printf("Setting the directory metadata completed with status code %d", response.statusCode()); + // END: com.azure.storage.file.directoryClient.setMetadata#map + } + + /** + * Generates a code sample for using {@link DirectoryAsyncClient#setMetadata(Map)} + */ + public void setMetadataAsync() { + DirectoryAsyncClient directoryAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryAsyncClient.setMetadata#map + directoryAsyncClient.setMetadata(Collections.singletonMap("directory", "updatedMetadata")) + .subscribe(response -> System.out.printf("Setting the directory metadata completed with status code %d", + response.statusCode())); + // END: com.azure.storage.file.directoryAsyncClient.setMetadata#map + } + + /** + * Generates a code sample for using {@link DirectoryClient#setMetadata(Map)} to clear the metadata. + */ + public void clearMetadata() { + DirectoryClient directoryClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryClient.setMetadata#map.clearMetadata + Response response = directoryClient.setMetadata(null); + System.out.printf("Directory latest modified date is %s.", response.statusCode()); + // END: com.azure.storage.file.directoryClient.setMetadata#map.clearMetadata + } + + /** + * Generates a code sample for using {@link DirectoryAsyncClient#setMetadata(Map)} to clear the metadata. + */ + public void clearMetadataAsync() { + DirectoryAsyncClient directoryAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryAsyncClient.setMetadata#map.clearMetadata + directoryAsyncClient.setMetadata(null) + .subscribe(response -> System.out.printf("Clearing the directory metadata completed with status code %d", + response.statusCode())); + // END: com.azure.storage.file.directoryAsyncClient.setMetadata#map.clearMetadata + } + + /** + * Generates a code sample for using {@link DirectoryClient#getHandles(Integer, boolean)} + */ + public void getHandles() { + DirectoryClient directoryClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryClient.getHandles + Iterable result = directoryClient.getHandles(10, true); + System.out.printf("Get handles completed with handle id %s", result.iterator().next().handleId()); + // END: com.azure.storage.file.directoryClient.getHandles + } + + /** + * Generates a code sample for using {@link DirectoryAsyncClient#getHandles(Integer, boolean)} + */ + public void getHandlesAsync() { + DirectoryAsyncClient directoryAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryAsyncClient.getHandles + directoryAsyncClient.getHandles(10, true) + .subscribe(handleItem -> System.out.printf("Get handles completed with handle id %s", + handleItem.handleId())); + // END: com.azure.storage.file.directoryAsyncClient.getHandles + } + + /** + * Generates a code sample for using {@link DirectoryClient#forceCloseHandles(String, boolean)} + */ + public void forceCloseHandles() { + DirectoryClient directoryClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryClient.forceCloseHandles + Iterable result = directoryClient.getHandles(10, true); + result.forEach(handleItem -> { + directoryClient.forceCloseHandles(handleItem.handleId(), true).forEach(numOfClosedHandles -> + System.out.printf("Get handles completed with handle id %s", handleItem.handleId())); + }); + // END: com.azure.storage.file.directoryClient.forceCloseHandles + } + + /** + * Generates a code sample for using {@link DirectoryAsyncClient#forceCloseHandles(String, boolean)} + */ + public void forceCloseHandlesAsync() { + DirectoryAsyncClient directoryAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.directoryAsyncClient.forceCloseHandles + directoryAsyncClient.getHandles(10, true) + .subscribe(handleItem -> { + directoryAsyncClient.forceCloseHandles(handleItem.handleId(), true) + .subscribe(numOfClosedHandles -> + System.out.printf("Close %d handles.", numOfClosedHandles)); + }); + // END: com.azure.storage.file.directoryAsyncClient.forceCloseHandles + } + + /** + * Generates a code sample for using {@link DirectoryClient#getShareSnapshotId()} + */ + public void getShareSnapshotId() { + // BEGIN: com.azure.storage.file.directoryClient.getShareSnapshotId + OffsetDateTime currentTime = OffsetDateTime.of(LocalDateTime.now(), ZoneOffset.UTC); + DirectoryClient directoryClient = new DirectoryClientBuilder() + .endpoint("https://${accountName}.file.core.windows.net") + .credential(SASTokenCredential.fromQuery("${SASToken}")) + .shareName("myshare") + .directoryPath("mydirectory") + .snapshot(currentTime.toString()) + .buildClient(); + directoryClient.getShareSnapshotId(); + // END: com.azure.storage.file.directoryClient.getShareSnapshotId + } + + /** + * Generates a code sample for using {@link DirectoryAsyncClient#getShareSnapshotId()} + */ + public void getShareSnapshotIdAsync() { + // BEGIN: com.azure.storage.file.directoryAsyncClient.getShareSnapshotId + OffsetDateTime currentTime = OffsetDateTime.of(LocalDateTime.now(), ZoneOffset.UTC); + DirectoryAsyncClient directoryAsyncClient = new DirectoryClientBuilder() + .endpoint("https://${accountName}.file.core.windows.net") + .credential(SASTokenCredential.fromQuery("${SASToken}")) + .shareName("myshare") + .directoryPath("mydirectory") + .snapshot(currentTime.toString()) + .buildAsyncClient(); + directoryAsyncClient.getShareSnapshotId(); + // END: com.azure.storage.file.directoryAsyncClient.getShareSnapshotId + } +} diff --git a/storage/client/file/src/samples/java/com/azure/storage/file/FileJavaDocCodeSamples.java b/storage/client/file/src/samples/java/com/azure/storage/file/FileJavaDocCodeSamples.java new file mode 100644 index 0000000000000..146abbe97d04b --- /dev/null +++ b/storage/client/file/src/samples/java/com/azure/storage/file/FileJavaDocCodeSamples.java @@ -0,0 +1,785 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.file; + +import com.azure.core.http.rest.Response; +import com.azure.core.http.rest.VoidResponse; +import com.azure.storage.common.credentials.SASTokenCredential; +import com.azure.storage.common.credentials.SharedKeyCredential; +import com.azure.storage.file.models.FileCopyInfo; +import com.azure.storage.file.models.FileDownloadInfo; +import com.azure.storage.file.models.FileHTTPHeaders; +import com.azure.storage.file.models.FileInfo; +import com.azure.storage.file.models.FileMetadataInfo; +import com.azure.storage.file.models.FileProperties; +import com.azure.storage.file.models.FileRange; +import com.azure.storage.file.models.FileRangeWriteType; +import com.azure.storage.file.models.FileUploadInfo; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.Collections; +import java.util.Map; +import reactor.core.publisher.Flux; + +/** + * Contains code snippets when generating javadocs through doclets for {@link FileClient} and {@link FileAsyncClient}. + */ +public class FileJavaDocCodeSamples { + /** + * Generates code sample for {@link FileClient} instantiation. + */ + public void initialization() { + // BEGIN: com.azure.storage.file.fileClient.instantiation + FileClient client = new FileClientBuilder() + .connectionString("${connectionString}") + .endpoint("${endpoint}") + .buildClient(); + // END: com.azure.storage.file.fileClient.instantiation + } + + /** + * Generates code sample for {@link FileAsyncClient} instantiation. + */ + public void asyncInitialization() { + // BEGIN: com.azure.storage.file.fileAsyncClient.instantiation + FileAsyncClient client = new FileClientBuilder() + .connectionString("${connectionString}") + .endpoint("${endpoint}") + .buildAsyncClient(); + // END: com.azure.storage.file.fileAsyncClient.instantiation + } + + /** + * Generates code sample for creating a {@link FileClient} with {@link SASTokenCredential} + * @return An instance of {@link FileClient} + */ + public FileClient createClientWithSASToken() { + + // BEGIN: com.azure.storage.file.fileClient.instantiation.sastoken + FileClient fileClient = new FileClientBuilder() + .endpoint("https://${accountName}.file.core.windows.net?${SASToken}") + .shareName("myshare") + .filePath("myfilepath") + .buildClient(); + // END: com.azure.storage.file.fileClient.instantiation.sastoken + return fileClient; + } + + /** + * Generates code sample for creating a {@link FileAsyncClient} with {@link SASTokenCredential} + * @return An instance of {@link FileAsyncClient} + */ + public FileAsyncClient createAsyncClientWithSASToken() { + // BEGIN: com.azure.storage.file.fileAsyncClient.instantiation.sastoken + FileAsyncClient fileAsyncClient = new FileClientBuilder() + .endpoint("https://{accountName}.file.core.windows.net?{SASToken}") + .shareName("myshare") + .filePath("myfilepath") + .buildAsyncClient(); + // END: com.azure.storage.file.fileAsyncClient.instantiation.sastoken + return fileAsyncClient; + } + + /** + * Generates code sample for creating a {@link FileClient} with {@link SASTokenCredential} + * @return An instance of {@link FileClient} + */ + public FileClient createClientWithCredential() { + + // BEGIN: com.azure.storage.file.fileClient.instantiation.credential + FileClient fileClient = new FileClientBuilder() + .endpoint("https://${accountName}.file.core.windows.net") + .credential(SASTokenCredential.fromQuery("${SASTokenQueryParams}")) + .shareName("myshare") + .filePath("myfilepath") + .buildClient(); + // END: com.azure.storage.file.fileClient.instantiation.credential + return fileClient; + } + + /** + * Generates code sample for creating a {@link FileAsyncClient} with {@link SASTokenCredential} + * @return An instance of {@link FileAsyncClient} + */ + public FileAsyncClient createAsyncClientWithCredential() { + // BEGIN: com.azure.storage.file.fileAsyncClient.instantiation.credential + FileAsyncClient fileAsyncClient = new FileClientBuilder() + .endpoint("https://{accountName}.file.core.windows.net") + .credential(SASTokenCredential.fromQuery("${SASTokenQueryParams}")) + .shareName("myshare") + .filePath("myfilepath") + .buildAsyncClient(); + // END: com.azure.storage.file.fileAsyncClient.instantiation.credential + return fileAsyncClient; + } + + /** + * Generates code sample for creating a {@link FileClient} with {@code connectionString} which turns into {@link SharedKeyCredential} + * @return An instance of {@link FileClient} + */ + public FileClient createClientWithConnectionString() { + // BEGIN: com.azure.storage.file.fileClient.instantiation.connectionstring + String connectionString = "DefaultEndpointsProtocol=https;AccountName={name};AccountKey={key};" + + "EndpointSuffix={core.windows.net}"; + FileClient fileClient = new FileClientBuilder() + .connectionString(connectionString).shareName("myshare").filePath("myfilepath") + .buildClient(); + // END: com.azure.storage.file.fileClient.instantiation.connectionstring + return fileClient; + } + + /** + * Generates code sample for creating a {@link FileAsyncClient} with {@code connectionString} which turns into {@link SharedKeyCredential} + * @return An instance of {@link FileAsyncClient} + */ + public FileAsyncClient createAsyncClientWithConnectionString() { + // BEGIN: com.azure.storage.file.fileAsyncClient.instantiation.connectionstring + String connectionString = "DefaultEndpointsProtocol=https;AccountName={name};AccountKey={key};" + + "EndpointSuffix={core.windows.net}"; + FileAsyncClient fileAsyncClient = new FileClientBuilder() + .connectionString(connectionString).shareName("myshare").filePath("myfilepath") + .buildAsyncClient(); + // END: com.azure.storage.file.fileAsyncClient.instantiation.connectionstring + return fileAsyncClient; + } + + /** + * Generates a code sample for using {@link FileClient#create(long)} + */ + public void createFile() { + FileClient fileClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileClient.create + Response response = fileClient.create(1024); + System.out.println("Complete creating the file with status code: " + response.statusCode()); + // END: com.azure.storage.file.fileClient.create + } + + /** + * Generates a code sample for using {@link FileAsyncClient#create(long)} + */ + public void createFileAsync() { + FileAsyncClient fileAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileAsyncClient.create + fileAsyncClient.create(1024).subscribe( + response -> { }, + error -> System.err.print(error.toString()), + () -> System.out.println("Complete creating the file!") + ); + // END: com.azure.storage.file.fileAsyncClient.create + } + + /** + * Generates a code sample for using {@link FileClient#create(long, FileHTTPHeaders, Map)} + */ + public void createFileMaxOverload() { + FileClient fileClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileClient.create#long-filehttpheaders-map + FileHTTPHeaders httpHeaders = new FileHTTPHeaders().fileContentType("text/plain"); + Response response = fileClient.create(1024, httpHeaders, + Collections.singletonMap("file", "updatedMetadata")); + System.out.printf("Creating the file completed with status code %d", response.statusCode()); + // END: com.azure.storage.file.fileClient.create#long-filehttpheaders-map + } + + /** + * Generates a code sample for using {@link FileAsyncClient#create(long, FileHTTPHeaders, Map)} + */ + public void createFileAsyncMaxOverload() { + FileAsyncClient fileAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileAsyncClient.create#long-filehttpheaders-map + FileHTTPHeaders httpHeaders = new FileHTTPHeaders().fileContentType("text/plain"); + fileAsyncClient.create(1024, httpHeaders, Collections.singletonMap("file", "updatedMetadata")) + .subscribe(response -> System.out.printf("Creating the file completed with status code %d", + response.statusCode())); + // END: com.azure.storage.file.fileAsyncClient.create#long-filehttpheaders-map + } + + /** + * Generates a code sample for using {@link FileClient#startCopy(String, Map)} + */ + public void copyFile() { + FileClient fileClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileClient.startCopy#string-map + Response response = fileClient.startCopy( + "https://{accountName}.file.core.windows.net?{SASToken}", + Collections.singletonMap("file", "metadata")); + System.out.println("Complete copying the file with status code: " + response.statusCode()); + // END: com.azure.storage.file.fileClient.startCopy#string-map + } + + /** + * Generates a code sample for using {@link FileClient#startCopy(String, Map)} + */ + public void copyFileAsync() { + FileAsyncClient fileAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileAsyncClient.startCopy#string-map + fileAsyncClient.startCopy("https://{accountName}.file.core.windows.net?{SASToken}", + Collections.singletonMap("file", "metadata")).subscribe( + response -> System.out.println("Successfully copying the file with status code: " + response.statusCode()), + error -> System.err.println(error.toString()), + () -> System.out.println("Complete copying the file.") + ); + // END: com.azure.storage.file.fileAsyncClient.startCopy#string-map + } + + /** + * Generates a code sample for using {@link FileClient#abortCopy(String)} + */ + public void abortCopyFile() { + FileClient fileClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileClient.abortCopy#string + VoidResponse response = fileClient.abortCopy("someCopyId"); + System.out.printf("Abort copying the file completed with status code %d", response.statusCode()); + // END: com.azure.storage.file.fileClient.abortCopy#string + } + + /** + * Generates a code sample for using {@link FileClient#abortCopy(String)} + */ + public void abortCopyFileAsync() { + FileAsyncClient fileAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileAsyncClient.abortCopy#string + fileAsyncClient.abortCopy("someCopyId") + .subscribe(response -> System.out.printf("Abort copying the file completed with status code %d", + response.statusCode())); + // END: com.azure.storage.file.fileAsyncClient.abortCopy#string + } + + /** + * Generates a code sample for using {@link FileClient#upload(ByteBuf, long)} + */ + public void uploadData() { + FileClient fileClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileClient.upload#flux-long + ByteBuf defaultData = Unpooled.wrappedBuffer("default".getBytes(StandardCharsets.UTF_8)); + Response response = fileClient.upload(defaultData, defaultData.readableBytes()); + System.out.println("Complete uploading the data with status code: " + response.statusCode()); + // END: com.azure.storage.file.fileClient.upload#flux-long + } + + /** + * Generates a code sample for using {@link FileAsyncClient#upload(Flux, long)} + */ + public void uploadDataAsync() { + FileAsyncClient fileAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileAsyncClient.upload#flux-long + ByteBuf defaultData = Unpooled.wrappedBuffer("default".getBytes(StandardCharsets.UTF_8)); + fileAsyncClient.upload(Flux.just(defaultData), defaultData.readableBytes()).subscribe( + response -> { }, + error -> System.err.print(error.toString()), + () -> System.out.println("Complete deleting the file!") + ); + // END: com.azure.storage.file.fileAsyncClient.upload#flux-long + } + + /** + * Generates a code sample for using {@link FileClient#upload(ByteBuf, long, int, FileRangeWriteType)} + */ + public void uploadDataMaxOverload() { + FileClient fileClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileClient.upload#bytebuf-long-int-filerangewritetype + ByteBuf defaultData = Unpooled.wrappedBuffer("default".getBytes(StandardCharsets.UTF_8)); + Response response = fileClient.upload(defaultData, defaultData.readableBytes(), 1024, + FileRangeWriteType.UPDATE); + System.out.println("Complete uploading the data with status code: " + response.statusCode()); + // END: com.azure.storage.file.fileClient.upload#bytebuf-long-int-filerangewritetype + } + + /** + * Generates a code sample for using {@link FileAsyncClient#upload(Flux, long)} + */ + public void uploadDataAsyncMaxOverload() { + FileAsyncClient fileAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileAsyncClient.upload#bytebuf-long-int-filerangewritetype + ByteBuf defaultData = Unpooled.wrappedBuffer("default".getBytes(StandardCharsets.UTF_8)); + fileAsyncClient.upload(Flux.just(defaultData), defaultData.readableBytes(), 1024, + FileRangeWriteType.UPDATE).subscribe( + response -> { }, + error -> System.err.print(error.toString()), + () -> System.out.println("Complete deleting the file!") + ); + // END: com.azure.storage.file.fileAsyncClient.upload#bytebuf-long-int-filerangewritetype + } + + /** + * Generates a code sample for using {@link FileClient#uploadFromFile(String)} + */ + public void uploadFile() { + FileClient fileClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileClient.uploadFromFile#string + fileClient.uploadFromFile("someFilePath"); + // END: com.azure.storage.file.fileClient.uploadFromFile#string + } + + /** + * Generates a code sample for using {@link FileAsyncClient#uploadFromFile(String)} + */ + public void uploadFileAsync() { + FileAsyncClient fileAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileAsyncClient.uploadFromFile#string + fileAsyncClient.uploadFromFile("someFilePath").subscribe( + response -> { }, + error -> System.err.print(error.toString()), + () -> System.out.println("Complete deleting the file!") + ); + // END: com.azure.storage.file.fileAsyncClient.uploadFromFile#string + } + + /** + * Generates a code sample for using {@link FileClient#uploadFromFile(String, FileRangeWriteType)} + */ + public void uploadFileMaxOverload() { + FileClient fileClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileClient.uploadFromFile#string-filerangewritetype + fileClient.uploadFromFile("someFilePath", FileRangeWriteType.UPDATE); + if (fileClient.getProperties() != null) { + System.out.printf("Upload the file with length of %d completed", + fileClient.getProperties().value().contentLength()); + } + // END: com.azure.storage.file.fileClient.uploadFromFile#string-filerangewritetype + } + + /** + * Generates a code sample for using {@link FileAsyncClient#uploadFromFile(String, FileRangeWriteType)} + */ + public void uploadFileAsyncMaxOverload() { + FileAsyncClient fileAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileAsyncClient.uploadFromFile#string-filerangewritetype + fileAsyncClient.uploadFromFile("someFilePath", FileRangeWriteType.UPDATE) + .subscribe(response -> { + if (fileAsyncClient.getProperties() != null) { + System.out.printf("Upload the file with length of %d completed", + fileAsyncClient.getProperties().block().value().contentLength()); + } + }); + // END: com.azure.storage.file.fileAsyncClient.uploadFromFile#string-filerangewritetype + } + + /** + * Generates a code sample for using {@link FileClient#downloadWithProperties()} + */ + public void downloadData() { + FileClient fileClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileClient.downloadWithProperties + Response response = fileClient.downloadWithProperties(); + System.out.println("Complete downloading the data with status code: " + response.statusCode()); + response.value().body().subscribe( + byteBuf -> System.out.println("Complete downloading the data with body: " + + byteBuf.toString(StandardCharsets.UTF_8)), + error -> System.err.print(error.toString()), + () -> System.out.println("Complete downloading the data!") + ); + // END: com.azure.storage.file.fileClient.downloadWithProperties + } + + /** + * Generates a code sample for using {@link FileAsyncClient#downloadWithProperties()} + */ + public void downloadDataAsync() { + FileAsyncClient fileAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileAsyncClient.downloadWithProperties + fileAsyncClient.downloadWithProperties().subscribe( + response -> { }, + error -> System.err.print(error.toString()), + () -> System.out.println("Complete downloading the data!") + ); + // END: com.azure.storage.file.fileAsyncClient.downloadWithProperties + } + + /** + * Generates a code sample for using {@link FileClient#downloadWithProperties(FileRange, Boolean)} + */ + public void downloadDataMaxOverload() { + FileClient fileClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileClient.downloadWithProperties#filerange-boolean + Response response = fileClient.downloadWithProperties(new FileRange(1024, 2047L), + false); + System.out.println("Complete downloading the data with status code: " + response.statusCode()); + response.value().body().subscribe( + byteBuf -> System.out.println("Complete downloading the data with body: " + + byteBuf.toString(StandardCharsets.UTF_8)), + error -> System.err.print(error.toString()), + () -> System.out.println("Complete downloading the data!") + ); + // END: com.azure.storage.file.fileClient.downloadWithProperties#filerange-boolean + } + + /** + * Generates a code sample for using {@link FileAsyncClient#downloadWithProperties(FileRange, Boolean)} + */ + public void downloadDataAsyncMaxOverload() { + FileAsyncClient fileAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileAsyncClient.downloadWithProperties#filerange-boolean + fileAsyncClient.downloadWithProperties(new FileRange(1024, 2047L), false).subscribe( + response -> { }, + error -> System.err.print(error.toString()), + () -> System.out.println("Complete downloading the data!") + ); + // END: com.azure.storage.file.fileAsyncClient.downloadWithProperties#filerange-boolean + } + + /** + * Generates a code sample for using {@link FileClient#downloadToFile(String)} + */ + public void downloadFile() { + FileClient fileClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileClient.downloadToFile#string + fileClient.downloadToFile("somelocalfilepath"); + if (Files.exists(Paths.get("somelocalfilepath"))) { + System.out.println("Complete downloading the file."); + } + // END: com.azure.storage.file.fileClient.downloadToFile#string + } + + /** + * Generates a code sample for using {@link FileAsyncClient#downloadToFile(String)} + */ + public void downloadFileAsync() { + FileAsyncClient fileAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileAsyncClient.downloadToFile#string + fileAsyncClient.downloadToFile("somelocalfilepath").subscribe( + response -> { + if (Files.exists(Paths.get("somelocalfilepath"))) { + System.out.println("Successfully downloaded the file."); + } + }, + error -> System.err.print(error.toString()), + () -> System.out.println("Complete downloading the file!") + ); + // END: com.azure.storage.file.fileAsyncClient.downloadToFile#string + } + + /** + * Generates a code sample for using {@link FileClient#downloadToFile(String, FileRange)} + */ + public void downloadFileMaxOverload() { + FileClient fileClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileClient.downloadToFile#string-filerange + fileClient.downloadToFile("somelocalfilepath", new FileRange(1024, 2047L)); + if (Files.exists(Paths.get("somelocalfilepath"))) { + System.out.println("Complete downloading the file."); + } + // END: com.azure.storage.file.fileClient.downloadToFile#string-filerange + } + + /** + * Generates a code sample for using {@link FileAsyncClient#downloadToFile(String, FileRange)} + */ + public void downloadFileAsyncMaxOverload() { + FileAsyncClient fileAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileAsyncClient.downloadToFile#string-filerange + fileAsyncClient.downloadToFile("somelocalfilepath", new FileRange(1024, 2047L)).subscribe( + response -> { + if (Files.exists(Paths.get("somelocalfilepath"))) { + System.out.println("Successfully downloaded the file."); + } + }, + error -> System.err.print(error.toString()), + () -> System.out.println("Complete downloading the file!") + ); + // END: com.azure.storage.file.fileAsyncClient.downloadToFile#string-filerange + } + + /** + * Generates a code sample for using {@link FileClient#delete()} + */ + public void deleteFile() { + FileClient fileClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileClient.delete + VoidResponse response = fileClient.delete(); + System.out.println("Complete deleting the file with status code: " + response.statusCode()); + // END: com.azure.storage.file.fileClient.delete + } + + /** + * Generates a code sample for using {@link FileAsyncClient#delete()} + */ + public void deleteFileAsync() { + FileAsyncClient fileAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileAsyncClient.delete + fileAsyncClient.delete().subscribe( + response -> { }, + error -> System.err.print(error.toString()), + () -> System.out.println("Complete deleting the file!") + ); + // END: com.azure.storage.file.fileAsyncClient.delete + } + + /** + * Generates a code sample for using {@link FileClient#getProperties()} + */ + public void getProperties() { + FileClient fileClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileClient.getProperties + Response response = fileClient.getProperties(); + FileProperties properties = response.value(); + System.out.printf("File latest modified date is %s.", properties.lastModified()); + // END: com.azure.storage.file.fileClient.getProperties + } + + /** + * Generates a code sample for using {@link FileAsyncClient#getProperties()} + */ + public void getPropertiesAsync() { + FileAsyncClient fileAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileAsyncClient.getProperties + fileAsyncClient.getProperties() + .subscribe(response -> { + FileProperties properties = response.value(); + System.out.printf("File latest modified date is %s.", properties.lastModified()); + }); + // END: com.azure.storage.file.fileAsyncClient.getProperties + } + + /** + * Generates a code sample for using {@link FileClient#setMetadata(Map)} + */ + public void setMetadata() { + FileClient fileClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileClient.setMetadata#map + Response response = fileClient.setMetadata( + Collections.singletonMap("file", "updatedMetadata")); + System.out.printf("Setting the file metadata completed with status code %d", response.statusCode()); + // END: com.azure.storage.file.fileClient.setMetadata#map + } + + /** + * Generates a code sample for using {@link FileAsyncClient#setMetadata(Map)} + */ + public void setMetadataAsync() { + FileAsyncClient fileAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileAsyncClient.setMetadata#map + fileAsyncClient.setMetadata(Collections.singletonMap("file", "updatedMetadata")) + .subscribe(response -> System.out.printf("Setting the file metadata completed with status code %d", + response.statusCode())); + // END: com.azure.storage.file.fileAsyncClient.setMetadata#map + } + + /** + * Generates a code sample for using {@link FileClient#setMetadata(Map)} to clear metadata. + */ + public void clearMetadata() { + FileClient fileClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileClient.setMetadata#map.clearMetadata + Response response = fileClient.setMetadata(null); + System.out.printf("Setting the file metadata completed with status code %d", response.statusCode()); + // END: com.azure.storage.file.fileClient.setMetadata#map.clearMetadata + } + + /** + * Generates a code sample for using {@link FileAsyncClient#setMetadata(Map)} to clear metadata. + */ + public void clearMetadataAsync() { + FileAsyncClient fileAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileAsyncClient.setMetadata#map.clearMetadata + fileAsyncClient.setMetadata(null) + .subscribe(response -> System.out.printf("Setting the file metadata completed with status code %d", + response.statusCode())); + // END: com.azure.storage.file.fileAsyncClient.setMetadata#map.clearMetadata + } + + /** + * Generates a code sample for using {@link FileClient#setHttpHeaders(long, FileHTTPHeaders)} + */ + public void setHTTPHeaders() { + FileClient fileClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileClient.setHttpHeaders#long-filehttpheaders + FileHTTPHeaders httpHeaders = new FileHTTPHeaders().fileContentType("text/plain"); + Response response = fileClient.setHttpHeaders(1024, httpHeaders); + System.out.printf("Setting the file httpHeaders completed with status code %d", response.statusCode()); + // END: com.azure.storage.file.fileClient.setHttpHeaders#long-filehttpheaders + } + + /** + * Generates a code sample for using {@link FileAsyncClient#setHttpHeaders(long, FileHTTPHeaders)} + */ + public void setHTTPHeadersAsync() { + FileAsyncClient fileAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileAsyncClient.setHttpHeaders#long-filehttpheaders + FileHTTPHeaders httpHeaders = new FileHTTPHeaders().fileContentType("text/plain"); + fileAsyncClient.setHttpHeaders(1024, httpHeaders) + .subscribe(response -> System.out.printf("Setting the file httpHeaders completed with status code %d", + response.statusCode())); + // END: com.azure.storage.file.fileAsyncClient.setHttpHeaders#long-filehttpheaders + } + + /** + * Generates a code sample for using {@link FileClient#setHttpHeaders(long, FileHTTPHeaders)} to clear httpHeaders. + */ + public void clearHTTPHeaders() { + FileClient fileClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileClient.setHttpHeaders#long-filehttpheaders.clearHttpHeaders + Response response = fileClient.setHttpHeaders(1024, null); + System.out.printf("Setting the file httpHeaders completed with status code %d", response.statusCode()); + // END: com.azure.storage.file.fileClient.setHttpHeaders#long-filehttpheaders.clearHttpHeaders + } + + /** + * Generates a code sample for using {@link FileAsyncClient#setHttpHeaders(long, FileHTTPHeaders)} to clear httpHeaders. + */ + public void clearHTTPHeadersAsync() { + FileAsyncClient fileAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileAsyncClient.setHttpHeaders#long-filehttpheaders.clearHttpHeaders + fileAsyncClient.setHttpHeaders(1024, null) + .subscribe(response -> System.out.printf("Setting the file httpHeaders completed with status code %d", + response.statusCode())); + // END: com.azure.storage.file.fileAsyncClient.setHttpHeaders#long-filehttpheaders.clearHttpHeaders + } + + /** + * Generates a code sample for using {@link FileClient#listRanges()} + */ + public void listRanges() { + FileClient fileClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileClient.listRanges + Iterable ranges = fileClient.listRanges(); + ranges.forEach(range -> + System.out.printf("List ranges completed with start: %d, end: %d", range.start(), range.end())); + // END: com.azure.storage.file.fileClient.listRanges + } + + /** + * Generates a code sample for using {@link FileAsyncClient#listRanges()} + */ + public void listRangesAsync() { + FileAsyncClient fileAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileAsyncClient.listRanges + fileAsyncClient.listRanges().subscribe(range -> + System.out.printf("List ranges completed with start: %d, end: %d", range.start(), range.end())); + // END: com.azure.storage.file.fileAsyncClient.listRanges + } + /** + * Generates a code sample for using {@link FileClient#listRanges(FileRange)} + */ + public void listRangesMaxOverload() { + FileClient fileClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileClient.listRanges#filerange + Iterable ranges = fileClient.listRanges(new FileRange(1024, 2048L)); + ranges.forEach(range -> + System.out.printf("List ranges completed with start: %d, end: %d", range.start(), range.end())); + // END: com.azure.storage.file.fileClient.listRanges#filerange + } + + /** + * Generates a code sample for using {@link FileAsyncClient#listRanges(FileRange)} + */ + public void listRangesAsyncMaxOverload() { + FileAsyncClient fileAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileAsyncClient.listRanges#filerange + fileAsyncClient.listRanges(new FileRange(1024, 2048L)) + .subscribe(result -> System.out.printf("List ranges completed with start: %d, end: %d", + result.start(), result.end())); + // END: com.azure.storage.file.fileAsyncClient.listRanges#filerange + } + /** + * Generates a code sample for using {@link FileClient#listHandles()} + */ + public void listHandles() { + FileClient fileClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileClient.listHandles + fileClient.listHandles() + .forEach(handleItem -> System.out.printf("List handles completed with handleId %d", + handleItem.handleId())); + // END: com.azure.storage.file.fileClient.listHandles + } + + /** + * Generates a code sample for using {@link FileAsyncClient#listHandles()} + */ + public void listHandlesAsync() { + FileAsyncClient fileAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileAsyncClient.listHandles + fileAsyncClient.listHandles() + .subscribe(result -> System.out.printf("List handles completed with handle id %s", result.handleId())); + // END: com.azure.storage.file.fileAsyncClient.listHandles + } + /** + * Generates a code sample for using {@link FileClient#listHandles(Integer)} + */ + public void listHandlesWithOverload() { + FileClient fileClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileClient.listHandles#integer + fileClient.listHandles(10) + .forEach(handleItem -> System.out.printf("List handles completed with handleId %d", + handleItem.handleId())); + // END: com.azure.storage.file.fileClient.listHandles#integer + } + + /** + * Generates a code sample for using {@link FileAsyncClient#listHandles(Integer)} + */ + public void listHandlesAsyncMaxOverload() { + FileAsyncClient fileAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileAsyncClient.listHandles#integer + fileAsyncClient.listHandles(10) + .subscribe(result -> System.out.printf("List handles completed with handle id %s", result.handleId())); + // END: com.azure.storage.file.fileAsyncClient.listHandles#integer + } + /** + * Generates a code sample for using {@link FileClient#forceCloseHandles(String)} + */ + public void forceCloseHandles() { + FileClient fileClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileClient.forceCloseHandles#string + fileClient.listHandles(10) + .forEach(result -> + fileClient.forceCloseHandles(result.handleId()).forEach(numOfClosedHandles -> + System.out.printf("Close %d handles.", numOfClosedHandles) + )); + // END: com.azure.storage.file.fileClient.forceCloseHandles#string + } + + /** + * Generates a code sample for using {@link FileAsyncClient#forceCloseHandles(String)} + */ + public void forceCloseHandlesAsync() { + FileAsyncClient fileAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileAsyncClient.forceCloseHandles#string + fileAsyncClient.listHandles(10) + .subscribe(result -> { + fileAsyncClient.forceCloseHandles(result.handleId()).subscribe(numOfClosedHandles -> + System.out.printf("Close %d handles.", numOfClosedHandles)); + }); + // END: com.azure.storage.file.fileAsyncClient.forceCloseHandles#string + } + + /** + * Generates a code sample for using {@link FileClient#getShareSnapshotId()} + */ + public void getShareSnapshotId() { + // BEGIN: com.azure.storage.file.fileClient.getShareSnapshotId + OffsetDateTime currentTime = OffsetDateTime.of(LocalDateTime.now(), ZoneOffset.UTC); + FileClient fileClient = new FileClientBuilder() + .endpoint("https://${accountName}.file.core.windows.net") + .credential(SASTokenCredential.fromQuery("${SASToken}")) + .shareName("myshare") + .filePath("myfile") + .snapshot(currentTime.toString()) + .buildClient(); + fileClient.getShareSnapshotId(); + // END: com.azure.storage.file.fileClient.getShareSnapshotId + } + + /** + * Generates a code sample for using {@link FileAsyncClient#getShareSnapshotId()} + */ + public void getShareSnapshotIdAsync() { + // BEGIN: com.azure.storage.file.fileAsyncClient.getShareSnapshotId + OffsetDateTime currentTime = OffsetDateTime.of(LocalDateTime.now(), ZoneOffset.UTC); + FileAsyncClient fileAsyncClient = new FileClientBuilder() + .endpoint("https://${accountName}.file.core.windows.net") + .credential(SASTokenCredential.fromQuery("${SASToken}")) + .shareName("myshare") + .filePath("myfiile") + .snapshot(currentTime.toString()) + .buildAsyncClient(); + fileAsyncClient.getShareSnapshotId(); + // END: com.azure.storage.file.fileAsyncClient.getShareSnapshotId + } +} diff --git a/storage/client/file/src/samples/java/com/azure/storage/file/FileServiceJavaDocCodeSamples.java b/storage/client/file/src/samples/java/com/azure/storage/file/FileServiceJavaDocCodeSamples.java new file mode 100644 index 0000000000000..95e6971804713 --- /dev/null +++ b/storage/client/file/src/samples/java/com/azure/storage/file/FileServiceJavaDocCodeSamples.java @@ -0,0 +1,423 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.file; + +import com.azure.core.http.rest.Response; +import com.azure.core.http.rest.VoidResponse; +import com.azure.storage.common.credentials.SASTokenCredential; +import com.azure.storage.common.credentials.SharedKeyCredential; +import com.azure.storage.file.models.FileServiceProperties; +import com.azure.storage.file.models.ListSharesOptions; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.Collections; +import java.util.Map; + +/** + * Contains code snippets when generating javadocs through doclets for {@link FileServiceClient} and {@link FileServiceAsyncClient}. + */ +public class FileServiceJavaDocCodeSamples { + /** + * Generates code sample for {@link FileServiceClient} instantiation. + */ + public void initialization() { + // BEGIN: com.azure.storage.file.fileServiceClient.instantiation + FileServiceClient client = new FileServiceClientBuilder() + .connectionString("${connectionString}") + .endpoint("${endpoint}") + .buildClient(); + // END: com.azure.storage.file.fileServiceClient.instantiation + } + + /** + * Generates code sample for {@link FileServiceAsyncClient} instantiation. + */ + public void asyncInitialization() { + // BEGIN: com.azure.storage.file.fileServiceAsyncClient.instantiation + ShareAsyncClient client = new ShareClientBuilder() + .connectionString("${connectionString}") + .endpoint("${endpoint}") + .buildAsyncClient(); + // END: com.azure.storage.file.fileServiceAsyncClient.instantiation + } + /** + * Generates code sample for creating a {@link FileServiceClient} with {@link SASTokenCredential} + * @return An instance of {@link FileServiceClient} + */ + public FileServiceClient createClientWithSASToken() { + // BEGIN: com.azure.storage.file.fileServiceClient.instantiation.sastoken + FileServiceClient fileServiceClient = new FileServiceClientBuilder() + .endpoint("https://${accountName}.file.core.windows.net?${SASToken}") + .buildClient(); + // END: com.azure.storage.file.fileServiceClient.instantiation.sastoken + return fileServiceClient; + } + + /** + * Generates code sample for creating a {@link FileServiceAsyncClient} with {@link SASTokenCredential} + * @return An instance of {@link FileServiceAsyncClient} + */ + public FileServiceAsyncClient createAsyncClientWithSASToken() { + // BEGIN: com.azure.storage.file.fileServiceAsyncClient.instantiation.sastoken + FileServiceAsyncClient fileServiceAsyncClient = new FileServiceClientBuilder() + .endpoint("https://{accountName}.file.core.windows.net?{SASToken}") + .buildAsyncClient(); + // END: com.azure.storage.file.fileServiceAsyncClient.instantiation.sastoken + return fileServiceAsyncClient; + } + + /** + * Generates code sample for creating a {@link FileServiceClient} with {@link SASTokenCredential} + * {@code SASTokenQueryParams} is composed of the Key + * @return An instance of {@link FileServiceClient} + */ + public FileServiceClient createClientWithCredential() { + // BEGIN: com.azure.storage.file.fileServiceClient.instantiation.credential + FileServiceClient fileServiceClient = new FileServiceClientBuilder() + .endpoint("https://{accountName}.file.core.windows.net") + .credential(SASTokenCredential.fromQuery("${SASTokenQueryParams}")) + .buildClient(); + // END: com.azure.storage.file.fileServiceClient.instantiation.credential + return fileServiceClient; + } + + /** + * Generates code sample for creating a {@link FileServiceAsyncClient} with {@link SASTokenCredential} + * @return An instance of {@link FileServiceAsyncClient} + */ + public FileServiceAsyncClient createAsyncClientWithCredential() { + // BEGIN: com.azure.storage.file.fileServiceAsyncClient.instantiation.credential + FileServiceAsyncClient fileServiceAsyncClient = new FileServiceClientBuilder() + .endpoint("https://{accountName}.file.core.windows.net") + .credential(SASTokenCredential.fromQuery("${SASTokenQueryParams}")) + .buildAsyncClient(); + // END: com.azure.storage.file.fileServiceAsyncClient.instantiation.credential + return fileServiceAsyncClient; + } + + /** + * Generates code sample for creating a {@link FileServiceClient} with {@code connectionString} which turns into {@link SharedKeyCredential} + * @return An instance of {@link FileServiceClient} + */ + public FileServiceClient createClientWithConnectionString() { + // BEGIN: com.azure.storage.file.fileServiceClient.instantiation.connectionstring + String connectionString = "DefaultEndpointsProtocol=https;AccountName={name};AccountKey={key};" + + "EndpointSuffix={core.windows.net}"; + FileServiceClient fileServiceClient = new FileServiceClientBuilder() + .connectionString(connectionString) + .buildClient(); + // END: com.azure.storage.file.fileServiceClient.instantiation.connectionstring + return fileServiceClient; + } + + /** + * Generates code sample for creating a {@link FileServiceAsyncClient} with {@code connectionString} which turns into {@link SharedKeyCredential} + * @return An instance of {@link FileServiceAsyncClient} + */ + public FileServiceAsyncClient createAsyncClientWithConnectionString() { + // BEGIN: com.azure.storage.file.fileServiceAsyncClient.instantiation.connectionstring + String connectionString = "DefaultEndpointsProtocol=https;AccountName={name};AccountKey={key};" + + "EndpointSuffix={core.windows.net}"; + FileServiceAsyncClient fileServiceAsyncClient = new FileServiceClientBuilder() + .connectionString(connectionString) + .buildAsyncClient(); + // END: com.azure.storage.file.fileServiceAsyncClient.instantiation.connectionstring + return fileServiceAsyncClient; + } + + /** + * Generates a code sample for using {@link FileServiceClient#createShare(String)} + */ + public void createShare() { + FileServiceClient fileServiceClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileServiceClient.createShare#string + Response response = fileServiceClient.createShare("myshare"); + System.out.printf("Creating the share completed with status code %d", response.statusCode()); + // END: com.azure.storage.file.fileServiceClient.createShare#string + } + + /** + * Generates a code sample for using {@link FileServiceAsyncClient#createShare(String)} + */ + public void createShareAsync() { + FileServiceAsyncClient fileServiceAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileServiceAsyncClient.createShare#string + fileServiceAsyncClient.createShare("myshare").subscribe( + response -> { }, + error -> System.err.print(error.toString()), + () -> System.out.println("Complete creating the share!") + ); + // END: com.azure.storage.file.fileServiceAsyncClient.createShare#string + } + + /** + * Generates a code sample for using {@link FileServiceClient#createShare(String, Map, Integer)} with metadata + */ + public void createShareWithMetadata() { + FileServiceClient fileServiceClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileServiceClient.createShare#string-map-integer.metadata + Response response = fileServiceClient.createShare("test", + Collections.singletonMap("share", "metadata"), null); + System.out.printf("Creating the share completed with status code %d", response.statusCode()); + // END: com.azure.storage.file.fileServiceClient.createShare#string-map-integer.metadata + } + + /** + * Generates a code sample for using {@link FileServiceAsyncClient#createShare(String, Map, Integer)} with metadata + */ + public void createShareAsyncWithMetadata() { + FileServiceAsyncClient fileServiceAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileServiceAsyncClient.createShare#string-map-integer.metadata + fileServiceAsyncClient.createShare("test", Collections.singletonMap("share", "metadata"), null) + .subscribe( + response -> System.out.printf("Creating the share completed with status code %d", response.statusCode()), + error -> System.err.print(error.toString()), + () -> System.out.println("Complete creating the share!") + ); + // END: com.azure.storage.file.fileServiceAsyncClient.createShare#string-map-integer.metadata + } + + /** + * Generates a code sample for using {@link FileServiceClient#createShare(String, Map, Integer)} with quota. + */ + public void createShareWithQuota() { + FileServiceClient fileServiceClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileServiceClient.createShare#string-map-integer.quota + Response response = fileServiceClient.createShare("test", null, 10); + System.out.printf("Creating the share completed with status code %d", response.statusCode()); + // END: com.azure.storage.file.fileServiceClient.createShare#string-map-integer.quota + } + + /** + * Generates a code sample for using {@link FileServiceAsyncClient#createShare(String, Map, Integer)} with quota. + */ + public void createShareAsyncWithQuota() { + FileServiceAsyncClient fileServiceAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileServiceAsyncClient.createShare#string-map-integer.quota + fileServiceAsyncClient.createShare("test", null, 10) + .subscribe( + response -> System.out.printf("Creating the share completed with status code %d", + response.statusCode()), + error -> System.err.print(error.toString()), + () -> System.out.println("Complete creating the share!") + ); + // END: com.azure.storage.file.fileServiceAsyncClient.createShare#string-map-integer.quota + } + + /** + * Generates a code sample for using {@link FileServiceClient#listShares()} + */ + public void listShares() { + FileServiceClient fileServiceClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileServiceClient.listShares + fileServiceClient.listShares().forEach( + shareItem -> System.out.printf("Share %s exists in the account", shareItem.name()) + ); + // END: com.azure.storage.file.fileServiceClient.listShares + } + + /** + * Generates a code sample for using {@link FileServiceAsyncClient#listShares()} + */ + public void listSharesAsync() { + FileServiceAsyncClient fileServiceAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileServiceAsyncClient.listShares + fileServiceAsyncClient.listShares().subscribe( + shareItem -> System.out.printf("Share %s exists in the account", shareItem.name()), + error -> System.err.print(error.toString()), + () -> System.out.println("Complete listing the shares!") + ); + // END: com.azure.storage.file.fileServiceAsyncClient.listShares + } + + /** + * Generates a code sample for using {@link FileServiceClient#listShares(ListSharesOptions)} of prefix. + */ + public void listSharesWithPrefix() { + FileServiceClient fileServiceClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileServiceClient.listShares#ListSharesOptions.prefix + fileServiceClient.listShares(new ListSharesOptions().prefix("azure")).forEach( + shareItem -> System.out.printf("Share %s exists in the account", shareItem.name()) + ); + // END: com.azure.storage.file.fileServiceClient.listShares#ListSharesOptions.prefix + } + + /** + * Generates a code sample for using {@link FileServiceAsyncClient#listShares(ListSharesOptions)} of prefix. + */ + public void listSharesAsyncWithPrefix() { + FileServiceAsyncClient fileServiceAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileServiceAsyncClient.listShares#ListSharesOptions.prefix + fileServiceAsyncClient.listShares(new ListSharesOptions().prefix("azure")).subscribe( + shareItem -> System.out.printf("Share %s exists in the account", shareItem.name()), + error -> System.err.print(error.toString()), + () -> System.out.println("Complete listing the shares!") + ); + // END: com.azure.storage.file.fileServiceAsyncClient.listShares#ListSharesOptions.prefix + } + + /** + * Generates a code sample for using {@link FileServiceClient#listShares(ListSharesOptions)} of metadata and snapshot. + */ + public void listSharesWithMetadataAndSnapshot() { + FileServiceClient fileServiceClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileServiceClient.listShares#ListSharesOptions.metadata.snapshot + fileServiceClient.listShares(new ListSharesOptions().includeMetadata(true) + .includeSnapshots(true)).forEach( + shareItem -> System.out.printf("Share %s exists in the account", shareItem.name()) + ); + // END: com.azure.storage.file.fileServiceClient.listShares#ListSharesOptions.metadata.snapshot + } + + /** + * Generates a code sample for using {@link FileServiceAsyncClient#listShares(ListSharesOptions)} of metadata and snapshot. + */ + public void listSharesAsyncWithOverload() { + FileServiceAsyncClient fileServiceAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileServiceAsyncClient.listShares#ListSharesOptions.metadata.snapshot + fileServiceAsyncClient.listShares(new ListSharesOptions().includeMetadata(true) + .includeSnapshots(true)).subscribe( + shareItem -> System.out.printf("Share %s exists in the account", shareItem.name()), + error -> System.err.print(error.toString()), + () -> System.out.println("Complete listing the shares!") + ); + // END: com.azure.storage.file.fileServiceAsyncClient.listShares#ListSharesOptions.metadata.snapshot + } + + /** + * Generates a code sample for using {@link FileServiceClient#deleteShare(String)} + */ + public void deleteShare() { + FileServiceClient fileServiceClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileServiceClient.deleteShare#string + fileServiceClient.deleteShare("myshare"); + // END: com.azure.storage.file.fileServiceClient.deleteShare#string + } + + /** + * Generates a code sample for using {@link FileServiceAsyncClient#deleteShare(String)} + */ + public void deleteShareAsync() { + FileServiceAsyncClient fileServiceAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileServiceAsyncClient.deleteShare#string + fileServiceAsyncClient.deleteShare("test").subscribe( + response -> System.out.println("Deleting the share completed with status code: " + response.statusCode()) + ); + // END: com.azure.storage.file.fileServiceAsyncClient.deleteShare#string + } + + /** + * Generates a code sample for using {@link FileServiceClient#deleteShare(String, String)} + */ + public void deleteShareMaxOverload() { + FileServiceClient fileServiceClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileServiceClient.deleteShare#string-string + OffsetDateTime midnight = OffsetDateTime.of(LocalDateTime.now(), ZoneOffset.UTC); + VoidResponse response = fileServiceClient.deleteShare("test", midnight.toString()); + System.out.printf("Deleting the snapshot completed with status code %d", response.statusCode()); + // END: com.azure.storage.file.fileServiceClient.deleteShare#string-string + } + + /** + * Generates a code sample for using {@link FileServiceAsyncClient#deleteShare(String, String)} + */ + public void deleteShareAsyncMaxOverload() { + FileServiceAsyncClient fileServiceAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileServiceAsyncClient.deleteShare#string-string + OffsetDateTime midnight = OffsetDateTime.of(LocalDateTime.now(), ZoneOffset.UTC); + fileServiceAsyncClient.deleteShare("test", midnight.toString()) + .subscribe(response -> System.out.printf("Deleting the snapshot completed with status code %d", + response.statusCode())); + // END: com.azure.storage.file.fileServiceAsyncClient.deleteShare#string-string + } + + /** + * Generates a code sample for using {@link FileServiceClient#getProperties()} + */ + public void getProperties() { + FileServiceClient fileServiceClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileServiceClient.getProperties + FileServiceProperties properties = fileServiceClient.getProperties().value(); + System.out.printf("Hour metrics enabled: %b, Minute metrics enabled: %b", properties.hourMetrics().enabled(), + properties.minuteMetrics().enabled()); + // END: com.azure.storage.file.fileServiceClient.getProperties + } + + /** + * Generates a code sample for using {@link FileServiceAsyncClient#getProperties()} + */ + public void getPropertiesAsync() { + FileServiceAsyncClient fileServiceAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileServiceAsyncClient.getProperties + fileServiceAsyncClient.getProperties() + .subscribe(response -> { + FileServiceProperties properties = response.value(); + System.out.printf("Hour metrics enabled: %b, Minute metrics enabled: %b", + properties.hourMetrics().enabled(), properties.minuteMetrics().enabled()); + }); + // END: com.azure.storage.file.fileServiceAsyncClient.getProperties + } + + /** + * Generates a code sample for using {@link FileServiceClient#setProperties(FileServiceProperties)} + */ + public void setProperties() { + FileServiceClient fileServiceClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileServiceClient.setProperties#fileServiceProperties + FileServiceProperties properties = fileServiceClient.getProperties().value(); + + properties.minuteMetrics().enabled(true); + properties.hourMetrics().enabled(true); + + VoidResponse response = fileServiceClient.setProperties(properties); + System.out.printf("Setting File service properties completed with status code %d", response.statusCode()); + // END: com.azure.storage.file.fileServiceClient.setProperties#fileServiceProperties + } + + /** + * Generates a code sample for using {@link FileServiceAsyncClient#setProperties(FileServiceProperties)} + */ + public void setPropertiesAsync() { + FileServiceAsyncClient fileServiceAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileServiceAsyncClient.setProperties#fileServiceProperties + FileServiceProperties properties = fileServiceAsyncClient.getProperties().block().value(); + + properties.minuteMetrics().enabled(true); + properties.hourMetrics().enabled(true); + + fileServiceAsyncClient.setProperties(properties) + .subscribe(response -> System.out.printf("Setting File service properties completed with status code %d", + response.statusCode())); + // END: com.azure.storage.file.fileServiceAsyncClient.setProperties#fileServiceProperties + } + + /** + * Generates a code sample for using {@link FileServiceClient#setProperties(FileServiceProperties)} to clear CORS in file service. + */ + public void clearProperties() { + FileServiceClient fileServiceClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileServiceClient.setProperties#fileServiceProperties.clearCORS + FileServiceProperties properties = fileServiceClient.getProperties().value(); + properties.cors(Collections.emptyList()); + + VoidResponse response = fileServiceClient.setProperties(properties); + System.out.printf("Setting File service properties completed with status code %d", response.statusCode()); + // END: com.azure.storage.file.fileServiceClient.setProperties#fileServiceProperties.clearCORS + } + + /** + * Generates a code sample for using {@link FileServiceAsyncClient#setProperties(FileServiceProperties)} to clear CORS in file service. + */ + public void clearPropertiesAsync() { + FileServiceAsyncClient fileServiceAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.fileServiceAsyncClient.setProperties#fileServiceProperties.clearCORS + FileServiceProperties properties = fileServiceAsyncClient.getProperties().block().value(); + properties.cors(Collections.emptyList()); + + fileServiceAsyncClient.setProperties(properties) + .subscribe(response -> System.out.printf("Setting File service properties completed with status code %d", + response.statusCode())); + // END: com.azure.storage.file.fileServiceAsyncClient.setProperties#fileServiceProperties.clearCORS + } +} diff --git a/storage/client/file/src/samples/java/com/azure/storage/file/ShareJavaDocCodeSamples.java b/storage/client/file/src/samples/java/com/azure/storage/file/ShareJavaDocCodeSamples.java new file mode 100644 index 0000000000000..01730abaf94a7 --- /dev/null +++ b/storage/client/file/src/samples/java/com/azure/storage/file/ShareJavaDocCodeSamples.java @@ -0,0 +1,580 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.file; + +import com.azure.core.http.rest.Response; +import com.azure.core.http.rest.VoidResponse; +import com.azure.storage.common.credentials.SASTokenCredential; +import com.azure.storage.common.credentials.SharedKeyCredential; +import com.azure.storage.file.models.AccessPolicy; +import com.azure.storage.file.models.ShareInfo; +import com.azure.storage.file.models.ShareProperties; +import com.azure.storage.file.models.ShareSnapshotInfo; +import com.azure.storage.file.models.ShareStatistics; +import com.azure.storage.file.models.SignedIdentifier; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Contains code snippets when generating javadocs through doclets for {@link ShareClient} and {@link ShareAsyncClient}. + */ +public class ShareJavaDocCodeSamples { + /** + * Generates code sample for {@link ShareClient} instantiation. + */ + public void initialization() { + // BEGIN: com.azure.storage.file.shareClient.instantiation + ShareClient client = new ShareClientBuilder() + .connectionString("${connectionString}") + .endpoint("${endpoint}") + .buildClient(); + // END: com.azure.storage.file.shareClient.instantiation + } + + /** + * Generates code sample for {@link ShareAsyncClient} instantiation. + */ + public void asyncInitialization() { + // BEGIN: com.azure.storage.file.shareAsyncClient.instantiation + ShareAsyncClient client = new ShareClientBuilder() + .connectionString("${connectionString}") + .endpoint("${endpoint}") + .buildAsyncClient(); + // END: com.azure.storage.file.shareAsyncClient.instantiation + } + + /** + * Generates code sample for creating a {@link ShareClient} with {@link SASTokenCredential} + * @return An instance of {@link ShareClient} + */ + public ShareClient createClientWithSASToken() { + + // BEGIN: com.azure.storage.file.shareClient.instantiation.sastoken + ShareClient shareClient = new ShareClientBuilder() + .endpoint("https://${accountName}.file.core.windows.net?${SASToken}") + .shareName("myshare") + .buildClient(); + // END: com.azure.storage.file.shareClient.instantiation.sastoken + return shareClient; + } + + /** + * Generates code sample for creating a {@link ShareAsyncClient} with {@link SASTokenCredential} + * @return An instance of {@link ShareAsyncClient} + */ + public ShareAsyncClient createAsyncClientWithSASToken() { + // BEGIN: com.azure.storage.file.shareAsyncClient.instantiation.sastoken + ShareAsyncClient shareAsyncClient = new ShareClientBuilder() + .endpoint("https://{accountName}.file.core.windows.net?{SASToken}") + .shareName("myshare") + .buildAsyncClient(); + // END: com.azure.storage.file.shareAsyncClient.instantiation.sastoken + return shareAsyncClient; + } + + /** + * Generates code sample for creating a {@link ShareClient} with {@link SASTokenCredential} + * @return An instance of {@link ShareClient} + */ + public ShareClient createClientWithCredential() { + + // BEGIN: com.azure.storage.file.shareClient.instantiation.credential + ShareClient shareClient = new ShareClientBuilder() + .endpoint("https://${accountName}.file.core.windows.net") + .credential(SASTokenCredential.fromQuery("${SASTokenQueryParams}")) + .shareName("myshare") + .buildClient(); + // END: com.azure.storage.file.shareClient.instantiation.credential + return shareClient; + } + + /** + * Generates code sample for creating a {@link ShareAsyncClient} with {@link SASTokenCredential} + * @return An instance of {@link ShareAsyncClient} + */ + public ShareAsyncClient createAsyncClientWithCredential() { + // BEGIN: com.azure.storage.file.shareAsyncClient.instantiation.credential + ShareAsyncClient shareAsyncClient = new ShareClientBuilder() + .endpoint("https://{accountName}.file.core.windows.net") + .credential(SASTokenCredential.fromQuery("${SASTokenQueryParams}")) + .shareName("myshare") + .buildAsyncClient(); + // END: com.azure.storage.file.shareAsyncClient.instantiation.credential + return shareAsyncClient; + } + + /** + * Generates code sample for creating a {@link ShareClient} with {@code connectionString} which turns into {@link SharedKeyCredential} + * @return An instance of {@link ShareClient} + */ + public ShareClient createClientWithConnectionString() { + // BEGIN: com.azure.storage.file.shareClient.instantiation.connectionstring + String connectionString = "DefaultEndpointsProtocol=https;AccountName={name};AccountKey={key};" + + "EndpointSuffix={core.windows.net}"; + ShareClient shareClient = new ShareClientBuilder() + .connectionString(connectionString).shareName("myshare") + .buildClient(); + // END: com.azure.storage.file.shareClient.instantiation.connectionstring + return shareClient; + } + + /** + * Generates code sample for creating a {@link ShareAsyncClient} with {@code connectionString} which turns into {@link SharedKeyCredential} + * @return An instance of {@link ShareAsyncClient} + */ + public ShareAsyncClient createAsyncClientWithConnectionString() { + // BEGIN: com.azure.storage.file.shareAsyncClient.instantiation.connectionstring + String connectionString = "DefaultEndpointsProtocol=https;AccountName={name};AccountKey={key};" + + "EndpointSuffix={core.windows.net}"; + ShareAsyncClient shareAsyncClient = new ShareClientBuilder() + .connectionString(connectionString).shareName("myshare") + .buildAsyncClient(); + // END: com.azure.storage.file.shareAsyncClient.instantiation.connectionstring + return shareAsyncClient; + } + + /** + * Generates a code sample for using {@link ShareClient#create()} + */ + public void createShare() { + ShareClient shareClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareClient.create + Response response = shareClient.create(); + System.out.println("Complete creating the shares with status code: " + response.statusCode()); + // END: com.azure.storage.file.shareClient.create + } + + /** + * Generates a code sample for using {@link ShareAsyncClient#create} + */ + public void createShareAsync() { + ShareAsyncClient shareAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareAsyncClient.create + shareAsyncClient.create().subscribe( + response -> { }, + error -> System.err.print(error.toString()), + () -> System.out.println("Complete creating the share!") + ); + // END: com.azure.storage.file.shareAsyncClient.create + } + + /** + * Generates a code sample for using {@link ShareClient#create(Map, Integer)} with Metadata. + */ + public void createShareMaxOverloadMetadata() { + ShareClient shareClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareClient.create#map-integer.metadata + Response response = shareClient.create(Collections.singletonMap("share", "metadata"), null); + System.out.println("Complete creating the shares with status code: " + response.statusCode()); + // END: com.azure.storage.file.shareClient.create#map-integer.metadata + } + + /** + * Generates a code sample for using {@link ShareAsyncClient#create(Map, Integer)} with Metadata. + */ + public void createShareAsyncMaxOverloadMetadata() { + ShareAsyncClient shareAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareAsyncClient.create#map-integer.metadata + shareAsyncClient.create(Collections.singletonMap("share", "metadata"), null).subscribe( + response -> System.out.printf("Creating the share completed with status code %d", response.statusCode()), + error -> System.err.print(error.toString()), + () -> System.out.println("Complete creating the share!") + ); + // END: com.azure.storage.file.shareAsyncClient.create#map-integer.metadata + } + + /** + * Generates a code sample for using {@link ShareClient#create(Map, Integer)} with Quota. + */ + public void createShareMaxOverloadQuota() { + ShareClient shareClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareClient.create#map-integer.quota + Response response = shareClient.create(null, 10); + System.out.println("Complete creating the shares with status code: " + response.statusCode()); + // END: com.azure.storage.file.shareClient.create#map-integer.quota + } + + /** + * Generates a code sample for using {@link ShareAsyncClient#create(Map, Integer)} with Quota. + */ + public void createShareAsyncMaxOverloadQuota() { + ShareAsyncClient shareAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareAsyncClient.create#map-integer.quota + shareAsyncClient.create(null, 10).subscribe( + response -> System.out.printf("Creating the share completed with status code %d", response.statusCode()), + error -> System.err.print(error.toString()), + () -> System.out.println("Complete creating the share!") + ); + // END: com.azure.storage.file.shareAsyncClient.create#map-integer.quota + } + /** + * Generates a code sample for using {@link ShareClient#createDirectory(String)} ()} + */ + public void createDirectory() { + ShareClient shareClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareClient.createDirectory#string + Response response = shareClient.createDirectory("mydirectory"); + System.out.println("Complete creating the directory with status code: " + response.statusCode()); + // END: com.azure.storage.file.shareClient.createDirectory#string + } + + /** + * Generates a code sample for using {@link ShareAsyncClient#createDirectory(String)} + */ + public void createDirectoryAsync() { + ShareAsyncClient shareAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareAsyncClient.createDirectory#string + shareAsyncClient.createDirectory("mydirectory").subscribe( + response -> { }, + error -> System.err.print(error.toString()), + () -> System.out.println("Complete creating the directory!") + ); + // END: com.azure.storage.file.shareAsyncClient.createDirectory#string + } + + /** + * Generates a code sample for using {@link ShareClient#createSnapshot()()} + */ + public void createSnapshot() { + ShareClient shareClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareClient.createSnapshot + Response response = shareClient.createSnapshot(); + System.out.println("Complete creating the share snpashot with snapshot id: " + response.value().snapshot()); + // END: com.azure.storage.file.shareClient.createSnapshot + } + + /** + * Generates a code sample for using {@link ShareAsyncClient#createSnapshot()} + */ + public void createSnapshotAsync() { + ShareAsyncClient shareAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareAsyncClient.createSnapshot + shareAsyncClient.createSnapshot().subscribe( + response -> System.out.println("Successfully creating the share snapshot with snapshot id: " + + response.value().snapshot()), + error -> System.err.println(error.toString()), + () -> System.out.println("Complete creating the share snapshot.") + ); + // END: com.azure.storage.file.shareAsyncClient.createSnapshot + } + + /** + * Generates a code sample for using {@link ShareClient#createSnapshot(Map)} + */ + public void createSnapshotWithMetadata() { + ShareClient shareClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareClient.createSnapshot#map + Response response = + shareClient.createSnapshot(Collections.singletonMap("snpashot", "metadata")); + System.out.println("Complete creating the share snpashot with snapshot id: " + response.value().snapshot()); + // END: com.azure.storage.file.shareClient.createSnapshot#map + } + + /** + * Generates a code sample for using {@link ShareAsyncClient#createSnapshot(Map)} + */ + public void createSnapshotAsyncWithMetadata() { + ShareAsyncClient shareAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareAsyncClient.createSnapshot#map + shareAsyncClient.createSnapshot(Collections.singletonMap("snapshot", "metadata")).subscribe( + response -> System.out.println("Successfully creating the share snapshot with snapshot id: " + + response.value().snapshot()), + error -> System.err.println(error.toString()), + () -> System.out.println("Complete creating the share snapshot.") + ); + // END: com.azure.storage.file.shareAsyncClient.createSnapshot#map + } + + /** + * Generates a code sample for using {@link ShareClient#createDirectory(String, Map)} + */ + public void createDirectoryMaxOverload() { + ShareClient shareClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareClient.createDirectory#string-map + Response response = shareClient.createDirectory("documents", + Collections.singletonMap("directory", "metadata")); + System.out.printf("Creating the directory completed with status code %d", response.statusCode()); + // END: com.azure.storage.file.shareClient.createDirectory#string-map + } + + /** + * Generates a code sample for using {@link ShareAsyncClient#createDirectory(String, Map)} + */ + public void createDirectoryAsyncMaxOverload() { + ShareAsyncClient shareAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareAsyncClient.createDirectory#string-map + shareAsyncClient.createDirectory("documents", Collections.singletonMap("directory", "metadata")) + .subscribe(response -> System.out.printf("Creating the directory completed with status code %d", + response.statusCode())); + // END: com.azure.storage.file.shareAsyncClient.createDirectory#string-map + } + + /** + * Generates a code sample for using {@link ShareClient#deleteDirectory(String)()} + */ + public void deleteDirectory() { + ShareClient shareClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareClient.deleteDirectory#string + VoidResponse response = shareClient.deleteDirectory("mydirectory"); + System.out.println("Complete deleting the directory with status code: " + response.statusCode()); + // END: com.azure.storage.file.shareClient.deleteDirectory#string + } + + /** + * Generates a code sample for using {@link ShareAsyncClient#deleteDirectory(String)()} + */ + public void deleteDirectoryAsync() { + ShareAsyncClient shareAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareAsyncClient.deleteDirectory#string + shareAsyncClient.deleteDirectory("mydirectory").subscribe( + response -> { }, + error -> System.err.println(error.toString()), + () -> System.out.println("Complete deleting the directory.") + ); + // END: com.azure.storage.file.shareAsyncClient.deleteDirectory#string + } + + /** + * Generates a code sample for using {@link ShareClient#delete} + */ + public void deleteShare() { + ShareClient shareClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareClient.delete + VoidResponse response = shareClient.delete(); + System.out.println("Complete deleting the share with status code: " + response.statusCode()); + // END: com.azure.storage.file.shareClient.delete + } + + + /** + * Generates a code sample for using {@link ShareAsyncClient#delete} + */ + public void deleteShareAsync() { + ShareAsyncClient shareAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareAsyncClient.delete + shareAsyncClient.delete().subscribe( + response -> System.out.println("Deleting the shareAsyncClient completed with status code: " + + response.statusCode()), + error -> System.err.println(error.toString()), + () -> System.out.println("Complete deleting the share.") + ); + // END: com.azure.storage.file.shareAsyncClient.delete + } + + /** + * Generates a code sample for using {@link ShareClient#getProperties()} + */ + public void getProperties() { + ShareClient shareClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareClient.getProperties + ShareProperties properties = shareClient.getProperties().value(); + System.out.printf("Share quota: %d, Metadata: %s", properties.quota(), properties.metadata()); + // END: com.azure.storage.file.shareClient.getProperties + } + + + /** + * Generates a code sample for using {@link ShareAsyncClient#getProperties()} + */ + public void getPropertiesAsync() { + ShareAsyncClient shareAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareAsyncClient.getProperties + shareAsyncClient.getProperties() + .subscribe(response -> { + ShareProperties properties = response.value(); + System.out.printf("Share quota: %d, Metadata: %s", properties.quota(), properties.metadata()); + }); + // END: com.azure.storage.file.shareAsyncClient.getProperties + } + + /** + * Generates a code sample for using {@link ShareClient#setQuota(int)} + */ + public void setQuota() { + ShareClient shareClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareClient.setQuota + Response response = shareClient.setQuota(1024); + System.out.printf("Setting the share quota completed with status code %d", response.statusCode()); + // END: com.azure.storage.file.shareClient.setQuota + } + + + /** + * Generates a code sample for using {@link ShareAsyncClient#setQuota(int)} + */ + public void setQuotaAsync() { + ShareAsyncClient shareAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareAsyncClient.setQuota + shareAsyncClient.setQuota(1024) + .subscribe(response -> + System.out.printf("Setting the share quota completed with status code %d", response.statusCode()) + ); + // END: com.azure.storage.file.shareAsyncClient.setQuota + } + + /** + * Generates a code sample for using {@link ShareClient#setMetadata(Map)} + */ + public void setMetadata() { + ShareClient shareClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareClient.setMetadata#map + Response response = shareClient.setMetadata(Collections.singletonMap("share", "updatedMetadata")); + System.out.printf("Setting the share metadata completed with status code %d", response.statusCode()); + // END: com.azure.storage.file.shareClient.setMetadata#map + } + + + /** + * Generates a code sample for using {@link ShareAsyncClient#setMetadata(Map)} + */ + public void setMetadataAsync() { + ShareAsyncClient shareAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareAsyncClient.setMetadata#map + shareAsyncClient.setMetadata(Collections.singletonMap("share", "updatedMetadata")) + .subscribe(response -> + System.out.printf("Setting the share metadata completed with status code %d", response.statusCode()) + ); + // END: com.azure.storage.file.shareAsyncClient.setMetadata#map + } + + /** + * Generates a code sample for using {@link ShareClient#setMetadata(Map)} to clear the metadata. + */ + public void clearMetadata() { + ShareClient shareClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareClient.clearMetadata#map + Response response = shareClient.setMetadata(null); + System.out.printf("Setting the share metadata completed with status code %d", response.statusCode()); + // END: com.azure.storage.file.shareClient.clearMetadata#map + } + + + /** + * Generates a code sample for using {@link ShareAsyncClient#setMetadata(Map)} to clear the metadata. + */ + public void clearMetadataAsync() { + ShareAsyncClient shareAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareAsyncClient.clearMetadata#map + shareAsyncClient.setMetadata(null) + .subscribe(response -> + System.out.printf("Setting the share metadata completed with status code %d", response.statusCode()) + ); + // END: com.azure.storage.file.shareAsyncClient.clearMetadata#map + } + + /** + * Generates a code sample for using {@link ShareClient#getAccessPolicy()} + */ + public void getAccessPolicy() { + ShareClient shareClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareClient.getAccessPolicy + for (SignedIdentifier result : shareClient.getAccessPolicy()) { + System.out.printf("Access policy %s allows these permissions: %s", result.id(), result.accessPolicy().permission()); + } + // END: com.azure.storage.file.shareClient.getAccessPolicy + } + + /** + * Generates a code sample for using {@link ShareAsyncClient#getAccessPolicy()} + */ + public void getAccessPolicyAsync() { + ShareAsyncClient shareAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareAsyncClient.getAccessPolicy + shareAsyncClient.getAccessPolicy() + .subscribe(result -> System.out.printf("Access policy %s allows these permissions: %s", result.id(), + result.accessPolicy().permission()) + ); + // END: com.azure.storage.file.shareAsyncClient.getAccessPolicy + } + + /** + * Generates a code sample for using {@link ShareClient#setAccessPolicy(List)} + */ + public void setAccessPolicy() { + ShareClient shareClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareClient.setAccessPolicy + AccessPolicy accessPolicy = new AccessPolicy().permission("r") + .start(OffsetDateTime.now(ZoneOffset.UTC)) + .expiry(OffsetDateTime.now(ZoneOffset.UTC).plusDays(10)); + + SignedIdentifier permission = new SignedIdentifier().id("mypolicy").accessPolicy(accessPolicy); + + Response response = shareClient.setAccessPolicy(Collections.singletonList(permission)); + System.out.printf("Setting access policies completed with status code %d", response.statusCode()); + // END: com.azure.storage.file.shareClient.setAccessPolicy + } + + /** + * Generates a code sample for using {@link ShareAsyncClient#setAccessPolicy(List)} + */ + public void setAccessPolicyAsync() { + ShareAsyncClient shareAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareAsyncClient.setAccessPolicy + AccessPolicy accessPolicy = new AccessPolicy().permission("r") + .start(OffsetDateTime.now(ZoneOffset.UTC)) + .expiry(OffsetDateTime.now(ZoneOffset.UTC).plusDays(10)); + + SignedIdentifier permission = new SignedIdentifier().id("mypolicy").accessPolicy(accessPolicy); + shareAsyncClient.setAccessPolicy(Collections.singletonList(permission)) + .subscribe(response -> System.out.printf("Setting access policies completed with status code %d", + response.statusCode())); + // END: com.azure.storage.file.shareAsyncClient.setAccessPolicy + } + + /** + * Generates a code sample for using {@link ShareClient#getStatistics()} + */ + public void getStatistics() { + ShareClient shareClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareClient.getStatistics + Response response = shareClient.getStatistics(); + System.out.printf("The share is using %d GB", response.value().getShareUsageInGB()); + // END: com.azure.storage.file.shareClient.getStatistics + } + + /** + * Generates a code sample for using {@link ShareAsyncClient#getStatistics()} + */ + public void getStatisticsAsync() { + ShareAsyncClient shareAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.file.shareAsyncClient.getStatistics + shareAsyncClient.getStatistics() + .subscribe(response -> System.out.printf("The share is using %d GB", + response.value().getShareUsageInGB())); + // END: com.azure.storage.file.shareAsyncClient.getStatistics + } + + /** + * Generates a code sample for using {@link ShareClient#getSnapshotId()} + */ + public void getSnapshotId() { + // BEGIN: com.azure.storage.file.shareClient.getSnapshotId + OffsetDateTime currentTime = OffsetDateTime.of(LocalDateTime.now(), ZoneOffset.UTC); + ShareClient shareClient = new ShareClientBuilder().endpoint("https://${accountName}.file.core.windows.net") + .credential(SASTokenCredential.fromQuery("${SASToken}")) + .shareName("myshare") + .snapshot(currentTime.toString()) + .buildClient(); + shareClient.getSnapshotId(); + // END: com.azure.storage.file.shareClient.getSnapshotId + } + + /** + * Generates a code sample for using {@link ShareAsyncClient#getSnapshotId()} + */ + public void getSnapshotIdAsync() { + // BEGIN: com.azure.storage.file.shareAsyncClient.getSnapshotId + OffsetDateTime currentTime = OffsetDateTime.of(LocalDateTime.now(), ZoneOffset.UTC); + ShareAsyncClient shareAysncClient = new ShareClientBuilder().endpoint("https://${accountName}.file.core.windows.net") + .credential(SASTokenCredential.fromQuery("${SASToken}")) + .shareName("myshare") + .snapshot(currentTime.toString()) + .buildAsyncClient(); + shareAysncClient.getSnapshotId(); + // END: com.azure.storage.file.shareAsyncClient.getSnapshotId + } +} diff --git a/storage/client/file/src/test/java/com/azure/storage/file/DirectoryAsyncClientTests.java b/storage/client/file/src/test/java/com/azure/storage/file/DirectoryAsyncClientTests.java new file mode 100644 index 0000000000000..7fc208fd3f222 --- /dev/null +++ b/storage/client/file/src/test/java/com/azure/storage/file/DirectoryAsyncClientTests.java @@ -0,0 +1,321 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.file; + +import com.azure.core.http.HttpClient; +import com.azure.core.http.policy.HttpLogDetailLevel; +import com.azure.core.implementation.http.UrlBuilder; +import com.azure.core.test.TestMode; +import com.azure.core.util.configuration.ConfigurationManager; +import com.azure.core.util.logging.ClientLogger; +import com.azure.storage.file.models.FileProperty; +import com.azure.storage.file.models.FileRef; +import com.azure.storage.file.models.HandleItem; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import reactor.test.StepVerifier; + +import static com.azure.storage.file.FileTestHelpers.setupClient; + +public class DirectoryAsyncClientTests extends DirectoryClientTestBase { + private final ClientLogger directoryAsyncLogger = new ClientLogger(DirectoryAsyncClientTests.class); + private static ShareClient shareClient; + private static String shareName = "dirsharename"; + private DirectoryAsyncClient client; + + @Override + public void beforeTest() { + beforeDirectoryTest(); + if (interceptorManager.isPlaybackMode()) { + client = setupClient((connectionString, endpoint) -> new DirectoryClientBuilder() + .connectionString(connectionString) + .endpoint(endpoint) + .shareName(shareName) + .directoryPath(dirName) + .httpClient(interceptorManager.getPlaybackClient()) + .httpLogDetailLevel(HttpLogDetailLevel.BODY_AND_HEADERS) + .buildAsyncClient(), true, directoryAsyncLogger); + } else { + client = setupClient((connectionString, endpoint) -> new DirectoryClientBuilder() + .connectionString(connectionString) + .endpoint(endpoint) + .shareName(shareName) + .directoryPath(dirName) + .httpClient(HttpClient.createDefault().wiretap(true)) + .httpLogDetailLevel(HttpLogDetailLevel.BODY_AND_HEADERS) + .addPolicy(interceptorManager.getRecordPolicy()) + .buildAsyncClient(), false, directoryAsyncLogger); + } + } + + @BeforeClass + public static void beforeClass() { + if (FileTestHelpers.getTestMode() == TestMode.PLAYBACK) { + return; + } + FileServiceClient fileServiceClient = new FileServiceClientBuilder() + .connectionString(ConfigurationManager.getConfiguration().get("AZURE_STORAGE_CONNECTION_STRING")) + .buildClient(); + shareClient = fileServiceClient.getShareClient(shareName); + shareClient.create(); + } + + @AfterClass + public static void tearDown() { + if (FileTestHelpers.getTestMode() == TestMode.PLAYBACK) { + return; + } + shareClient.delete(); + FileTestHelpers.sleepInRecordMode(Duration.ofSeconds(45)); + } + + @Override + public void urlFromDirClient() { + if (interceptorManager.isPlaybackMode()) { + azureStorageFileEndpoint = "https://teststorage.file.core.windows.net/"; + } + UrlBuilder urlBuilder = UrlBuilder.parse(azureStorageFileEndpoint); + String endpointURL = new UrlBuilder().scheme(urlBuilder.scheme()).host(urlBuilder.host()).toString(); + Assert.assertTrue(endpointURL.equals(client.getDirectoryUrl().toString())); + } + + @Override + public void createMinFromDirClient() { + StepVerifier.create(client.create()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + } + + @Override + public void createTwiceFromDirClient() { + client.create().block(); + StepVerifier.create(client.create()) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 409)); + } + + @Override + public void createWithMetadataFromDirClient() { + StepVerifier.create(client.create(basicMetadata)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + } + + @Override + public void getFileClientFromDirClient() { + FileAsyncClient fileClient = client.getFileClient(testResourceNamer.randomName("getFileAsync", 16)); + Assert.assertNotNull(fileClient); + } + + @Override + public void getSubDirectoryClient() { + Assert.assertNotNull(client.getSubDirectoryClient("getSubdirectoryASync")); + } + + @Override + public void deleteFromDirClient() { + StepVerifier.create(client.create()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + StepVerifier.create(client.delete()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 202)) + .verifyComplete(); + } + + @Override + public void deleteNotExistFromDirClient() { + StepVerifier.create(client.delete()) + .verifyErrorSatisfies(exception -> FileTestHelpers.assertExceptionErrorMessage(exception, "ResourceNotFound")); + + } + + @Override + public void getPropertiesFromDirClient() { + StepVerifier.create(client.create()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + StepVerifier.create(client.getProperties()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 200)) + .verifyComplete(); + } + + @Override + public void clearMetadataFromDirClient() { + StepVerifier.create(client.create()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + StepVerifier.create(client.setMetadata(null)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 200)) + .verifyComplete(); + } + + @Override + public void setMetadataFromDirClient() { + client.create().block(); + StepVerifier.create(client.setMetadata(basicMetadata)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 200)) + .verifyComplete(); + } + + @Override + public void setMetadataInvalidKeyFromDirClient() { + client.create().block(); + StepVerifier.create(client.setMetadata(invalidMetadata)) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 400)); + } + + @Override + public void listFilesAndDirectoriesFromDirClient() { + StepVerifier.create(client.create()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + List fileRefs = new ArrayList<>(); + int repeatTimes = 3; + // generate some subdirectories + String fileNameSameLayer = testResourceNamer.randomName("sameLayer", 16); + String fileNameNextLayer = testResourceNamer.randomName("nextLayer", 16); + + for (int i = 0; i < repeatTimes; i++) { + String directoryName = dirName + i; + DirectoryAsyncClient subDirectoryClient = client.getSubDirectoryClient(directoryName); + StepVerifier.create(subDirectoryClient.create()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + fileRefs.add(new FileRef(directoryName, true, null)); + + fileNameSameLayer = fileNameSameLayer + i; + fileNameNextLayer = fileNameNextLayer + i; + + StepVerifier.create(client.createFile(fileNameSameLayer, 1024)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + fileRefs.add(new FileRef(fileNameSameLayer, false, new FileProperty().contentLength(1024))); + StepVerifier.create(subDirectoryClient.createFile(fileNameNextLayer, 1024)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + } + + Collections.sort(fileRefs, new LexicographicComparator()); + StepVerifier.create(client.listFilesAndDirectories(null, null)) + .assertNext(fileRef -> FileTestHelpers.assertFileRefsAreEqual(fileRefs.get(0), fileRef)) + .assertNext(fileRef -> FileTestHelpers.assertFileRefsAreEqual(fileRefs.get(1), fileRef)) + .assertNext(fileRef -> FileTestHelpers.assertFileRefsAreEqual(fileRefs.get(2), fileRef)) + .assertNext(fileRef -> FileTestHelpers.assertFileRefsAreEqual(fileRefs.get(3), fileRef)) + .assertNext(fileRef -> FileTestHelpers.assertFileRefsAreEqual(fileRefs.get(4), fileRef)) + .assertNext(fileRef -> FileTestHelpers.assertFileRefsAreEqual(fileRefs.get(5), fileRef)) + .verifyComplete(); + } + + class LexicographicComparator implements Comparator { + @Override + public int compare(FileRef a, FileRef b) { + return a.name().compareToIgnoreCase(b.name()); + } + } + + @Override + public void getHandlesFromDirClient() { + // TODO: Need to figure out way of creating handlers first. + +// StepVerifier.create(client.create()) +// .assertNext(response -> StorageTestBase.assertResponseStatusCode(response, 201)) +// .verifyComplete(); +// StepVerifier.create(client.getHandles(null, true)) +// .assertNext(response -> Assert.assertNotNull(response)) +// .verifyComplete(); + } + + @Override + public void forceCloseHandlesFromDirClient() { + client.create().block(); + client.createFile("test", 1024); + Iterable handleItems = client.getHandles(null, true).toIterable(); + handleItems.forEach(handleItem -> { + StepVerifier.create(client.forceCloseHandles(handleItem.handleId(), true)) + .assertNext(numOfClosedHandles -> Assert.assertTrue(numOfClosedHandles.longValue() > 0)) + .verifyComplete(); + }); + } + + @Override + public void createSubDirectory() { + StepVerifier.create(client.create()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + StepVerifier.create(client.createSubDirectory(testResourceNamer.randomName("dir", 16))) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + } + + @Override + public void createSubDirectoryWithMetadata() { + StepVerifier.create(client.create()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + StepVerifier.create(client.createSubDirectory(testResourceNamer.randomName("dir", 16), basicMetadata)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + } + + @Override + public void createSubDirectoryTwiceSameMetadata() { + StepVerifier.create(client.create()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + String dirName = testResourceNamer.randomName("dir", 16); + StepVerifier.create(client.createSubDirectory(dirName, basicMetadata)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + StepVerifier.create(client.createSubDirectory(dirName, basicMetadata)) + .verifyErrorSatisfies(exception -> FileTestHelpers.assertExceptionErrorMessage(exception, "ResourceAlreadyExists")); + } + + @Override + public void deleteSubDirectory() { + client.create().block(); + String dirName = testResourceNamer.randomName("dir", 16); + StepVerifier.create(client.createSubDirectory(dirName)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + StepVerifier.create(client.deleteSubDirectory(dirName)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 202)) + .verifyComplete(); + } + + @Override + public void createFileFromDirClient() { + client.create().block(); + StepVerifier.create(client.createFile("testfile", 1024)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + } + + @Override + public void createFileWithoutCreateDirFromDirClient() { + StepVerifier.create(client.createFile("testfile", 1024)) + .verifyErrorSatisfies(response -> FileTestHelpers.assertExceptionStatusCode(response, 404)); + } + + @Override + public void deleteFileFromDirClient() { + client.create().block(); + client.createFile("testfile", 1024).block(); + StepVerifier.create(client.deleteFile("testfile")) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 202)) + .verifyComplete(); + } + + @Override + public void deleteFileWithoutCreateFileFromDirClient() { + client.create().block(); + StepVerifier.create(client.deleteFile("testfile")) + .verifyErrorSatisfies(response -> FileTestHelpers.assertExceptionStatusCode(response, 404)); + } + +} diff --git a/storage/client/file/src/test/java/com/azure/storage/file/DirectoryClientTestBase.java b/storage/client/file/src/test/java/com/azure/storage/file/DirectoryClientTestBase.java new file mode 100644 index 0000000000000..f86f1c2ec14a0 --- /dev/null +++ b/storage/client/file/src/test/java/com/azure/storage/file/DirectoryClientTestBase.java @@ -0,0 +1,113 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.file; + +import com.azure.core.test.TestBase; +import com.azure.core.util.configuration.ConfigurationManager; +import java.util.Collections; +import java.util.Map; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TestName; + +public abstract class DirectoryClientTestBase extends TestBase { + protected String dirName; + protected String shareName; + + String azureStorageFileEndpoint = ConfigurationManager.getConfiguration().get("AZURE_STORAGE_FILE_ENDPOINT"); + final Map basicMetadata = Collections.singletonMap("test", "metadata"); + final Map invalidMetadata = Collections.singletonMap("1", "metadata"); + + @Rule + public TestName testName = new TestName(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + void beforeDirectoryTest() { + dirName = testResourceNamer.randomName("directory", 16); + } + + /** + * Gets the name of the current test being run. + *

                  + * NOTE: This could not be implemented in the base class using {@link TestName} because it always returns {@code + * null}. See https://stackoverflow.com/a/16113631/4220757. + * + * @return The name of the current test. + */ + @Override + protected String testName() { + return testName.getMethodName(); + } + + @Test + public abstract void urlFromDirClient(); + + @Test + public abstract void getFileClientFromDirClient(); + + @Test + public abstract void getSubDirectoryClient(); + + @Test + public abstract void createMinFromDirClient(); + + @Test + public abstract void createTwiceFromDirClient(); + + @Test + public abstract void createWithMetadataFromDirClient(); + + @Test + public abstract void deleteFromDirClient(); + + @Test + public abstract void deleteNotExistFromDirClient(); + + @Test + public abstract void getPropertiesFromDirClient(); + + @Test + public abstract void clearMetadataFromDirClient(); + + @Test + public abstract void setMetadataFromDirClient(); + + @Test + public abstract void setMetadataInvalidKeyFromDirClient(); + + @Test + public abstract void listFilesAndDirectoriesFromDirClient(); + + @Test + public abstract void getHandlesFromDirClient(); + + @Test + public abstract void forceCloseHandlesFromDirClient(); + + @Test + public abstract void createSubDirectory(); + + @Test + public abstract void createSubDirectoryWithMetadata(); + + @Test + public abstract void createSubDirectoryTwiceSameMetadata(); + + @Test + public abstract void deleteSubDirectory(); + + @Test + public abstract void createFileFromDirClient(); + + @Test + public abstract void createFileWithoutCreateDirFromDirClient(); + + @Test + public abstract void deleteFileFromDirClient(); + + @Test + public abstract void deleteFileWithoutCreateFileFromDirClient(); +} diff --git a/storage/client/file/src/test/java/com/azure/storage/file/DirectoryClientTests.java b/storage/client/file/src/test/java/com/azure/storage/file/DirectoryClientTests.java new file mode 100644 index 0000000000000..64103f0d110d4 --- /dev/null +++ b/storage/client/file/src/test/java/com/azure/storage/file/DirectoryClientTests.java @@ -0,0 +1,259 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.file; + +import com.azure.core.http.HttpClient; +import com.azure.core.http.policy.HttpLogDetailLevel; +import com.azure.core.implementation.http.UrlBuilder; +import com.azure.core.test.TestMode; +import com.azure.core.util.configuration.ConfigurationManager; +import com.azure.core.util.logging.ClientLogger; +import com.azure.storage.file.models.FileRef; +import com.azure.storage.file.models.StorageErrorException; +import java.time.Duration; +import java.util.Iterator; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; + +import static com.azure.storage.file.FileTestHelpers.setupClient; +import static org.junit.Assert.assertTrue; + +public class DirectoryClientTests extends DirectoryClientTestBase { + private final ClientLogger directoryLogger = new ClientLogger(DirectoryClientTests.class); + private static String shareName = "dirsharename"; + private static ShareClient shareClient; + private DirectoryClient directoryClient; + + @Override + public void beforeTest() { + beforeDirectoryTest(); + if (interceptorManager.isPlaybackMode()) { + directoryClient = setupClient((connectionString, endpoint) -> new DirectoryClientBuilder() + .connectionString(connectionString) + .endpoint(endpoint) + .shareName(shareName) + .directoryPath(dirName) + .httpClient(interceptorManager.getPlaybackClient()) + .httpLogDetailLevel(HttpLogDetailLevel.BODY_AND_HEADERS) + .buildClient(), true, directoryLogger); + } else { + directoryClient = setupClient((connectionString, endpoint) -> new DirectoryClientBuilder() + .connectionString(connectionString) + .endpoint(endpoint) + .shareName(shareName) + .directoryPath(dirName) + .httpClient(HttpClient.createDefault().wiretap(true)) + .httpLogDetailLevel(HttpLogDetailLevel.BODY_AND_HEADERS) + .addPolicy(interceptorManager.getRecordPolicy()) + .buildClient(), false, directoryLogger); + } + } + + @BeforeClass + public static void beforeClass() { + if (FileTestHelpers.getTestMode() == TestMode.PLAYBACK) { + return; + } + FileServiceClient fileServiceClient = new FileServiceClientBuilder() + .connectionString(ConfigurationManager.getConfiguration().get("AZURE_STORAGE_CONNECTION_STRING")) + .buildClient(); + shareClient = fileServiceClient.getShareClient(shareName); + shareClient.create(); + } + + @AfterClass + public static void tearDown() { + if (FileTestHelpers.getTestMode() == TestMode.PLAYBACK) { + return; + } + shareClient.delete(); + FileTestHelpers.sleepInRecordMode(Duration.ofSeconds(45)); + } + + @Override + public void urlFromDirClient() { + if (interceptorManager.isPlaybackMode()) { + azureStorageFileEndpoint = "https://teststorage.file.core.windows.net/"; + } + UrlBuilder urlBuilder = UrlBuilder.parse(azureStorageFileEndpoint); + String endpointURL = new UrlBuilder().scheme(urlBuilder.scheme()).host(urlBuilder.host()).toString(); + Assert.assertEquals(endpointURL, directoryClient.getDirectoryUrl().toString()); + } + + @Override + public void createMinFromDirClient() { + FileTestHelpers.assertResponseStatusCode(directoryClient.create(), 201); + } + + @Override + public void createTwiceFromDirClient() { + directoryClient.create(); + thrown.expect(StorageErrorException.class); + thrown.expectMessage("ResourceAlreadyExists"); + directoryClient.create(); + } + + @Override + public void createWithMetadataFromDirClient() { + FileTestHelpers.assertResponseStatusCode(directoryClient.create(basicMetadata), 201); + } + + @Override + public void getFileClientFromDirClient() { + Assert.assertNotNull(directoryClient.getFileClient(testResourceNamer.randomName("getFileSync", 16))); + } + + @Override + public void getSubDirectoryClient() { + Assert.assertNotNull(directoryClient.getSubDirectoryClient("getSubdirectorySync")); + } + + @Override + public void deleteFromDirClient() { + directoryClient.create(); + FileTestHelpers.assertResponseStatusCode(directoryClient.delete(), 202); + } + + @Override + public void deleteNotExistFromDirClient() { + thrown.expect(StorageErrorException.class); + thrown.expectMessage("ResourceNotFound"); + directoryClient.delete(); + } + + @Override + public void getPropertiesFromDirClient() { + directoryClient.create(); + FileTestHelpers.assertResponseStatusCode(directoryClient.getProperties(), 200); + } + + @Override + public void clearMetadataFromDirClient() { + directoryClient.create(); + FileTestHelpers.assertResponseStatusCode(directoryClient.setMetadata(null), 200); + } + + @Override + public void setMetadataFromDirClient() { + directoryClient.create(); + FileTestHelpers.assertResponseStatusCode(directoryClient.setMetadata(basicMetadata), 200); + } + + @Override + public void setMetadataInvalidKeyFromDirClient() { + directoryClient.create(); + thrown.expect(StorageErrorException.class); + thrown.expectMessage("InvalidMetadata"); + directoryClient.setMetadata(invalidMetadata); + } + + @Override + public void listFilesAndDirectoriesFromDirClient() { + FileTestHelpers.assertResponseStatusCode(directoryClient.create(), 201); + int repeatTimes = 3; + String fileNameSameLayer = testResourceNamer.randomName("sameLayer", 16); + String fileNameNextLayer = testResourceNamer.randomName("nextLayer", 16); + for (int i = 0; i < repeatTimes; i++) { + DirectoryClient subDirClient = directoryClient.getSubDirectoryClient(dirName + i); + FileTestHelpers.assertResponseStatusCode(subDirClient.create(), 201); + fileNameSameLayer = fileNameSameLayer + i; + fileNameNextLayer = fileNameNextLayer + i; + FileTestHelpers.assertResponseStatusCode(directoryClient.createFile(fileNameSameLayer, 1024), 201); + FileTestHelpers.assertResponseStatusCode(subDirClient.createFile(fileNameNextLayer, 1024), 201); + } + + Iterable fileRefs = directoryClient.listFilesAndDirectories(); + + int count = 0; + Iterator it = fileRefs.iterator(); + while (it.hasNext()) { + count++; + FileRef ref = it.next(); + Assert.assertNotNull(ref); + if (!ref.isDirectory()) { + assertTrue("It is supposed to list the files in same layer.", ref.name().contains("samelayer")); + } + } + Assert.assertEquals(repeatTimes * 2, count); + } + + @Override + public void getHandlesFromDirClient() { + // TODO: Need to open channel and create handlers first. +// StorageTestBase.assertResponseStatusCode(directoryClient.create(), 201); +// Assert.assertTrue(directoryClient.getHandles(null, true).iterator().hasNext()); + } + + @Override + public void forceCloseHandlesFromDirClient() { + FileTestHelpers.assertResponseStatusCode(directoryClient.create(), 201); + directoryClient.getHandles(null, true).forEach( + handleItem -> { + directoryClient.forceCloseHandles(handleItem.handleId(), true).forEach( + numberOfClosedHandles -> assertTrue(numberOfClosedHandles > 0) + ); + } + ); + } + + @Override + public void createSubDirectory() { + FileTestHelpers.assertResponseStatusCode(directoryClient.create(), 201); + FileTestHelpers.assertResponseStatusCode(directoryClient.createSubDirectory(testResourceNamer.randomName("directory", 16)), 201); + } + + @Override + public void createSubDirectoryWithMetadata() { + FileTestHelpers.assertResponseStatusCode(directoryClient.create(), 201); + FileTestHelpers.assertResponseStatusCode(directoryClient.createSubDirectory(testResourceNamer.randomName("directory", 16), basicMetadata), 201); + } + + @Override + public void createSubDirectoryTwiceSameMetadata() { + directoryClient.create(); + String dirName = testResourceNamer.randomName("dir", 16); + FileTestHelpers.assertResponseStatusCode(directoryClient.createSubDirectory(dirName, basicMetadata), 201); + thrown.expect(StorageErrorException.class); + thrown.expectMessage("ResourceAlreadyExists"); + directoryClient.createSubDirectory(dirName, basicMetadata); + } + + + @Override + public void deleteSubDirectory() { + directoryClient.create(); + String dirName = testResourceNamer.randomName("dir", 16); + FileTestHelpers.assertResponseStatusCode(directoryClient.createSubDirectory(dirName), 201); + FileTestHelpers.assertResponseStatusCode(directoryClient.deleteSubDirectory(dirName), 202); + } + + @Override + public void createFileFromDirClient() { + directoryClient.create(); + FileTestHelpers.assertResponseStatusCode(directoryClient.createFile("testfile", 1024), 201); + } + + @Override + public void createFileWithoutCreateDirFromDirClient() { + thrown.expect(StorageErrorException.class); + thrown.expectMessage("ParentNotFound"); + directoryClient.createFile("testfile", 1024); + } + + @Override + public void deleteFileFromDirClient() { + directoryClient.create(); + directoryClient.createFile("testfile", 1024); + FileTestHelpers.assertResponseStatusCode(directoryClient.deleteFile("testfile"), 202); + } + + @Override + public void deleteFileWithoutCreateFileFromDirClient() { + directoryClient.create(); + thrown.expect(StorageErrorException.class); + thrown.expectMessage("ResourceNotFound"); + directoryClient.deleteFile("testfile"); + } + +} diff --git a/storage/client/file/src/test/java/com/azure/storage/file/FileAsyncClientTest.java b/storage/client/file/src/test/java/com/azure/storage/file/FileAsyncClientTest.java new file mode 100644 index 0000000000000..3f8199113888c --- /dev/null +++ b/storage/client/file/src/test/java/com/azure/storage/file/FileAsyncClientTest.java @@ -0,0 +1,215 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.file; + +import com.azure.core.http.HttpClient; +import com.azure.core.http.policy.HttpLogDetailLevel; +import com.azure.core.test.TestMode; +import com.azure.core.util.configuration.ConfigurationManager; +import com.azure.core.util.logging.ClientLogger; +import com.azure.storage.file.models.FileHTTPHeaders; +import com.azure.storage.file.models.HandleItem; +import java.io.File; +import java.net.URL; +import java.nio.file.Files; +import java.time.Duration; +import java.util.Arrays; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import reactor.core.publisher.Flux; +import reactor.test.StepVerifier; + +import static com.azure.storage.file.FileTestHelpers.assertTwoFilesAreSame; +import static com.azure.storage.file.FileTestHelpers.setupClient; + +public class FileAsyncClientTest extends FileClientTestBase { + private final ClientLogger fileAsyncLogger = new ClientLogger(FileAsyncClientTest.class); + String filePath; + private static String shareName = "filesharename"; + private static String dirName = "testdir/"; + private static ShareClient shareClient; + private FileAsyncClient fileAsyncClient; + + @Override + public void beforeTest() { + filePath = dirName + testResourceNamer.randomName("file", 16); + if (interceptorManager.isPlaybackMode()) { + fileAsyncClient = setupClient((connectionString, endpoint) -> new FileClientBuilder() + .connectionString(connectionString) + .shareName(shareName) + .filePath(filePath) + .endpoint(endpoint) + .httpClient(interceptorManager.getPlaybackClient()) + .httpLogDetailLevel(HttpLogDetailLevel.BODY_AND_HEADERS) + .buildAsyncClient(), true, fileAsyncLogger); + } else { + fileAsyncClient = setupClient((connectionString, endpoint) -> new FileClientBuilder() + .connectionString(connectionString) + .shareName(shareName) + .filePath(filePath) + .endpoint(endpoint) + .httpClient(HttpClient.createDefault().wiretap(true)) + .httpLogDetailLevel(HttpLogDetailLevel.BODY_AND_HEADERS) + .addPolicy(interceptorManager.getRecordPolicy()) + .buildAsyncClient(), false, fileAsyncLogger); + } + } + @BeforeClass + public static void beforeClass() { + if (FileTestHelpers.getTestMode() == TestMode.PLAYBACK) { + return; + } + FileServiceClient fileServiceClient = new FileServiceClientBuilder() + .connectionString(ConfigurationManager.getConfiguration().get("AZURE_STORAGE_CONNECTION_STRING")) + .buildClient(); + shareClient = fileServiceClient.getShareClient(shareName); + shareClient.create(); + shareClient.createDirectory(dirName); + } + + @AfterClass + public static void tearDown() { + if (FileTestHelpers.getTestMode() == TestMode.PLAYBACK) { + return; + } + shareClient.delete(); + FileTestHelpers.sleepInRecordMode(Duration.ofSeconds(45)); + } + + @Override + public void createFromFileClient() { + StepVerifier.create(fileAsyncClient.create(1024)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + StepVerifier.create(fileAsyncClient.getProperties()) + .assertNext(filePropertiesResponse -> Assert.assertTrue(filePropertiesResponse.value().contentLength() == 1024)) + .verifyComplete(); + } + + @Override + public void createExcessMaxSizeFromFileClient() { + StepVerifier.create(fileAsyncClient.create(1024 * 1024 * 1024 * 1024, null, null)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + StepVerifier.create(fileAsyncClient.getProperties()) + .assertNext(filePropertiesResponse -> Assert.assertTrue(filePropertiesResponse.value().contentLength() == 0)) + .verifyComplete(); + } + + @Override + public void startCopy() throws Exception { + fileAsyncClient.create(1024).block(); + String sourceURL = fileAsyncClient.getFileUrl().toString() + "/" + shareName + "/" + filePath; + StepVerifier.create(fileAsyncClient.startCopy(sourceURL, null)) + .assertNext(response -> { + FileTestHelpers.assertResponseStatusCode(response, 202); + Assert.assertTrue(response.value().copyId() != null); + }) + .verifyComplete(); + } + + @Override + public void abortCopy() { + // TODO: need to mock a pending copy process. + } + + @Override + public void downloadWithProperties() { + fileAsyncClient.create(1024, null, null).block(); + StepVerifier.create(fileAsyncClient.downloadWithProperties()) + .assertNext(response -> FileTestHelpers.assertResponseListStatusCode(response, Arrays.asList(200, 206))) + .verifyComplete(); + } + + @Override + public void uploadToStorageAndDownloadToFile() throws Exception { + URL fileFolder = FileClientTestBase.class.getClassLoader().getResource("testfiles"); + File uploadFile = new File(fileFolder.getPath() + "/helloworld"); + File downloadFile = new File(fileFolder.getPath() + "/testDownload"); + + if (!Files.exists(downloadFile.toPath())) { + downloadFile.createNewFile(); + } + fileAsyncClient.create(uploadFile.length()).block(); + StepVerifier.create(fileAsyncClient.uploadFromFile(uploadFile.toString())) + .verifyComplete(); + StepVerifier.create(fileAsyncClient.downloadToFile(downloadFile.toString())) + .verifyComplete(); + + assertTwoFilesAreSame(uploadFile, downloadFile); + } + + @Override + public void deleteFromFileClient() { + fileAsyncClient.create(1024, null, null).block(); + StepVerifier.create(fileAsyncClient.delete()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 202)) + .verifyComplete(); + } + + @Override + public void getPropertiesFromFileClient() { + fileAsyncClient.create(1024).block(); + StepVerifier.create(fileAsyncClient.getProperties()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 200)) + .verifyComplete(); + } + + @Override + public void setHttpHeadersFromFileClient() { + fileAsyncClient.create(1024).block(); + FileHTTPHeaders headers = new FileHTTPHeaders(); + headers.fileContentMD5(new byte[0]); + StepVerifier.create(fileAsyncClient.setHttpHeaders(1024, headers)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 200)) + .verifyComplete(); + } + + @Override + public void setMeatadataFromFileClient() { + fileAsyncClient.create(1024).block(); + StepVerifier.create(fileAsyncClient.setMetadata(basicMetadata)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 200)) + .verifyComplete(); + } + + @Override + public void upload() { + fileAsyncClient.create(1024 * 5, null, null).block(); + StepVerifier.create(fileAsyncClient.upload(Flux.just(defaultData), defaultData.readableBytes())) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + } + + @Override + public void listRangesFromFileClient() { + fileAsyncClient.create(512, null, null).block(); + fileAsyncClient.upload(Flux.just(defaultData), defaultData.readableBytes()).block(); + StepVerifier.create(fileAsyncClient.listRanges()) + .assertNext(response -> Assert.assertTrue(response.start() == 0 && response.end() == 511)) + .verifyComplete(); + } + + @Override + public void listHandlesFromFileClient() { + //TODO: need to create a handle first + fileAsyncClient.create(1024).block(); + StepVerifier.create(fileAsyncClient.listHandles()) + .verifyComplete(); + } + + @Override + public void forceCloseHandlesFromFileClient() { + //TODO: need to figureout create a handle first + fileAsyncClient.create(1024).block(); + Iterable handles = fileAsyncClient.listHandles(10).toIterable(); + handles.forEach( + response -> { + StepVerifier.create(fileAsyncClient.forceCloseHandles(response.handleId())) + .assertNext(forceCloseHandles -> Assert.assertTrue(forceCloseHandles > 0)) + .verifyComplete(); + } + ); + } +} diff --git a/storage/client/file/src/test/java/com/azure/storage/file/FileClientTest.java b/storage/client/file/src/test/java/com/azure/storage/file/FileClientTest.java new file mode 100644 index 0000000000000..2ba243ff90464 --- /dev/null +++ b/storage/client/file/src/test/java/com/azure/storage/file/FileClientTest.java @@ -0,0 +1,202 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.file; + +import com.azure.core.http.HttpClient; +import com.azure.core.http.policy.HttpLogDetailLevel; +import com.azure.core.http.rest.Response; +import com.azure.core.test.TestMode; +import com.azure.core.util.configuration.ConfigurationManager; +import com.azure.core.util.logging.ClientLogger; +import com.azure.storage.file.models.FileCopyInfo; +import com.azure.storage.file.models.FileHTTPHeaders; +import com.azure.storage.file.models.FileInfo; +import com.azure.storage.file.models.FileProperties; +import com.azure.storage.file.models.FileRange; +import java.io.File; +import java.net.URL; +import java.nio.file.Files; +import java.time.Duration; +import java.util.Arrays; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; + +import static com.azure.storage.file.FileTestHelpers.assertTwoFilesAreSame; +import static com.azure.storage.file.FileTestHelpers.setupClient; + +public class FileClientTest extends FileClientTestBase { + private final ClientLogger fileLogger = new ClientLogger(FileClientTest.class); + private static String shareName = "filesharename"; + private static String dirName = "testdir/"; + private static ShareClient shareClient; + String filePath; + + private FileClient fileClient; + + @Override + public void beforeTest() { + filePath = dirName + testResourceNamer.randomName("file", 16); + if (interceptorManager.isPlaybackMode()) { + fileClient = setupClient((connectionString, endpoint) -> new FileClientBuilder() + .connectionString(connectionString) + .shareName(shareName) + .filePath(filePath) + .endpoint(endpoint) + .httpClient(interceptorManager.getPlaybackClient()) + .httpLogDetailLevel(HttpLogDetailLevel.BODY_AND_HEADERS) + .buildClient(), true, fileLogger); + } else { + fileClient = setupClient((connectionString, endpoint) -> new FileClientBuilder() + .connectionString(connectionString) + .shareName(shareName) + .filePath(filePath) + .endpoint(endpoint) + .httpClient(HttpClient.createDefault().wiretap(true)) + .httpLogDetailLevel(HttpLogDetailLevel.BODY_AND_HEADERS) + .addPolicy(interceptorManager.getRecordPolicy()) + .buildClient(), false, fileLogger); + } + } + @BeforeClass + public static void beforeClass() { + if (FileTestHelpers.getTestMode() == TestMode.PLAYBACK) { + return; + } + FileServiceClient fileServiceClient = new FileServiceClientBuilder() + .connectionString(ConfigurationManager.getConfiguration().get("AZURE_STORAGE_CONNECTION_STRING")) + .buildClient(); + shareClient = fileServiceClient.getShareClient(shareName); + shareClient.create(); + shareClient.createDirectory(dirName); + } + + @AfterClass + public static void tearDown() { + if (FileTestHelpers.getTestMode() == TestMode.PLAYBACK) { + return; + } + shareClient.delete(); + FileTestHelpers.sleepInRecordMode(Duration.ofSeconds(45)); + } + + @Override + public void createFromFileClient() { + FileTestHelpers.assertResponseStatusCode(fileClient.create(1024), 201); + Assert.assertTrue(fileClient.getProperties().value().contentLength() == 1024); + } + + @Override + public void createExcessMaxSizeFromFileClient() { + FileTestHelpers.assertResponseStatusCode(fileClient.create(1024 * 1024 * 1024 * 1024, null, null), 201); + Assert.assertTrue(fileClient.getProperties().value().contentLength() == 0); + } + + @Override + public void startCopy() throws Exception { + FileTestHelpers.assertResponseStatusCode(fileClient.create(1024, null, null), 201); + String sourceURL = fileClient.getFileUrl().toString() + "/" + shareName + "/" + filePath; + Response copyInfoResponse = fileClient.startCopy(sourceURL, null); + FileTestHelpers.assertResponseStatusCode(copyInfoResponse, 202); + Assert.assertTrue(copyInfoResponse.value().copyId() != null); + } + + @Override + public void abortCopy() { + // TODO: Need to construct or mock a copy with pending status + } + + @Override + public void downloadWithProperties() { + fileClient.create(1024, null, null); + FileRange range = new FileRange(0, 1024L); + FileTestHelpers.assertResponseListStatusCode(fileClient.downloadWithProperties(range, null), Arrays.asList(200, 206)); + } + + @Override + public void uploadToStorageAndDownloadToFile() throws Exception { + URL fileFolder = FileClientTestBase.class.getClassLoader().getResource("testfiles"); + File uploadFile = new File(fileFolder.getPath() + "/helloworld"); + File downloadFile = new File(fileFolder.getPath() + "/testDownload"); + + if (!Files.exists(downloadFile.toPath())) { + downloadFile.createNewFile(); + } + + fileClient.create(uploadFile.length()); + fileClient.uploadFromFile(uploadFile.toString()); + fileClient.downloadToFile(downloadFile.toString()); + assertTwoFilesAreSame(uploadFile, downloadFile); + } + + @Override + public void deleteFromFileClient() { + fileClient.create(1024, null, null); + FileTestHelpers.assertResponseStatusCode(fileClient.delete(), 202); + } + + @Override + public void getPropertiesFromFileClient() { + fileClient.create(1024, null, null); + Response propertiesResponse = fileClient.getProperties(); + Assert.assertNotNull(propertiesResponse.value()); + Assert.assertNotNull(propertiesResponse.value().contentLength() == 1024); + Assert.assertNotNull(propertiesResponse.value().eTag()); + Assert.assertNotNull(propertiesResponse.value().lastModified()); + } + + @Override + public void setHttpHeadersFromFileClient() { + fileClient.create(1024, null, null); + FileHTTPHeaders headers = new FileHTTPHeaders(); + headers.fileContentMD5(new byte[0]); + Response response = fileClient.setHttpHeaders(1024, headers); + FileTestHelpers.assertResponseStatusCode(response, 200); + Assert.assertNotNull(response.value().eTag()); + } + + @Override + public void setMeatadataFromFileClient() { + fileClient.create(1024, null, null); + FileTestHelpers.assertResponseStatusCode(fileClient.setMetadata(basicMetadata), 200); + } + + @Override + public void upload() { + fileClient.create(1024 * 5, null, null); + FileTestHelpers.assertResponseStatusCode(fileClient.upload(defaultData, defaultData.readableBytes()), 201); + } + + @Override + public void listRangesFromFileClient() { + fileClient.create(1024, null, null); + fileClient.upload(defaultData, defaultData.readableBytes()); + fileClient.listRanges(new FileRange(0, 511L)).forEach( + fileRangeInfo -> { + Assert.assertTrue(fileRangeInfo.start() == 0); + Assert.assertTrue(fileRangeInfo.end() == 511); + } + ); + } + + @Override + public void listHandlesFromFileClient() { + //TODO: need to find a way to create handles. + fileClient.create(1024); + fileClient.listHandles().forEach( + handleItem -> { + Assert.assertNotNull(handleItem.handleId()); + } + ); + } + + @Override + public void forceCloseHandlesFromFileClient() { + fileClient.create(1024, null, null); + fileClient.listHandles(10).forEach( + handleItem -> { + Assert.assertNotNull(fileClient.forceCloseHandles(handleItem.handleId())); + } + ); + } +} diff --git a/storage/client/file/src/test/java/com/azure/storage/file/FileClientTestBase.java b/storage/client/file/src/test/java/com/azure/storage/file/FileClientTestBase.java new file mode 100644 index 0000000000000..9aba9e055bbc3 --- /dev/null +++ b/storage/client/file/src/test/java/com/azure/storage/file/FileClientTestBase.java @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.file; + +import com.azure.core.test.TestBase; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.Map; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TestName; + +public abstract class FileClientTestBase extends TestBase { + final Map basicMetadata = Collections.singletonMap("test", "metadata"); + static final String DEFAULT_TEXT = "default"; + final ByteBuf defaultData = Unpooled.wrappedBuffer(DEFAULT_TEXT.getBytes(StandardCharsets.UTF_8)); + + @Rule + public TestName testName = new TestName(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + /** + * Gets the name of the current test being run. + *

                  + * NOTE: This could not be implemented in the base class using {@link TestName} because it always returns {@code + * null}. See https://stackoverflow.com/a/16113631/4220757. + * + * @return The name of the current test. + */ + @Override + protected String testName() { + return testName.getMethodName(); + } + + @Test + public abstract void createFromFileClient(); + + @Test + public abstract void createExcessMaxSizeFromFileClient(); + + @Test + public abstract void startCopy() throws Exception; + + @Test + public abstract void abortCopy(); + + @Test + public abstract void downloadWithProperties(); + + @Test + public abstract void uploadToStorageAndDownloadToFile() throws Exception; + + @Test + public abstract void deleteFromFileClient(); + + @Test + public abstract void getPropertiesFromFileClient(); + + @Test + public abstract void setHttpHeadersFromFileClient(); + + @Test + public abstract void setMeatadataFromFileClient(); + + @Test + public abstract void upload(); + + @Test + public abstract void listRangesFromFileClient() throws Exception; + + @Test + public abstract void listHandlesFromFileClient(); + + @Test + public abstract void forceCloseHandlesFromFileClient(); +} diff --git a/storage/client/file/src/test/java/com/azure/storage/file/FileServiceClientAsyncTests.java b/storage/client/file/src/test/java/com/azure/storage/file/FileServiceClientAsyncTests.java new file mode 100644 index 0000000000000..cc302acca59a5 --- /dev/null +++ b/storage/client/file/src/test/java/com/azure/storage/file/FileServiceClientAsyncTests.java @@ -0,0 +1,472 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.file; + +import com.azure.core.http.HttpClient; +import com.azure.core.http.policy.HttpLogDetailLevel; +import com.azure.core.util.logging.ClientLogger; +import com.azure.storage.file.models.CorsRule; +import com.azure.storage.file.models.FileServiceProperties; +import com.azure.storage.file.models.ListSharesOptions; +import com.azure.storage.file.models.Metrics; +import com.azure.storage.file.models.RetentionPolicy; +import com.azure.storage.file.models.ShareItem; +import com.azure.storage.file.models.ShareProperties; +import com.azure.storage.file.models.StorageErrorException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import reactor.test.StepVerifier; + +import static com.azure.storage.file.FileTestHelpers.setupClient; +import static org.junit.Assert.fail; + +public class FileServiceClientAsyncTests extends FileServiceClientTestBase { + private final ClientLogger fileServiceAsyncLogger = new ClientLogger(FileServiceClientAsyncTests.class); + + private FileServiceAsyncClient fileServiceAsyncClient; + + @Override + public void beforeTest() { + shareName = getShareName(); + + if (interceptorManager.isPlaybackMode()) { + fileServiceAsyncClient = setupClient((connectionString, endpoint) -> new FileServiceClientBuilder() + .connectionString(connectionString) + .endpoint(endpoint) + .httpClient(interceptorManager.getPlaybackClient()) + .httpLogDetailLevel(HttpLogDetailLevel.BODY_AND_HEADERS) + .buildAsyncClient(), true, fileServiceAsyncLogger); + } else { + fileServiceAsyncClient = setupClient((connectionString, endpoint) -> new FileServiceClientBuilder() + .connectionString(connectionString) + .endpoint(endpoint) + .httpClient(HttpClient.createDefault().wiretap(true)) + .httpLogDetailLevel(HttpLogDetailLevel.BODY_AND_HEADERS) + .addPolicy(interceptorManager.getRecordPolicy()) + .buildAsyncClient(), false, fileServiceAsyncLogger); + } + } + + @Override + public void afterTest() { + fileServiceAsyncClient.listShares(new ListSharesOptions().prefix(shareName)) + .collectList() + .block() + .forEach(share -> { + ShareAsyncClient client = fileServiceAsyncClient.getShareAsyncClient(share.name()); + try { + client.delete().block(); + } catch (StorageErrorException ex) { + // Share already deleted, that's what we wanted anyways. + } + }); + } + + @Override + public void getShareDoesNotCreateAShare() { + ShareAsyncClient client = fileServiceAsyncClient.getShareAsyncClient(shareName); + try { + client.getStatistics().block(); + fail("getShareAsyncClient shouldn't create a share in Azure."); + } catch (Exception ex) { + FileTestHelpers.assertExceptionStatusCode(ex, 404); + } + } + + @Override + public void createShare() { + StepVerifier.create(fileServiceAsyncClient.createShare(shareName)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + } + + @Override + public void createShareTwiceSameMetadata() { + StepVerifier.create(fileServiceAsyncClient.createShare(shareName)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(fileServiceAsyncClient.createShare(shareName)) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 409)); + } + + @Override + public void createShareTwiceDifferentMetadata() { + Map metadata = Collections.singletonMap("test", "metadata"); + + StepVerifier.create(fileServiceAsyncClient.createShare(shareName)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(fileServiceAsyncClient.createShare(shareName, metadata, null)) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 409)); + } + + @Override + public void createShareInvalidQuota() { + StepVerifier.create(fileServiceAsyncClient.createShare(shareName, null, -1)) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 400)); + + StepVerifier.create(fileServiceAsyncClient.createShare(shareName, null, 9999999)) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 400)); + } + + @Override + public void deleteShare() { + StepVerifier.create(fileServiceAsyncClient.createShare(shareName)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(fileServiceAsyncClient.deleteShare(shareName)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 202)) + .verifyComplete(); + } + + @Override + public void deleteShareDoesNotExist() { + StepVerifier.create(fileServiceAsyncClient.deleteShare(shareName)) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 404)); + } + + @Override + public void deleteThenCreateShareFromFileServiceClient() { + StepVerifier.create(fileServiceAsyncClient.createShare(shareName)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(fileServiceAsyncClient.deleteShare(shareName)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 202)) + .verifyComplete(); + + FileTestHelpers.sleepInRecordMode(Duration.ofSeconds(45)); + + StepVerifier.create(fileServiceAsyncClient.createShare(shareName)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + } + + @Override + public void deleteThenCreateShareTooSoonFromFileServiceClient() { + StepVerifier.create(fileServiceAsyncClient.createShare(shareName)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(fileServiceAsyncClient.deleteShare(shareName)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 202)) + .verifyComplete(); + + StepVerifier.create(fileServiceAsyncClient.createShare(shareName)) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 409)); + } + + @Override + public void listShares() { + LinkedList testShares = new LinkedList<>(); + for (int i = 0; i < 3; i++) { + ShareItem share = new ShareItem().name(shareName + i) + .properties(new ShareProperties().quota(2)); + + testShares.add(share); + StepVerifier.create(fileServiceAsyncClient.createShare(share.name(), share.metadata(), share.properties().quota())) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + } + + StepVerifier.create(fileServiceAsyncClient.listShares(defaultOptions())) + .assertNext(share -> FileTestHelpers.assertSharesAreEqual(testShares.pop(), share)) + .assertNext(share -> FileTestHelpers.assertSharesAreEqual(testShares.pop(), share)) + .assertNext(share -> FileTestHelpers.assertSharesAreEqual(testShares.pop(), share)) + .verifyComplete(); + } + + @Override + public void listSharesWithPrefix() { + LinkedList testShares = new LinkedList<>(); + for (int i = 0; i < 3; i++) { + ShareItem share = new ShareItem().properties(new ShareProperties().quota(2)); + + if (i % 2 == 0) { + share.name(shareName + "prefix" + i); + testShares.add(share); + } else { + share.name(shareName + i); + } + + StepVerifier.create(fileServiceAsyncClient.createShare(share.name(), share.metadata(), share.properties().quota())) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + } + + StepVerifier.create(fileServiceAsyncClient.listShares(defaultOptions().prefix(shareName + "prefix"))) + .assertNext(share -> FileTestHelpers.assertSharesAreEqual(testShares.pop(), share)) + .assertNext(share -> FileTestHelpers.assertSharesAreEqual(testShares.pop(), share)) + .verifyComplete(); + } + + @Override + public void listSharesWithLimit() { + LinkedList testShares = new LinkedList<>(); + for (int i = 0; i < 3; i++) { + ShareItem share = new ShareItem().name(shareName + i) + .properties(new ShareProperties().quota(2)); + + testShares.add(share); + StepVerifier.create(fileServiceAsyncClient.createShare(share.name(), share.metadata(), share.properties().quota())) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + } + + StepVerifier.create(fileServiceAsyncClient.listShares(defaultOptions().maxResults(2))) + .assertNext(share -> FileTestHelpers.assertSharesAreEqual(testShares.pop(), share)) + .assertNext(share -> FileTestHelpers.assertSharesAreEqual(testShares.pop(), share)) + .verifyComplete(); + } + + @Override + public void listSharesInvalidMaxResults() { + StepVerifier.create(fileServiceAsyncClient.listShares(defaultOptions().maxResults(-1))) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 400)); + + StepVerifier.create(fileServiceAsyncClient.listShares(defaultOptions().maxResults(0))) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 400)); + } + + @Override + public void listSharesIncludeMetadata() { + Map metadata = Collections.singletonMap("test", "metadata"); + + LinkedList testShares = new LinkedList<>(); + for (int i = 0; i < 3; i++) { + ShareItem share = new ShareItem().name(shareName + i) + .properties(new ShareProperties().quota(2)); + + if (i % 2 == 0) { + share.metadata(metadata); + } + + testShares.add(share); + StepVerifier.create(fileServiceAsyncClient.createShare(share.name(), share.metadata(), share.properties().quota())) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + } + + StepVerifier.create(fileServiceAsyncClient.listShares(defaultOptions().includeMetadata(true))) + .assertNext(share -> FileTestHelpers.assertSharesAreEqual(testShares.pop(), share)) + .assertNext(share -> FileTestHelpers.assertSharesAreEqual(testShares.pop(), share)) + .assertNext(share -> FileTestHelpers.assertSharesAreEqual(testShares.pop(), share)) + .verifyComplete(); + } + + @Override + public void listSharesIncludeSnapshots() { + LinkedList testShares = new LinkedList<>(); + for (int i = 0; i < 3; i++) { + ShareItem share = new ShareItem().name(shareName + i) + .properties(new ShareProperties().quota(2)); + + ShareAsyncClient client = fileServiceAsyncClient.getShareAsyncClient(share.name()); + + StepVerifier.create(client.create(null, share.properties().quota())) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + if (i % 2 == 0) { + StepVerifier.create(client.createSnapshot()) + .assertNext(response -> { + testShares.add(new ShareItem().name(share.name()) + .snapshot(response.value().snapshot()) + .properties(share.properties())); + + FileTestHelpers.assertResponseStatusCode(response, 201); + }) + .verifyComplete(); + } + + testShares.add(share); + } + + StepVerifier.create(fileServiceAsyncClient.listShares(defaultOptions().includeSnapshots(true))) + .assertNext(share -> FileTestHelpers.assertSharesAreEqual(testShares.pop(), share)) + .assertNext(share -> FileTestHelpers.assertSharesAreEqual(testShares.pop(), share)) + .assertNext(share -> FileTestHelpers.assertSharesAreEqual(testShares.pop(), share)) + .assertNext(share -> FileTestHelpers.assertSharesAreEqual(testShares.pop(), share)) + .assertNext(share -> FileTestHelpers.assertSharesAreEqual(testShares.pop(), share)) + .verifyComplete(); + } + + @Override + public void listSharesIncludeMetadataAndSnapshots() { + Map metadata = Collections.singletonMap("test", "metadata"); + + LinkedList testShares = new LinkedList<>(); + for (int i = 0; i < 3; i++) { + ShareItem share = new ShareItem().name(shareName + i) + .properties(new ShareProperties().quota(2)); + + if (i % 2 == 0) { + share.metadata(metadata); + } + + ShareAsyncClient client = fileServiceAsyncClient.getShareAsyncClient(share.name()); + + StepVerifier.create(client.create(share.metadata(), share.properties().quota())) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + if (i % 2 == 0) { + StepVerifier.create(client.createSnapshot()) + .assertNext(response -> { + testShares.add(new ShareItem().name(share.name()) + .snapshot(response.value().snapshot()) + .properties(share.properties())); + + FileTestHelpers.assertResponseStatusCode(response, 201); + }) + .verifyComplete(); + } + + testShares.add(share); + } + + StepVerifier.create(fileServiceAsyncClient.listShares(defaultOptions().includeMetadata(true).includeSnapshots(true))) + .assertNext(share -> FileTestHelpers.assertSharesAreEqual(testShares.pop(), share)) + .assertNext(share -> FileTestHelpers.assertSharesAreEqual(testShares.pop(), share)) + .assertNext(share -> FileTestHelpers.assertSharesAreEqual(testShares.pop(), share)) + .assertNext(share -> FileTestHelpers.assertSharesAreEqual(testShares.pop(), share)) + .assertNext(share -> FileTestHelpers.assertSharesAreEqual(testShares.pop(), share)) + .verifyComplete(); + } + + @Override + public void setFileServiceProperties() { + FileServiceProperties originalProperties = fileServiceAsyncClient.getProperties().block().value(); + + RetentionPolicy retentionPolicy = new RetentionPolicy().enabled(true) + .days(3); + + Metrics metrics = new Metrics().enabled(true) + .includeAPIs(false) + .retentionPolicy(retentionPolicy) + .version("1.0"); + + FileServiceProperties updatedProperties = new FileServiceProperties().hourMetrics(metrics) + .minuteMetrics(metrics) + .cors(new ArrayList<>()); + + StepVerifier.create(fileServiceAsyncClient.setProperties(updatedProperties)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 202)) + .verifyComplete(); + + StepVerifier.create(fileServiceAsyncClient.getProperties()) + .assertNext(response -> FileTestHelpers.assertFileServicePropertiesAreEqual(updatedProperties, response.value())) + .verifyComplete(); + + StepVerifier.create(fileServiceAsyncClient.setProperties(originalProperties)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 202)) + .verifyComplete(); + + StepVerifier.create(fileServiceAsyncClient.getProperties()) + .assertNext(response -> FileTestHelpers.assertFileServicePropertiesAreEqual(originalProperties, response.value())) + .verifyComplete(); + } + + @Override + public void setPropertiesTooManyRules() { + RetentionPolicy retentionPolicy = new RetentionPolicy().enabled(true) + .days(3); + + Metrics metrics = new Metrics().enabled(true) + .includeAPIs(false) + .retentionPolicy(retentionPolicy) + .version("1.0"); + + List cors = new ArrayList<>(); + for (int i = 0; i < 6; i++) { + cors.add(new CorsRule()); + } + + FileServiceProperties properties = new FileServiceProperties().hourMetrics(metrics) + .minuteMetrics(metrics) + .cors(cors); + + StepVerifier.create(fileServiceAsyncClient.setProperties(properties)) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 400)); + } + + @Override + public void setPropertiesInvalidAllowedHeader() { + RetentionPolicy retentionPolicy = new RetentionPolicy().enabled(true) + .days(3); + + Metrics metrics = new Metrics().enabled(true) + .includeAPIs(false) + .retentionPolicy(retentionPolicy) + .version("1.0"); + + FileServiceProperties properties = new FileServiceProperties().hourMetrics(metrics) + .minuteMetrics(metrics) + .cors(Collections.singletonList(new CorsRule().allowedHeaders(reallyLongString))); + + StepVerifier.create(fileServiceAsyncClient.setProperties(properties)) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 400)); + } + + @Override + public void setPropertiesInvalidExposedHeader() { + RetentionPolicy retentionPolicy = new RetentionPolicy().enabled(true) + .days(3); + + Metrics metrics = new Metrics().enabled(true) + .includeAPIs(false) + .retentionPolicy(retentionPolicy) + .version("1.0"); + + FileServiceProperties properties = new FileServiceProperties().hourMetrics(metrics) + .minuteMetrics(metrics) + .cors(Collections.singletonList(new CorsRule().exposedHeaders(reallyLongString))); + + StepVerifier.create(fileServiceAsyncClient.setProperties(properties)) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 400)); + } + + @Override + public void setPropertiesInvalidAllowedOrigin() { + RetentionPolicy retentionPolicy = new RetentionPolicy().enabled(true) + .days(3); + + Metrics metrics = new Metrics().enabled(true) + .includeAPIs(false) + .retentionPolicy(retentionPolicy) + .version("1.0"); + + FileServiceProperties properties = new FileServiceProperties().hourMetrics(metrics) + .minuteMetrics(metrics) + .cors(Collections.singletonList(new CorsRule().allowedOrigins(reallyLongString))); + + StepVerifier.create(fileServiceAsyncClient.setProperties(properties)) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 400)); + } + + @Override + public void setPropertiesInvalidAllowedMethod() { + RetentionPolicy retentionPolicy = new RetentionPolicy().enabled(true) + .days(3); + + Metrics metrics = new Metrics().enabled(true) + .includeAPIs(false) + .retentionPolicy(retentionPolicy) + .version("1.0"); + + FileServiceProperties properties = new FileServiceProperties().hourMetrics(metrics) + .minuteMetrics(metrics) + .cors(Collections.singletonList(new CorsRule().allowedMethods("NOTAREALHTTPMETHOD"))); + + StepVerifier.create(fileServiceAsyncClient.setProperties(properties)) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 400)); + } +} diff --git a/storage/client/file/src/test/java/com/azure/storage/file/FileServiceClientTestBase.java b/storage/client/file/src/test/java/com/azure/storage/file/FileServiceClientTestBase.java new file mode 100644 index 0000000000000..6a34f910052f3 --- /dev/null +++ b/storage/client/file/src/test/java/com/azure/storage/file/FileServiceClientTestBase.java @@ -0,0 +1,121 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.file; + +import com.azure.core.test.TestBase; +import com.azure.storage.file.models.ListSharesOptions; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; + +public abstract class FileServiceClientTestBase extends TestBase { + String shareName; + + String reallyLongString = "thisisareallylongstringthatexceedsthe64characterlimitallowedoncertainproperties"; + + @Rule + public TestName testName = new TestName(); + + /** + * Gets the name of the current test being run. + *

                  + * NOTE: This could not be implemented in the base class using {@link TestName} because it always returns {@code + * null}. See https://stackoverflow.com/a/16113631/4220757. + * + * @return The name of the current test. + */ + @Override + protected String testName() { + return testName.getMethodName(); + } + + String getShareName() { + return testResourceNamer.randomName("share", 16).toLowerCase(); + } + + ListSharesOptions defaultOptions() { + return new ListSharesOptions().prefix(shareName); + } + + @Test + public abstract void getShareDoesNotCreateAShare(); + + @Test + public abstract void createShare(); + + @Test + public abstract void createShareTwiceSameMetadata(); + + @Test + public abstract void createShareTwiceDifferentMetadata(); + + @Test + public abstract void createShareInvalidQuota(); + + @Test + public abstract void deleteShare(); + + @Test + public abstract void deleteShareDoesNotExist(); + + @Test + public abstract void deleteThenCreateShareFromFileServiceClient(); + + /** + * Cannot re-create a share within 30 seconds of it being deleted. + */ + @Test + public abstract void deleteThenCreateShareTooSoonFromFileServiceClient(); + + @Test + public abstract void listShares(); + + @Test + public abstract void listSharesWithPrefix(); + + @Test + public abstract void listSharesWithLimit(); + + @Test + public abstract void listSharesInvalidMaxResults(); + + @Test + public abstract void listSharesIncludeMetadata(); + + @Test + public abstract void listSharesIncludeSnapshots(); + + @Test + public abstract void listSharesIncludeMetadataAndSnapshots(); + + @Test + public abstract void setFileServiceProperties(); + + @Test + public abstract void setPropertiesTooManyRules(); + + /** + * A header in AllowedHeaders is not allowed to exceed 256 characters. + */ + @Test + public abstract void setPropertiesInvalidAllowedHeader(); + + /** + * A header in ExposedHeaders is not allowed to exceed 256 characters. + */ + @Test + public abstract void setPropertiesInvalidExposedHeader(); + + /** + * An origin in AllowedOrigins is not allowed to exceed 256 characters. + */ + @Test + public abstract void setPropertiesInvalidAllowedOrigin(); + + /** + * AllowedMethods only supports DELETE, GET, HEAD, MERGE, POST, OPTIONS, and PUT. + */ + @Test + public abstract void setPropertiesInvalidAllowedMethod(); +} diff --git a/storage/client/file/src/test/java/com/azure/storage/file/FileServiceClientTests.java b/storage/client/file/src/test/java/com/azure/storage/file/FileServiceClientTests.java new file mode 100644 index 0000000000000..3377458e2584c --- /dev/null +++ b/storage/client/file/src/test/java/com/azure/storage/file/FileServiceClientTests.java @@ -0,0 +1,479 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.file; + +import com.azure.core.http.HttpClient; +import com.azure.core.http.policy.HttpLogDetailLevel; +import com.azure.core.http.rest.Response; +import com.azure.core.util.logging.ClientLogger; +import com.azure.storage.file.models.CorsRule; +import com.azure.storage.file.models.FileServiceProperties; +import com.azure.storage.file.models.ListSharesOptions; +import com.azure.storage.file.models.Metrics; +import com.azure.storage.file.models.RetentionPolicy; +import com.azure.storage.file.models.ShareInfo; +import com.azure.storage.file.models.ShareItem; +import com.azure.storage.file.models.ShareProperties; +import com.azure.storage.file.models.ShareSnapshotInfo; +import com.azure.storage.file.models.StorageErrorException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import static com.azure.storage.file.FileTestHelpers.setupClient; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.fail; + +public class FileServiceClientTests extends FileServiceClientTestBase { + private final ClientLogger fileServiceLogger = new ClientLogger(FileServiceClientTests.class); + + private FileServiceClient fileServiceClient; + + @Override + public void beforeTest() { + shareName = getShareName(); + + if (interceptorManager.isPlaybackMode()) { + fileServiceClient = setupClient((connectionString, endpoint) -> new FileServiceClientBuilder() + .connectionString(connectionString) + .endpoint(endpoint) + .httpClient(interceptorManager.getPlaybackClient()) + .httpLogDetailLevel(HttpLogDetailLevel.BODY_AND_HEADERS) + .buildClient(), true, fileServiceLogger); + } else { + fileServiceClient = setupClient((connectionString, endpoint) -> new FileServiceClientBuilder() + .connectionString(connectionString) + .endpoint(endpoint) + .httpClient(HttpClient.createDefault().wiretap(true)) + .httpLogDetailLevel(HttpLogDetailLevel.BODY_AND_HEADERS) + .addPolicy(interceptorManager.getRecordPolicy()) + .buildClient(), false, fileServiceLogger); + } + } + + @Override + public void afterTest() { + for (ShareItem share : fileServiceClient.listShares(new ListSharesOptions().prefix(shareName))) { + ShareClient client = fileServiceClient.getShareClient(share.name()); + try { + client.delete(); + } catch (StorageErrorException ex) { + // Share already deleted, that's what we wanted anyways. + } + } + } + + @Override + public void getShareDoesNotCreateAShare() { + ShareClient client = fileServiceClient.getShareClient(shareName); + try { + client.getStatistics(); + fail("getShareAsyncClient shouldn't create a share in Azure."); + } catch (Exception ex) { + FileTestHelpers.assertExceptionStatusCode(ex, 404); + } + } + + @Override + public void createShare() { + FileTestHelpers.assertResponseStatusCode(fileServiceClient.createShare(shareName), 201); + } + + @Override + public void createShareTwiceSameMetadata() { + FileTestHelpers.assertResponseStatusCode(fileServiceClient.createShare(shareName), 201); + + try { + fileServiceClient.createShare(shareName); + } catch (Exception exception) { + FileTestHelpers.assertExceptionStatusCode(exception, 409); + } + } + + @Override + public void createShareTwiceDifferentMetadata() { + Map metadata = Collections.singletonMap("test", "metadata"); + + FileTestHelpers.assertResponseStatusCode(fileServiceClient.createShare(shareName), 201); + + try { + fileServiceClient.createShare(shareName, metadata, null); + fail("Attempting to create the share twice with different metadata should throw an exception."); + } catch (Exception exception) { + FileTestHelpers.assertExceptionStatusCode(exception, 409); + } + } + + @Override + public void createShareInvalidQuota() { + try { + fileServiceClient.createShare(shareName, null, -1); + fail("Attempting to create a share with a negative quota should throw an exception."); + } catch (Exception exception) { + FileTestHelpers.assertExceptionStatusCode(exception, 400); + } + + try { + fileServiceClient.createShare(shareName, null, 9999999); + fail("Attempting to create a share with a quota above 5120 GB should throw an exception."); + } catch (Exception exception) { + FileTestHelpers.assertExceptionStatusCode(exception, 400); + } + } + + @Override + public void deleteShare() { + FileTestHelpers.assertResponseStatusCode(fileServiceClient.createShare(shareName), 201); + FileTestHelpers.assertResponseStatusCode(fileServiceClient.deleteShare(shareName), 202); + } + + @Override + public void deleteShareDoesNotExist() { + try { + fileServiceClient.deleteShare(shareName); + fail("Attempting to delete a share that doesn't exist should throw an exception."); + } catch (Exception exception) { + FileTestHelpers.assertExceptionStatusCode(exception, 404); + } + } + + @Override + public void deleteThenCreateShareFromFileServiceClient() { + FileTestHelpers.assertResponseStatusCode(fileServiceClient.createShare(shareName), 201); + FileTestHelpers.assertResponseStatusCode(fileServiceClient.deleteShare(shareName), 202); + + FileTestHelpers.sleepInRecordMode(Duration.ofSeconds(45)); + + FileTestHelpers.assertResponseStatusCode(fileServiceClient.createShare(shareName), 201); + } + + @Override + public void deleteThenCreateShareTooSoonFromFileServiceClient() { + FileTestHelpers.assertResponseStatusCode(fileServiceClient.createShare(shareName), 201); + FileTestHelpers.assertResponseStatusCode(fileServiceClient.deleteShare(shareName), 202); + + try { + fileServiceClient.createShare(shareName); + fail("Attempting to re-create a share within 30 seconds of deleting it should throw an exception."); + } catch (Exception exception) { + FileTestHelpers.assertExceptionStatusCode(exception, 409); + } + } + + @Override + public void listShares() { + LinkedList testShares = new LinkedList<>(); + for (int i = 0; i < 3; i++) { + ShareItem share = new ShareItem().name(shareName + i) + .properties(new ShareProperties().quota(2)); + + testShares.add(share); + FileTestHelpers.assertResponseStatusCode(fileServiceClient.createShare(share.name(), share.metadata(), share.properties().quota()), 201); + } + + Iterator shares = fileServiceClient.listShares(defaultOptions()).iterator(); + for (int i = 0; i < 3; i++) { + FileTestHelpers.assertSharesAreEqual(testShares.pop(), shares.next()); + } + assertFalse(shares.hasNext()); + } + + @Override + public void listSharesWithPrefix() { + LinkedList testShares = new LinkedList<>(); + for (int i = 0; i < 3; i++) { + ShareItem share = new ShareItem().properties(new ShareProperties().quota(2)); + + if (i % 2 == 0) { + share.name(shareName + "prefix" + i); + testShares.add(share); + } else { + share.name(shareName + i); + } + + FileTestHelpers.assertResponseStatusCode(fileServiceClient.createShare(share.name(), share.metadata(), share.properties().quota()), 201); + } + + Iterator shares = fileServiceClient.listShares(defaultOptions().prefix(shareName + "prefix")).iterator(); + for (int i = 0; i < 2; i++) { + FileTestHelpers.assertSharesAreEqual(testShares.pop(), shares.next()); + } + assertFalse(shares.hasNext()); + } + + @Override + public void listSharesWithLimit() { + LinkedList testShares = new LinkedList<>(); + for (int i = 0; i < 3; i++) { + ShareItem share = new ShareItem().name(shareName + i) + .properties(new ShareProperties().quota(2)); + + testShares.add(share); + FileTestHelpers.assertResponseStatusCode(fileServiceClient.createShare(share.name(), share.metadata(), share.properties().quota()), 201); + } + + Iterator shares = fileServiceClient.listShares(defaultOptions().maxResults(2)).iterator(); + for (int i = 0; i < 2; i++) { + FileTestHelpers.assertSharesAreEqual(testShares.pop(), shares.next()); + } + assertFalse(shares.hasNext()); + } + + @Override + public void listSharesInvalidMaxResults() { + try { + fileServiceClient.listShares(defaultOptions().maxResults(-1)).iterator().hasNext(); + fail("Attempting to list shares with a negative maximum should throw an error"); + } catch (Exception exception) { + FileTestHelpers.assertExceptionStatusCode(exception, 400); + } + + try { + fileServiceClient.listShares(defaultOptions().maxResults(0)).iterator().hasNext(); + fail("Attempting to list shares with a zero maximum should throw an error"); + } catch (Exception exception) { + FileTestHelpers.assertExceptionStatusCode(exception, 400); + } + } + + @Override + public void listSharesIncludeMetadata() { + Map metadata = Collections.singletonMap("test", "metadata"); + + LinkedList testShares = new LinkedList<>(); + for (int i = 0; i < 3; i++) { + ShareItem share = new ShareItem().name(shareName + i) + .properties(new ShareProperties().quota(2)); + + if (i % 2 == 0) { + share.metadata(metadata); + } + + testShares.add(share); + FileTestHelpers.assertResponseStatusCode(fileServiceClient.createShare(share.name(), share.metadata(), share.properties().quota()), 201); + } + + Iterator shares = fileServiceClient.listShares(defaultOptions().includeMetadata(true)).iterator(); + for (int i = 0; i < 3; i++) { + FileTestHelpers.assertSharesAreEqual(testShares.pop(), shares.next()); + } + assertFalse(shares.hasNext()); + } + + @Override + public void listSharesIncludeSnapshots() { + LinkedList testShares = new LinkedList<>(); + for (int i = 0; i < 3; i++) { + ShareItem share = new ShareItem().name(shareName + i) + .properties(new ShareProperties().quota(2)); + + ShareClient client = fileServiceClient.getShareClient(share.name()); + + Response createResponse = client.create(null, share.properties().quota()); + FileTestHelpers.assertResponseStatusCode(createResponse, 201); + + if (i % 2 == 0) { + Response snapshotResponse = client.createSnapshot(); + FileTestHelpers.assertResponseStatusCode(snapshotResponse, 201); + + testShares.add(new ShareItem().name(share.name()) + .snapshot(snapshotResponse.value().snapshot()) + .properties(new ShareProperties().quota(share.properties().quota()))); + } + + testShares.add(share); + } + + Iterator shares = fileServiceClient.listShares(defaultOptions().includeSnapshots(true)).iterator(); + for (int i = 0; i < 5; i++) { + FileTestHelpers.assertSharesAreEqual(testShares.pop(), shares.next()); + } + assertFalse(shares.hasNext()); + } + + @Override + public void listSharesIncludeMetadataAndSnapshots() { + Map metadata = Collections.singletonMap("test", "metadata"); + + LinkedList testShares = new LinkedList<>(); + for (int i = 0; i < 3; i++) { + ShareItem share = new ShareItem().name(shareName + i) + .properties(new ShareProperties().quota(2)); + + if (i % 2 == 0) { + share.metadata(metadata); + } + + ShareClient client = fileServiceClient.getShareClient(share.name()); + + Response createResponse = client.create(share.metadata(), share.properties().quota()); + FileTestHelpers.assertResponseStatusCode(createResponse, 201); + + if (i % 2 == 0) { + Response snapshotResponse = client.createSnapshot(); + FileTestHelpers.assertResponseStatusCode(snapshotResponse, 201); + + testShares.add(new ShareItem().name(share.name()) + .snapshot(snapshotResponse.value().snapshot()) + .properties(share.properties())); + } + + testShares.add(share); + } + + Iterator shares = fileServiceClient.listShares(defaultOptions().includeMetadata(true).includeSnapshots(true)).iterator(); + for (int i = 0; i < 5; i++) { + FileTestHelpers.assertSharesAreEqual(testShares.pop(), shares.next()); + } + assertFalse(shares.hasNext()); + } + + @Override + public void setFileServiceProperties() { + FileServiceProperties originalProperties = fileServiceClient.getProperties().value(); + + RetentionPolicy retentionPolicy = new RetentionPolicy().enabled(true) + .days(3); + + Metrics metrics = new Metrics().enabled(true) + .includeAPIs(false) + .retentionPolicy(retentionPolicy) + .version("1.0"); + + FileServiceProperties updatedProperties = new FileServiceProperties().hourMetrics(metrics) + .minuteMetrics(metrics) + .cors(new ArrayList<>()); + + FileTestHelpers.assertResponseStatusCode(fileServiceClient.setProperties(updatedProperties), 202); + + Response getResponse = fileServiceClient.getProperties(); + FileTestHelpers.assertResponseStatusCode(getResponse, 200); + FileTestHelpers.assertFileServicePropertiesAreEqual(updatedProperties, getResponse.value()); + + FileTestHelpers.assertResponseStatusCode(fileServiceClient.setProperties(originalProperties), 202); + + getResponse = fileServiceClient.getProperties(); + FileTestHelpers.assertResponseStatusCode(getResponse, 200); + FileTestHelpers.assertFileServicePropertiesAreEqual(originalProperties, getResponse.value()); + } + + @Override + public void setPropertiesTooManyRules() { + RetentionPolicy retentionPolicy = new RetentionPolicy().enabled(true) + .days(3); + + Metrics metrics = new Metrics().enabled(true) + .includeAPIs(false) + .retentionPolicy(retentionPolicy) + .version("1.0"); + + List cors = new ArrayList<>(); + for (int i = 0; i < 6; i++) { + cors.add(new CorsRule()); + } + + FileServiceProperties properties = new FileServiceProperties().hourMetrics(metrics) + .minuteMetrics(metrics) + .cors(cors); + + try { + fileServiceClient.setProperties(properties); + fail("Attempting to set more than 5 CorsRules on files should throw an exception."); + } catch (Exception exception) { + FileTestHelpers.assertExceptionStatusCode(exception, 400); + } + } + + @Override + public void setPropertiesInvalidAllowedHeader() { + RetentionPolicy retentionPolicy = new RetentionPolicy().enabled(true) + .days(3); + + Metrics metrics = new Metrics().enabled(true) + .includeAPIs(false) + .retentionPolicy(retentionPolicy) + .version("1.0"); + + FileServiceProperties properties = new FileServiceProperties().hourMetrics(metrics) + .minuteMetrics(metrics) + .cors(Collections.singletonList(new CorsRule().allowedHeaders(reallyLongString))); + + try { + fileServiceClient.setProperties(properties); + fail("Attempting to set an allowed header longer than 64 characters should throw an exception."); + } catch (Exception exception) { + FileTestHelpers.assertExceptionStatusCode(exception, 400); + } + } + + @Override + public void setPropertiesInvalidExposedHeader() { + RetentionPolicy retentionPolicy = new RetentionPolicy().enabled(true) + .days(3); + + Metrics metrics = new Metrics().enabled(true) + .includeAPIs(false) + .retentionPolicy(retentionPolicy) + .version("1.0"); + + FileServiceProperties properties = new FileServiceProperties().hourMetrics(metrics) + .minuteMetrics(metrics) + .cors(Collections.singletonList(new CorsRule().exposedHeaders(reallyLongString))); + + try { + fileServiceClient.setProperties(properties); + fail("Attempting to set an exposed header longer than 64 characters should throw an exception."); + } catch (Exception exception) { + FileTestHelpers.assertExceptionStatusCode(exception, 400); + } + } + + @Override + public void setPropertiesInvalidAllowedOrigin() { + RetentionPolicy retentionPolicy = new RetentionPolicy().enabled(true) + .days(3); + + Metrics metrics = new Metrics().enabled(true) + .includeAPIs(false) + .retentionPolicy(retentionPolicy) + .version("1.0"); + + FileServiceProperties properties = new FileServiceProperties().hourMetrics(metrics) + .minuteMetrics(metrics) + .cors(Collections.singletonList(new CorsRule().allowedOrigins(reallyLongString))); + + try { + fileServiceClient.setProperties(properties); + fail("Attempting to set an allowed origin longer than 64 characters should throw an exception."); + } catch (Exception exception) { + FileTestHelpers.assertExceptionStatusCode(exception, 400); + } + } + + @Override + public void setPropertiesInvalidAllowedMethod() { + RetentionPolicy retentionPolicy = new RetentionPolicy().enabled(true) + .days(3); + + Metrics metrics = new Metrics().enabled(true) + .includeAPIs(false) + .retentionPolicy(retentionPolicy) + .version("1.0"); + + FileServiceProperties properties = new FileServiceProperties().hourMetrics(metrics) + .minuteMetrics(metrics) + .cors(Collections.singletonList(new CorsRule().allowedMethods("NOTAREALHTTPMETHOD"))); + + try { + fileServiceClient.setProperties(properties); + fail("Attempting to set an invalid allowed method should throw an exception."); + } catch (Exception exception) { + FileTestHelpers.assertExceptionStatusCode(exception, 400); + } + } +} diff --git a/storage/client/file/src/test/java/com/azure/storage/file/FileTestHelpers.java b/storage/client/file/src/test/java/com/azure/storage/file/FileTestHelpers.java new file mode 100644 index 0000000000000..44a7d27bf7c50 --- /dev/null +++ b/storage/client/file/src/test/java/com/azure/storage/file/FileTestHelpers.java @@ -0,0 +1,257 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.file; + +import com.azure.core.http.rest.Response; +import com.azure.core.implementation.util.ImplUtils; +import com.azure.core.test.InterceptorManager; +import com.azure.core.test.TestBase; +import com.azure.core.test.TestMode; +import com.azure.core.util.configuration.ConfigurationManager; +import com.azure.core.util.logging.ClientLogger; +import com.azure.storage.file.models.CorsRule; +import com.azure.storage.file.models.FileRef; +import com.azure.storage.file.models.FileServiceProperties; +import com.azure.storage.file.models.Metrics; +import com.azure.storage.file.models.RetentionPolicy; +import com.azure.storage.file.models.ShareItem; +import com.azure.storage.file.models.SignedIdentifier; +import com.azure.storage.file.models.StorageErrorException; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.security.NoSuchAlgorithmException; +import java.time.Duration; +import java.util.List; +import java.util.Locale; +import java.util.function.BiFunction; +import org.junit.Assert; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +class FileTestHelpers { + private static final String AZURE_TEST_MODE = "AZURE_TEST_MODE"; + + static T setupClient(BiFunction clientBuilder, boolean isPlayback, ClientLogger logger) { + String connectionString = "DefaultEndpointsProtocol=https;AccountName=teststorage;AccountKey=atestaccountkey;EndpointSuffix=core.windows.net"; + String endpoint = "https://teststorage.file.core.windows.net/"; + + if (!isPlayback) { + connectionString = ConfigurationManager.getConfiguration().get("AZURE_STORAGE_CONNECTION_STRING"); + endpoint = ConfigurationManager.getConfiguration().get("AZURE_STORAGE_FILE_ENDPOINT"); + } + + if (ImplUtils.isNullOrEmpty(connectionString) && ImplUtils.isNullOrEmpty(endpoint)) { + logger.asWarning().log("Connection string or endpoint must be set to buildClient the testing client"); + fail(); + return null; + } + + return clientBuilder.apply(connectionString, endpoint); + } + + static ShareClientBuilder createShareClientWithSnapshot(InterceptorManager interceptorManager, String shareName, String snapshot) { + String connectionString = "DefaultEndpointsProtocol=https;AccountName=teststorage;AccountKey=atestaccountkey;EndpointSuffix=core.windows.net"; + String endpoint = "https://teststorage.file.core.windows.net/"; + + ShareClientBuilder shareClientBuilder; + if (!interceptorManager.isPlaybackMode()) { + connectionString = ConfigurationManager.getConfiguration().get("AZURE_STORAGE_CONNECTION_STRING"); + endpoint = ConfigurationManager.getConfiguration().get("AZURE_STORAGE_FILE_ENDPOINT"); + shareClientBuilder = new ShareClientBuilder() + .endpoint(endpoint) + .connectionString(connectionString) + .addPolicy(interceptorManager.getRecordPolicy()) + .shareName(shareName) + .snapshot(snapshot); + } else { + shareClientBuilder = new ShareClientBuilder() + .endpoint(endpoint) + .connectionString(connectionString) + .httpClient(interceptorManager.getPlaybackClient()) + .shareName(shareName) + .snapshot(snapshot); + } + return shareClientBuilder; + } + + static void assertResponseStatusCode(Response response, int expectedStatusCode) { + assertEquals(expectedStatusCode, response.statusCode()); + } + + static void assertResponseListStatusCode(Response response, List expectedStatusCodeList) { + for (Integer expectedStatusCode: expectedStatusCodeList) { + if (expectedStatusCode == response.statusCode()) { + return; + } + } + fail("The response status code did not include in the list."); + } + + static void assertExceptionStatusCode(Throwable throwable, int expectedStatusCode) { + assertTrue(throwable instanceof StorageErrorException); + StorageErrorException exception = (StorageErrorException) throwable; + assertEquals(expectedStatusCode, exception.response().statusCode()); + } + + static void assertExceptionErrorMessage(Throwable throwable, String message) { + assertTrue(throwable instanceof StorageErrorException); + StorageErrorException exception = (StorageErrorException) throwable; + assertTrue(exception.getMessage().contains(message)); + } + + static void assertExceptionStatusCode(Runnable thrower, int expectedStatusCode) { + try { + thrower.run(); + fail(); + } catch (Exception ex) { + assertTrue(ex instanceof StorageErrorException); + StorageErrorException exception = (StorageErrorException) ex; + assertEquals(expectedStatusCode, exception.response().statusCode()); + } + } + + static void sleepInRecordMode(Duration duration) { + if (getTestMode() == TestMode.RECORD) { + sleep(duration); + } + } + + static void sleep(Duration duration) { + try { + Thread.sleep(duration.toMillis()); + } catch (InterruptedException ex) { + // Ignore the exception + } + } + + static void assertSharesAreEqual(ShareItem expected, ShareItem actual) { + if (expected == null) { + assertNull(actual); + } else { + assertEquals(expected.name(), actual.name()); + assertEquals(expected.properties().quota(), actual.properties().quota()); + + if (expected.metadata() != null) { + assertEquals(expected.metadata(), actual.metadata()); + } + + if (expected.snapshot() != null) { + assertEquals(expected.snapshot(), actual.snapshot()); + } + } + } + + static void assertFileServicePropertiesAreEqual(FileServiceProperties expected, FileServiceProperties actual) { + if (expected == null) { + assertNull(actual); + } else { + assertMetricsAreEqual(expected.hourMetrics(), actual.hourMetrics()); + assertMetricsAreEqual(expected.minuteMetrics(), actual.minuteMetrics()); + assertCorsAreEqual(expected.cors(), actual.cors()); + } + } + + static void assertFileRefsAreEqual(FileRef expected, FileRef actual) { + if (expected == null) { + assertNull(actual); + } else { + assertEquals(expected.name(), actual.name()); + assertEquals(expected.isDirectory(), actual.isDirectory()); + + if (expected.fileProperties() != null && actual.fileProperties() != null) { + assertEquals(expected.fileProperties().contentLength(), actual.fileProperties().contentLength()); + } + } + } + + static void assertPermissionsAreEqual(SignedIdentifier expected, SignedIdentifier actual) { + assertEquals(expected.id(), actual.id()); + assertEquals(expected.accessPolicy().permission(), actual.accessPolicy().permission()); + assertEquals(expected.accessPolicy().start(), actual.accessPolicy().start()); + assertEquals(expected.accessPolicy().expiry(), actual.accessPolicy().expiry()); + } + + static TestMode getTestMode() { + final Logger logger = LoggerFactory.getLogger(TestBase.class); + final String azureTestMode = ConfigurationManager.getConfiguration().get(AZURE_TEST_MODE); + + if (azureTestMode != null) { + try { + return TestMode.valueOf(azureTestMode.toUpperCase(Locale.US)); + } catch (IllegalArgumentException e) { + if (logger.isErrorEnabled()) { + logger.error("Could not parse '{}' into TestEnum. Using 'Playback' mode.", azureTestMode); + } + + return TestMode.PLAYBACK; + } + } + + if (logger.isInfoEnabled()) { + logger.info("Environment variable '{}' has not been set yet. Using 'Playback' mode.", AZURE_TEST_MODE); + } + return TestMode.PLAYBACK; + } + + private static void assertMetricsAreEqual(Metrics expected, Metrics actual) { + if (expected == null) { + assertNull(actual); + } else { + assertEquals(expected.enabled(), actual.enabled()); + assertEquals(expected.includeAPIs(), actual.includeAPIs()); + assertEquals(expected.version(), actual.version()); + assertRetentionPoliciesAreEqual(expected.retentionPolicy(), actual.retentionPolicy()); + } + } + + static void assertTwoFilesAreSame(File f1, File f2) throws IOException, NoSuchAlgorithmException { + List uploadFileString = Files.readAllLines(f1.toPath()); + List downloadFileString = Files.readAllLines(f2.toPath()); + if (uploadFileString != null && downloadFileString != null) { + downloadFileString.removeAll(uploadFileString); + } + while (!downloadFileString.isEmpty()) { + Assert.assertTrue("The download file is supposed to be the same as the upload file.", downloadFileString.get(0).isEmpty()); + downloadFileString.remove(0); + } + } + + private static void assertRetentionPoliciesAreEqual(RetentionPolicy expected, RetentionPolicy actual) { + if (expected == null) { + assertNull(actual); + } else { + assertEquals(expected.days(), actual.days()); + assertEquals(expected.enabled(), actual.enabled()); + } + } + + private static void assertCorsAreEqual(List expected, List actual) { + if (expected == null) { + assertTrue(ImplUtils.isNullOrEmpty(actual)); + } else { + assertEquals(expected.size(), actual.size()); + for (int i = 0; i < expected.size(); i++) { + assertCorRulesAreEqual(expected.get(i), actual.get(i)); + } + } + } + + private static void assertCorRulesAreEqual(CorsRule expected, CorsRule actual) { + if (expected == null) { + assertNull(actual); + } else { + assertEquals(expected.allowedHeaders(), actual.allowedHeaders()); + assertEquals(expected.allowedMethods(), actual.allowedMethods()); + assertEquals(expected.allowedOrigins(), actual.allowedOrigins()); + assertEquals(expected.exposedHeaders(), actual.exposedHeaders()); + assertEquals(expected.maxAgeInSeconds(), actual.maxAgeInSeconds()); + } + } +} diff --git a/storage/client/file/src/test/java/com/azure/storage/file/LargeFileTest.java b/storage/client/file/src/test/java/com/azure/storage/file/LargeFileTest.java new file mode 100644 index 0000000000000..aecce061802c8 --- /dev/null +++ b/storage/client/file/src/test/java/com/azure/storage/file/LargeFileTest.java @@ -0,0 +1,129 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.file; + +import com.azure.core.test.TestBase; +import com.azure.core.test.TestMode; +import com.azure.core.util.configuration.ConfigurationManager; +import com.azure.core.util.logging.ClientLogger; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.net.URL; +import java.nio.file.Files; +import java.security.MessageDigest; +import java.time.Duration; +import java.time.OffsetDateTime; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; + +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; + +public class LargeFileTest extends TestBase { + private final ClientLogger largeFileLogger = new ClientLogger(LargeFileTest.class); + private final String azureStorageConnectionString = ConfigurationManager.getConfiguration().get("AZURE_STORAGE_CONNECTION_STRING"); + private static final String LARGE_TEST_FOLDER = "test-large-files/"; + private static FileClient largeFileClient; + private static String fileName; + private static File largeFile; + private ShareClient shareClient; + private File dirPath; + + @Rule + public TestName testName = new TestName(); + + @Before + public void setup() { + assumeTrue("The test is only for live mode.", FileTestHelpers.getTestMode() == TestMode.RECORD); + fileName = testResourceNamer.randomName("largefile", 16); + URL folderUrl = LargeFileTest.class.getClassLoader().getResource("."); + dirPath = new File(folderUrl.getPath() + LARGE_TEST_FOLDER); + if (dirPath.exists() || dirPath.mkdir()) { + largeFile = new File(folderUrl.getPath() + LARGE_TEST_FOLDER + fileName); + } else { + largeFileLogger.asWarning().log("Failed to create the large file dir."); + } + String shareName = testResourceNamer.randomName("largefileshare", 32); + shareClient = new ShareClientBuilder().connectionString(azureStorageConnectionString).shareName(shareName) + .buildClient(); + shareClient.create(); + shareClient.createDirectory("largefiledir"); + largeFileClient = shareClient.getDirectoryClient("largefiledir").getFileClient(fileName); + } + + @Ignore + @Test + public void uploadAndDownloadLargeFile() throws Exception { + Long fileSize = 5 * 1024 * 1024L; + RandomAccessFile raf = new RandomAccessFile(largeFile, "rw"); + raf.setLength(fileSize); + largeFileClient.create(fileSize); + if (largeFile.exists()) { + largeFileClient.uploadFromFile(largeFile.getPath()); + largeFileLogger.asWarning().log("Uploaded success!"); + } else { + fail("Did not find the upload file."); + } + OffsetDateTime start = OffsetDateTime.now(); + File downloadFile = new File(dirPath + "/" + testResourceNamer.randomName("download", 16)); + downloadFile.createNewFile(); + largeFileClient.downloadToFile(downloadFile.getPath()); + System.out.println("Download " + downloadFile.length() + " bytes took " + Duration.between(start, OffsetDateTime.now()).getSeconds() + " seconds"); + if (Files.exists(downloadFile.toPath()) && Files.exists(largeFile.toPath())) { + String checksumUpload = getFileChecksum(largeFile); + String checksumDownload = getFileChecksum(downloadFile); + Assert.assertEquals(checksumUpload, checksumDownload); + } else { + fail("Did not find the download file."); + } + } + + @After + public void cleanUp() { + assumeTrue("The test is only for live mode.", FileTestHelpers.getTestMode() == TestMode.RECORD); + shareClient.delete(); + FileTestHelpers.sleepInRecordMode(Duration.ofSeconds(45)); + } + + private String getFileChecksum(File file) throws Exception { + MessageDigest md = MessageDigest.getInstance("MD5"); + md.update(Files.readAllBytes(file.toPath())); + int numRead; + InputStream fis = new FileInputStream(file.getPath()); + byte[] buffer = new byte[1024]; + do { + numRead = fis.read(buffer); + if (numRead > 0) { + md.update(buffer, 0, numRead); + } + } while (numRead != -1); + + fis.close(); + byte[] digestByte = md.digest(); + String result = ""; + for (int i = 0; i < digestByte.length; i++) { + result += Integer.toString((digestByte[i] & 0xff) + 0x100, 16).substring(1); + } + return result; + } + + /** + * Gets the name of the current test being run. + *

                  + * NOTE: This could not be implemented in the base class using {@link TestName} because it always returns {@code + * null}. See https://stackoverflow.com/a/16113631/4220757. + * + * @return The name of the current test. + */ + @Override + protected String testName() { + return testName.getMethodName(); + } +} diff --git a/storage/client/file/src/test/java/com/azure/storage/file/ShareAsyncClientTests.java b/storage/client/file/src/test/java/com/azure/storage/file/ShareAsyncClientTests.java new file mode 100644 index 0000000000000..d98273108b491 --- /dev/null +++ b/storage/client/file/src/test/java/com/azure/storage/file/ShareAsyncClientTests.java @@ -0,0 +1,618 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.file; + +import com.azure.core.http.HttpClient; +import com.azure.core.http.policy.HttpLogDetailLevel; +import com.azure.core.http.rest.Response; +import com.azure.core.util.logging.ClientLogger; +import com.azure.storage.file.models.AccessPolicy; +import com.azure.storage.file.models.ShareSnapshotInfo; +import com.azure.storage.file.models.SignedIdentifier; +import com.azure.storage.file.models.StorageErrorException; +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.junit.Assert; +import reactor.test.StepVerifier; + +import static com.azure.storage.file.FileTestHelpers.createShareClientWithSnapshot; +import static com.azure.storage.file.FileTestHelpers.setupClient; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class ShareAsyncClientTests extends ShareClientTestBase { + private final ClientLogger shareAsyncLogger = new ClientLogger(ShareAsyncClientTests.class); + + private ShareAsyncClient shareAsyncClient; + + @Override + public void beforeTest() { + shareName = getShareName(); + + if (interceptorManager.isPlaybackMode()) { + shareAsyncClient = setupClient((connectionString, endpoint) -> new ShareClientBuilder() + .connectionString(connectionString) + .endpoint(endpoint) + .shareName(shareName) + .httpClient(interceptorManager.getPlaybackClient()) + .httpLogDetailLevel(HttpLogDetailLevel.BODY_AND_HEADERS) + .buildAsyncClient(), true, shareAsyncLogger); + } else { + shareAsyncClient = setupClient((connectionString, endpoint) -> new ShareClientBuilder() + .connectionString(connectionString) + .endpoint(endpoint) + .shareName(shareName) + .httpClient(HttpClient.createDefault().wiretap(true)) + .httpLogDetailLevel(HttpLogDetailLevel.BODY_AND_HEADERS) + .addPolicy(interceptorManager.getRecordPolicy()) + .buildAsyncClient(), false, shareAsyncLogger); + } + } + + @Override + public void afterTest() { + try { + shareAsyncClient.delete().block(); + } catch (StorageErrorException ex) { + // Ignore the exception as the share is already deleted and that is what we wanted. + } + } + + @Override + public void getRootDirectoryDoesNotCreateADirectory() { + shareAsyncClient.create().block(); + DirectoryAsyncClient directoryAsyncClient = shareAsyncClient.getRootDirectoryClient(); + StepVerifier.create(directoryAsyncClient.getProperties()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 200)) + .verifyComplete(); + } + + @Override + public void getDirectoryDoesNotCreateADirectory() { + shareAsyncClient.create().block(); + DirectoryAsyncClient directoryAsyncClient = shareAsyncClient.getDirectoryClient("testshare"); + Assert.assertNotNull(directoryAsyncClient); + StepVerifier.create(directoryAsyncClient.getProperties()) + .verifyErrorSatisfies(response -> FileTestHelpers.assertExceptionStatusCode(response, 404)); + } + + @Override + public void createDirectoryFromShareClient() { + shareAsyncClient.create().block(); + StepVerifier.create(shareAsyncClient.createDirectory("testshare")) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + } + + @Override + public void createDirectoryInvalidNameFromShareClient() { + shareAsyncClient.create().block(); + StepVerifier.create(shareAsyncClient.createDirectory("test/share")) + .verifyErrorSatisfies(response -> FileTestHelpers.assertExceptionStatusCode(response, 404)); + } + + @Override + public void createDirectoryAlreadyExistsFromShareClient() { + shareAsyncClient.create().block(); + shareAsyncClient.createDirectory("testshare").block(); + StepVerifier.create(shareAsyncClient.createDirectory("testshare")) + .verifyErrorSatisfies(response -> FileTestHelpers.assertExceptionStatusCode(response, 409)); + } + + @Override + public void deleteDirectoryFromShareClient() { + shareAsyncClient.create().block(); + shareAsyncClient.createDirectory("testshare").block(); + StepVerifier.create(shareAsyncClient.deleteDirectory("testshare")) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 202)) + .verifyComplete(); + } + + @Override + public void deleteDirectoryDoesNotExistFromShareClient() { + shareAsyncClient.create().block(); + StepVerifier.create(shareAsyncClient.deleteDirectory("testshare")) + .verifyErrorSatisfies(response -> FileTestHelpers.assertExceptionStatusCode(response, 404)); + } + + @Override + public void createFromShareClient() { + StepVerifier.create(shareAsyncClient.create()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + } + + @Override + public void createTwiceSameMetadataFromShareClient() { + Map metadata = Collections.singletonMap("test", "metadata"); + + StepVerifier.create(shareAsyncClient.create(metadata, 2)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(shareAsyncClient.create(metadata, 2)) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 409)); + } + + @Override + public void createTwiceDifferentMetadataFromShareClient() { + Map metadata = Collections.singletonMap("test", "metadata"); + + StepVerifier.create(shareAsyncClient.create()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(shareAsyncClient.create(metadata, 2)) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 409)); + } + + @Override + public void createInvalidQuotaFromShareClient() { + StepVerifier.create(shareAsyncClient.create(null, -1)) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 400)); + + StepVerifier.create(shareAsyncClient.create(null, 0)) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 400)); + } + + @Override + public void deleteFromShareClient() { + StepVerifier.create(shareAsyncClient.create()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(shareAsyncClient.delete()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 202)) + .verifyComplete(); + } + + @Override + public void deleteDoesNotExistFromShareClient() { + StepVerifier.create(shareAsyncClient.delete()) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 404)); + } + + @Override + public void deleteThenCreateFromShareClient() { + StepVerifier.create(shareAsyncClient.create()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(shareAsyncClient.delete()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 202)) + .verifyComplete(); + + FileTestHelpers.sleepInRecordMode(Duration.ofSeconds(45)); + + StepVerifier.create(shareAsyncClient.create()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + } + + @Override + public void deleteThenCreateTooSoonFromShareClient() { + StepVerifier.create(shareAsyncClient.create()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(shareAsyncClient.delete()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 202)) + .verifyComplete(); + + StepVerifier.create(shareAsyncClient.create()) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 409)); + } + + @Override + public void snapshot() { + StepVerifier.create(shareAsyncClient.create()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(shareAsyncClient.createSnapshot()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + } + + @Override + public void deleteSnapshotFromShareClient() { + StepVerifier.create(shareAsyncClient.create()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + Response snapshotInfoResponse = shareAsyncClient.createSnapshot().block(); + assertNotNull(snapshotInfoResponse); + FileTestHelpers.assertResponseStatusCode(snapshotInfoResponse, 201); + + ShareAsyncClient shareAsyncClientWithSnapshot = createShareClientWithSnapshot( + interceptorManager, shareName, snapshotInfoResponse.value().snapshot()).buildAsyncClient(); + StepVerifier.create(shareAsyncClientWithSnapshot.delete()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 202)) + .verifyComplete(); + + StepVerifier.create(shareAsyncClient.createSnapshot()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + } + + @Override + public void snapshotSameMetadata() { + Map metadata = Collections.singletonMap("test", "metadata"); + + StepVerifier.create(shareAsyncClient.create(metadata, 2)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + Response snapshotInfoResponse = shareAsyncClient.createSnapshot(metadata).block(); + assertNotNull(snapshotInfoResponse); + FileTestHelpers.assertResponseStatusCode(snapshotInfoResponse, 201); + + ShareAsyncClient shareAsyncClientWithSnapshot = createShareClientWithSnapshot( + interceptorManager, shareName, snapshotInfoResponse.value().snapshot()).buildAsyncClient(); + StepVerifier.create(shareAsyncClientWithSnapshot.getProperties()) + .assertNext(response -> { + FileTestHelpers.assertResponseStatusCode(response, 200); + assertEquals(metadata, response.value().metadata()); + }) + .verifyComplete(); + } + + @Override + public void snapshotDifferentMetadata() { + Map createMetadata = Collections.singletonMap("create", "metadata"); + + StepVerifier.create(shareAsyncClient.create(createMetadata, 2)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + Map updateMetadata = Collections.singletonMap("update", "metadata"); + Response snapshotInfoResponse = shareAsyncClient.createSnapshot(updateMetadata).block(); + assertNotNull(snapshotInfoResponse); + FileTestHelpers.assertResponseStatusCode(snapshotInfoResponse, 201); + + StepVerifier.create(shareAsyncClient.getProperties()) + .assertNext(response -> { + FileTestHelpers.assertResponseStatusCode(response, 200); + assertEquals(createMetadata, response.value().metadata()); + }) + .verifyComplete(); + ShareAsyncClient shareAsyncClientWithSnapshot = createShareClientWithSnapshot(interceptorManager, + shareName, snapshotInfoResponse.value().snapshot()).buildAsyncClient(); + + StepVerifier.create(shareAsyncClientWithSnapshot.getProperties()) + .assertNext(response -> { + FileTestHelpers.assertResponseStatusCode(response, 200); + assertEquals(updateMetadata, response.value().metadata()); + }) + .verifyComplete(); + } + + @Override + public void snapshotDoesNotExist() { + StepVerifier.create(shareAsyncClient.createSnapshot()) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 404)); + } + + @Override + public void getPropertiesFromShareClient() { + final int quotaInGB = 2; + Map metadata = Collections.singletonMap("test", "metadata"); + + StepVerifier.create(shareAsyncClient.create(metadata, quotaInGB)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(shareAsyncClient.getProperties()) + .assertNext(response -> { + FileTestHelpers.assertResponseStatusCode(response, 200); + assertEquals(quotaInGB, response.value().quota()); + assertEquals(metadata, response.value().metadata()); + }) + .verifyComplete(); + } + + @Override + public void getSnapshotPropertiesFromShareClient() { + final int quotaInGB = 2; + Map snapshotMetadata = Collections.singletonMap("snapshot", "metadata"); + + StepVerifier.create(shareAsyncClient.create(null, quotaInGB)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + Response snapshotInfoResponse = shareAsyncClient.createSnapshot(snapshotMetadata).block(); + assertNotNull(snapshotInfoResponse); + FileTestHelpers.assertResponseStatusCode(snapshotInfoResponse, 201); + ShareAsyncClient shareAsyncClientWithSnapshot = createShareClientWithSnapshot(interceptorManager, + shareName, snapshotInfoResponse.value().snapshot()).buildAsyncClient(); + StepVerifier.create(shareAsyncClientWithSnapshot.getProperties()) + .assertNext(response -> { + FileTestHelpers.assertResponseStatusCode(response, 200); + assertEquals(quotaInGB, response.value().quota()); + assertEquals(snapshotMetadata, response.value().metadata()); + }) + .verifyComplete(); + } + + @Override + public void getPropertiesDoesNotExistFromShareClient() { + StepVerifier.create(shareAsyncClient.getProperties()) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 404)); + } + + @Override + public void getSnapshotPropertiesDoesNotExist() { + StepVerifier.create(shareAsyncClient.create()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + ShareAsyncClient shareAsyncClientWithSnapshot = createShareClientWithSnapshot(interceptorManager, + shareName, "snapshot").buildAsyncClient(); + StepVerifier.create(shareAsyncClientWithSnapshot.getProperties()) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 400)); + } + + @Override + public void setPropertiesFromShareClient() { + final int initialQuoteInGB = 2; + + StepVerifier.create(shareAsyncClient.create(null, initialQuoteInGB)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(shareAsyncClient.getProperties()) + .assertNext(response -> { + FileTestHelpers.assertResponseStatusCode(response, 200); + assertEquals(initialQuoteInGB, response.value().quota()); + }) + .verifyComplete(); + + final int updatedQuotaInGB = 4; + StepVerifier.create(shareAsyncClient.setQuota(updatedQuotaInGB)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 200)) + .verifyComplete(); + + StepVerifier.create(shareAsyncClient.getProperties()) + .assertNext(response -> { + FileTestHelpers.assertResponseStatusCode(response, 200); + assertEquals(updatedQuotaInGB, response.value().quota()); + }) + .verifyComplete(); + } + + @Override + public void setPropertiesInvalidQuotaFromShareClient() { + StepVerifier.create(shareAsyncClient.create()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(shareAsyncClient.setQuota(-1)) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 400)); + + StepVerifier.create(shareAsyncClient.setQuota(9999)) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 400)); + } + + @Override + public void setPropertiesDoesNotExistFromShareClient() { + StepVerifier.create(shareAsyncClient.setQuota(2)) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 404)); + } + + @Override + public void getMetadataFromShareClient() { + Map metadata = Collections.singletonMap("test", "metadata"); + StepVerifier.create(shareAsyncClient.create(metadata, 2)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(shareAsyncClient.getProperties()) + .assertNext(response -> { + FileTestHelpers.assertResponseStatusCode(response, 200); + assertEquals(metadata, response.value().metadata()); + }) + .verifyComplete(); + } + + @Override + public void getSnapshotMetadataFromShareClient() { + Map metadata = Collections.singletonMap("test", "metadata"); + StepVerifier.create(shareAsyncClient.create(metadata, 2)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + Response snapshotInfoResponse = shareAsyncClient.createSnapshot().block(); + assertNotNull(snapshotInfoResponse); + FileTestHelpers.assertResponseStatusCode(snapshotInfoResponse, 201); + + ShareAsyncClient shareAsyncClientWithSnapshot = createShareClientWithSnapshot(interceptorManager, + shareName, snapshotInfoResponse.value().snapshot()).buildAsyncClient(); + StepVerifier.create(shareAsyncClientWithSnapshot.getProperties()) + .assertNext(response -> { + FileTestHelpers.assertResponseStatusCode(response, 200); + assertEquals(metadata, response.value().metadata()); + }) + .verifyComplete(); + } + + @Override + public void getMetadataDoesNotExistFromShareClient() { + StepVerifier.create(shareAsyncClient.getProperties()) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 404)); + } + + @Override + public void getSnapshotMetadataDoesNotExistFromShareClient() { + StepVerifier.create(shareAsyncClient.create()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + ShareAsyncClient shareAsyncClientWithSnapshot = createShareClientWithSnapshot(interceptorManager, + shareName, "snapshot").buildAsyncClient(); + StepVerifier.create(shareAsyncClientWithSnapshot.getProperties()) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 400)); + } + + @Override + public void setMetadataFromShareClient() { + StepVerifier.create(shareAsyncClient.create()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + Map metadata = Collections.singletonMap("setting", "metadata"); + StepVerifier.create(shareAsyncClient.setMetadata(metadata)) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 200)) + .verifyComplete(); + + StepVerifier.create(shareAsyncClient.getProperties()) + .assertNext(response -> { + FileTestHelpers.assertResponseStatusCode(response, 200); + assertEquals(metadata, response.value().metadata()); + }) + .verifyComplete(); + } + + @Override + public void setMetadataInvalidMetadataFromShareClient() { + StepVerifier.create(shareAsyncClient.create()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + Map metadata = Collections.singletonMap("", "metadata"); + StepVerifier.create(shareAsyncClient.setMetadata(metadata)) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 400)); + } + + @Override + public void setMetadataDoesNotExistFromShareClient() { + Map metadata = Collections.singletonMap("test", "metadata"); + StepVerifier.create(shareAsyncClient.setMetadata(metadata)) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 404)); + } + + @Override + public void getPolicies() { + StepVerifier.create(shareAsyncClient.create()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(shareAsyncClient.getAccessPolicy()) + .expectNextCount(0) + .verifyComplete(); + } + + @Override + public void getPoliciesDoesNotExist() { + StepVerifier.create(shareAsyncClient.getAccessPolicy()) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 404)); + } + + @Override + public void setPolicies() { + StepVerifier.create(shareAsyncClient.create()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + AccessPolicy policy = new AccessPolicy().permission("r") + .start(OffsetDateTime.of(LocalDateTime.of(2000, 1, 1, 0, 0), ZoneOffset.UTC)) + .expiry(OffsetDateTime.of(LocalDateTime.of(2020, 1, 1, 0, 0), ZoneOffset.UTC)); + + SignedIdentifier permission = new SignedIdentifier().id("test") + .accessPolicy(policy); + + StepVerifier.create(shareAsyncClient.setAccessPolicy(Collections.singletonList(permission))) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 200)) + .verifyComplete(); + + StepVerifier.create(shareAsyncClient.getAccessPolicy()) + .assertNext(responsePermission -> FileTestHelpers.assertPermissionsAreEqual(permission, responsePermission)) + .verifyComplete(); + } + + @Override + public void setPoliciesInvalidPermission() { + StepVerifier.create(shareAsyncClient.create()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + AccessPolicy policy = new AccessPolicy().permission("abcdefg") + .start(OffsetDateTime.of(LocalDateTime.of(2000, 1, 1, 0, 0), ZoneOffset.UTC)) + .expiry(OffsetDateTime.of(LocalDateTime.of(2020, 1, 1, 0, 0), ZoneOffset.UTC)); + + SignedIdentifier permission = new SignedIdentifier().id("test") + .accessPolicy(policy); + + StepVerifier.create(shareAsyncClient.setAccessPolicy(Collections.singletonList(permission))) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 400)); + } + + @Override + public void setPoliciesTooManyPermissions() { + StepVerifier.create(shareAsyncClient.create()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + List permissions = new ArrayList<>(); + for (int i = 0; i < 6; i++) { + AccessPolicy policy = new AccessPolicy().permission("r") + .start(OffsetDateTime.of(LocalDateTime.of(2000, 1, 1, 0, 0), ZoneOffset.UTC)) + .expiry(OffsetDateTime.of(LocalDateTime.of(2020, 1, 1, 0, 0), ZoneOffset.UTC)); + + permissions.add(new SignedIdentifier().id("test" + i).accessPolicy(policy)); + } + + StepVerifier.create(shareAsyncClient.setAccessPolicy(permissions)) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 400)); + } + + @Override + public void setPoliciesDoesNotExist() { + AccessPolicy policy = new AccessPolicy().permission("r") + .start(OffsetDateTime.of(LocalDateTime.of(2000, 1, 1, 0, 0), ZoneOffset.UTC)) + .expiry(OffsetDateTime.of(LocalDateTime.of(2020, 1, 1, 0, 0), ZoneOffset.UTC)); + + SignedIdentifier permission = new SignedIdentifier().id("test") + .accessPolicy(policy); + + StepVerifier.create(shareAsyncClient.setAccessPolicy(Collections.singletonList(permission))) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 404)); + } + + @Override + public void getStats() { + StepVerifier.create(shareAsyncClient.create()) + .assertNext(response -> FileTestHelpers.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(shareAsyncClient.getStatistics()) + .assertNext(response -> { + FileTestHelpers.assertResponseStatusCode(response, 200); + assertEquals(0, response.value().getShareUsageInGB()); + }) + .verifyComplete(); + } + + @Override + public void getStatsDoesNotExist() { + StepVerifier.create(shareAsyncClient.getStatistics()) + .verifyErrorSatisfies(throwable -> FileTestHelpers.assertExceptionStatusCode(throwable, 404)); + } + + @Override + public void getSnapshotId() { + shareAsyncClient.create().block(); + StepVerifier.create(shareAsyncClient.createSnapshot()) + .assertNext(response -> { + ShareAsyncClient shareAsyncClientWithSnapshot = createShareClientWithSnapshot(interceptorManager, + shareName, response.value().snapshot()).buildAsyncClient(); + Assert.assertEquals(response.value().snapshot(), shareAsyncClientWithSnapshot.getSnapshotId()); + }) + .verifyComplete(); + + } +} diff --git a/storage/client/file/src/test/java/com/azure/storage/file/ShareClientTestBase.java b/storage/client/file/src/test/java/com/azure/storage/file/ShareClientTestBase.java new file mode 100644 index 0000000000000..fd1d1b81206da --- /dev/null +++ b/storage/client/file/src/test/java/com/azure/storage/file/ShareClientTestBase.java @@ -0,0 +1,170 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.file; + +import com.azure.core.test.TestBase; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TestName; + +public abstract class ShareClientTestBase extends TestBase { + String shareName; + + @Rule + public TestName testName = new TestName(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + /** + * Gets the name of the current test being run. + *

                  + * NOTE: This could not be implemented in the base class using {@link TestName} because it always returns {@code + * null}. See https://stackoverflow.com/a/16113631/4220757. + * + * @return The name of the current test. + */ + + @Override + public String testName() { + return testName.getMethodName(); + } + + String getShareName() { + return testResourceNamer.randomName("share", 16).toLowerCase(); + } + + @Test + public abstract void getRootDirectoryDoesNotCreateADirectory(); + + @Test + public abstract void getDirectoryDoesNotCreateADirectory(); + + @Test + public abstract void createDirectoryFromShareClient(); + + @Test + public abstract void createDirectoryInvalidNameFromShareClient(); + + @Test + public abstract void createDirectoryAlreadyExistsFromShareClient(); + + @Test + public abstract void deleteDirectoryFromShareClient(); + + @Test + public abstract void deleteDirectoryDoesNotExistFromShareClient(); + + @Test + public abstract void createFromShareClient(); + + @Test + public abstract void createTwiceSameMetadataFromShareClient(); + + @Test + public abstract void createTwiceDifferentMetadataFromShareClient(); + + @Test + public abstract void createInvalidQuotaFromShareClient(); + + @Test + public abstract void deleteFromShareClient(); + + @Test + public abstract void deleteDoesNotExistFromShareClient(); + + @Test + public abstract void deleteThenCreateFromShareClient(); + + /** + * Cannot re-create a share within 30 seconds of it being deleted. + */ + @Test + public abstract void deleteThenCreateTooSoonFromShareClient(); + + @Test + public abstract void snapshot(); + + @Test + public abstract void deleteSnapshotFromShareClient(); + + @Test + public abstract void snapshotSameMetadata(); + + @Test + public abstract void snapshotDifferentMetadata(); + + @Test + public abstract void snapshotDoesNotExist(); + + @Test + public abstract void getPropertiesFromShareClient(); + + @Test + public abstract void getSnapshotPropertiesFromShareClient(); + + @Test + public abstract void getPropertiesDoesNotExistFromShareClient(); + + @Test + public abstract void getSnapshotPropertiesDoesNotExist(); + + @Test + public abstract void setPropertiesFromShareClient(); + + @Test + public abstract void setPropertiesInvalidQuotaFromShareClient(); + + @Test + public abstract void setPropertiesDoesNotExistFromShareClient(); + + @Test + public abstract void getMetadataFromShareClient(); + + @Test + public abstract void getSnapshotMetadataFromShareClient(); + + @Test + public abstract void getMetadataDoesNotExistFromShareClient(); + + @Test + public abstract void getSnapshotMetadataDoesNotExistFromShareClient(); + + @Test + public abstract void setMetadataFromShareClient(); + + @Test + public abstract void setMetadataInvalidMetadataFromShareClient(); + + @Test + public abstract void setMetadataDoesNotExistFromShareClient(); + + @Test + public abstract void getPolicies(); + + @Test + public abstract void getPoliciesDoesNotExist(); + + @Test + public abstract void setPolicies(); + + @Test + public abstract void setPoliciesInvalidPermission(); + + @Test + public abstract void setPoliciesTooManyPermissions(); + + @Test + public abstract void setPoliciesDoesNotExist(); + + @Test + public abstract void getStats(); + + @Test + public abstract void getStatsDoesNotExist(); + + @Test + public abstract void getSnapshotId(); +} diff --git a/storage/client/file/src/test/java/com/azure/storage/file/ShareClientTests.java b/storage/client/file/src/test/java/com/azure/storage/file/ShareClientTests.java new file mode 100644 index 0000000000000..059d704630654 --- /dev/null +++ b/storage/client/file/src/test/java/com/azure/storage/file/ShareClientTests.java @@ -0,0 +1,490 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.file; + +import com.azure.core.http.HttpClient; +import com.azure.core.http.policy.HttpLogDetailLevel; +import com.azure.core.http.rest.Response; +import com.azure.core.util.logging.ClientLogger; +import com.azure.storage.file.models.AccessPolicy; +import com.azure.storage.file.models.DirectoryProperties; +import com.azure.storage.file.models.ShareProperties; +import com.azure.storage.file.models.ShareSnapshotInfo; +import com.azure.storage.file.models.ShareStatistics; +import com.azure.storage.file.models.SignedIdentifier; +import com.azure.storage.file.models.StorageErrorException; +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import org.junit.Assert; + +import static com.azure.storage.file.FileTestHelpers.createShareClientWithSnapshot; +import static com.azure.storage.file.FileTestHelpers.setupClient; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +public class ShareClientTests extends ShareClientTestBase { + private final ClientLogger shareLogger = new ClientLogger(ShareClient.class); + + private ShareClient shareClient; + + @Override + public void beforeTest() { + shareName = getShareName(); + + if (interceptorManager.isPlaybackMode()) { + shareClient = setupClient((connectionString, endpoint) -> new ShareClientBuilder() + .connectionString(connectionString) + .endpoint(endpoint) + .shareName(shareName) + .httpClient(interceptorManager.getPlaybackClient()) + .httpLogDetailLevel(HttpLogDetailLevel.BODY_AND_HEADERS) + .buildClient(), true, shareLogger); + } else { + shareClient = setupClient((connectionString, endpoint) -> new ShareClientBuilder() + .connectionString(connectionString) + .endpoint(endpoint) + .shareName(shareName) + .httpClient(HttpClient.createDefault().wiretap(true)) + .httpLogDetailLevel(HttpLogDetailLevel.BODY_AND_HEADERS) + .addPolicy(interceptorManager.getRecordPolicy()) + .buildClient(), false, shareLogger); + } + } + + @Override + public void afterTest() { + try { + shareClient.delete(); + } catch (StorageErrorException ex) { + // Ignore the exception as the share is already deleted and that is what we wanted. + } + } + + @Override + public void getRootDirectoryDoesNotCreateADirectory() { + shareClient.create(); + DirectoryClient directoryClient = shareClient.getRootDirectoryClient(); + Response response = directoryClient.getProperties(); + Assert.assertEquals(response.statusCode(), 200); + Assert.assertNotNull(response.value().eTag()); + } + + @Override + public void getDirectoryDoesNotCreateADirectory() { + shareClient.create(); + DirectoryClient directoryClient = shareClient.getDirectoryClient("testshare"); + Assert.assertNotNull(directoryClient); + thrown.expect(StorageErrorException.class); + thrown.expectMessage("ResourceNotFound"); + directoryClient.getProperties(); + } + + @Override + public void createDirectoryFromShareClient() { + shareClient.create(); + FileTestHelpers.assertResponseStatusCode(shareClient.createDirectory("testshare"), 201); + } + + @Override + public void createDirectoryInvalidNameFromShareClient() { + shareClient.create(); + thrown.expect(StorageErrorException.class); + thrown.expectMessage("ParentNotFound"); + shareClient.createDirectory("test/share"); + } + + @Override + public void createDirectoryAlreadyExistsFromShareClient() { + shareClient.create(); + shareClient.createDirectory("testshare"); + thrown.expect(StorageErrorException.class); + thrown.expectMessage("ResourceAlreadyExists"); + shareClient.createDirectory("testshare"); + } + + @Override + public void deleteDirectoryFromShareClient() { + shareClient.create(); + shareClient.createDirectory("testshare"); + FileTestHelpers.assertResponseStatusCode(shareClient.deleteDirectory("testshare"), 202); + } + + @Override + public void deleteDirectoryDoesNotExistFromShareClient() { + shareClient.create(); + thrown.expect(StorageErrorException.class); + thrown.expectMessage("ResourceNotFound"); + shareClient.deleteDirectory("testshare"); + } + + @Override + public void createFromShareClient() { + FileTestHelpers.assertResponseStatusCode(shareClient.create(), 201); + } + + @Override + public void createTwiceSameMetadataFromShareClient() { + Map metadata = Collections.singletonMap("test", "metadata"); + + FileTestHelpers.assertResponseStatusCode(shareClient.create(metadata, 2), 201); + + FileTestHelpers.assertExceptionStatusCode(() -> shareClient.create(metadata, 2), 409); + } + + @Override + public void createTwiceDifferentMetadataFromShareClient() { + Map metadata = Collections.singletonMap("test", "metadata"); + + FileTestHelpers.assertResponseStatusCode(shareClient.create(), 201); + + FileTestHelpers.assertExceptionStatusCode(() -> shareClient.create(metadata, 2), 409); + } + + @Override + public void createInvalidQuotaFromShareClient() { + FileTestHelpers.assertExceptionStatusCode(() -> shareClient.create(null, -1), 400); + + FileTestHelpers.assertExceptionStatusCode(() -> shareClient.create(null, 0), 400); + } + + @Override + public void deleteFromShareClient() { + FileTestHelpers.assertResponseStatusCode(shareClient.create(), 201); + + FileTestHelpers.assertResponseStatusCode(shareClient.delete(), 202); + } + + @Override + public void deleteDoesNotExistFromShareClient() { + FileTestHelpers.assertExceptionStatusCode(() -> shareClient.delete(), 404); + } + + @Override + public void deleteThenCreateFromShareClient() { + FileTestHelpers.assertResponseStatusCode(shareClient.create(), 201); + + FileTestHelpers.assertResponseStatusCode(shareClient.delete(), 202); + + FileTestHelpers.sleepInRecordMode(Duration.ofSeconds(45)); + + FileTestHelpers.assertResponseStatusCode(shareClient.create(), 201); + } + + @Override + public void deleteThenCreateTooSoonFromShareClient() { + FileTestHelpers.assertResponseStatusCode(shareClient.create(), 201); + + FileTestHelpers.assertResponseStatusCode(shareClient.delete(), 202); + + FileTestHelpers.assertExceptionStatusCode(() -> shareClient.create(), 409); + } + + @Override + public void snapshot() { + FileTestHelpers.assertResponseStatusCode(shareClient.create(), 201); + + FileTestHelpers.assertResponseStatusCode(shareClient.createSnapshot(), 201); + } + + @Override + public void deleteSnapshotFromShareClient() { + FileTestHelpers.assertResponseStatusCode(shareClient.create(), 201); + + Response snapshotInfoResponse = shareClient.createSnapshot(); + FileTestHelpers.assertResponseStatusCode(snapshotInfoResponse, 201); + + ShareClient shareClientWithSnapshot = createShareClientWithSnapshot(interceptorManager, shareName, + snapshotInfoResponse.value().snapshot()).buildClient(); + FileTestHelpers.assertResponseStatusCode(shareClientWithSnapshot.delete(), 202); + + FileTestHelpers.assertResponseStatusCode(shareClient.createSnapshot(), 201); + } + + @Override + public void snapshotSameMetadata() { + Map metadata = Collections.singletonMap("test", "metadata"); + + FileTestHelpers.assertResponseStatusCode(shareClient.create(metadata, 2), 201); + + Response snapshotInfoResponse = shareClient.createSnapshot(metadata); + FileTestHelpers.assertResponseStatusCode(snapshotInfoResponse, 201); + + ShareClient shareClientWithSnapshot = createShareClientWithSnapshot(interceptorManager, shareName, + snapshotInfoResponse.value().snapshot()).buildClient(); + Response propertiesResponse = shareClientWithSnapshot.getProperties(); + FileTestHelpers.assertResponseStatusCode(propertiesResponse, 200); + assertEquals(metadata, propertiesResponse.value().metadata()); + } + + @Override + public void snapshotDifferentMetadata() { + Map createMetadata = Collections.singletonMap("create", "metadata"); + + FileTestHelpers.assertResponseStatusCode(shareClient.create(createMetadata, 2), 201); + + Map updateMetadata = Collections.singletonMap("update", "metadata"); + Response snapshotInfoResponse = shareClient.createSnapshot(updateMetadata); + FileTestHelpers.assertResponseStatusCode(snapshotInfoResponse, 201); + + Response propertiesResponse = shareClient.getProperties(); + FileTestHelpers.assertResponseStatusCode(propertiesResponse, 200); + assertEquals(createMetadata, propertiesResponse.value().metadata()); + + ShareClient shareClientWithSnapshot = createShareClientWithSnapshot(interceptorManager, shareName, + snapshotInfoResponse.value().snapshot()).buildClient(); + propertiesResponse = shareClientWithSnapshot.getProperties(); + FileTestHelpers.assertResponseStatusCode(propertiesResponse, 200); + assertEquals(updateMetadata, propertiesResponse.value().metadata()); + } + + @Override + public void snapshotDoesNotExist() { + FileTestHelpers.assertExceptionStatusCode(() -> shareClient.createSnapshot(), 404); + } + + @Override + public void getPropertiesFromShareClient() { + final int quotaInGB = 2; + Map metadata = Collections.singletonMap("test", "metadata"); + + FileTestHelpers.assertResponseStatusCode(shareClient.create(metadata, quotaInGB), 201); + + Response propertiesResponse = shareClient.getProperties(); + FileTestHelpers.assertResponseStatusCode(propertiesResponse, 200); + assertEquals(quotaInGB, propertiesResponse.value().quota()); + assertEquals(metadata, propertiesResponse.value().metadata()); + } + + @Override + public void getSnapshotPropertiesFromShareClient() { + final int quotaInGB = 2; + Map snapshotMetadata = Collections.singletonMap("snapshot", "metadata"); + + FileTestHelpers.assertResponseStatusCode(shareClient.create(null, quotaInGB), 201); + + Response snapshotInfoResponse = shareClient.createSnapshot(snapshotMetadata); + FileTestHelpers.assertResponseStatusCode(snapshotInfoResponse, 201); + + ShareClient shareClientWithSnapshot = createShareClientWithSnapshot(interceptorManager, shareName, + snapshotInfoResponse.value().snapshot()).buildClient(); + Response propertiesResponse = shareClientWithSnapshot.getProperties(); + FileTestHelpers.assertResponseStatusCode(propertiesResponse, 200); + assertEquals(quotaInGB, propertiesResponse.value().quota()); + assertEquals(snapshotMetadata, propertiesResponse.value().metadata()); + } + + @Override + public void getPropertiesDoesNotExistFromShareClient() { + FileTestHelpers.assertExceptionStatusCode(() -> shareClient.getProperties(), 404); + } + + @Override + public void getSnapshotPropertiesDoesNotExist() { + FileTestHelpers.assertResponseStatusCode(shareClient.create(), 201); + + ShareClient shareClientWithSnapshot = createShareClientWithSnapshot(interceptorManager, shareName, + "snapshot").buildClient(); + FileTestHelpers.assertExceptionStatusCode(() -> shareClientWithSnapshot.getProperties(), 400); + } + + @Override + public void setPropertiesFromShareClient() { + final int initialQuoteInGB = 2; + + FileTestHelpers.assertResponseStatusCode(shareClient.create(null, initialQuoteInGB), 201); + + Response propertiesResponse = shareClient.getProperties(); + FileTestHelpers.assertResponseStatusCode(propertiesResponse, 200); + assertEquals(initialQuoteInGB, propertiesResponse.value().quota()); + + final int updatedQuotaInGB = 4; + FileTestHelpers.assertResponseStatusCode(shareClient.setQuota(updatedQuotaInGB), 200); + + propertiesResponse = shareClient.getProperties(); + FileTestHelpers.assertResponseStatusCode(propertiesResponse, 200); + assertEquals(updatedQuotaInGB, propertiesResponse.value().quota()); + } + + @Override + public void setPropertiesInvalidQuotaFromShareClient() { + FileTestHelpers.assertResponseStatusCode(shareClient.create(), 201); + + FileTestHelpers.assertExceptionStatusCode(() -> shareClient.setQuota(-1), 400); + FileTestHelpers.assertExceptionStatusCode(() -> shareClient.setQuota(9999), 400); + } + + @Override + public void setPropertiesDoesNotExistFromShareClient() { + FileTestHelpers.assertExceptionStatusCode(() -> shareClient.setQuota(2), 404); + } + + @Override + public void getMetadataFromShareClient() { + Map metadata = Collections.singletonMap("test", "metadata"); + FileTestHelpers.assertResponseStatusCode(shareClient.create(metadata, 2), 201); + + Response propertiesResponse = shareClient.getProperties(); + FileTestHelpers.assertResponseStatusCode(propertiesResponse, 200); + assertEquals(metadata, propertiesResponse.value().metadata()); + } + + @Override + public void getSnapshotMetadataFromShareClient() { + Map metadata = Collections.singletonMap("test", "metadata"); + + FileTestHelpers.assertResponseStatusCode(shareClient.create(metadata, 2), 201); + + Response snapshotInfoResponse = shareClient.createSnapshot(); + FileTestHelpers.assertResponseStatusCode(snapshotInfoResponse, 201); + + ShareClient shareClientWithSnapshot = createShareClientWithSnapshot(interceptorManager, shareName, + snapshotInfoResponse.value().snapshot()).buildClient(); + Response propertiesResponse = shareClientWithSnapshot.getProperties(); + FileTestHelpers.assertResponseStatusCode(propertiesResponse, 200); + assertEquals(metadata, propertiesResponse.value().metadata()); + } + + @Override + public void getMetadataDoesNotExistFromShareClient() { + FileTestHelpers.assertExceptionStatusCode(() -> shareClient.getProperties(), 404); + } + + @Override + public void getSnapshotMetadataDoesNotExistFromShareClient() { + FileTestHelpers.assertResponseStatusCode(shareClient.create(), 201); + + ShareClient shareClientWithSnapshot = createShareClientWithSnapshot(interceptorManager, shareName, + "snapshot").buildClient(); + FileTestHelpers.assertExceptionStatusCode(() -> shareClientWithSnapshot.getProperties(), 400); + } + + @Override + public void setMetadataFromShareClient() { + FileTestHelpers.assertResponseStatusCode(shareClient.create(), 201); + + Map metadata = Collections.singletonMap("setting", "metadata"); + FileTestHelpers.assertResponseStatusCode(shareClient.setMetadata(metadata), 200); + + Response propertiesResponse = shareClient.getProperties(); + FileTestHelpers.assertResponseStatusCode(propertiesResponse, 200); + assertEquals(metadata, propertiesResponse.value().metadata()); + } + + @Override + public void setMetadataInvalidMetadataFromShareClient() { + FileTestHelpers.assertResponseStatusCode(shareClient.create(), 201); + + Map metadata = Collections.singletonMap("", "metadata"); + FileTestHelpers.assertExceptionStatusCode(() -> shareClient.setMetadata(metadata), 400); + } + + @Override + public void setMetadataDoesNotExistFromShareClient() { + Map metadata = Collections.singletonMap("test", "metadata"); + FileTestHelpers.assertExceptionStatusCode(() -> shareClient.setMetadata(metadata), 404); + } + + @Override + public void getPolicies() { + FileTestHelpers.assertResponseStatusCode(shareClient.create(), 201); + + Iterator accessPolicies = shareClient.getAccessPolicy().iterator(); + assertFalse(accessPolicies.hasNext()); + } + + @Override + public void getPoliciesDoesNotExist() { + FileTestHelpers.assertExceptionStatusCode(() -> shareClient.getAccessPolicy().iterator().hasNext(), 404); + } + + @Override + public void setPolicies() { + FileTestHelpers.assertResponseStatusCode(shareClient.create(), 201); + + AccessPolicy policy = new AccessPolicy().permission("r") + .start(OffsetDateTime.of(LocalDateTime.of(2000, 1, 1, 0, 0), ZoneOffset.UTC)) + .expiry(OffsetDateTime.of(LocalDateTime.of(2020, 1, 1, 0, 0), ZoneOffset.UTC)); + + SignedIdentifier permission = new SignedIdentifier().id("test") + .accessPolicy(policy); + + FileTestHelpers.assertResponseStatusCode(shareClient.setAccessPolicy(Collections.singletonList(permission)), 200); + + Iterator permissions = shareClient.getAccessPolicy().iterator(); + FileTestHelpers.assertPermissionsAreEqual(permission, permissions.next()); + assertFalse(permissions.hasNext()); + } + + @Override + public void setPoliciesInvalidPermission() { + FileTestHelpers.assertResponseStatusCode(shareClient.create(), 201); + + AccessPolicy policy = new AccessPolicy().permission("abcdefg") + .start(OffsetDateTime.of(LocalDateTime.of(2000, 1, 1, 0, 0), ZoneOffset.UTC)) + .expiry(OffsetDateTime.of(LocalDateTime.of(2020, 1, 1, 0, 0), ZoneOffset.UTC)); + + SignedIdentifier permission = new SignedIdentifier().id("test") + .accessPolicy(policy); + + FileTestHelpers.assertExceptionStatusCode(() -> shareClient.setAccessPolicy(Collections.singletonList(permission)), 400); + } + + @Override + public void setPoliciesTooManyPermissions() { + FileTestHelpers.assertResponseStatusCode(shareClient.create(), 201); + + List permissions = new ArrayList<>(); + for (int i = 0; i < 6; i++) { + AccessPolicy policy = new AccessPolicy().permission("r") + .start(OffsetDateTime.of(LocalDateTime.of(2000, 1, 1, 0, 0), ZoneOffset.UTC)) + .expiry(OffsetDateTime.of(LocalDateTime.of(2020, 1, 1, 0, 0), ZoneOffset.UTC)); + + permissions.add(new SignedIdentifier().id("test" + i).accessPolicy(policy)); + } + + FileTestHelpers.assertExceptionStatusCode(() -> shareClient.setAccessPolicy(permissions), 400); + } + + @Override + public void setPoliciesDoesNotExist() { + AccessPolicy policy = new AccessPolicy().permission("r") + .start(OffsetDateTime.of(LocalDateTime.of(2000, 1, 1, 0, 0), ZoneOffset.UTC)) + .expiry(OffsetDateTime.of(LocalDateTime.of(2020, 1, 1, 0, 0), ZoneOffset.UTC)); + + SignedIdentifier permission = new SignedIdentifier().id("test") + .accessPolicy(policy); + + FileTestHelpers.assertExceptionStatusCode(() -> shareClient.setAccessPolicy(Collections.singletonList(permission)), 404); + } + + @Override + public void getStats() { + FileTestHelpers.assertResponseStatusCode(shareClient.create(), 201); + + Response statisticsResponse = shareClient.getStatistics(); + FileTestHelpers.assertResponseStatusCode(statisticsResponse, 200); + assertEquals(0, statisticsResponse.value().getShareUsageInGB()); + } + + @Override + public void getStatsDoesNotExist() { + FileTestHelpers.assertExceptionStatusCode(() -> shareClient.getStatistics(), 404); + } + + @Override + public void getSnapshotId() { + shareClient.create(); + String actualSnapshot = shareClient.createSnapshot().value().snapshot(); + ShareClient shareClientWithSnapshot = createShareClientWithSnapshot(interceptorManager, + shareName, actualSnapshot).buildClient(); + Assert.assertEquals(actualSnapshot, shareClientWithSnapshot.getSnapshotId()); + } +} diff --git a/storage/client/file/src/test/resources/session-records/abortCopy.json b/storage/client/file/src/test/resources/session-records/abortCopy.json new file mode 100644 index 0000000000000..44f4caf813585 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/abortCopy.json @@ -0,0 +1,4 @@ +{ + "networkCallRecords" : [ ], + "variables" : [ "file10519411" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/clearMetadataFromDirClient.json b/storage/client/file/src/test/resources/session-records/clearMetadataFromDirClient.json new file mode 100644 index 0000000000000..d9f06f7e804cf --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/clearMetadataFromDirClient.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory863334?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB67B1F98\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:12 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781ee1-001a-006b-3775-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:11 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory863334?restype=directory&comp=metadata", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB6831042\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:12 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781ee2-001a-006b-3875-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:11 GMT" + } + } ], + "variables" : [ "directory863334" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/createDirectoryAlreadyExistsFromShareClient.json b/storage/client/file/src/test/resources/session-records/createDirectoryAlreadyExistsFromShareClient.json new file mode 100644 index 0000000000000..ed9ad15510e4c --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/createDirectoryAlreadyExistsFromShareClient.json @@ -0,0 +1,76 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share22756b14?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C78DAADCB\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:29 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781d48-001a-006b-4875-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:28 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share22756b14/testshare?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C78E3B87F\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:29 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781d4a-001a-006b-4975-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:40:28 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share22756b14/testshare?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ResourceAlreadyExists", + "retry-after" : "0", + "Content-Length" : "228", + "StatusCode" : "409", + "x-ms-request-id" : "a4781d4b-001a-006b-4a75-4192c4000000", + "Body" : "ResourceAlreadyExistsThe specified resource already exists.\nRequestId:a4781d4b-001a-006b-4a75-4192c4000000\nTime:2019-07-23T16:40:29.4840714Z", + "Date" : "Tue, 23 Jul 2019 16:40:28 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share22756b14?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781d4c-001a-006b-4b75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:28 GMT" + } + } ], + "variables" : [ "share22756b14" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/createDirectoryFromShareClient.json b/storage/client/file/src/test/resources/session-records/createDirectoryFromShareClient.json new file mode 100644 index 0000000000000..b1e911a9b4d14 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/createDirectoryFromShareClient.json @@ -0,0 +1,57 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share2125750f?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C950469F4\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:41:16 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781dc0-001a-006b-7a75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:15 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share2125750f/testshare?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C950E2F68\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:41:16 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781dc2-001a-006b-7b75-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:41:15 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share2125750f?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781dc3-001a-006b-7c75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:16 GMT" + } + } ], + "variables" : [ "share2125750f" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/createDirectoryInvalidNameFromShareClient.json b/storage/client/file/src/test/resources/session-records/createDirectoryInvalidNameFromShareClient.json new file mode 100644 index 0000000000000..c1b5e624d12bb --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/createDirectoryInvalidNameFromShareClient.json @@ -0,0 +1,57 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share01242c86?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C76E38AFB\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:26 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781d01-001a-006b-1175-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:25 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share01242c86/test%2fshare?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ParentNotFound", + "retry-after" : "0", + "Content-Length" : "224", + "StatusCode" : "404", + "x-ms-request-id" : "a4781d03-001a-006b-1275-4192c4000000", + "Body" : "ParentNotFoundThe specified parent path does not exist.\nRequestId:a4781d03-001a-006b-1275-4192c4000000\nTime:2019-07-23T16:40:26.1306790Z", + "Date" : "Tue, 23 Jul 2019 16:40:25 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share01242c86?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781d04-001a-006b-1375-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:25 GMT" + } + } ], + "variables" : [ "share01242c86" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/createExcessMaxSizeFromFileClient.json b/storage/client/file/src/test/resources/session-records/createExcessMaxSizeFromFileClient.json new file mode 100644 index 0000000000000..4bb5ab15e2e22 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/createExcessMaxSizeFromFileClient.json @@ -0,0 +1,45 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile6519698b", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C3EF47F55\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:38:52 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781c3b-001a-006b-4075-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:38:51 GMT" + } + }, { + "Method" : "HEAD", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile6519698b", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "Last-Modified" : "Tue, 23 Jul 2019 16:38:52 GMT", + "retry-after" : "0", + "StatusCode" : "200", + "Date" : "Tue, 23 Jul 2019 16:38:51 GMT", + "x-ms-server-encrypted" : "true", + "x-ms-type" : "File", + "ETag" : "\"0x8D70F8C3EF47F55\"", + "Content-Length" : "0", + "x-ms-request-id" : "a4781c3c-001a-006b-4175-4192c4000000", + "Body" : "", + "Content-Type" : "application/octet-stream" + } + } ], + "variables" : [ "file6519698b" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/createFileFromDirClient.json b/storage/client/file/src/test/resources/session-records/createFileFromDirClient.json new file mode 100644 index 0000000000000..094a1138d065f --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/createFileFromDirClient.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory863163?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB576C6DE\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:11 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781ec3-001a-006b-1975-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:10 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory863163%2ftestfile", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB57F7AFF\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:11 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781ec4-001a-006b-1a75-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:10 GMT" + } + } ], + "variables" : [ "directory863163" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/createFileWithoutCreateDirFromDirClient.json b/storage/client/file/src/test/resources/session-records/createFileWithoutCreateDirFromDirClient.json new file mode 100644 index 0000000000000..0505db3315249 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/createFileWithoutCreateDirFromDirClient.json @@ -0,0 +1,23 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory886246%2ftestfile", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ParentNotFound", + "retry-after" : "0", + "Content-Length" : "224", + "StatusCode" : "404", + "x-ms-request-id" : "a4781ee0-001a-006b-3675-4192c4000000", + "Body" : "ParentNotFoundThe specified parent path does not exist.\nRequestId:a4781ee0-001a-006b-3675-4192c4000000\nTime:2019-07-23T16:42:12.6968560Z", + "Date" : "Tue, 23 Jul 2019 16:42:11 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "directory886246" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/createFromFileClient.json b/storage/client/file/src/test/resources/session-records/createFromFileClient.json new file mode 100644 index 0000000000000..2ae2af729c6b0 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/createFromFileClient.json @@ -0,0 +1,45 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile91357575", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C3F05724F\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:38:52 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781c3d-001a-006b-4275-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:38:51 GMT" + } + }, { + "Method" : "HEAD", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile91357575", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "Last-Modified" : "Tue, 23 Jul 2019 16:38:52 GMT", + "retry-after" : "0", + "StatusCode" : "200", + "Date" : "Tue, 23 Jul 2019 16:38:51 GMT", + "x-ms-server-encrypted" : "true", + "x-ms-type" : "File", + "ETag" : "\"0x8D70F8C3F05724F\"", + "Content-Length" : "1024", + "x-ms-request-id" : "a4781c3e-001a-006b-4375-4192c4000000", + "Body" : "", + "Content-Type" : "application/octet-stream" + } + } ], + "variables" : [ "file91357575" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/createFromShareClient.json b/storage/client/file/src/test/resources/session-records/createFromShareClient.json new file mode 100644 index 0000000000000..c802b0ddf43e0 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/createFromShareClient.json @@ -0,0 +1,38 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share923617a7?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C956279A2\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:41:17 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781dcc-001a-006b-0375-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:16 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share923617a7?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781dce-001a-006b-0475-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:16 GMT" + } + } ], + "variables" : [ "share923617a7" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/createInvalidQuotaFromShareClient.json b/storage/client/file/src/test/resources/session-records/createInvalidQuotaFromShareClient.json new file mode 100644 index 0000000000000..e6ca2bc82bca8 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/createInvalidQuotaFromShareClient.json @@ -0,0 +1,61 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share58139809?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "InvalidHeaderValue", + "retry-after" : "0", + "Content-Length" : "324", + "StatusCode" : "400", + "x-ms-request-id" : "a4781dac-001a-006b-6b75-4192c4000000", + "Body" : "InvalidHeaderValueThe value for one of the HTTP headers is not in the correct format.\nRequestId:a4781dac-001a-006b-6b75-4192c4000000\nTime:2019-07-23T16:41:15.7831129Zx-ms-share-quota-1", + "Date" : "Tue, 23 Jul 2019 16:41:15 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share58139809?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "InvalidHeaderValue", + "retry-after" : "0", + "Content-Length" : "323", + "StatusCode" : "400", + "x-ms-request-id" : "a4781dae-001a-006b-6c75-4192c4000000", + "Body" : "InvalidHeaderValueThe value for one of the HTTP headers is not in the correct format.\nRequestId:a4781dae-001a-006b-6c75-4192c4000000\nTime:2019-07-23T16:41:15.8301458Zx-ms-share-quota0", + "Date" : "Tue, 23 Jul 2019 16:41:15 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share58139809?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "a4781daf-001a-006b-6d75-4192c4000000", + "Body" : "ShareNotFoundThe specified share does not exist.\nRequestId:a4781daf-001a-006b-6d75-4192c4000000\nTime:2019-07-23T16:41:15.8781809Z", + "Date" : "Tue, 23 Jul 2019 16:41:15 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share58139809" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/createMinFromDirClient.json b/storage/client/file/src/test/resources/session-records/createMinFromDirClient.json new file mode 100644 index 0000000000000..4a20dcab8864b --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/createMinFromDirClient.json @@ -0,0 +1,23 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory59046b?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB55B98CF\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:10 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781ec0-001a-006b-1675-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:10 GMT" + } + } ], + "variables" : [ "directory59046b" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/createShare.json b/storage/client/file/src/test/resources/session-records/createShare.json new file mode 100644 index 0000000000000..47eb2267cbd53 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/createShare.json @@ -0,0 +1,56 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share975192ac?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CED4EC679\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:43:44 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781f80-001a-006b-7075-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:43 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share975192ac&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781f82-001a-006b-7175-4192c4000000", + "Body" : "share975192acshare975192acTue, 23 Jul 2019 16:43:44 GMT\"0x8D70F8CED4EC679\"5120", + "Date" : "Tue, 23 Jul 2019 16:43:43 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share975192ac?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781f83-001a-006b-7275-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:43 GMT" + } + } ], + "variables" : [ "share975192ac" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/createShareInvalidQuota.json b/storage/client/file/src/test/resources/session-records/createShareInvalidQuota.json new file mode 100644 index 0000000000000..40109d33b40f6 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/createShareInvalidQuota.json @@ -0,0 +1,60 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share783156eb?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "InvalidHeaderValue", + "retry-after" : "0", + "Content-Length" : "324", + "StatusCode" : "400", + "x-ms-request-id" : "a4781f7b-001a-006b-6b75-4192c4000000", + "Body" : "InvalidHeaderValueThe value for one of the HTTP headers is not in the correct format.\nRequestId:a4781f7b-001a-006b-6b75-4192c4000000\nTime:2019-07-23T16:43:44.4843579Zx-ms-share-quota-1", + "Date" : "Tue, 23 Jul 2019 16:43:43 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share783156eb?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "InvalidHeaderValue", + "retry-after" : "0", + "Content-Length" : "329", + "StatusCode" : "400", + "x-ms-request-id" : "a4781f7c-001a-006b-6c75-4192c4000000", + "Body" : "InvalidHeaderValueThe value for one of the HTTP headers is not in the correct format.\nRequestId:a4781f7c-001a-006b-6c75-4192c4000000\nTime:2019-07-23T16:43:44.5303917Zx-ms-share-quota9999999", + "Date" : "Tue, 23 Jul 2019 16:43:43 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share783156eb&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781f7d-001a-006b-6d75-4192c4000000", + "Body" : "share783156eb", + "Date" : "Tue, 23 Jul 2019 16:43:43 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share783156eb" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/createShareTwiceDifferentMetadata.json b/storage/client/file/src/test/resources/session-records/createShareTwiceDifferentMetadata.json new file mode 100644 index 0000000000000..d00c5e4cd4fbe --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/createShareTwiceDifferentMetadata.json @@ -0,0 +1,75 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share09717677?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CECAA2B0C\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:43:43 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781f67-001a-006b-5c75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:42 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share09717677?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareAlreadyExists", + "retry-after" : "0", + "Content-Length" : "222", + "StatusCode" : "409", + "x-ms-request-id" : "a4781f69-001a-006b-5d75-4192c4000000", + "Body" : "ShareAlreadyExistsThe specified share already exists.\nRequestId:a4781f69-001a-006b-5d75-4192c4000000\nTime:2019-07-23T16:43:43.7168101Z", + "Date" : "Tue, 23 Jul 2019 16:43:42 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share09717677&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781f6a-001a-006b-5e75-4192c4000000", + "Body" : "share09717677share09717677Tue, 23 Jul 2019 16:43:43 GMT\"0x8D70F8CECAA2B0C\"5120", + "Date" : "Tue, 23 Jul 2019 16:43:42 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share09717677?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781f6b-001a-006b-5f75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:42 GMT" + } + } ], + "variables" : [ "share09717677" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/createShareTwiceSameMetadata.json b/storage/client/file/src/test/resources/session-records/createShareTwiceSameMetadata.json new file mode 100644 index 0000000000000..d3959d887cd48 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/createShareTwiceSameMetadata.json @@ -0,0 +1,75 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share56293760?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CD18A473F\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:58 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781f17-001a-006b-4775-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:42:57 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share56293760?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareAlreadyExists", + "retry-after" : "0", + "Content-Length" : "222", + "StatusCode" : "409", + "x-ms-request-id" : "a4781f1a-001a-006b-4875-4192c4000000", + "Body" : "ShareAlreadyExistsThe specified share already exists.\nRequestId:a4781f1a-001a-006b-4875-4192c4000000\nTime:2019-07-23T16:42:58.2083345Z", + "Date" : "Tue, 23 Jul 2019 16:42:57 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share56293760&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781f1b-001a-006b-4975-4192c4000000", + "Body" : "share56293760share56293760Tue, 23 Jul 2019 16:42:58 GMT\"0x8D70F8CD18A473F\"5120", + "Date" : "Tue, 23 Jul 2019 16:42:57 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share56293760?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781f1c-001a-006b-4a75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:42:57 GMT" + } + } ], + "variables" : [ "share56293760" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/createSubDirectory.json b/storage/client/file/src/test/resources/session-records/createSubDirectory.json new file mode 100644 index 0000000000000..59d64ac3fbdc8 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/createSubDirectory.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory343921?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB5222EF0\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:10 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781eb8-001a-006b-1075-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:09 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory343921%2fdir0928634ae?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB529F881\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:10 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781eb9-001a-006b-1175-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:09 GMT" + } + } ], + "variables" : [ "directory343921", "dir0928634ae" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/createSubDirectoryTwiceSameMetadata.json b/storage/client/file/src/test/resources/session-records/createSubDirectoryTwiceSameMetadata.json new file mode 100644 index 0000000000000..a64488dace84c --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/createSubDirectoryTwiceSameMetadata.json @@ -0,0 +1,61 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory539316?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB658EB53\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:12 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781edd-001a-006b-3375-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:11 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory539316%2fdir505430058?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB6608DDA\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:12 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781ede-001a-006b-3475-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:11 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory539316%2fdir505430058?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ResourceAlreadyExists", + "retry-after" : "0", + "Content-Length" : "228", + "StatusCode" : "409", + "x-ms-request-id" : "a4781edf-001a-006b-3575-4192c4000000", + "Body" : "ResourceAlreadyExistsThe specified resource already exists.\nRequestId:a4781edf-001a-006b-3575-4192c4000000\nTime:2019-07-23T16:42:12.6388146Z", + "Date" : "Tue, 23 Jul 2019 16:42:11 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "directory539316", "dir505430058" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/createSubDirectoryWithMetadata.json b/storage/client/file/src/test/resources/session-records/createSubDirectoryWithMetadata.json new file mode 100644 index 0000000000000..f9f82666b5f61 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/createSubDirectoryWithMetadata.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory669858?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB6469886\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:12 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781edb-001a-006b-3175-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:11 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory669858%2fdir2413813c3?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB64F73C5\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:12 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781edc-001a-006b-3275-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:11 GMT" + } + } ], + "variables" : [ "directory669858", "dir2413813c3" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/createTwiceDifferentMetadataFromShareClient.json b/storage/client/file/src/test/resources/session-records/createTwiceDifferentMetadataFromShareClient.json new file mode 100644 index 0000000000000..8aca8fa7be88d --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/createTwiceDifferentMetadataFromShareClient.json @@ -0,0 +1,57 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share09921d47?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C9625C67A\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:41:18 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781de7-001a-006b-1775-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:17 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share09921d47?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareAlreadyExists", + "retry-after" : "0", + "Content-Length" : "222", + "StatusCode" : "409", + "x-ms-request-id" : "a4781de9-001a-006b-1875-4192c4000000", + "Body" : "ShareAlreadyExistsThe specified share already exists.\nRequestId:a4781de9-001a-006b-1875-4192c4000000\nTime:2019-07-23T16:41:18.5641031Z", + "Date" : "Tue, 23 Jul 2019 16:41:17 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share09921d47?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781dea-001a-006b-1975-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:17 GMT" + } + } ], + "variables" : [ "share09921d47" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/createTwiceFromDirClient.json b/storage/client/file/src/test/resources/session-records/createTwiceFromDirClient.json new file mode 100644 index 0000000000000..e69c7f627159e --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/createTwiceFromDirClient.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory874061?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB5C6F129\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:11 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781ecc-001a-006b-2275-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:10 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory874061?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ResourceAlreadyExists", + "retry-after" : "0", + "Content-Length" : "228", + "StatusCode" : "409", + "x-ms-request-id" : "a4781ecd-001a-006b-2375-4192c4000000", + "Body" : "ResourceAlreadyExistsThe specified resource already exists.\nRequestId:a4781ecd-001a-006b-2375-4192c4000000\nTime:2019-07-23T16:42:11.6300936Z", + "Date" : "Tue, 23 Jul 2019 16:42:10 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "directory874061" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/createTwiceSameMetadataFromShareClient.json b/storage/client/file/src/test/resources/session-records/createTwiceSameMetadataFromShareClient.json new file mode 100644 index 0000000000000..eeb6b80df7382 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/createTwiceSameMetadataFromShareClient.json @@ -0,0 +1,57 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share097329d5?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C7955C0AA\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:30 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781d59-001a-006b-5575-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:29 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share097329d5?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareAlreadyExists", + "retry-after" : "0", + "Content-Length" : "222", + "StatusCode" : "409", + "x-ms-request-id" : "a4781d5b-001a-006b-5675-4192c4000000", + "Body" : "ShareAlreadyExistsThe specified share already exists.\nRequestId:a4781d5b-001a-006b-5675-4192c4000000\nTime:2019-07-23T16:40:30.2245998Z", + "Date" : "Tue, 23 Jul 2019 16:40:29 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share097329d5?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781d5c-001a-006b-5775-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:29 GMT" + } + } ], + "variables" : [ "share097329d5" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/createWithMetadataFromDirClient.json b/storage/client/file/src/test/resources/session-records/createWithMetadataFromDirClient.json new file mode 100644 index 0000000000000..7105baad63f9e --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/createWithMetadataFromDirClient.json @@ -0,0 +1,23 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory435036?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB50A5CCE\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:10 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781eb6-001a-006b-0e75-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:09 GMT" + } + } ], + "variables" : [ "directory435036" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/deleteDirectoryDoesNotExistFromShareClient.json b/storage/client/file/src/test/resources/session-records/deleteDirectoryDoesNotExistFromShareClient.json new file mode 100644 index 0000000000000..f75e1d0d2d188 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/deleteDirectoryDoesNotExistFromShareClient.json @@ -0,0 +1,57 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share439185d7?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C78A0A76D\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:28 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781d3f-001a-006b-4175-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:28 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share439185d7/testshare?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ResourceNotFound", + "retry-after" : "0", + "Content-Length" : "223", + "StatusCode" : "404", + "x-ms-request-id" : "a4781d41-001a-006b-4275-4192c4000000", + "Body" : "ResourceNotFoundThe specified resource does not exist.\nRequestId:a4781d41-001a-006b-4275-4192c4000000\nTime:2019-07-23T16:40:29.0387539Z", + "Date" : "Tue, 23 Jul 2019 16:40:28 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share439185d7?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781d42-001a-006b-4375-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:28 GMT" + } + } ], + "variables" : [ "share439185d7" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/deleteDirectoryFromShareClient.json b/storage/client/file/src/test/resources/session-records/deleteDirectoryFromShareClient.json new file mode 100644 index 0000000000000..dfc32b3d44f4b --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/deleteDirectoryFromShareClient.json @@ -0,0 +1,73 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share89717092?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C77583406\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:26 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781d12-001a-006b-1e75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:26 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share89717092/testshare?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C77605492\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:26 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781d14-001a-006b-1f75-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:40:26 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share89717092/testshare?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781d15-001a-006b-2075-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:26 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share89717092?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781d16-001a-006b-2175-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:26 GMT" + } + } ], + "variables" : [ "share89717092" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/deleteDoesNotExistFromShareClient.json b/storage/client/file/src/test/resources/session-records/deleteDoesNotExistFromShareClient.json new file mode 100644 index 0000000000000..a46553c998e03 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/deleteDoesNotExistFromShareClient.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share3115815a?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "a4781d24-001a-006b-2c75-4192c4000000", + "Body" : "ShareNotFoundThe specified share does not exist.\nRequestId:a4781d24-001a-006b-2c75-4192c4000000\nTime:2019-07-23T16:40:27.6797839Z", + "Date" : "Tue, 23 Jul 2019 16:40:27 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share3115815a?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "a4781d25-001a-006b-2d75-4192c4000000", + "Body" : "ShareNotFoundThe specified share does not exist.\nRequestId:a4781d25-001a-006b-2d75-4192c4000000\nTime:2019-07-23T16:40:27.7278190Z", + "Date" : "Tue, 23 Jul 2019 16:40:27 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share3115815a" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/deleteFileFromDirClient.json b/storage/client/file/src/test/resources/session-records/deleteFileFromDirClient.json new file mode 100644 index 0000000000000..3adb5ab423fe4 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/deleteFileFromDirClient.json @@ -0,0 +1,58 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory626180?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB68C1295\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:12 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781ee3-001a-006b-3975-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:12 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory626180%2ftestfile", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB697D48C\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:12 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781ee5-001a-006b-3a75-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:12 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory626180%2ftestfile", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781ee6-001a-006b-3b75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:42:12 GMT" + } + } ], + "variables" : [ "directory626180" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/deleteFileWithoutCreateFileFromDirClient.json b/storage/client/file/src/test/resources/session-records/deleteFileWithoutCreateFileFromDirClient.json new file mode 100644 index 0000000000000..3c115479ceb8d --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/deleteFileWithoutCreateFileFromDirClient.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory07934d?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB5B64C58\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:11 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781eca-001a-006b-2075-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:10 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory07934d%2ftestfile", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ResourceNotFound", + "retry-after" : "0", + "Content-Length" : "223", + "StatusCode" : "404", + "x-ms-request-id" : "a4781ecb-001a-006b-2175-4192c4000000", + "Body" : "ResourceNotFoundThe specified resource does not exist.\nRequestId:a4781ecb-001a-006b-2175-4192c4000000\nTime:2019-07-23T16:42:11.5220165Z", + "Date" : "Tue, 23 Jul 2019 16:42:10 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "directory07934d" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/deleteFromDirClient.json b/storage/client/file/src/test/resources/session-records/deleteFromDirClient.json new file mode 100644 index 0000000000000..a8a76a8698efa --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/deleteFromDirClient.json @@ -0,0 +1,39 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory046037?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB5A1FD55\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:11 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781ec8-001a-006b-1e75-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:10 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory046037?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781ec9-001a-006b-1f75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:42:10 GMT" + } + } ], + "variables" : [ "directory046037" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/deleteFromFileClient.json b/storage/client/file/src/test/resources/session-records/deleteFromFileClient.json new file mode 100644 index 0000000000000..cffa83f7633a6 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/deleteFromFileClient.json @@ -0,0 +1,39 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile29223793", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C3DE92082\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:38:50 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781c23-001a-006b-2975-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:38:49 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile29223793", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781c24-001a-006b-2a75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:38:50 GMT" + } + } ], + "variables" : [ "file29223793" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/deleteFromShareClient.json b/storage/client/file/src/test/resources/session-records/deleteFromShareClient.json new file mode 100644 index 0000000000000..53a92388620c4 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/deleteFromShareClient.json @@ -0,0 +1,57 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share5654566a?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C954995E4\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:41:17 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781dc8-001a-006b-8075-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:16 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share5654566a?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781dca-001a-006b-0175-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:16 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share5654566a?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareBeingDeleted", + "retry-after" : "0", + "Content-Length" : "244", + "StatusCode" : "409", + "x-ms-request-id" : "a4781dcb-001a-006b-0275-4192c4000000", + "Body" : "ShareBeingDeletedThe specified share is being deleted. Try operation later.\nRequestId:a4781dcb-001a-006b-0275-4192c4000000\nTime:2019-07-23T16:41:17.1721039Z", + "Date" : "Tue, 23 Jul 2019 16:41:16 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share5654566a" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/deleteNotExistFromDirClient.json b/storage/client/file/src/test/resources/session-records/deleteNotExistFromDirClient.json new file mode 100644 index 0000000000000..25e82ae32c1ad --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/deleteNotExistFromDirClient.json @@ -0,0 +1,23 @@ +{ + "networkCallRecords" : [ { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory41395a?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ResourceNotFound", + "retry-after" : "0", + "Content-Length" : "223", + "StatusCode" : "404", + "x-ms-request-id" : "a4781eb7-001a-006b-0f75-4192c4000000", + "Body" : "ResourceNotFoundThe specified resource does not exist.\nRequestId:a4781eb7-001a-006b-0f75-4192c4000000\nTime:2019-07-23T16:42:10.4402453Z", + "Date" : "Tue, 23 Jul 2019 16:42:09 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "directory41395a" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/deleteShare.json b/storage/client/file/src/test/resources/session-records/deleteShare.json new file mode 100644 index 0000000000000..89ea9be57e9e9 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/deleteShare.json @@ -0,0 +1,56 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share18053aa4?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CED0E7D6D\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:43:44 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781f77-001a-006b-6875-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:43 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share18053aa4?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781f79-001a-006b-6975-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:43 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share18053aa4&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781f7a-001a-006b-6a75-4192c4000000", + "Body" : "share18053aa4", + "Date" : "Tue, 23 Jul 2019 16:43:43 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share18053aa4" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/deleteShareDoesNotExist.json b/storage/client/file/src/test/resources/session-records/deleteShareDoesNotExist.json new file mode 100644 index 0000000000000..b4eb91ed496b4 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/deleteShareDoesNotExist.json @@ -0,0 +1,41 @@ +{ + "networkCallRecords" : [ { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share60248f77?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "a4781f91-001a-006b-7d75-4192c4000000", + "Body" : "ShareNotFoundThe specified share does not exist.\nRequestId:a4781f91-001a-006b-7d75-4192c4000000\nTime:2019-07-23T16:43:45.5241000Z", + "Date" : "Tue, 23 Jul 2019 16:43:44 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share60248f77&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781f92-001a-006b-7e75-4192c4000000", + "Body" : "share60248f77", + "Date" : "Tue, 23 Jul 2019 16:43:44 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share60248f77" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/deleteSnapshotFromShareClient.json b/storage/client/file/src/test/resources/session-records/deleteSnapshotFromShareClient.json new file mode 100644 index 0000000000000..af124b98d68ab --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/deleteSnapshotFromShareClient.json @@ -0,0 +1,95 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share14317636?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C78330487\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:28 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781d31-001a-006b-3675-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:27 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share14317636?restype=share&comp=snapshot", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "x-ms-snapshot" : "2019-07-23T16:40:28.0000000Z", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C78330487\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:28 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781d33-001a-006b-3775-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:27 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share14317636?sharesnapshot=2019-07-23T16%3a40%3a28.0000000Z&restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "3975b159-b01a-003d-1975-417ab4000000", + "Date" : "Tue, 23 Jul 2019 16:40:27 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share14317636?restype=share&comp=snapshot", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "x-ms-snapshot" : "2019-07-23T16:40:28.0000000Z", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C78330487\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:28 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781d34-001a-006b-3875-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:27 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share14317636?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareHasSnapshots", + "retry-after" : "0", + "Content-Length" : "250", + "StatusCode" : "409", + "x-ms-request-id" : "a4781d35-001a-006b-3975-4192c4000000", + "Body" : "ShareHasSnapshotsThe share has snapshots and the operation requires no snapshots.\nRequestId:a4781d35-001a-006b-3975-4192c4000000\nTime:2019-07-23T16:40:28.5564098Z", + "Date" : "Tue, 23 Jul 2019 16:40:27 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share14317636" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/deleteSubDirectory.json b/storage/client/file/src/test/resources/session-records/deleteSubDirectory.json new file mode 100644 index 0000000000000..c1ee2fab26aeb --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/deleteSubDirectory.json @@ -0,0 +1,58 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory283117?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB588CB7C\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:11 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781ec5-001a-006b-1b75-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:10 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory283117%2fdir94734a771?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB5910A5F\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:11 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781ec6-001a-006b-1c75-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:10 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory283117%2fdir94734a771?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781ec7-001a-006b-1d75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:42:10 GMT" + } + } ], + "variables" : [ "directory283117", "dir94734a771" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/deleteThenCreateFromShareClient.json b/storage/client/file/src/test/resources/session-records/deleteThenCreateFromShareClient.json new file mode 100644 index 0000000000000..40b598a321a9a --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/deleteThenCreateFromShareClient.json @@ -0,0 +1,72 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share2914350c?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C798FEE18\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:30 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781d61-001a-006b-5b75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:29 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share2914350c?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781d64-001a-006b-5c75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:29 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share2914350c?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C9473F66D\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:41:15 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781da8-001a-006b-6975-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:14 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share2914350c?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781dab-001a-006b-6a75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:14 GMT" + } + } ], + "variables" : [ "share2914350c" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/deleteThenCreateShareFromFileServiceClient.json b/storage/client/file/src/test/resources/session-records/deleteThenCreateShareFromFileServiceClient.json new file mode 100644 index 0000000000000..f19d87aafb782 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/deleteThenCreateShareFromFileServiceClient.json @@ -0,0 +1,90 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share90775610?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CD1AACD7E\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:58 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781f1e-001a-006b-4b75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:42:57 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share90775610?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781f20-001a-006b-4c75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:42:57 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share90775610?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CEC8DC438\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:43:43 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781f62-001a-006b-5975-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:42 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share90775610&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781f65-001a-006b-5a75-4192c4000000", + "Body" : "share90775610share90775610Tue, 23 Jul 2019 16:43:43 GMT\"0x8D70F8CEC8DC438\"5120", + "Date" : "Tue, 23 Jul 2019 16:43:42 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share90775610?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781f66-001a-006b-5b75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:42 GMT" + } + } ], + "variables" : [ "share90775610" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/deleteThenCreateShareTooSoonFromFileServiceClient.json b/storage/client/file/src/test/resources/session-records/deleteThenCreateShareTooSoonFromFileServiceClient.json new file mode 100644 index 0000000000000..db37837c21d40 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/deleteThenCreateShareTooSoonFromFileServiceClient.json @@ -0,0 +1,75 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share18271560?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CEDD6ACE2\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:43:45 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781f93-001a-006b-7f75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:44 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share18271560?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781f95-001a-006b-8075-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:44 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share18271560?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareBeingDeleted", + "retry-after" : "0", + "Content-Length" : "244", + "StatusCode" : "409", + "x-ms-request-id" : "a4781f96-001a-006b-0175-4192c4000000", + "Body" : "ShareBeingDeletedThe specified share is being deleted. Try operation later.\nRequestId:a4781f96-001a-006b-0175-4192c4000000\nTime:2019-07-23T16:43:45.7392533Z", + "Date" : "Tue, 23 Jul 2019 16:43:44 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share18271560&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781f97-001a-006b-0275-4192c4000000", + "Body" : "share18271560", + "Date" : "Tue, 23 Jul 2019 16:43:44 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share18271560" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/deleteThenCreateTooSoonFromShareClient.json b/storage/client/file/src/test/resources/session-records/deleteThenCreateTooSoonFromShareClient.json new file mode 100644 index 0000000000000..797a7a88f9656 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/deleteThenCreateTooSoonFromShareClient.json @@ -0,0 +1,76 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share5053509d?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C77B90350\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:27 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781d1f-001a-006b-2875-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:26 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share5053509d?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781d21-001a-006b-2975-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:26 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share5053509d?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareBeingDeleted", + "retry-after" : "0", + "Content-Length" : "244", + "StatusCode" : "409", + "x-ms-request-id" : "a4781d22-001a-006b-2a75-4192c4000000", + "Body" : "ShareBeingDeletedThe specified share is being deleted. Try operation later.\nRequestId:a4781d22-001a-006b-2a75-4192c4000000\nTime:2019-07-23T16:40:27.5717072Z", + "Date" : "Tue, 23 Jul 2019 16:40:26 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share5053509d?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareBeingDeleted", + "retry-after" : "0", + "Content-Length" : "244", + "StatusCode" : "409", + "x-ms-request-id" : "a4781d23-001a-006b-2b75-4192c4000000", + "Body" : "ShareBeingDeletedThe specified share is being deleted. Try operation later.\nRequestId:a4781d23-001a-006b-2b75-4192c4000000\nTime:2019-07-23T16:40:27.6207424Z", + "Date" : "Tue, 23 Jul 2019 16:40:26 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share5053509d" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/downloadWithProperties.json b/storage/client/file/src/test/resources/session-records/downloadWithProperties.json new file mode 100644 index 0000000000000..3370a153ba34a --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/downloadWithProperties.json @@ -0,0 +1,46 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile95252ec1", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C3EE16905\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:38:52 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781c39-001a-006b-3e75-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:38:51 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile95252ec1", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "Last-Modified" : "Tue, 23 Jul 2019 16:38:52 GMT", + "retry-after" : "0", + "StatusCode" : "200", + "Date" : "Tue, 23 Jul 2019 16:38:51 GMT", + "Accept-Ranges" : "bytes", + "x-ms-server-encrypted" : "true", + "x-ms-type" : "File", + "ETag" : "\"0x8D70F8C3EE16905\"", + "Content-Length" : "1024", + "x-ms-request-id" : "a4781c3a-001a-006b-3f75-4192c4000000", + "Body" : "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", + "Content-Type" : "application/octet-stream" + } + } ], + "variables" : [ "file95252ec1" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/forceCloseHandlesFromDirClient.json b/storage/client/file/src/test/resources/session-records/forceCloseHandlesFromDirClient.json new file mode 100644 index 0000000000000..c9591fdb93263 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/forceCloseHandlesFromDirClient.json @@ -0,0 +1,41 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory240914?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB633D07C\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:12 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781ed9-001a-006b-2f75-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:11 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory240914?comp=listhandles", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781eda-001a-006b-3075-4192c4000000", + "Body" : "", + "Date" : "Tue, 23 Jul 2019 16:42:11 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "directory240914" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/forceCloseHandlesFromFileClient.json b/storage/client/file/src/test/resources/session-records/forceCloseHandlesFromFileClient.json new file mode 100644 index 0000000000000..70454bdb9891a --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/forceCloseHandlesFromFileClient.json @@ -0,0 +1,41 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile32781af7", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C3E9C6451\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:38:51 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781c31-001a-006b-3675-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:38:51 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile32781af7?maxresults=10&comp=listhandles", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781c32-001a-006b-3775-4192c4000000", + "Body" : "10", + "Date" : "Tue, 23 Jul 2019 16:38:51 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "file32781af7" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/getDirectoryDoesNotCreateADirectory.json b/storage/client/file/src/test/resources/session-records/getDirectoryDoesNotCreateADirectory.json new file mode 100644 index 0000000000000..4046d646879b5 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/getDirectoryDoesNotCreateADirectory.json @@ -0,0 +1,57 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share10876776?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C94C66B41\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:41:16 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781db7-001a-006b-7375-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:15 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/share10876776/testshare?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ResourceNotFound", + "retry-after" : "0", + "Content-Length" : "223", + "StatusCode" : "404", + "x-ms-request-id" : "a4781db9-001a-006b-7475-4192c4000000", + "Body" : "ResourceNotFoundThe specified resource does not exist.\nRequestId:a4781db9-001a-006b-7475-4192c4000000\nTime:2019-07-23T16:41:16.2604523Z", + "Date" : "Tue, 23 Jul 2019 16:41:15 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share10876776?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781dba-001a-006b-7575-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:15 GMT" + } + } ], + "variables" : [ "share10876776" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/getFileClientFromDirClient.json b/storage/client/file/src/test/resources/session-records/getFileClientFromDirClient.json new file mode 100644 index 0000000000000..8fa3f19ff9cd4 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/getFileClientFromDirClient.json @@ -0,0 +1,4 @@ +{ + "networkCallRecords" : [ ], + "variables" : [ "directory877236", "96d7c3acc68d4acc" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/getHandlesFromDirClient.json b/storage/client/file/src/test/resources/session-records/getHandlesFromDirClient.json new file mode 100644 index 0000000000000..0b31e3f9121c8 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/getHandlesFromDirClient.json @@ -0,0 +1,4 @@ +{ + "networkCallRecords" : [ ], + "variables" : [ "directory12788e" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/getMetadataDoesNotExistFromShareClient.json b/storage/client/file/src/test/resources/session-records/getMetadataDoesNotExistFromShareClient.json new file mode 100644 index 0000000000000..f98ba99546d8c --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/getMetadataDoesNotExistFromShareClient.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/share620248bc?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "a4781d1c-001a-006b-2675-4192c4000000", + "Body" : "ShareNotFoundThe specified share does not exist.\nRequestId:a4781d1c-001a-006b-2675-4192c4000000\nTime:2019-07-23T16:40:27.3515501Z", + "Date" : "Tue, 23 Jul 2019 16:40:26 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share620248bc?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "a4781d1e-001a-006b-2775-4192c4000000", + "Body" : "ShareNotFoundThe specified share does not exist.\nRequestId:a4781d1e-001a-006b-2775-4192c4000000\nTime:2019-07-23T16:40:27.4035872Z", + "Date" : "Tue, 23 Jul 2019 16:40:26 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share620248bc" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/getMetadataFromShareClient.json b/storage/client/file/src/test/resources/session-records/getMetadataFromShareClient.json new file mode 100644 index 0000000000000..6f7501b7b4c18 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/getMetadataFromShareClient.json @@ -0,0 +1,60 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share14921607?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C7868C45C\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:28 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781d36-001a-006b-3a75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:27 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/share14921607?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-meta-test" : "metadata", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:28 GMT", + "retry-after" : "0", + "StatusCode" : "200", + "Date" : "Tue, 23 Jul 2019 16:40:27 GMT", + "x-ms-has-legal-hold" : "false", + "x-ms-share-quota" : "2", + "ETag" : "\"0x8D70F8C7868C45C\"", + "x-ms-has-immutability-policy" : "false", + "Content-Length" : "0", + "x-ms-request-id" : "a4781d38-001a-006b-3b75-4192c4000000" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share14921607?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781d39-001a-006b-3c75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:28 GMT" + } + } ], + "variables" : [ "share14921607" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/getPolicies.json b/storage/client/file/src/test/resources/session-records/getPolicies.json new file mode 100644 index 0000000000000..18ca1b282ab69 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/getPolicies.json @@ -0,0 +1,58 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share62141b58?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C7725F74B\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:26 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781d0a-001a-006b-1875-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:25 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/share62141b58?restype=share&comp=acl", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C7725F74B\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:26 GMT", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781d0c-001a-006b-1975-4192c4000000", + "Body" : "", + "Date" : "Tue, 23 Jul 2019 16:40:25 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share62141b58?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781d0d-001a-006b-1a75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:25 GMT" + } + } ], + "variables" : [ "share62141b58" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/getPoliciesDoesNotExist.json b/storage/client/file/src/test/resources/session-records/getPoliciesDoesNotExist.json new file mode 100644 index 0000000000000..7831055f2c6d1 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/getPoliciesDoesNotExist.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/share03196264?restype=share&comp=acl", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "a4781cfe-001a-006b-0f75-4192c4000000", + "Body" : "ShareNotFoundThe specified share does not exist.\nRequestId:a4781cfe-001a-006b-0f75-4192c4000000\nTime:2019-07-23T16:40:25.9575562Z", + "Date" : "Tue, 23 Jul 2019 16:40:25 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share03196264?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "a4781d00-001a-006b-1075-4192c4000000", + "Body" : "ShareNotFoundThe specified share does not exist.\nRequestId:a4781d00-001a-006b-1075-4192c4000000\nTime:2019-07-23T16:40:26.0095929Z", + "Date" : "Tue, 23 Jul 2019 16:40:25 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share03196264" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/getPropertiesDoesNotExistFromShareClient.json b/storage/client/file/src/test/resources/session-records/getPropertiesDoesNotExistFromShareClient.json new file mode 100644 index 0000000000000..c366d9279459f --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/getPropertiesDoesNotExistFromShareClient.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/share31141b34?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "a4781dbd-001a-006b-7875-4192c4000000", + "Body" : "ShareNotFoundThe specified share does not exist.\nRequestId:a4781dbd-001a-006b-7875-4192c4000000\nTime:2019-07-23T16:41:16.4866141Z", + "Date" : "Tue, 23 Jul 2019 16:41:15 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share31141b34?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "a4781dbf-001a-006b-7975-4192c4000000", + "Body" : "ShareNotFoundThe specified share does not exist.\nRequestId:a4781dbf-001a-006b-7975-4192c4000000\nTime:2019-07-23T16:41:16.5346484Z", + "Date" : "Tue, 23 Jul 2019 16:41:15 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share31141b34" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/getPropertiesFromDirClient.json b/storage/client/file/src/test/resources/session-records/getPropertiesFromDirClient.json new file mode 100644 index 0000000000000..5c8e80b286d06 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/getPropertiesFromDirClient.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory937333?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB566491E\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:10 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781ec1-001a-006b-1775-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:10 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory937333?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-server-encrypted" : "true", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB566491E\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:10 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781ec2-001a-006b-1875-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:42:10 GMT" + } + } ], + "variables" : [ "directory937333" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/getPropertiesFromFileClient.json b/storage/client/file/src/test/resources/session-records/getPropertiesFromFileClient.json new file mode 100644 index 0000000000000..34c88368bdd9e --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/getPropertiesFromFileClient.json @@ -0,0 +1,45 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile66368470", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C3EBE716D\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:38:51 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781c35-001a-006b-3a75-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:38:51 GMT" + } + }, { + "Method" : "HEAD", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile66368470", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "Last-Modified" : "Tue, 23 Jul 2019 16:38:51 GMT", + "retry-after" : "0", + "StatusCode" : "200", + "Date" : "Tue, 23 Jul 2019 16:38:51 GMT", + "x-ms-server-encrypted" : "true", + "x-ms-type" : "File", + "ETag" : "\"0x8D70F8C3EBE716D\"", + "Content-Length" : "1024", + "x-ms-request-id" : "a4781c36-001a-006b-3b75-4192c4000000", + "Body" : "", + "Content-Type" : "application/octet-stream" + } + } ], + "variables" : [ "file66368470" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/getPropertiesFromShareClient.json b/storage/client/file/src/test/resources/session-records/getPropertiesFromShareClient.json new file mode 100644 index 0000000000000..dc43fd0afb917 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/getPropertiesFromShareClient.json @@ -0,0 +1,60 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share19304121?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C796F40BC\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:30 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781d5d-001a-006b-5875-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:29 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/share19304121?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-meta-test" : "metadata", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:30 GMT", + "retry-after" : "0", + "StatusCode" : "200", + "Date" : "Tue, 23 Jul 2019 16:40:29 GMT", + "x-ms-has-legal-hold" : "false", + "x-ms-share-quota" : "2", + "ETag" : "\"0x8D70F8C796F40BC\"", + "x-ms-has-immutability-policy" : "false", + "Content-Length" : "0", + "x-ms-request-id" : "a4781d5f-001a-006b-5975-4192c4000000" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share19304121?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781d60-001a-006b-5a75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:29 GMT" + } + } ], + "variables" : [ "share19304121" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/getRootDirectoryDoesNotCreateADirectory.json b/storage/client/file/src/test/resources/session-records/getRootDirectoryDoesNotCreateADirectory.json new file mode 100644 index 0000000000000..ecd4380eb4e78 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/getRootDirectoryDoesNotCreateADirectory.json @@ -0,0 +1,57 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share33794f21?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C9528C16E\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:41:16 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781dc4-001a-006b-7d75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:16 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/share33794f21/?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-server-encrypted" : "false", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C953238AE\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:41:16 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781dc6-001a-006b-7e75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:16 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share33794f21?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781dc7-001a-006b-7f75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:16 GMT" + } + } ], + "variables" : [ "share33794f21" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/getShareDoesNotCreateAShare.json b/storage/client/file/src/test/resources/session-records/getShareDoesNotCreateAShare.json new file mode 100644 index 0000000000000..1b9367c3df656 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/getShareDoesNotCreateAShare.json @@ -0,0 +1,41 @@ +{ + "networkCallRecords" : [ { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/share96662a2c?restype=share&comp=stats", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "a4781fa7-001a-006b-0d75-4192c4000000", + "Body" : "ShareNotFoundThe specified share does not exist.\nRequestId:a4781fa7-001a-006b-0d75-4192c4000000\nTime:2019-07-23T16:43:46.2786384Z", + "Date" : "Tue, 23 Jul 2019 16:43:45 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share96662a2c&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781fa9-001a-006b-0e75-4192c4000000", + "Body" : "share96662a2c", + "Date" : "Tue, 23 Jul 2019 16:43:45 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share96662a2c" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/getSnapshotId.json b/storage/client/file/src/test/resources/session-records/getSnapshotId.json new file mode 100644 index 0000000000000..02715f00c488a --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/getSnapshotId.json @@ -0,0 +1,60 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share69666dbd?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C773F5043\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:26 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781d0e-001a-006b-1b75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:25 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share69666dbd?restype=share&comp=snapshot", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "x-ms-snapshot" : "2019-07-23T16:40:26.0000000Z", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C773F5043\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:26 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781d10-001a-006b-1c75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:26 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share69666dbd?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareHasSnapshots", + "retry-after" : "0", + "Content-Length" : "250", + "StatusCode" : "409", + "x-ms-request-id" : "a4781d11-001a-006b-1d75-4192c4000000", + "Body" : "ShareHasSnapshotsThe share has snapshots and the operation requires no snapshots.\nRequestId:a4781d11-001a-006b-1d75-4192c4000000\nTime:2019-07-23T16:40:26.7761389Z", + "Date" : "Tue, 23 Jul 2019 16:40:26 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share69666dbd" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/getSnapshotMetadataDoesNotExistFromShareClient.json b/storage/client/file/src/test/resources/session-records/getSnapshotMetadataDoesNotExistFromShareClient.json new file mode 100644 index 0000000000000..b6fb1b9aab467 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/getSnapshotMetadataDoesNotExistFromShareClient.json @@ -0,0 +1,57 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share053068d6?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C79224B3F\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:29 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781d52-001a-006b-5075-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:29 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/share053068d6?sharesnapshot=snapshot&restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "InvalidQueryParameterValue", + "retry-after" : "0", + "Content-Length" : "445", + "StatusCode" : "400", + "x-ms-request-id" : "3975b15b-b01a-003d-1a75-417ab4000000", + "Body" : "InvalidQueryParameterValueValue for one of the query parameters specified in the request URI is invalid.\nRequestId:3975b15b-b01a-003d-1a75-417ab4000000\nTime:2019-07-23T16:40:29.8874826ZsharesnapshotsnapshotMust be in the specific snapshot date time format.", + "Date" : "Tue, 23 Jul 2019 16:40:29 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share053068d6?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781d54-001a-006b-5175-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:29 GMT" + } + } ], + "variables" : [ "share053068d6" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/getSnapshotMetadataFromShareClient.json b/storage/client/file/src/test/resources/session-records/getSnapshotMetadataFromShareClient.json new file mode 100644 index 0000000000000..4b6cfa5a5c4ac --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/getSnapshotMetadataFromShareClient.json @@ -0,0 +1,82 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share31872175?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C78061FF3\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:27 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781d2c-001a-006b-3375-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:27 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share31872175?restype=share&comp=snapshot", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "x-ms-snapshot" : "2019-07-23T16:40:28.0000000Z", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C78061FF3\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:27 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781d2e-001a-006b-3475-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:27 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/share31872175?sharesnapshot=2019-07-23T16%3a40%3a28.0000000Z&restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-meta-test" : "metadata", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:27 GMT", + "retry-after" : "0", + "StatusCode" : "200", + "Date" : "Tue, 23 Jul 2019 16:40:27 GMT", + "x-ms-has-legal-hold" : "false", + "x-ms-share-quota" : "2", + "ETag" : "\"0x8D70F8C78061FF3\"", + "x-ms-has-immutability-policy" : "false", + "Content-Length" : "0", + "x-ms-request-id" : "3975b156-b01a-003d-1875-417ab4000000" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share31872175?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareHasSnapshots", + "retry-after" : "0", + "Content-Length" : "250", + "StatusCode" : "409", + "x-ms-request-id" : "a4781d30-001a-006b-3575-4192c4000000", + "Body" : "ShareHasSnapshotsThe share has snapshots and the operation requires no snapshots.\nRequestId:a4781d30-001a-006b-3575-4192c4000000\nTime:2019-07-23T16:40:28.1841446Z", + "Date" : "Tue, 23 Jul 2019 16:40:27 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share31872175" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/getSnapshotPropertiesDoesNotExist.json b/storage/client/file/src/test/resources/session-records/getSnapshotPropertiesDoesNotExist.json new file mode 100644 index 0000000000000..f2da43b8c1a4d --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/getSnapshotPropertiesDoesNotExist.json @@ -0,0 +1,57 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share29111ff1?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C9574301E\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:41:17 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781dcf-001a-006b-0575-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:16 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/share29111ff1?sharesnapshot=snapshot&restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "InvalidQueryParameterValue", + "retry-after" : "0", + "Content-Length" : "445", + "StatusCode" : "400", + "x-ms-request-id" : "3975b18f-b01a-003d-2675-417ab4000000", + "Body" : "InvalidQueryParameterValueValue for one of the query parameters specified in the request URI is invalid.\nRequestId:3975b18f-b01a-003d-2675-417ab4000000\nTime:2019-07-23T16:41:17.4022825ZsharesnapshotsnapshotMust be in the specific snapshot date time format.", + "Date" : "Tue, 23 Jul 2019 16:41:16 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share29111ff1?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781dd1-001a-006b-0675-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:16 GMT" + } + } ], + "variables" : [ "share29111ff1" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/getSnapshotPropertiesFromShareClient.json b/storage/client/file/src/test/resources/session-records/getSnapshotPropertiesFromShareClient.json new file mode 100644 index 0000000000000..8cac81d604f3c --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/getSnapshotPropertiesFromShareClient.json @@ -0,0 +1,82 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share49629d81?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C9603B94A\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:41:18 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781de3-001a-006b-1475-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:17 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share49629d81?restype=share&comp=snapshot", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "x-ms-snapshot" : "2019-07-23T16:41:18.0000000Z", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C95D7CB00\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:41:18 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781de5-001a-006b-1575-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:17 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/share49629d81?sharesnapshot=2019-07-23T16%3a41%3a18.0000000Z&restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-meta-snapshot" : "metadata", + "Last-Modified" : "Tue, 23 Jul 2019 16:41:18 GMT", + "retry-after" : "0", + "StatusCode" : "200", + "Date" : "Tue, 23 Jul 2019 16:41:17 GMT", + "x-ms-has-legal-hold" : "false", + "x-ms-share-quota" : "2", + "ETag" : "\"0x8D70F8C95D7CB00\"", + "x-ms-has-immutability-policy" : "false", + "Content-Length" : "0", + "x-ms-request-id" : "3975b194-b01a-003d-2875-417ab4000000" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share49629d81?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareHasSnapshots", + "retry-after" : "0", + "Content-Length" : "250", + "StatusCode" : "409", + "x-ms-request-id" : "a4781de6-001a-006b-1675-4192c4000000", + "Body" : "ShareHasSnapshotsThe share has snapshots and the operation requires no snapshots.\nRequestId:a4781de6-001a-006b-1675-4192c4000000\nTime:2019-07-23T16:41:18.4480203Z", + "Date" : "Tue, 23 Jul 2019 16:41:17 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share49629d81" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/getStats.json b/storage/client/file/src/test/resources/session-records/getStats.json new file mode 100644 index 0000000000000..b4ecd38b1caf6 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/getStats.json @@ -0,0 +1,56 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share67188f15?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C963EAA38\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:41:18 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781deb-001a-006b-1a75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:17 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/share67188f15?restype=share&comp=stats", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781ded-001a-006b-1b75-4192c4000000", + "Body" : "0", + "Date" : "Tue, 23 Jul 2019 16:41:17 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share67188f15?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781dee-001a-006b-1c75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:18 GMT" + } + } ], + "variables" : [ "share67188f15" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/getStatsDoesNotExist.json b/storage/client/file/src/test/resources/session-records/getStatsDoesNotExist.json new file mode 100644 index 0000000000000..97ecc310dd26f --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/getStatsDoesNotExist.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/share840508c1?restype=share&comp=stats", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "a4781db4-001a-006b-7175-4192c4000000", + "Body" : "ShareNotFoundThe specified share does not exist.\nRequestId:a4781db4-001a-006b-7175-4192c4000000\nTime:2019-07-23T16:41:16.1013399Z", + "Date" : "Tue, 23 Jul 2019 16:41:15 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share840508c1?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "a4781db6-001a-006b-7275-4192c4000000", + "Body" : "ShareNotFoundThe specified share does not exist.\nRequestId:a4781db6-001a-006b-7275-4192c4000000\nTime:2019-07-23T16:41:16.1493733Z", + "Date" : "Tue, 23 Jul 2019 16:41:15 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share840508c1" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/getSubDirectoryClient.json b/storage/client/file/src/test/resources/session-records/getSubDirectoryClient.json new file mode 100644 index 0000000000000..786a8d1614b5d --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/getSubDirectoryClient.json @@ -0,0 +1,4 @@ +{ + "networkCallRecords" : [ ], + "variables" : [ "directory092514" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/listFilesAndDirectoriesFromDirClient.json b/storage/client/file/src/test/resources/session-records/listFilesAndDirectoriesFromDirClient.json new file mode 100644 index 0000000000000..8102f80f00dd5 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/listFilesAndDirectoriesFromDirClient.json @@ -0,0 +1,212 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory601397?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB5D8325F\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:11 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781ece-001a-006b-2475-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:10 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory601397%2fdirectory6013970?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB5E04A24\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:11 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781ecf-001a-006b-2575-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:10 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory601397%2fsamelayer02223a0", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB5E83ACF\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:11 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781ed0-001a-006b-2675-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:10 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory601397%2fdirectory6013970%2fnextlayer57757b0", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB5F13D27\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:11 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781ed1-001a-006b-2775-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:11 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory601397%2fdirectory6013971?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB5F92DCE\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:11 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781ed2-001a-006b-2875-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:11 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory601397%2fsamelayer02223a01", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB6011E86\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:11 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781ed3-001a-006b-2975-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:11 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory601397%2fdirectory6013971%2fnextlayer57757b01", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB608E814\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:12 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781ed4-001a-006b-2a75-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:11 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory601397%2fdirectory6013972?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB610B1B3\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:12 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781ed5-001a-006b-2b75-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:11 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory601397%2fsamelayer02223a012", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB618F08D\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:12 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781ed6-001a-006b-2c75-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:11 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory601397%2fdirectory6013972%2fnextlayer57757b012", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB620BA31\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:12 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781ed7-001a-006b-2d75-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:11 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory601397?restype=directory&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781ed8-001a-006b-2e75-4192c4000000", + "Body" : "directory6013970directory6013971directory6013972samelayer02223a01024samelayer02223a011024samelayer02223a0121024", + "Date" : "Tue, 23 Jul 2019 16:42:11 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "directory601397", "samelayer02223a", "nextlayer57757b" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/listHandlesFromFileClient.json b/storage/client/file/src/test/resources/session-records/listHandlesFromFileClient.json new file mode 100644 index 0000000000000..a8429ac169638 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/listHandlesFromFileClient.json @@ -0,0 +1,41 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile80711456", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C3E02EEBF\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:38:50 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781c25-001a-006b-2b75-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:38:50 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile80711456?comp=listhandles", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781c27-001a-006b-2c75-4192c4000000", + "Body" : "", + "Date" : "Tue, 23 Jul 2019 16:38:50 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "file80711456" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/listRangesFromFileClient.json b/storage/client/file/src/test/resources/session-records/listRangesFromFileClient.json new file mode 100644 index 0000000000000..26f25a74de99f --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/listRangesFromFileClient.json @@ -0,0 +1,65 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile053136bd", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C3E1408CE\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:38:50 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781c28-001a-006b-2d75-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:38:50 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile053136bd?comp=range", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/octet-stream" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C3E1DCE9C\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:38:50 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781c29-001a-006b-2e75-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:38:50 GMT", + "Content-MD5" : "wh+Wm18D0z1D4E+PE252gg==" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile053136bd?comp=rangelist", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-content-length" : "512", + "ETag" : "\"0x8D70F8C3E1DCE9C\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:38:50 GMT", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781c2a-001a-006b-2f75-4192c4000000", + "Body" : "0511", + "Date" : "Tue, 23 Jul 2019 16:38:50 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "file053136bd" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/listShares.json b/storage/client/file/src/test/resources/session-records/listShares.json new file mode 100644 index 0000000000000..9b4198a90038a --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/listShares.json @@ -0,0 +1,142 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share334220300?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CEDF8BA1A\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:43:45 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781f99-001a-006b-0475-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:44 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share334220301?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CEE0083B4\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:43:45 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781f9c-001a-006b-0675-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:44 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share334220302?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CEE084D4E\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:43:45 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781f9f-001a-006b-0775-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:44 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share33422030&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781fa2-001a-006b-0875-4192c4000000", + "Body" : "share33422030share334220300Tue, 23 Jul 2019 16:43:45 GMT\"0x8D70F8CEDF8BA1A\"2share334220301Tue, 23 Jul 2019 16:43:45 GMT\"0x8D70F8CEE0083B4\"2share334220302Tue, 23 Jul 2019 16:43:45 GMT\"0x8D70F8CEE084D4E\"2", + "Date" : "Tue, 23 Jul 2019 16:43:45 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share33422030&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781fa3-001a-006b-0975-4192c4000000", + "Body" : "share33422030share334220300Tue, 23 Jul 2019 16:43:45 GMT\"0x8D70F8CEDF8BA1A\"2share334220301Tue, 23 Jul 2019 16:43:45 GMT\"0x8D70F8CEE0083B4\"2share334220302Tue, 23 Jul 2019 16:43:45 GMT\"0x8D70F8CEE084D4E\"2", + "Date" : "Tue, 23 Jul 2019 16:43:45 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share334220300?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781fa4-001a-006b-0a75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:45 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share334220301?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781fa5-001a-006b-0b75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:45 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share334220302?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781fa6-001a-006b-0c75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:45 GMT" + } + } ], + "variables" : [ "share33422030" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/listSharesIncludeMetadata.json b/storage/client/file/src/test/resources/session-records/listSharesIncludeMetadata.json new file mode 100644 index 0000000000000..74e4401568be0 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/listSharesIncludeMetadata.json @@ -0,0 +1,142 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share116742420?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CEEFF5D8E\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:43:47 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781fc4-001a-006b-2375-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:46 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share116742421?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CEF077557\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:43:47 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781fc6-001a-006b-2475-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:46 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share116742422?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CEF0F3EF5\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:43:47 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781fc8-001a-006b-2575-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:46 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share11674242&include=metadata&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781fca-001a-006b-2675-4192c4000000", + "Body" : "share11674242share116742420Tue, 23 Jul 2019 16:43:47 GMT\"0x8D70F8CEEFF5D8E\"2metadatashare116742421Tue, 23 Jul 2019 16:43:47 GMT\"0x8D70F8CEF077557\"2share116742422Tue, 23 Jul 2019 16:43:47 GMT\"0x8D70F8CEF0F3EF5\"2metadata", + "Date" : "Tue, 23 Jul 2019 16:43:46 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share11674242&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781fcb-001a-006b-2775-4192c4000000", + "Body" : "share11674242share116742420Tue, 23 Jul 2019 16:43:47 GMT\"0x8D70F8CEEFF5D8E\"2share116742421Tue, 23 Jul 2019 16:43:47 GMT\"0x8D70F8CEF077557\"2share116742422Tue, 23 Jul 2019 16:43:47 GMT\"0x8D70F8CEF0F3EF5\"2", + "Date" : "Tue, 23 Jul 2019 16:43:46 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share116742420?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781fcc-001a-006b-2875-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:46 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share116742421?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781fcd-001a-006b-2975-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:46 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share116742422?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781fce-001a-006b-2a75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:46 GMT" + } + } ], + "variables" : [ "share11674242" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/listSharesIncludeMetadataAndSnapshots.json b/storage/client/file/src/test/resources/session-records/listSharesIncludeMetadataAndSnapshots.json new file mode 100644 index 0000000000000..fc52365263ae3 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/listSharesIncludeMetadataAndSnapshots.json @@ -0,0 +1,186 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share112342ce0?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CED6BA295\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:43:44 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781f84-001a-006b-7375-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:43 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share112342ce0?restype=share&comp=snapshot", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "x-ms-snapshot" : "2019-07-23T16:43:44.0000000Z", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CED6BA295\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:43:44 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781f86-001a-006b-7475-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:44 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share112342ce1?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CED7C4768\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:43:45 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781f87-001a-006b-7575-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:44 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share112342ce2?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CED8745EA\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:43:45 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781f89-001a-006b-7675-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:44 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share112342ce2?restype=share&comp=snapshot", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "x-ms-snapshot" : "2019-07-23T16:43:45.0000000Z", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CED8745EA\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:43:45 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781f8b-001a-006b-7775-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:44 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share112342ce&include=metadata%2csnapshots&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781f8c-001a-006b-7875-4192c4000000", + "Body" : "share112342ceshare112342ce02019-07-23T16:43:44.0000000ZTue, 23 Jul 2019 16:43:44 GMT\"0x8D70F8CED6BA295\"2metadatashare112342ce0Tue, 23 Jul 2019 16:43:44 GMT\"0x8D70F8CED6BA295\"2metadatashare112342ce1Tue, 23 Jul 2019 16:43:45 GMT\"0x8D70F8CED7C4768\"2share112342ce22019-07-23T16:43:45.0000000ZTue, 23 Jul 2019 16:43:45 GMT\"0x8D70F8CED8745EA\"2metadatashare112342ce2Tue, 23 Jul 2019 16:43:45 GMT\"0x8D70F8CED8745EA\"2metadata", + "Date" : "Tue, 23 Jul 2019 16:43:44 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share112342ce&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781f8d-001a-006b-7975-4192c4000000", + "Body" : "share112342ceshare112342ce0Tue, 23 Jul 2019 16:43:44 GMT\"0x8D70F8CED6BA295\"2share112342ce1Tue, 23 Jul 2019 16:43:45 GMT\"0x8D70F8CED7C4768\"2share112342ce2Tue, 23 Jul 2019 16:43:45 GMT\"0x8D70F8CED8745EA\"2", + "Date" : "Tue, 23 Jul 2019 16:43:44 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share112342ce0?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareHasSnapshots", + "retry-after" : "0", + "Content-Length" : "250", + "StatusCode" : "409", + "x-ms-request-id" : "a4781f8e-001a-006b-7a75-4192c4000000", + "Body" : "ShareHasSnapshotsThe share has snapshots and the operation requires no snapshots.\nRequestId:a4781f8e-001a-006b-7a75-4192c4000000\nTime:2019-07-23T16:43:45.3519776Z", + "Date" : "Tue, 23 Jul 2019 16:43:44 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share112342ce1?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781f8f-001a-006b-7b75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:44 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share112342ce2?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareHasSnapshots", + "retry-after" : "0", + "Content-Length" : "250", + "StatusCode" : "409", + "x-ms-request-id" : "a4781f90-001a-006b-7c75-4192c4000000", + "Body" : "ShareHasSnapshotsThe share has snapshots and the operation requires no snapshots.\nRequestId:a4781f90-001a-006b-7c75-4192c4000000\nTime:2019-07-23T16:43:45.4540500Z", + "Date" : "Tue, 23 Jul 2019 16:43:44 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share112342ce" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/listSharesIncludeSnapshots.json b/storage/client/file/src/test/resources/session-records/listSharesIncludeSnapshots.json new file mode 100644 index 0000000000000..9274ad655d14a --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/listSharesIncludeSnapshots.json @@ -0,0 +1,186 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share305309dc0?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CEE49A7FE\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:43:46 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781faa-001a-006b-0f75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:45 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share305309dc0?restype=share&comp=snapshot", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "x-ms-snapshot" : "2019-07-23T16:43:46.0000000Z", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CEE49A7FE\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:43:46 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781fac-001a-006b-1075-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:45 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share305309dc1?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CEE5A4CDF\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:43:46 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781fad-001a-006b-1175-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:45 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share305309dc2?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CEE6CC6C8\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:43:46 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781faf-001a-006b-1275-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:45 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share305309dc2?restype=share&comp=snapshot", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "x-ms-snapshot" : "2019-07-23T16:43:46.0000000Z", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CEE6CC6C8\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:43:46 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781fb1-001a-006b-1375-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:45 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share305309dc&include=snapshots&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781fb2-001a-006b-1475-4192c4000000", + "Body" : "share305309dcshare305309dc02019-07-23T16:43:46.0000000ZTue, 23 Jul 2019 16:43:46 GMT\"0x8D70F8CEE49A7FE\"2share305309dc0Tue, 23 Jul 2019 16:43:46 GMT\"0x8D70F8CEE49A7FE\"2share305309dc1Tue, 23 Jul 2019 16:43:46 GMT\"0x8D70F8CEE5A4CDF\"2share305309dc22019-07-23T16:43:46.0000000ZTue, 23 Jul 2019 16:43:46 GMT\"0x8D70F8CEE6CC6C8\"2share305309dc2Tue, 23 Jul 2019 16:43:46 GMT\"0x8D70F8CEE6CC6C8\"2", + "Date" : "Tue, 23 Jul 2019 16:43:45 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share305309dc&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781fb3-001a-006b-1575-4192c4000000", + "Body" : "share305309dcshare305309dc0Tue, 23 Jul 2019 16:43:46 GMT\"0x8D70F8CEE49A7FE\"2share305309dc1Tue, 23 Jul 2019 16:43:46 GMT\"0x8D70F8CEE5A4CDF\"2share305309dc2Tue, 23 Jul 2019 16:43:46 GMT\"0x8D70F8CEE6CC6C8\"2", + "Date" : "Tue, 23 Jul 2019 16:43:45 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share305309dc0?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareHasSnapshots", + "retry-after" : "0", + "Content-Length" : "250", + "StatusCode" : "409", + "x-ms-request-id" : "a4781fb4-001a-006b-1675-4192c4000000", + "Body" : "ShareHasSnapshotsThe share has snapshots and the operation requires no snapshots.\nRequestId:a4781fb4-001a-006b-1675-4192c4000000\nTime:2019-07-23T16:43:46.8250278Z", + "Date" : "Tue, 23 Jul 2019 16:43:45 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share305309dc1?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781fb5-001a-006b-1775-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:45 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share305309dc2?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareHasSnapshots", + "retry-after" : "0", + "Content-Length" : "250", + "StatusCode" : "409", + "x-ms-request-id" : "a4781fb6-001a-006b-1875-4192c4000000", + "Body" : "ShareHasSnapshotsThe share has snapshots and the operation requires no snapshots.\nRequestId:a4781fb6-001a-006b-1875-4192c4000000\nTime:2019-07-23T16:43:46.9331058Z", + "Date" : "Tue, 23 Jul 2019 16:43:45 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share305309dc" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/listSharesInvalidMaxResults.json b/storage/client/file/src/test/resources/session-records/listSharesInvalidMaxResults.json new file mode 100644 index 0000000000000..82d415fce89ee --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/listSharesInvalidMaxResults.json @@ -0,0 +1,60 @@ +{ + "networkCallRecords" : [ { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share557594ed&maxresults=-1&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "OutOfRangeQueryParameterValue", + "retry-after" : "0", + "Content-Length" : "461", + "StatusCode" : "400", + "x-ms-request-id" : "a4781fe0-001a-006b-3775-4192c4000000", + "Body" : "OutOfRangeQueryParameterValueOne of the query parameters specified in the request URI is outside the permissible range.\nRequestId:a4781fe0-001a-006b-3775-4192c4000000\nTime:2019-07-23T16:43:48.6823538Zmaxresults-112147483647", + "Date" : "Tue, 23 Jul 2019 16:43:47 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share557594ed&maxresults=0&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "OutOfRangeQueryParameterValue", + "retry-after" : "0", + "Content-Length" : "460", + "StatusCode" : "400", + "x-ms-request-id" : "a4781fe1-001a-006b-3875-4192c4000000", + "Body" : "OutOfRangeQueryParameterValueOne of the query parameters specified in the request URI is outside the permissible range.\nRequestId:a4781fe1-001a-006b-3875-4192c4000000\nTime:2019-07-23T16:43:48.7303877Zmaxresults012147483647", + "Date" : "Tue, 23 Jul 2019 16:43:47 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share557594ed&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781fe2-001a-006b-3975-4192c4000000", + "Body" : "share557594ed", + "Date" : "Tue, 23 Jul 2019 16:43:47 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share557594ed" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/listSharesWithLimit.json b/storage/client/file/src/test/resources/session-records/listSharesWithLimit.json new file mode 100644 index 0000000000000..ba78ebd77bdae --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/listSharesWithLimit.json @@ -0,0 +1,142 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share058285ff0?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CECCDBF15\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:43:43 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781f6c-001a-006b-6075-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:42 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share058285ff1?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CECD62507\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:43:43 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781f6e-001a-006b-6175-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:42 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share058285ff2?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CECDDC795\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:43:44 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781f70-001a-006b-6275-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:43 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share058285ff&maxresults=2&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781f72-001a-006b-6375-4192c4000000", + "Body" : "share058285ff2share058285ff0Tue, 23 Jul 2019 16:43:43 GMT\"0x8D70F8CECCDBF15\"2share058285ff1Tue, 23 Jul 2019 16:43:43 GMT\"0x8D70F8CECD62507\"2/sima/share058285ff2", + "Date" : "Tue, 23 Jul 2019 16:43:43 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share058285ff&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781f73-001a-006b-6475-4192c4000000", + "Body" : "share058285ffshare058285ff0Tue, 23 Jul 2019 16:43:43 GMT\"0x8D70F8CECCDBF15\"2share058285ff1Tue, 23 Jul 2019 16:43:43 GMT\"0x8D70F8CECD62507\"2share058285ff2Tue, 23 Jul 2019 16:43:44 GMT\"0x8D70F8CECDDC795\"2", + "Date" : "Tue, 23 Jul 2019 16:43:43 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share058285ff0?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781f74-001a-006b-6575-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:43 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share058285ff1?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781f75-001a-006b-6675-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:43 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share058285ff2?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781f76-001a-006b-6775-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:43 GMT" + } + } ], + "variables" : [ "share058285ff" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/listSharesWithPrefix.json b/storage/client/file/src/test/resources/session-records/listSharesWithPrefix.json new file mode 100644 index 0000000000000..eec9f6affa44f --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/listSharesWithPrefix.json @@ -0,0 +1,142 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share98599009prefix0?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CEEA9B3D7\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:43:47 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781fb7-001a-006b-1975-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:46 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share985990091?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CEEB48B44\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:43:47 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781fb9-001a-006b-1a75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:46 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share98599009prefix2?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CEEBC7BF7\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:43:47 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781fbb-001a-006b-1b75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:46 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share98599009prefix&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781fbd-001a-006b-1c75-4192c4000000", + "Body" : "share98599009prefixshare98599009prefix0Tue, 23 Jul 2019 16:43:47 GMT\"0x8D70F8CEEA9B3D7\"2share98599009prefix2Tue, 23 Jul 2019 16:43:47 GMT\"0x8D70F8CEEBC7BF7\"2", + "Date" : "Tue, 23 Jul 2019 16:43:46 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share98599009&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781fbe-001a-006b-1d75-4192c4000000", + "Body" : "share98599009share985990091Tue, 23 Jul 2019 16:43:47 GMT\"0x8D70F8CEEB48B44\"2share98599009prefix0Tue, 23 Jul 2019 16:43:47 GMT\"0x8D70F8CEEA9B3D7\"2share98599009prefix2Tue, 23 Jul 2019 16:43:47 GMT\"0x8D70F8CEEBC7BF7\"2", + "Date" : "Tue, 23 Jul 2019 16:43:46 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share985990091?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781fbf-001a-006b-1e75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:46 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share98599009prefix0?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781fc0-001a-006b-1f75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:46 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share98599009prefix2?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781fc1-001a-006b-2075-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:46 GMT" + } + } ], + "variables" : [ "share98599009" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/setFileServiceProperties.json b/storage/client/file/src/test/resources/session-records/setFileServiceProperties.json new file mode 100644 index 0000000000000..c72087f424609 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/setFileServiceProperties.json @@ -0,0 +1,110 @@ +{ + "networkCallRecords" : [ { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?restype=service&comp=properties", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781fcf-001a-006b-2b75-4192c4000000", + "Body" : "1.0truetruetrue71.0falsefalse", + "Date" : "Tue, 23 Jul 2019 16:43:47 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net?restype=service&comp=properties", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781fd0-001a-006b-2c75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:47 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?restype=service&comp=properties", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781fd5-001a-006b-2d75-4192c4000000", + "Body" : "1.0truefalsetrue31.0truefalsetrue3", + "Date" : "Tue, 23 Jul 2019 16:43:47 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net?restype=service&comp=properties", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781fd6-001a-006b-2e75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:43:47 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?restype=service&comp=properties", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781fd7-001a-006b-2f75-4192c4000000", + "Body" : "1.0truetruetrue71.0falsefalse", + "Date" : "Tue, 23 Jul 2019 16:43:47 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share6218730e&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781fd8-001a-006b-3075-4192c4000000", + "Body" : "share6218730e", + "Date" : "Tue, 23 Jul 2019 16:43:47 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share6218730e" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/setHttpHeadersFromFileClient.json b/storage/client/file/src/test/resources/session-records/setHttpHeadersFromFileClient.json new file mode 100644 index 0000000000000..2c2ad20a05635 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/setHttpHeadersFromFileClient.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile81885521", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C3EAD5747\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:38:51 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781c33-001a-006b-3875-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:38:51 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile81885521?comp=properties", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C3EB5962E\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:38:51 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781c34-001a-006b-3975-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:38:51 GMT" + } + } ], + "variables" : [ "file81885521" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/setMeatadataFromFileClient.json b/storage/client/file/src/test/resources/session-records/setMeatadataFromFileClient.json new file mode 100644 index 0000000000000..636ea1308eec5 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/setMeatadataFromFileClient.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile571383d4", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C3ECFB295\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:38:51 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781c37-001a-006b-3c75-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:38:51 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile571383d4?comp=metadata", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C3ED81888\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:38:52 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781c38-001a-006b-3d75-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:38:51 GMT" + } + } ], + "variables" : [ "file571383d4" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/setMetadataDoesNotExistFromShareClient.json b/storage/client/file/src/test/resources/session-records/setMetadataDoesNotExistFromShareClient.json new file mode 100644 index 0000000000000..a5c5b574deedc --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/setMetadataDoesNotExistFromShareClient.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share7646218d?restype=share&comp=metadata", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "a4781dbb-001a-006b-7675-4192c4000000", + "Body" : "ShareNotFoundThe specified share does not exist.\nRequestId:a4781dbb-001a-006b-7675-4192c4000000\nTime:2019-07-23T16:41:16.3775365Z", + "Date" : "Tue, 23 Jul 2019 16:41:15 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share7646218d?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "a4781dbc-001a-006b-7775-4192c4000000", + "Body" : "ShareNotFoundThe specified share does not exist.\nRequestId:a4781dbc-001a-006b-7775-4192c4000000\nTime:2019-07-23T16:41:16.4265718Z", + "Date" : "Tue, 23 Jul 2019 16:41:15 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share7646218d" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/setMetadataFromDirClient.json b/storage/client/file/src/test/resources/session-records/setMetadataFromDirClient.json new file mode 100644 index 0000000000000..6ecfa9d794603 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/setMetadataFromDirClient.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory54792b?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB5439FA6\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:10 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781ebc-001a-006b-1475-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:09 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory54792b?restype=directory&comp=metadata", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB54B9056\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:10 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781ebd-001a-006b-1575-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:09 GMT" + } + } ], + "variables" : [ "directory54792b" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/setMetadataFromShareClient.json b/storage/client/file/src/test/resources/session-records/setMetadataFromShareClient.json new file mode 100644 index 0000000000000..51d3bce22d346 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/setMetadataFromShareClient.json @@ -0,0 +1,78 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share75460f03?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C790176C5\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:29 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781d4d-001a-006b-4c75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:28 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share75460f03?restype=share&comp=metadata", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C79099325\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:29 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781d4f-001a-006b-4d75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:28 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/share75460f03?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:29 GMT", + "retry-after" : "0", + "StatusCode" : "200", + "Date" : "Tue, 23 Jul 2019 16:40:29 GMT", + "x-ms-has-legal-hold" : "false", + "x-ms-share-quota" : "5120", + "ETag" : "\"0x8D70F8C79099325\"", + "x-ms-has-immutability-policy" : "false", + "x-ms-meta-setting" : "metadata", + "Content-Length" : "0", + "x-ms-request-id" : "a4781d50-001a-006b-4e75-4192c4000000" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share75460f03?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781d51-001a-006b-4f75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:29 GMT" + } + } ], + "variables" : [ "share75460f03" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/setMetadataInvalidKeyFromDirClient.json b/storage/client/file/src/test/resources/session-records/setMetadataInvalidKeyFromDirClient.json new file mode 100644 index 0000000000000..ca338623cd5ab --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/setMetadataInvalidKeyFromDirClient.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory04212f?restype=directory", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8CB53321EA\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:42:10 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781eba-001a-006b-1275-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:42:09 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/dirsharename/directory04212f?restype=directory&comp=metadata", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "InvalidMetadata", + "retry-after" : "0", + "Content-Length" : "260", + "StatusCode" : "400", + "x-ms-request-id" : "a4781ebb-001a-006b-1375-4192c4000000", + "Body" : "InvalidMetadataThe metadata specified is invalid. It has characters that are not permitted.\nRequestId:a4781ebb-001a-006b-1375-4192c4000000\nTime:2019-07-23T16:42:10.6604019Z", + "Date" : "Tue, 23 Jul 2019 16:42:09 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "directory04212f" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/setMetadataInvalidMetadataFromShareClient.json b/storage/client/file/src/test/resources/session-records/setMetadataInvalidMetadataFromShareClient.json new file mode 100644 index 0000000000000..993c6b5ed18c0 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/setMetadataInvalidMetadataFromShareClient.json @@ -0,0 +1,57 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share911321d2?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C95EA6042\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:41:18 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781dde-001a-006b-1175-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:17 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share911321d2?restype=share&comp=metadata", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "EmptyMetadataKey", + "retry-after" : "0", + "Content-Length" : "281", + "StatusCode" : "400", + "x-ms-request-id" : "a4781de1-001a-006b-1275-4192c4000000", + "Body" : "EmptyMetadataKeyThe key for one of the metadata key-value pairs is empty.\nRequestId:a4781de1-001a-006b-1275-4192c4000000\nTime:2019-07-23T16:41:18.1748228Zmetadata", + "Date" : "Tue, 23 Jul 2019 16:41:17 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share911321d2?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781de2-001a-006b-1375-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:17 GMT" + } + } ], + "variables" : [ "share911321d2" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/setPolicies.json b/storage/client/file/src/test/resources/session-records/setPolicies.json new file mode 100644 index 0000000000000..a5247ae91c9ee --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/setPolicies.json @@ -0,0 +1,77 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share62017f60?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C7703C30B\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:26 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781d05-001a-006b-1475-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:25 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share62017f60?restype=share&comp=acl", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C770BDF57\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:26 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781d07-001a-006b-1575-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:25 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/share62017f60?restype=share&comp=acl", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C770BDF57\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:26 GMT", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781d08-001a-006b-1675-4192c4000000", + "Body" : "test2000-01-01T00:00:00.0000000Z2020-01-01T00:00:00.0000000Zr", + "Date" : "Tue, 23 Jul 2019 16:40:25 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share62017f60?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781d09-001a-006b-1775-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:25 GMT" + } + } ], + "variables" : [ "share62017f60" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/setPoliciesDoesNotExist.json b/storage/client/file/src/test/resources/session-records/setPoliciesDoesNotExist.json new file mode 100644 index 0000000000000..6f5ee4c9407d8 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/setPoliciesDoesNotExist.json @@ -0,0 +1,43 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share49743cbd?restype=share&comp=acl", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "a4781d43-001a-006b-4475-4192c4000000", + "Body" : "ShareNotFoundThe specified share does not exist.\nRequestId:a4781d43-001a-006b-4475-4192c4000000\nTime:2019-07-23T16:40:29.1508343Z", + "Date" : "Tue, 23 Jul 2019 16:40:28 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share49743cbd?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "a4781d44-001a-006b-4575-4192c4000000", + "Body" : "ShareNotFoundThe specified share does not exist.\nRequestId:a4781d44-001a-006b-4575-4192c4000000\nTime:2019-07-23T16:40:29.2008691Z", + "Date" : "Tue, 23 Jul 2019 16:40:28 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share49743cbd" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/setPoliciesInvalidPermission.json b/storage/client/file/src/test/resources/session-records/setPoliciesInvalidPermission.json new file mode 100644 index 0000000000000..f8c215e0dc597 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/setPoliciesInvalidPermission.json @@ -0,0 +1,58 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share2144443e?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C949D09C9\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:41:15 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781db0-001a-006b-6e75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:15 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share2144443e?restype=share&comp=acl", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "InvalidXmlDocument", + "retry-after" : "0", + "Content-Length" : "294", + "StatusCode" : "400", + "x-ms-request-id" : "a4781db2-001a-006b-6f75-4192c4000000", + "Body" : "InvalidXmlDocumentXML specified is not syntactically valid.\nRequestId:a4781db2-001a-006b-6f75-4192c4000000\nTime:2019-07-23T16:41:15.9892600Z00", + "Date" : "Tue, 23 Jul 2019 16:41:15 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share2144443e?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781db3-001a-006b-7075-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:15 GMT" + } + } ], + "variables" : [ "share2144443e" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/setPoliciesTooManyPermissions.json b/storage/client/file/src/test/resources/session-records/setPoliciesTooManyPermissions.json new file mode 100644 index 0000000000000..1e66291217bae --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/setPoliciesTooManyPermissions.json @@ -0,0 +1,58 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share28227ac8?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C77EA7C99\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:27 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781d26-001a-006b-2e75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:27 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share28227ac8?restype=share&comp=acl", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "InvalidXmlDocument", + "retry-after" : "0", + "Content-Length" : "294", + "StatusCode" : "400", + "x-ms-request-id" : "a4781d29-001a-006b-3075-4192c4000000", + "Body" : "InvalidXmlDocumentXML specified is not syntactically valid.\nRequestId:a4781d29-001a-006b-3075-4192c4000000\nTime:2019-07-23T16:40:27.8429018Z00", + "Date" : "Tue, 23 Jul 2019 16:40:27 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share28227ac8?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781d2b-001a-006b-3275-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:27 GMT" + } + } ], + "variables" : [ "share28227ac8" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/setPropertiesDoesNotExistFromShareClient.json b/storage/client/file/src/test/resources/session-records/setPropertiesDoesNotExistFromShareClient.json new file mode 100644 index 0000000000000..61be09fe38782 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/setPropertiesDoesNotExistFromShareClient.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share74233b07?restype=share&comp=properties", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "a4781dd6-001a-006b-0a75-4192c4000000", + "Body" : "ShareNotFoundThe specified share does not exist.\nRequestId:a4781dd6-001a-006b-0a75-4192c4000000\nTime:2019-07-23T16:41:17.7485158Z", + "Date" : "Tue, 23 Jul 2019 16:41:16 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share74233b07?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "a4781dd7-001a-006b-0b75-4192c4000000", + "Body" : "ShareNotFoundThe specified share does not exist.\nRequestId:a4781dd7-001a-006b-0b75-4192c4000000\nTime:2019-07-23T16:41:17.7975514Z", + "Date" : "Tue, 23 Jul 2019 16:41:17 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share74233b07" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/setPropertiesFromShareClient.json b/storage/client/file/src/test/resources/session-records/setPropertiesFromShareClient.json new file mode 100644 index 0000000000000..e137e0a5800e6 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/setPropertiesFromShareClient.json @@ -0,0 +1,98 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share71347507?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C95C1E93A\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:41:17 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781dd8-001a-006b-0c75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:17 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/share71347507?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "x-ms-share-quota" : "2", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C95C1E93A\"", + "x-ms-has-immutability-policy" : "false", + "Last-Modified" : "Tue, 23 Jul 2019 16:41:17 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781dda-001a-006b-0d75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:17 GMT", + "x-ms-has-legal-hold" : "false" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share71347507?restype=share&comp=properties", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C95D17F11\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:41:17 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781ddb-001a-006b-0e75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:17 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/share71347507?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "x-ms-share-quota" : "4", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C95D17F11\"", + "x-ms-has-immutability-policy" : "false", + "Last-Modified" : "Tue, 23 Jul 2019 16:41:17 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781ddc-001a-006b-0f75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:17 GMT", + "x-ms-has-legal-hold" : "false" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share71347507?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781ddd-001a-006b-1075-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:17 GMT" + } + } ], + "variables" : [ "share71347507" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/setPropertiesInvalidAllowedHeader.json b/storage/client/file/src/test/resources/session-records/setPropertiesInvalidAllowedHeader.json new file mode 100644 index 0000000000000..64f1db166613b --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/setPropertiesInvalidAllowedHeader.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net?restype=service&comp=properties", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "InvalidXmlDocument", + "retry-after" : "0", + "Content-Length" : "294", + "StatusCode" : "400", + "x-ms-request-id" : "a4781fc2-001a-006b-2175-4192c4000000", + "Body" : "InvalidXmlDocumentXML specified is not syntactically valid.\nRequestId:a4781fc2-001a-006b-2175-4192c4000000\nTime:2019-07-23T16:43:47.4734913Z00", + "Date" : "Tue, 23 Jul 2019 16:43:46 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share42833eb8&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781fc3-001a-006b-2275-4192c4000000", + "Body" : "share42833eb8", + "Date" : "Tue, 23 Jul 2019 16:43:46 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share42833eb8" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/setPropertiesInvalidAllowedMethod.json b/storage/client/file/src/test/resources/session-records/setPropertiesInvalidAllowedMethod.json new file mode 100644 index 0000000000000..bde0f4c241c81 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/setPropertiesInvalidAllowedMethod.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net?restype=service&comp=properties", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "InvalidXmlNodeValue", + "retry-after" : "0", + "Content-Length" : "340", + "StatusCode" : "400", + "x-ms-request-id" : "a4781fd9-001a-006b-3175-4192c4000000", + "Body" : "InvalidXmlNodeValueThe value for one of the XML nodes is not in the correct format.\nRequestId:a4781fd9-001a-006b-3175-4192c4000000\nTime:2019-07-23T16:43:48.3661282ZAllowedMethodsNOTAREALHTTPMETHOD", + "Date" : "Tue, 23 Jul 2019 16:43:47 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share26492564&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781fda-001a-006b-3275-4192c4000000", + "Body" : "share26492564", + "Date" : "Tue, 23 Jul 2019 16:43:47 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share26492564" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/setPropertiesInvalidAllowedOrigin.json b/storage/client/file/src/test/resources/session-records/setPropertiesInvalidAllowedOrigin.json new file mode 100644 index 0000000000000..fa758d47e3eff --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/setPropertiesInvalidAllowedOrigin.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net?restype=service&comp=properties", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "InvalidXmlDocument", + "retry-after" : "0", + "Content-Length" : "294", + "StatusCode" : "400", + "x-ms-request-id" : "a4781fdc-001a-006b-3375-4192c4000000", + "Body" : "InvalidXmlDocumentXML specified is not syntactically valid.\nRequestId:a4781fdc-001a-006b-3375-4192c4000000\nTime:2019-07-23T16:43:48.4682010Z00", + "Date" : "Tue, 23 Jul 2019 16:43:47 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share46216ea8&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781fdd-001a-006b-3475-4192c4000000", + "Body" : "share46216ea8", + "Date" : "Tue, 23 Jul 2019 16:43:47 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share46216ea8" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/setPropertiesInvalidExposedHeader.json b/storage/client/file/src/test/resources/session-records/setPropertiesInvalidExposedHeader.json new file mode 100644 index 0000000000000..9020928319556 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/setPropertiesInvalidExposedHeader.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net?restype=service&comp=properties", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "InvalidXmlDocument", + "retry-after" : "0", + "Content-Length" : "294", + "StatusCode" : "400", + "x-ms-request-id" : "a4781fde-001a-006b-3575-4192c4000000", + "Body" : "InvalidXmlDocumentXML specified is not syntactically valid.\nRequestId:a4781fde-001a-006b-3575-4192c4000000\nTime:2019-07-23T16:43:48.5772777Z00", + "Date" : "Tue, 23 Jul 2019 16:43:47 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share66999f9c&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781fdf-001a-006b-3675-4192c4000000", + "Body" : "share66999f9c", + "Date" : "Tue, 23 Jul 2019 16:43:47 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share66999f9c" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/setPropertiesInvalidQuotaFromShareClient.json b/storage/client/file/src/test/resources/session-records/setPropertiesInvalidQuotaFromShareClient.json new file mode 100644 index 0000000000000..768a60b78cadd --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/setPropertiesInvalidQuotaFromShareClient.json @@ -0,0 +1,76 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share46729b2d?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C788159EC\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:28 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781d3a-001a-006b-3d75-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:28 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share46729b2d?restype=share&comp=properties", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "InvalidHeaderValue", + "retry-after" : "0", + "Content-Length" : "324", + "StatusCode" : "400", + "x-ms-request-id" : "a4781d3c-001a-006b-3e75-4192c4000000", + "Body" : "InvalidHeaderValueThe value for one of the HTTP headers is not in the correct format.\nRequestId:a4781d3c-001a-006b-3e75-4192c4000000\nTime:2019-07-23T16:40:28.8296045Zx-ms-share-quota-1", + "Date" : "Tue, 23 Jul 2019 16:40:28 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share46729b2d?restype=share&comp=properties", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "InvalidHeaderValue", + "retry-after" : "0", + "Content-Length" : "326", + "StatusCode" : "400", + "x-ms-request-id" : "a4781d3d-001a-006b-3f75-4192c4000000", + "Body" : "InvalidHeaderValueThe value for one of the HTTP headers is not in the correct format.\nRequestId:a4781d3d-001a-006b-3f75-4192c4000000\nTime:2019-07-23T16:40:28.8756373Zx-ms-share-quota9999", + "Date" : "Tue, 23 Jul 2019 16:40:28 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share46729b2d?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a4781d3e-001a-006b-4075-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:28 GMT" + } + } ], + "variables" : [ "share46729b2d" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/setPropertiesTooManyRules.json b/storage/client/file/src/test/resources/session-records/setPropertiesTooManyRules.json new file mode 100644 index 0000000000000..885ff107c99ed --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/setPropertiesTooManyRules.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net?restype=service&comp=properties", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "InvalidXmlDocument", + "retry-after" : "0", + "Content-Length" : "294", + "StatusCode" : "400", + "x-ms-request-id" : "a4781f7e-001a-006b-6e75-4192c4000000", + "Body" : "InvalidXmlDocumentXML specified is not syntactically valid.\nRequestId:a4781f7e-001a-006b-6e75-4192c4000000\nTime:2019-07-23T16:43:44.6374670Z00", + "Date" : "Tue, 23 Jul 2019 16:43:43 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net?prefix=share41569da0&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "a4781f7f-001a-006b-6f75-4192c4000000", + "Body" : "share41569da0", + "Date" : "Tue, 23 Jul 2019 16:43:43 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share41569da0" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/snapshot.json b/storage/client/file/src/test/resources/session-records/snapshot.json new file mode 100644 index 0000000000000..08e6b88615e55 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/snapshot.json @@ -0,0 +1,60 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share14716ca7?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C793BF261\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:30 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781d55-001a-006b-5275-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:29 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share14716ca7?restype=share&comp=snapshot", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "x-ms-snapshot" : "2019-07-23T16:40:30.0000000Z", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C793BF261\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:30 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781d57-001a-006b-5375-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:29 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share14716ca7?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareHasSnapshots", + "retry-after" : "0", + "Content-Length" : "250", + "StatusCode" : "409", + "x-ms-request-id" : "a4781d58-001a-006b-5475-4192c4000000", + "Body" : "ShareHasSnapshotsThe share has snapshots and the operation requires no snapshots.\nRequestId:a4781d58-001a-006b-5475-4192c4000000\nTime:2019-07-23T16:40:30.1135230Z", + "Date" : "Tue, 23 Jul 2019 16:40:29 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share14716ca7" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/snapshotDifferentMetadata.json b/storage/client/file/src/test/resources/session-records/snapshotDifferentMetadata.json new file mode 100644 index 0000000000000..ff7dd6c695507 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/snapshotDifferentMetadata.json @@ -0,0 +1,104 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share10771b10?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C777A1A17\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:27 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781d17-001a-006b-2275-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:26 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share10771b10?restype=share&comp=snapshot", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "x-ms-snapshot" : "2019-07-23T16:40:27.0000000Z", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C7771CF80\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:27 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781d19-001a-006b-2375-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:40:26 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/share10771b10?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:27 GMT", + "retry-after" : "0", + "StatusCode" : "200", + "Date" : "Tue, 23 Jul 2019 16:40:26 GMT", + "x-ms-has-legal-hold" : "false", + "x-ms-meta-create" : "metadata", + "x-ms-share-quota" : "2", + "ETag" : "\"0x8D70F8C777A1A17\"", + "x-ms-has-immutability-policy" : "false", + "Content-Length" : "0", + "x-ms-request-id" : "a4781d1a-001a-006b-2475-4192c4000000" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/share10771b10?sharesnapshot=2019-07-23T16%3a40%3a27.0000000Z&restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-meta-update" : "metadata", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "Last-Modified" : "Tue, 23 Jul 2019 16:40:27 GMT", + "retry-after" : "0", + "StatusCode" : "200", + "Date" : "Tue, 23 Jul 2019 16:40:26 GMT", + "x-ms-has-legal-hold" : "false", + "x-ms-share-quota" : "2", + "ETag" : "\"0x8D70F8C7771CF80\"", + "x-ms-has-immutability-policy" : "false", + "Content-Length" : "0", + "x-ms-request-id" : "3975b150-b01a-003d-1775-417ab4000000" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share10771b10?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareHasSnapshots", + "retry-after" : "0", + "Content-Length" : "250", + "StatusCode" : "409", + "x-ms-request-id" : "a4781d1b-001a-006b-2575-4192c4000000", + "Body" : "ShareHasSnapshotsThe share has snapshots and the operation requires no snapshots.\nRequestId:a4781d1b-001a-006b-2575-4192c4000000\nTime:2019-07-23T16:40:27.2825020Z", + "Date" : "Tue, 23 Jul 2019 16:40:26 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share10771b10" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/snapshotDoesNotExist.json b/storage/client/file/src/test/resources/session-records/snapshotDoesNotExist.json new file mode 100644 index 0000000000000..364519a28f53c --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/snapshotDoesNotExist.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share00244732?restype=share&comp=snapshot", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "a4781d45-001a-006b-4675-4192c4000000", + "Body" : "ShareNotFoundThe specified share does not exist.\nRequestId:a4781d45-001a-006b-4675-4192c4000000\nTime:2019-07-23T16:40:29.2589105Z", + "Date" : "Tue, 23 Jul 2019 16:40:28 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share00244732?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "a4781d47-001a-006b-4775-4192c4000000", + "Body" : "ShareNotFoundThe specified share does not exist.\nRequestId:a4781d47-001a-006b-4775-4192c4000000\nTime:2019-07-23T16:40:29.3079458Z", + "Date" : "Tue, 23 Jul 2019 16:40:28 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share00244732" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/snapshotSameMetadata.json b/storage/client/file/src/test/resources/session-records/snapshotSameMetadata.json new file mode 100644 index 0000000000000..0e5c5452621c0 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/snapshotSameMetadata.json @@ -0,0 +1,82 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share60407a93?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C958F1003\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:41:17 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781dd2-001a-006b-0775-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:16 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/share60407a93?restype=share&comp=snapshot", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "x-ms-snapshot" : "2019-07-23T16:41:17.0000000Z", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C953F3480\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:41:17 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781dd4-001a-006b-0875-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:41:16 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/share60407a93?sharesnapshot=2019-07-23T16%3a41%3a17.0000000Z&restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-meta-test" : "metadata", + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "Last-Modified" : "Tue, 23 Jul 2019 16:41:17 GMT", + "retry-after" : "0", + "StatusCode" : "200", + "Date" : "Tue, 23 Jul 2019 16:41:16 GMT", + "x-ms-has-legal-hold" : "false", + "x-ms-share-quota" : "2", + "ETag" : "\"0x8D70F8C953F3480\"", + "x-ms-has-immutability-policy" : "false", + "Content-Length" : "0", + "x-ms-request-id" : "3975b191-b01a-003d-2775-417ab4000000" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.file.core.windows.net/share60407a93?restype=share", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "ShareHasSnapshots", + "retry-after" : "0", + "Content-Length" : "250", + "StatusCode" : "409", + "x-ms-request-id" : "a4781dd5-001a-006b-0975-4192c4000000", + "Body" : "ShareHasSnapshotsThe share has snapshots and the operation requires no snapshots.\nRequestId:a4781dd5-001a-006b-0975-4192c4000000\nTime:2019-07-23T16:41:17.6894731Z", + "Date" : "Tue, 23 Jul 2019 16:41:16 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "share60407a93" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/startCopy.json b/storage/client/file/src/test/resources/session-records/startCopy.json new file mode 100644 index 0000000000000..f2a527cd8e4b9 --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/startCopy.json @@ -0,0 +1,43 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile8920181a", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C3DB97C52\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:38:50 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781c20-001a-006b-2775-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:38:49 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile8920181a", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-copy-id" : "2d272a59-d40b-4b67-8fc2-b66d8aa4a210", + "ETag" : "\"0x8D70F8C3DDF5ACE\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:38:50 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "x-ms-copy-status" : "success", + "StatusCode" : "202", + "x-ms-request-id" : "a4781c21-001a-006b-2875-4192c4000000", + "Date" : "Tue, 23 Jul 2019 16:38:49 GMT" + } + } ], + "variables" : [ "file8920181a" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/upload.json b/storage/client/file/src/test/resources/session-records/upload.json new file mode 100644 index 0000000000000..457a29b5542cf --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/upload.json @@ -0,0 +1,44 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile98350d88", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C3E2EC19B\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:38:50 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781c2b-001a-006b-3075-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:38:50 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile98350d88?comp=range", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/octet-stream" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C3E372789\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:38:50 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781c2c-001a-006b-3175-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:38:50 GMT", + "Content-MD5" : "wh+Wm18D0z1D4E+PE252gg==" + } + } ], + "variables" : [ "file98350d88" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/uploadToStorageAndDownloadToFile.json b/storage/client/file/src/test/resources/session-records/uploadToStorageAndDownloadToFile.json new file mode 100644 index 0000000000000..dbc17e4548a1f --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/uploadToStorageAndDownloadToFile.json @@ -0,0 +1,90 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile11163511", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C3E4050F6\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:38:51 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781c2d-001a-006b-3275-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:38:50 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile11163511?comp=range", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/octet-stream" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "ETag" : "\"0x8D70F8C3E4ED28D\"", + "Last-Modified" : "Tue, 23 Jul 2019 16:38:51 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "a4781c2e-001a-006b-3375-4192c4000000", + "x-ms-request-server-encrypted" : "true", + "Date" : "Tue, 23 Jul 2019 16:38:50 GMT", + "Content-MD5" : "/CTzx8jXJ1MqqUsC1dJBUQ==" + } + }, { + "Method" : "HEAD", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile11163511", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "Last-Modified" : "Tue, 23 Jul 2019 16:38:51 GMT", + "retry-after" : "0", + "StatusCode" : "200", + "Date" : "Tue, 23 Jul 2019 16:38:50 GMT", + "x-ms-server-encrypted" : "true", + "x-ms-type" : "File", + "ETag" : "\"0x8D70F8C3E4ED28D\"", + "Content-Length" : "14", + "x-ms-request-id" : "a4781c2f-001a-006b-3475-4192c4000000", + "Body" : "", + "Content-Type" : "application/octet-stream" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.file.core.windows.net/filesharename/testdir%2ffile11163511", + "Headers" : { + "x-ms-version" : "2018-11-09", + "User-Agent" : "azsdk-java-azure-storage-file/12.0.0-preview.1 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-11-09", + "Server" : "Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0", + "Content-Range" : "bytes 0-13/14", + "Last-Modified" : "Tue, 23 Jul 2019 16:38:51 GMT", + "retry-after" : "0", + "StatusCode" : "206", + "Date" : "Tue, 23 Jul 2019 16:38:51 GMT", + "Accept-Ranges" : "bytes", + "x-ms-server-encrypted" : "true", + "x-ms-type" : "File", + "ETag" : "\"0x8D70F8C3E4ED28D\"", + "Content-Length" : "14", + "x-ms-request-id" : "a4781c30-001a-006b-3575-4192c4000000", + "Body" : "Hello world!\r\n", + "Content-Type" : "application/octet-stream" + } + } ], + "variables" : [ "file11163511" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/session-records/urlFromDirClient.json b/storage/client/file/src/test/resources/session-records/urlFromDirClient.json new file mode 100644 index 0000000000000..11fc90ded8aec --- /dev/null +++ b/storage/client/file/src/test/resources/session-records/urlFromDirClient.json @@ -0,0 +1,4 @@ +{ + "networkCallRecords" : [ ], + "variables" : [ "directory100251" ] +} \ No newline at end of file diff --git a/storage/client/file/src/test/resources/testfiles/helloworld b/storage/client/file/src/test/resources/testfiles/helloworld new file mode 100644 index 0000000000000..cd0875583aabe --- /dev/null +++ b/storage/client/file/src/test/resources/testfiles/helloworld @@ -0,0 +1 @@ +Hello world! diff --git a/storage/client/pom.xml b/storage/client/pom.xml new file mode 100644 index 0000000000000..645574936759e --- /dev/null +++ b/storage/client/pom.xml @@ -0,0 +1,123 @@ + + + 4.0.0 + + com.azure + azure-client-sdk-parent + 1.0.0 + ../../pom.client.xml + + + com.azure + azure-storage + 12.0.0 + + Microsoft Azure client library for Storage + This package contains the Microsoft Azure Storage client library. + https://github.com/Azure/azure-sdk-for-java + + + + azure-java-build-docs + ${site.url}/site/${project.artifactId} + + + + + scm:git:https://github.com/Azure/azure-sdk-for-java + scm:git:git@github.com:Azure/azure-sdk-for-java.git + HEAD + + + + + com.azure + azure-core + 1.0.0-preview.1 + + + + + + + org.slf4j + slf4j-api + + + + com.azure + azure-core-test + 1.0.0-preview.1 + test + + + junit + junit + test + + + org.slf4j + slf4j-simple + test + + + io.projectreactor + reactor-test + test + + + com.microsoft.azure + adal4j + test + + + org.spockframework + spock-core + test + + + cglib + cglib-nodep + test + + + uk.org.lidalia + slf4j-test + test + + + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + false + false + + + + org.apache.maven.plugins + maven-javadoc-plugin + + false + false + + + + + com.github.spotbugs + spotbugs-maven-plugin + + false + + + + + diff --git a/storage/client/queue/README.md b/storage/client/queue/README.md index 7b4dbbf1afadd..18f92018e21c5 100644 --- a/storage/client/queue/README.md +++ b/storage/client/queue/README.md @@ -26,9 +26,7 @@ A single queue message can be up to 64 KB in size, and a queue can contain milli To create a Storage Account you can use the Azure Portal or [Azure CLI][azure_cli]. ```Powershell -az group create \ - --name storage-resource-group \ - --location westus +az group create --name storage-resource-group --location westus ``` ### Authenticate the client @@ -52,7 +50,6 @@ az storage queue generate-sas ```Powershell CONNECTION_STRING= -#usage example az storage queue generate-sas --name javasdksas --expiry 2019-06-05 @@ -112,10 +109,10 @@ The queue service do operations on the queues in the storage account and manage ### Queue Service Client The client performs the interactions with the Queue service, create or delete a queue, getting and setting Queue properties, list queues in account, and get queue statistics. An asynchronous, `QueueServiceAsyncClient`, and synchronous, `QueueClient`, client exists in the SDK allowing for selection of a client based on an application's use case. -Once you have the value of the SASToken you can create the queue service client with `${accountName}`, `${sasToken}`. +Once you have the value of the SASToken you can create the queue service client with `${accountName}`, `${SASToken}`. ```Java -String queueServiceURL = String.format("https://%s.queue.core.windows.net/%s", accountName, sasToken) -QueueServiceClient queueServiceClient = QueueServiceClient.builder().endpoint(queueURL).build(); +String queueServiceURL = String.format("https://%s.queue.core.windows.net", accountName) +QueueServiceClient queueServiceClient = new QueueServiceClientBuilder().endpoint(queueURL).credential(SASToken).build(); QueueClient newQueueServiceClient = queueServiceClient.createQueue("myqueue"); ``` @@ -123,8 +120,9 @@ QueueClient newQueueServiceClient = queueServiceClient.createQueue("myqueue"); or ```Java -String queueServiceAsyncURL = String.format("https://%s.queue.core.windows.net/%s", accountName, sasToken) -QueueServiceAsyncClient queueServiceAsyncClient = QueueServiceAsyncClient.builder().endpoint(queueServiceAsyncURL).build(); +String queueServiceAsyncURL = String.format("https://%s.queue.core.windows.net/", accountName) +QueueServiceAsyncClient queueServiceAsyncClient = new QueueServiceClientBuilder().endpoint(queueServiceAsyncURL) + credential(SASToken).build(); queueServiceAsyncClient.createQueue("newAsyncQueue").subscribe( result -> { // do something when new queue created @@ -142,10 +140,10 @@ Azure Queue storage is a service for storing large numbers of messages that can A single queue message can be up to 64 KB in size, and a queue can contain millions of messages, up to the total capacity limit of a storage account. ### QueueClient -Once you have the value of the SASToken you can create the queue service client with `${accountName}`, `${queueName}`, `${sasToken}`. +Once you have the value of the SASToken you can create the queue service client with `${accountName}`, `${queueName}`, `${SASToken}`. ```Java -String queueURL = String.format("https://%s.queue.core.windows.net/%s%s", accountName, queueName, sasToken); -QueueClient queueClient = QueueClient.builder().endpoint(queueURL).build(); +String queueURL = String.format("https://%s.queue.core.windows.net/%s", accountName, queueName); +QueueClient queueClient = QueueClient.builder().endpoint(queueURL).credential(SASToken).build(); // metadata is map of key-value pair, timeout is client side timeout QueueClient newQueueClient = queueClient.create(metadata, timeout); ``` @@ -170,6 +168,7 @@ queueAsyncClient.create(metadata, timeout).subscribe( ## Examples The following sections provide several code snippets covering some of the most common Configuration Service tasks, including: +- [Build a client](#build-a-client) - [Create a Queue](#Create-a-queue) - [Delete a queue](#Delete-a-queue) - [List the queues in account](#List-queues-in-account) @@ -183,32 +182,54 @@ The following sections provide several code snippets covering some of the most c - [Delete message from a queue](#Delete-message-from-a-queue) - [Get a Queue properties](#Get-a-queue-properties) - [Set/Update a Queue metadata](#Set-a-queue-metadata) + +### Build a client +We have two ways of building QueueService or Queue Client. Here will take queueServiceClient as an example. Same things apply to queueClient. + +First, build client from full URL/endpoint (e.g. with queueName, with SASToken and etc.) + +```Java +String queueServiceURL = String.format("https://%s.queue.core.windows.net/%s", accountName, sasToken); +QueueServiceClient queueServiceClient = new QueueServiceClientBuilder().endpoint(queueServiceURL).buildClient(); +``` + +Or + +We can build the queueServiceClient from the builder using `${SASToken}` as credential. + +```Java +String queueServiceURL = String.format("https://%s.queue.core.windows.net", accountName); +QueueServiceClient queueServiceClient = new QueueServiceClientBuilder().endpoint(queueServiceURL).credential(SASToken).buildClient(); +``` + ### Create a queue -Create a queue in the Storage Account. Throws StorageErrorException If the queue fails to be created. +Create a queue in the Storage Account using `${SASToken}` as credential. +Throws StorageErrorException If the queue fails to be created. ```Java -String queueServiceURL = String.format("https://%s.queue.core.windows.net/%s", accountName, sasToken) -QueueServiceClient queueServiceClient = QueueServiceClient.builder().endpoint(queueURL).build(); +String queueServiceURL = String.format("https://%s.queue.core.windows.net", accountName); +QueueServiceClient queueServiceClient = new QueueServiceClientBuilder().endpoint(queueServiceURL).credential(SASToken).buildClient(); QueueClient newQueueServiceClient = queueServiceClient.createQueue("myqueue"); ``` ### Delete a queue -Delete a queue in the Storage Account. Throws StorageErrorException If the queue fails to be deleted. +Delete a queue in the Storage Account using `${SASToken}` as credential. +Throws StorageErrorException If the queue fails to be deleted. ```Java -String queueServiceURL = String.format("https://%s.queue.core.windows.net/%s", accountName, sasToken) -QueueServiceClient queueServiceClient = QueueServiceClient.builder().endpoint(queueURL).build(); +String queueServiceURL = String.format("https://%s.queue.core.windows.net", accountName); +QueueServiceClient queueServiceClient = new QueueServiceClientBuilder().endpoint(queueServiceURL).credential(SASToken).buildClient(); QueueClient newQueueServiceClient = queueServiceClient.deleteQueue("myqueue"); ``` ### List queues in account -List all the queues in account. +List all the queues in account using `${SASToken}` as credential. ```Java -String queueServiceURL = String.format("https://%s.queue.core.windows.net/%s", accountName, sasToken) -QueueServiceClient queueServiceClient = QueueServiceClient.builder().endpoint(queueURL).build(); +String queueServiceURL = String.format("https://%s.queue.core.windows.net", accountName); +QueueServiceClient queueServiceClient = new QueueServiceClientBuilder().endpoint(queueServiceURL).credential(SASToken).buildClient(); // @param marker: Starting point to list the queues // @param options: Filter for queue selection queueServiceClient.listQueuesSegment(marker, options).forEach{ @@ -219,9 +240,11 @@ queueServiceClient.listQueuesSegment(marker, options).forEach{ ### Get properties in queue account Get queue properties in account, including properties for Storage Analytics and CORS (Cross-Origin Resource Sharing) rules. + +Use `${SASToken}` as credential. ```Java -String queueServiceURL = String.format("https://%s.queue.core.windows.net/%s", accountName, sasToken) -QueueServiceClient queueServiceClient = QueueServiceClient.builder().endpoint(queueURL).build(); +String queueServiceURL = String.format("https://%s.queue.core.windows.net", accountName); +QueueServiceClient queueServiceClient = new QueueServiceClientBuilder().endpoint(queueServiceURL).credential(SASToken).buildClient(); Response properties = queueServiceClient.getProperties(); ``` @@ -229,9 +252,11 @@ Response properties = queueServiceClient.getProperties ### Set properties in queue account Set queue properties in account, including properties for Storage Analytics and CORS (Cross-Origin Resource Sharing) rules. + +Use `${SASToken}` as credential. ```Java -String queueServiceURL = String.format("https://%s.queue.core.windows.net/%s", accountName, sasToken) -QueueServiceClient queueServiceClient = QueueServiceClient.builder().endpoint(queueURL).build(); +String queueServiceURL = String.format("https://%s.queue.core.windows.net", accountName); +QueueServiceClient queueServiceClient = new QueueServiceClientBuilder().endpoint(queueServiceURL).credential(SASToken).buildClient(); StorageServiceProperties properties = new StorageServiceProperties() { // logging: some logging; @@ -244,30 +269,34 @@ queueServiceClient.setProperties(properties); ``` ### Get queue service statistics -he `Get Queue Service Stats` operation retrieves statistics related to replication for the Queue service. +The `Get Queue Service Stats` operation retrieves statistics related to replication for the Queue service. + +Use `${SASToken}` as credential. It is only available on the secondary location endpoint when read-access geo-redundant replication is enabled for the storage account. ```Java -String queueServiceURL = String.format("https://%s.queue.core.windows.net/%s", accountName, sasToken) -QueueServiceClient queueServiceClient = QueueServiceClient.builder().endpoint(queueURL).build(); +String queueServiceURL = String.format("https://%s.queue.core.windows.net", accountName); +QueueServiceClient queueServiceClient = new QueueServiceClientBuilder().endpoint(queueServiceURL).credential(SASToken).buildClient(); Response queueStats = queueServiceClient.getStatistics(); ``` ### Enqueue message into a queue The operation adds a new message to the back of the message queue. A visibility timeout can also be specified to make the message invisible until the visibility timeout expires. + +Use `${SASToken}` as credential. A message must be in a format that can be included in an XML request with UTF-8 encoding. The encoded message can be up to 64 KB in size for versions 2011-08-18 and newer, or 8 KB in size for previous versions. ```Java -String queueURL = String.format("https://%s.queue.core.windows.net/%s%s", accountName, queueName, sasToken); -QueueClient queueClient = QueueClient.builder().endpoint(queueURL).build(); +String queueSURL = String.format("https://%s.queue.core.windows.net", accountName); +QueueClient queueClient = new QueueClientBuilder().endpoint(queueURL).credential(SASToken).queueName("myqueue").buildClient(); queueClient.enqueueMessage("myMessage"); ``` ### Update messaged from a queue -The operation updates a message in the message queue. +The operation updates a message in the message queue. Use `${SASToken}` as credential. ```Java -String queueURL = String.format("https://%s.queue.core.windows.net/%s%s", accountName, queueName, sasToken); -QueueClient queueClient = QueueClient.builder().endpoint(queueURL).build(); +String queueSURL = String.format("https://%s.queue.core.windows.net", accountName); +QueueClient queueClient = new QueueClientBuilder().endpoint(queueURL).credential(SASToken).queueName("myqueue").buildClient(); // @param messageId Id of the message // @param popReceipt Unique identifier that must match the message for it to be updated // @param visibilityTimeout How long the message will be invisible in the queue in seconds @@ -275,48 +304,52 @@ queueClient.updateMessage(messageId, "new message", popReceipt, visibilityTimeou ``` ### Peek messages from a queue -The operation retrieves one or more messages from the front of the queue. +The operation retrieves one or more messages from the front of the queue. Use `${SASToken}` as credential. ```Java -String queueURL = String.format("https://%s.queue.core.windows.net/%s%s", accountName, queueName, sasToken); -QueueClient queueClient = QueueClient.builder().endpoint(queueURL).build(); +String queueSURL = String.format("https://%s.queue.core.windows.net", accountName); +QueueClient queueClient = new QueueClientBuilder().endpoint(queueURL).credential(SASToken).queueName("myqueue").buildClient(); queueClient.peekMessages().forEach(message-> {print message.messageText();}); ``` ### Dequeue messages from a queue -The operation retrieves one or more messages from the front of the queue. +The operation retrieves one or more messages from the front of the queue. Use `${SASToken}` as credential. ```Java -String queueURL = String.format("https://%s.queue.core.windows.net/%s%s", accountName, queueName, sasToken); -QueueClient queueClient = QueueClient.builder().endpoint(queueURL).build(); +String queueSURL = String.format("https://%s.queue.core.windows.net", accountName); +QueueClient queueClient = new QueueClientBuilder().endpoint(queueURL).credential(SASToken).queueName("myqueue").buildClient(); queueClient.dequeueMessage("myMessage").forEach(message-> {print message.messageText();}); ``` ### Delete message from a queue -The operation retrieves one or more messages from the front of the queue. +The operation retrieves one or more messages from the front of the queue. Use `${SASToken}` as credential. ```Java -String queueURL = String.format("https://%s.queue.core.windows.net/%s%s", accountName, queueName, sasToken); -QueueClient queueClient = QueueClient.builder().endpoint(queueURL).build(); +String queueSURL = String.format("https://%s.queue.core.windows.net", accountName); +QueueClient queueClient = new QueueClientBuilder().endpoint(queueURL).credential(SASToken).queueName("myqueue").buildClient(); queueClient.deleteMessage(messageId, popReceipt); ``` ### Get a queue properties The operation retrieves user-defined metadata and queue properties on the specified queue. Metadata is associated with the queue as name-values pairs. + +Use `${SASToken}` as credential. ```Java -String queueURL = String.format("https://%s.queue.core.windows.net/%s%s", accountName, queueName, sasToken); -QueueClient queueClient = QueueClient.builder().endpoint(queueURL).build(); +String queueSURL = String.format("https://%s.queue.core.windows.net", accountName); +QueueClient queueClient = new QueueClientBuilder().endpoint(queueURL).credential(SASToken).queueName("myqueue").buildClient(); Response properties = queueClient.getProperties(); ``` ### Set a queue metadata The operation sets user-defined metadata on the specified queue. Metadata is associated with the queue as name-value pairs. + +Use `${SASToken}` as credential. ```Java -String queueURL = String.format("https://%s.queue.core.windows.net/%s%s", accountName, queueName, sasToken); -QueueClient queueClient = QueueClient.builder().endpoint(queueURL).build(); +String queueSURL = String.format("https://%s.queue.core.windows.net", accountName); +QueueClient queueClient = new QueueClientBuilder().endpoint(queueURL).credential(SASToken).queueName("myqueue").buildClient(); Map metadata = new HashMap<>() {{ put("key1", "val1"); @@ -335,10 +368,11 @@ When you interact with queue using this Java client library, errors returned by ## Next steps ### More Samples -- QueueServiceSample -- MessageSample -- QueueExceptionSample -- AsyncSample +Get started with our [Queue samples][samples]: +- [QueueServiceSample](src/samples/java/queue/QueueServiceSample.java): Create, list and delete queues +- [MessageSample](src/samples/java/queue/MessageSample.java): Enqueue, peek dequeue, update, clear and delete messages. Get properties of the queue. +- [QueueExceptionSample](src/samples/java/queue/QueueExceptionSample.java): Handle the exceptions from storage queue service side. +- [AsyncSample](src/samples/java/queue/AsyncSample.java): Create queue and enqueue message using async queue client call. [Quickstart: Create a Java Spring app with App Configuration](https://docs.microsoft.com/en-us/azure/azure-app-configuration/quickstart-java-spring-app) @@ -375,3 +409,4 @@ If you would like to become an active contributor to this project please follow [azure_cli]: https://docs.microsoft.com/cli/azure [sas_token]: https://docs.microsoft.com/en-us/azure/storage/common/storage-dotnet-shared-access-signature-part-1 [storage_rest]: https://docs.microsoft.com/en-us/rest/api/storageservices/queue-service-error-codes +[samples]: samples/ diff --git a/storage/client/queue/pom.xml b/storage/client/queue/pom.xml new file mode 100644 index 0000000000000..c018da31ab260 --- /dev/null +++ b/storage/client/queue/pom.xml @@ -0,0 +1,78 @@ + + + + com.azure + azure-client-sdk-parent + 1.1.0 + ../../../pom.client.xml + + + 4.0.0 + + com.azure + azure-storage-queue + 12.0.0-preview.1 + + azure-storage-queue + https://github.com/Azure/azure-sdk-for-java + + + + azure-java-build-docs + ${site.url}/site/${project.artifactId} + + + + + scm:git:https://github.com/Azure/azure-sdk-for-java + scm:git:git@github.com:Azure/azure-sdk-for-java.git + HEAD + + + + + com.azure + azure-core + 1.0.0-preview.2 + + + org.slf4j + slf4j-api + + + + com.azure + azure-core-test + 1.0.0-preview.2 + test + + + com.azure + azure-identity + 1.0.0-preview.1 + test + + + junit + junit + test + + + org.slf4j + slf4j-simple + test + + + io.projectreactor + reactor-test + test + + + com.microsoft.azure + adal4j + test + + + diff --git a/storage/client/queue/src/main/java/com/azure/storage/common/credentials/SharedKeyCredential.java b/storage/client/queue/src/main/java/com/azure/storage/common/credentials/SharedKeyCredential.java index 19b2b4c307221..db8045e723a1a 100644 --- a/storage/client/queue/src/main/java/com/azure/storage/common/credentials/SharedKeyCredential.java +++ b/storage/client/queue/src/main/java/com/azure/storage/common/credentials/SharedKeyCredential.java @@ -4,6 +4,7 @@ package com.azure.storage.common.credentials; import com.azure.core.implementation.util.ImplUtils; +import com.azure.core.util.logging.ClientLogger; import io.netty.handler.codec.http.QueryStringDecoder; import javax.crypto.Mac; @@ -26,11 +27,12 @@ * SharedKey credential policy that is put into a header to authorize requests. */ public final class SharedKeyCredential { + private static final ClientLogger LOGGER = new ClientLogger(SharedKeyCredential.class); private static final String AUTHORIZATION_HEADER_FORMAT = "SharedKey %s:%s"; // Pieces of the connection string that are needed. - private static final String ACCOUNT_NAME = "AccountName".toLowerCase(); - private static final String ACCOUNT_KEY = "AccountKey".toLowerCase(); + private static final String ACCOUNT_NAME = "accountname"; + private static final String ACCOUNT_KEY = "accountkey"; private final String accountName; private final byte[] accountKey; @@ -59,7 +61,7 @@ public static SharedKeyCredential fromConnectionString(String connectionString) HashMap connectionStringPieces = new HashMap<>(); for (String connectionStringPiece : connectionString.split(";")) { String[] kvp = connectionStringPiece.split("=", 2); - connectionStringPieces.put(kvp[0].toLowerCase(), kvp[1]); + connectionStringPieces.put(kvp[0].toLowerCase(Locale.ROOT), kvp[1]); } String accountName = connectionStringPieces.get(ACCOUNT_NAME); @@ -98,9 +100,11 @@ public String generateAuthorizationHeader(URL requestURL, String httpMethod, Map * * @param stringToSign The UTF-8-encoded string to sign. * @return A {@code String} that contains the HMAC-SHA256-encoded signature. - * @throws InvalidKeyException If the accountKey is not a valid Base64-encoded string. + * @throws RuntimeException for one of the following cases: + * - If the HMAC-SHA256 signature for {@code sharedKeyCredentials} fails to generate. + * - If the an invalid key has been given to the client. */ - public String computeHmac256(final String stringToSign) throws InvalidKeyException { + public String computeHmac256(final String stringToSign) { try { /* We must get a new instance of the Mac calculator for each signature calculated because the instances are @@ -111,8 +115,12 @@ public String computeHmac256(final String stringToSign) throws InvalidKeyExcepti hmacSha256.init(new SecretKeySpec(this.accountKey, "HmacSHA256")); byte[] utf8Bytes = stringToSign.getBytes(StandardCharsets.UTF_8); return Base64.getEncoder().encodeToString(hmacSha256.doFinal(utf8Bytes)); - } catch (final NoSuchAlgorithmException e) { - throw new Error(e); + } catch (final NoSuchAlgorithmException e) { + LOGGER.asError().log(e.getMessage()); + throw new RuntimeException(e); + } catch (InvalidKeyException e) { + LOGGER.asError().log("Please double check the account key. Error details: " + e.getMessage()); + throw new RuntimeException("Please double check the account key. Error details: " + e.getMessage()); } } @@ -121,20 +129,20 @@ private String buildStringToSign(URL requestURL, String httpMethod, MapSample Code

                  * *

                  For more samples, please see the samples file

                  + * @throws IllegalArgumentException If one of the following case exists: + *
                    + *
                  • There is only one null value for retryDelay and maxRetryDelay.
                  • + *
                  • Unrecognized retry policy type.
                  • + *
                  */ public RequestRetryOptions(RetryPolicyType retryPolicyType, Integer maxTries, Integer tryTimeout, Long retryDelayInMs, Long maxRetryDelayInMs, String secondaryHost) { @@ -105,7 +110,6 @@ public RequestRetryOptions(RetryPolicyType retryPolicyType, Integer maxTries, In } this.maxRetryDelayInMs = TimeUnit.SECONDS.toMillis(120); } - this.secondaryHost = secondaryHost; } diff --git a/storage/client/queue/src/main/java/com/azure/storage/common/policy/RequestRetryPolicy.java b/storage/client/queue/src/main/java/com/azure/storage/common/policy/RequestRetryPolicy.java index b862b76928af0..1b4bf56936bf5 100644 --- a/storage/client/queue/src/main/java/com/azure/storage/common/policy/RequestRetryPolicy.java +++ b/storage/client/queue/src/main/java/com/azure/storage/common/policy/RequestRetryPolicy.java @@ -30,6 +30,10 @@ public final class RequestRetryPolicy implements HttpPipelinePolicy { private final RequestRetryOptions requestRetryOptions; + /** + * Create a policy of retrying a given HTTP request. + * @param requestRetryOptions Options for configuring the RequestRetryPolicy. + */ public RequestRetryPolicy(RequestRetryOptions requestRetryOptions) { this.requestRetryOptions = requestRetryOptions; } diff --git a/storage/client/queue/src/main/java/com/azure/storage/queue/QueueAsyncClient.java b/storage/client/queue/src/main/java/com/azure/storage/queue/QueueAsyncClient.java index eab2a19707d5c..8df2931f44d22 100644 --- a/storage/client/queue/src/main/java/com/azure/storage/queue/QueueAsyncClient.java +++ b/storage/client/queue/src/main/java/com/azure/storage/queue/QueueAsyncClient.java @@ -1,29 +1,474 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. package com.azure.storage.queue; -import com.azure.core.ServiceClient; import com.azure.core.http.HttpPipeline; +import com.azure.core.http.rest.Response; +import com.azure.core.http.rest.SimpleResponse; +import com.azure.core.http.rest.VoidResponse; +import com.azure.core.util.Context; +import com.azure.core.util.logging.ClientLogger; +import com.azure.storage.common.credentials.SASTokenCredential; +import com.azure.storage.common.credentials.SharedKeyCredential; import com.azure.storage.queue.implementation.AzureQueueStorageBuilder; import com.azure.storage.queue.implementation.AzureQueueStorageImpl; +import com.azure.storage.queue.models.DequeuedMessage; +import com.azure.storage.queue.models.EnqueuedMessage; +import com.azure.storage.queue.models.MessageIdUpdateHeaders; +import com.azure.storage.queue.models.MessageIdsUpdateResponse; +import com.azure.storage.queue.models.PeekedMessage; +import com.azure.storage.queue.models.QueueGetPropertiesHeaders; +import com.azure.storage.queue.models.QueueMessage; +import com.azure.storage.queue.models.QueueProperties; +import com.azure.storage.queue.models.QueuesGetPropertiesResponse; +import com.azure.storage.queue.models.SignedIdentifier; +import com.azure.storage.queue.models.StorageErrorException; +import com.azure.storage.queue.models.UpdatedMessage; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import java.net.MalformedURLException; import java.net.URL; +import java.time.Duration; +import java.util.List; +import java.util.Map; -public class QueueAsyncClient extends ServiceClient { - private final String endpoint; - private final AzureQueueStorageImpl generateClient; - private final String apiVersion; +/** + * This class provides a client that contains all the operations for interacting with a queue in Azure Storage Queue. + * Operations allowed by the client are creating and deleting the queue, retrieving and updating metadata and access + * policies of the queue, and enqueuing, dequeuing, peeking, updating, and deleting messages. + * + *

                  Instantiating an Asynchronous Queue Client

                  + * + * {@codesnippet com.azure.storage.queue.queueAsyncClient.instantiation} + * + *

                  View {@link QueueClientBuilder this} for additional ways to construct the client.

                  + * + * @see QueueClientBuilder + * @see QueueClient + * @see SharedKeyCredential + * @see SASTokenCredential + */ +public final class QueueAsyncClient { + private static final ClientLogger LOGGER = new ClientLogger(QueueAsyncClient.class); + private final AzureQueueStorageImpl client; + private final String queueName; - private QueueAsyncClient(URL endpoint, HttpPipeline httpPipeline) { - super(httpPipeline); - this.endpoint = endpoint.toString(); - this.generateClient = new AzureQueueStorageBuilder().pipeline(httpPipeline).url(this.endpoint).build(); - this.apiVersion = this.generateClient.version(); + /** + * Creates a QueueAsyncClient that sends requests to the storage queue service at {@code AzureQueueStorageImpl#url() endpoint}. + * Each service call goes through the {@link HttpPipeline pipeline} in the {@code AzureQueueStorageImpl client}. + * + * @param client Client that interacts with the service interfaces + * @param queueName Name of the queue + */ + QueueAsyncClient(AzureQueueStorageImpl client, String queueName) { + this.queueName = queueName; + + this.client = new AzureQueueStorageBuilder().pipeline(client.httpPipeline()) + .url(client.url()) + .version(client.version()) + .build(); + } + + /** + * Creates a QueueAsyncClient that sends requests to the storage queue service at {@code endpoint}. + * Each service call goes through the {@code httpPipeline}. + * + * @param endpoint URL for the Storage Queue service + * @param httpPipeline HttpPipeline that the HTTP requests and response flow through + * @param queueName Name of the queue + */ + QueueAsyncClient(URL endpoint, HttpPipeline httpPipeline, String queueName) { + this.queueName = queueName; + + this.client = new AzureQueueStorageBuilder().pipeline(httpPipeline) + .url(endpoint.toString()) + .build(); + } + + /** + * @return the URL of the storage queue + * @throws RuntimeException If the queue is using a malformed URL. + */ + public URL getQueueUrl() { + try { + return new URL(client.url()); + } catch (MalformedURLException ex) { + LOGGER.asError().log("Queue URL is malformed"); + throw new RuntimeException("Queue URL is malformed"); + } + } + + /** + * Creates a new queue. + * + *

                  Code Samples

                  + * + *

                  Create a queue

                  + * + * {@codesnippet com.azure.storage.queue.queueAsyncClient.create} + * + * @return A response that only contains headers and response status code + * @throws StorageErrorException If a queue with the same name already exists in the queue service. + */ + public Mono create() { + return create(null); + } + + /** + * Creates a new queue. + * + *

                  Code Samples

                  + * + *

                  Create a queue with metadata "queue:metadataMap"

                  + * + * {@codesnippet com.azure.storage.queue.queueAsyncClient.create#map} + * + * @param metadata Metadata to associate with the queue + * @return A response that only contains headers and response status code + * @throws StorageErrorException If a queue with the same name and different metadata already exists in the queue service. + */ + public Mono create(Map metadata) { + return client.queues().createWithRestResponseAsync(queueName, null, metadata, null, Context.NONE) + .map(VoidResponse::new); + } + + /** + * Permanently deletes the queue. + * + *

                  Code Samples

                  + * + *

                  Delete a queue

                  + * + * {@codesnippet com.azure.storage.queue.queueAsyncClient.delete} + * + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the queue doesn't exist + */ + public Mono delete() { + return client.queues().deleteWithRestResponseAsync(queueName, Context.NONE) + .map(VoidResponse::new); + } + + /** + * Retrieves metadata and approximate message count of the queue. + * + *

                  Code Samples

                  + * + *

                  Get the properties of the queue

                  + * + * {@codesnippet com.azure.storage.queue.queueAsyncClient.getProperties} + * + * @return A response containing a {@link QueueProperties} value which contains the metadata and approximate + * messages count of the queue. + * @throws StorageErrorException If the queue doesn't exist + */ + public Mono> getProperties() { + return client.queues().getPropertiesWithRestResponseAsync(queueName, Context.NONE) + .map(this::getQueuePropertiesResponse); + } + + /** + * Sets the metadata of the queue. + * + * Passing in a {@code null} value for metadata will clear the metadata associated with the queue. + * + *

                  Code Samples

                  + * + *

                  Set the queue's metadata to "queue:metadataMap"

                  + * + * {@codesnippet com.azure.storage.queue.queueAsyncClient.setMetadata#map} + * + *

                  Clear the queue's metadata

                  + * + * {@codesnippet com.azure.storage.queue.queueAsyncClient.clearMetadata#map} + * + * @param metadata Metadata to set on the queue + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the queue doesn't exist + */ + public Mono setMetadata(Map metadata) { + return client.queues().setMetadataWithRestResponseAsync(queueName, null, metadata, null, Context.NONE) + .map(VoidResponse::new); } /** - * Creates a appendBlobClientBuilder that can configure options for the SecretAsyncClient before creating an instance of it. - * @return A new appendBlobClientBuilder to create a SecretAsyncClient from. + * Retrieves stored access policies specified on the queue. + * + *

                  Code Samples

                  + * + *

                  List the stored access policies

                  + * + * {@codesnippet com.azure.storage.queue.queueAsyncClient.getAccessPolicy} + * + * @return The stored access policies specified on the queue. + * @throws StorageErrorException If the queue doesn't exist + */ + public Flux getAccessPolicy() { + return client.queues().getAccessPolicyWithRestResponseAsync(queueName, Context.NONE) + .flatMapMany(response -> Flux.fromIterable(response.value())); + } + + /** + * Sets stored access policies on the queue. + * + *

                  Code Samples

                  + * + *

                  Set a read only stored access policy

                  + * + * {@codesnippet com.azure.storage.queue.queueAsyncClient.setAccessPolicy} + * + * @param permissions Access policies to set on the queue + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the queue doesn't exist, a stored access policy doesn't have all fields filled out, + * or the queue will have more than five policies. + */ + public Mono setAccessPolicy(List permissions) { + return client.queues().setAccessPolicyWithRestResponseAsync(queueName, permissions, null, null, Context.NONE) + .map(VoidResponse::new); + } + + /** + * Deletes all messages in the queue. + * + *

                  Code Samples

                  + * + *

                  Clear the messages

                  + * + * {@codesnippet com.azure.storage.queue.queueAsyncClient.clearMessages} + * + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the queue doesn't exist + */ + public Mono clearMessages() { + return client.messages().clearWithRestResponseAsync(queueName, Context.NONE) + .map(VoidResponse::new); + } + + /** + * Enqueues a message that has a time-to-live of 7 days and is instantly visible. + * + *

                  Code Samples

                  + * + *

                  Enqueue a message of "Hello, Azure"

                  + * + * {@codesnippet com.azure.storage.queue.queueAsyncClient.enqueueMessage#string} + * + * @param messageText Message text + * @return A {@link EnqueuedMessage} value that contains the {@link EnqueuedMessage#messageId() messageId} and + * {@link EnqueuedMessage#popReceipt() popReceipt} that are used to interact with the message and other metadata + * about the enqueued message. + * @throws StorageErrorException If the queue doesn't exist + */ + public Mono> enqueueMessage(String messageText) { + return enqueueMessage(messageText, Duration.ofSeconds(0), Duration.ofDays(7)); + } + + /** + * Enqueues a message with a given time-to-live and a timeout period where the message is invisible in the queue. + * + *

                  Code Samples

                  + * + *

                  Add a message of "Hello, Azure" that has a timeout of 5 seconds

                  + * + * {@codesnippet com.azure.storage.queue.queueAsyncClient.enqueueMessage#string-duration-duration} + * + *

                  Add a message of "Goodbye, Azure" that has a time to live of 5 seconds

                  + * + * {@codesnippet com.azure.storage.queue.queueAsyncClient.enqueueMessageLiveTime#string-duration-duration} + * + * @param messageText Message text + * @param visibilityTimeout Optional. The timeout period for how long the message is invisible in the queue in seconds. + * If unset the value will default to 0 and the message will be instantly visible. The timeout must be between 0 + * seconds and 7 days. + * @param timeToLive Optional. How long the message will stay alive in the queue in seconds. If unset the value will + * default to 7 days, if -1 is passed the message will not expire. The time to live must be -1 or any positive number. + * @return A {@link EnqueuedMessage} value that contains the {@link EnqueuedMessage#messageId() messageId} and + * {@link EnqueuedMessage#popReceipt() popReceipt} that are used to interact with the message and other metadata + * about the enqueued message. + * @throws StorageErrorException If the queue doesn't exist or the {@code visibilityTimeout} or {@code timeToLive} + * are outside of the allowed limits. + */ + public Mono> enqueueMessage(String messageText, Duration visibilityTimeout, Duration timeToLive) { + Integer visibilityTimeoutInSeconds = (visibilityTimeout == null) ? null : (int) visibilityTimeout.getSeconds(); + Integer timeToLiveInSeconds = (timeToLive == null) ? null : (int) timeToLive.getSeconds(); + QueueMessage message = new QueueMessage().messageText(messageText); + + return client.messages().enqueueWithRestResponseAsync(queueName, message, visibilityTimeoutInSeconds, timeToLiveInSeconds, null, null, Context.NONE) + .map(response -> new SimpleResponse<>(response, response.value().get(0))); + } + + /** + * Retrieves the first message in the queue and hides it from other operations for 30 seconds. + * + *

                  Code Samples

                  + * + *

                  Dequeue a message

                  + * + * {@codesnippet com.azure.storage.queue.queueAsyncClient.dequeueMessages} + * + * @return The first {@link DequeuedMessage} in the queue, it contains + * {@link DequeuedMessage#messageId() messageId} and {@link DequeuedMessage#popReceipt() popReceipt} used to interact + * with the message, additionally it contains other metadata about the message. + * @throws StorageErrorException If the queue doesn't exist + */ + public Flux dequeueMessages() { + return dequeueMessages(1, Duration.ofSeconds(30)); + } + + /** + * Retrieves up to the maximum number of messages from the queue and hides them from other operations for 30 seconds. + * + *

                  Code Samples

                  + * + *

                  Dequeue up to 5 messages

                  + * + * {@codesnippet com.azure.storage.queue.queueAsyncClient.dequeueMessages#integer} + * + * @param maxMessages Optional. Maximum number of messages to get, if there are less messages exist in the queue than requested + * all the messages will be returned. If left empty only 1 message will be retrieved, the allowed range is 1 to 32 + * messages. + * @return Up to {@code maxMessages} {@link DequeuedMessage DequeuedMessages} from the queue. Each DequeuedMessage contains + * {@link DequeuedMessage#messageId() messageId} and {@link DequeuedMessage#popReceipt() popReceipt} used to interact + * with the message and other metadata about the message. + * @throws StorageErrorException If the queue doesn't exist or {@code maxMessages} is outside of the allowed bounds + */ + public Flux dequeueMessages(Integer maxMessages) { + return dequeueMessages(maxMessages, Duration.ofSeconds(30)); + } + + /** + * Retrieves up to the maximum number of messages from the queue and hides them from other operations for the + * timeout period. + * + *

                  Code Samples

                  + * + *

                  Dequeue up to 5 messages and give them a 60 second timeout period

                  + * + * {@codesnippet com.azure.storage.queue.queueAsyncClient.dequeueMessages#integer-duration} + * + * @param maxMessages Optional. Maximum number of messages to get, if there are less messages exist in the queue than requested + * all the messages will be returned. If left empty only 1 message will be retrieved, the allowed range is 1 to 32 + * messages. + * @param visibilityTimeout Optional. The timeout period for how long the message is invisible in the queue in seconds. + * If left empty the dequeued messages will be invisible for 30 seconds. The timeout must be between 1 second and 7 days. + * @return Up to {@code maxMessages} {@link DequeuedMessage DequeuedMessages} from the queue. Each DeqeuedMessage contains + * {@link DequeuedMessage#messageId() messageId} and {@link DequeuedMessage#popReceipt() popReceipt} used to interact + * with the message and other metadata about the message. + * @throws StorageErrorException If the queue doesn't exist or {@code maxMessages} or {@code visibilityTimeout} is + * outside of the allowed bounds + */ + public Flux dequeueMessages(Integer maxMessages, Duration visibilityTimeout) { + Integer visibilityTimeoutInSeconds = (visibilityTimeout == null) ? null : (int) visibilityTimeout.getSeconds(); + return client.messages().dequeueWithRestResponseAsync(queueName, maxMessages, visibilityTimeoutInSeconds, null, null, Context.NONE) + .flatMapMany(response -> Flux.fromIterable(response.value())); + } + + /** + * Peeks the first message in the queue. + * + * Peeked messages don't contain the necessary information needed to interact with the message nor will it hide + * messages from other operations on the queue. + * + *

                  Code Samples

                  + * + *

                  Peek the first message

                  + * + * {@codesnippet com.azure.storage.queue.queueAsyncClient.peekMessages} + * + * @return A {@link PeekedMessage} that contains metadata about the message. + */ + public Flux peekMessages() { + return peekMessages(null); + } + + /** + * Peek messages from the front of the queue up to the maximum number of messages. + * + * Peeked messages don't contain the necessary information needed to interact with the message nor will it hide + * messages from other operations on the queue. + * + *

                  Code Samples

                  + * + *

                  Peek up to the first five messages

                  + * + * {@codesnippet com.azure.storage.queue.queueAsyncClient.peekMessages#integer} + * + * @param maxMessages Optional. Maximum number of messages to peek, if there are less messages exist in the queue than requested + * all the messages will be peeked. If left empty only 1 message will be peeked, the allowed range is 1 to 32 + * messages. + * @return Up to {@code maxMessages} {@link PeekedMessage PeekedMessages} from the queue. Each PeekedMessage contains + * metadata about the message. + * @throws StorageErrorException If the queue doesn't exist or {@code maxMessages} is outside of the allowed bounds + */ + public Flux peekMessages(Integer maxMessages) { + return client.messages().peekWithRestResponseAsync(queueName, maxMessages, null, null, Context.NONE) + .flatMapMany(response -> Flux.fromIterable(response.value())); + } + + /** + * Updates the specific message in the queue with a new message and resets the visibility timeout. + * + *

                  Code Samples

                  + * + *

                  Dequeue the first message and update it to "Hello again, Azure" and hide it for 5 seconds

                  + * + * {@codesnippet com.azure.storage.queue.queueAsyncClient.updateMessage} + * + * @param messageText Updated value for the message + * @param messageId Id of the message to update + * @param popReceipt Unique identifier that must match for the message to be updated + * @param visibilityTimeout The timeout period for how long the message is invisible in the queue in seconds. The + * timeout period must be between 1 second and 7 days. + * @return A {@link UpdatedMessage} that contains the new {@link UpdatedMessage#popReceipt() popReceipt} to interact + * with the message, additionally contains the updated metadata about the message. + * @throws StorageErrorException If the queue or messageId don't exist, the popReceipt doesn't match on the message, + * or the {@code visibilityTimeout} is outside the allowed bounds + */ + public Mono> updateMessage(String messageText, String messageId, String popReceipt, Duration visibilityTimeout) { + QueueMessage message = new QueueMessage().messageText(messageText); + return client.messageIds().updateWithRestResponseAsync(queueName, messageId, message, popReceipt, (int) visibilityTimeout.getSeconds(), Context.NONE) + .map(this::getUpdatedMessageResponse); + } + + /** + * Deletes the specified message in the queue + * + *

                  Code Samples

                  + * + *

                  Delete the first message

                  + * + * {@codesnippet com.azure.storage.queue.queueAsyncClient.deleteMessage} + * + * @param messageId Id of the message to deleted + * @param popReceipt Unique identifier that must match for the message to be deleted + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the queue or messageId don't exist or the popReceipt doesn't match on the message + */ + public Mono deleteMessage(String messageId, String popReceipt) { + return client.messageIds().deleteWithRestResponseAsync(queueName, messageId, popReceipt, Context.NONE) + .map(VoidResponse::new); + } + + /* + * Maps the HTTP headers returned from the service to the expected response type + * @param response Service response + * @return Mapped response + */ + private Response getQueuePropertiesResponse(QueuesGetPropertiesResponse response) { + QueueGetPropertiesHeaders propertiesHeaders = response.deserializedHeaders(); + QueueProperties properties = new QueueProperties(propertiesHeaders.metadata(), propertiesHeaders.approximateMessagesCount()); + return new SimpleResponse<>(response, properties); + } + + /* + * Maps the HTTP headers returned from the service to the expected response type + * @param response Service response + * @return Mapped response */ - public static QueueAsyncClientBuilder builder() { - return new QueueAsyncClientBuilder(); + private Response getUpdatedMessageResponse(MessageIdsUpdateResponse response) { + MessageIdUpdateHeaders headers = response.deserializedHeaders(); + UpdatedMessage updatedMessage = new UpdatedMessage(headers.popReceipt(), headers.timeNextVisible()); + return new SimpleResponse<>(response, updatedMessage); } } diff --git a/storage/client/queue/src/main/java/com/azure/storage/queue/QueueAsyncClientBuilder.java b/storage/client/queue/src/main/java/com/azure/storage/queue/QueueAsyncClientBuilder.java deleted file mode 100644 index b76c9c63e8700..0000000000000 --- a/storage/client/queue/src/main/java/com/azure/storage/queue/QueueAsyncClientBuilder.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.azure.storage.queue; - -public class QueueAsyncClientBuilder { - -} diff --git a/storage/client/queue/src/main/java/com/azure/storage/queue/QueueClient.java b/storage/client/queue/src/main/java/com/azure/storage/queue/QueueClient.java new file mode 100644 index 0000000000000..763c58560871e --- /dev/null +++ b/storage/client/queue/src/main/java/com/azure/storage/queue/QueueClient.java @@ -0,0 +1,389 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.queue; + +import com.azure.core.http.rest.Response; +import com.azure.core.http.rest.VoidResponse; +import com.azure.storage.common.credentials.SASTokenCredential; +import com.azure.storage.common.credentials.SharedKeyCredential; +import com.azure.storage.queue.models.DequeuedMessage; +import com.azure.storage.queue.models.EnqueuedMessage; +import com.azure.storage.queue.models.PeekedMessage; +import com.azure.storage.queue.models.QueueProperties; +import com.azure.storage.queue.models.SignedIdentifier; +import com.azure.storage.queue.models.StorageErrorException; +import com.azure.storage.queue.models.UpdatedMessage; +import java.net.URL; +import java.time.Duration; +import java.util.List; +import java.util.Map; + +/** + * This class provides a client that contains all the operations for interacting with a queue in Azure Storage Queue. + * Operations allowed by the client are creating and deleting the queue, retrieving and updating metadata and access + * policies of the queue, and enqueuing, dequeuing, peeking, updating, and deleting messages. + * + *

                  Instantiating an Synchronous Queue Client

                  + * + * {@codesnippet com.azure.storage.queue.queueClient.instantiation} + * + *

                  View {@link QueueClientBuilder this} for additional ways to construct the client.

                  + * + * @see QueueClientBuilder + * @see QueueAsyncClient + * @see SharedKeyCredential + * @see SASTokenCredential + */ +public final class QueueClient { + private final QueueAsyncClient client; + + /** + * Creates a QueueClient that wraps a QueueAsyncClient and blocks requests. + * + * @param client QueueAsyncClient that is used to send requests + */ + QueueClient(QueueAsyncClient client) { + this.client = client; + } + + /** + * @return the URL of the storage queue. + * @throws RuntimeException If the queue is using a malformed URL. + */ + public URL getQueueUrl() { + return client.getQueueUrl(); + } + + /** + * Creates a new queue. + * + *

                  Code Samples

                  + * + *

                  Create a queue

                  + * + *{@codesnippet com.azure.storage.queue.queueClient.create} + * + * @return A response that only contains headers and response status code + * @throws StorageErrorException If a queue with the same name already exists in the queue service. + */ + public VoidResponse create() { + return create(null); + } + + /** + * Creates a new queue. + * + *

                  Code Samples

                  + * + *

                  Create a queue with metadata "queue:metadataMap"

                  + * + * {@codesnippet com.azure.storage.queue.queueClient.create#map} + * + * @param metadata Metadata to associate with the queue + * @return A response that only contains headers and response status code + * @throws StorageErrorException If a queue with the same name and different metadata already exists in the queue service. + */ + public VoidResponse create(Map metadata) { + return client.create(metadata).block(); + } + + /** + * Permanently deletes the queue. + * + *

                  Code Samples

                  + * + *

                  Delete a queue

                  + * + * {@codesnippet com.azure.storage.queue.queueClient.delete} + * + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the queue doesn't exist + */ + public VoidResponse delete() { + return client.delete().block(); + } + + /** + * Retrieves metadata and approximate message count of the queue. + * + *

                  Code Samples

                  + * + *

                  Get the properties of the queue

                  + * + * {@codesnippet com.azure.storage.queue.queueClient.getProperties} + * + * @return A response containing a {@link QueueProperties} value which contains the metadata and approximate + * messages count of the queue. + * @throws StorageErrorException If the queue doesn't exist + */ + public Response getProperties() { + return client.getProperties().block(); + } + + /** + * Sets the metadata of the queue. + * + * Passing in a {@code null} value for metadata will clear the metadata associated with the queue. + * + *

                  Code Samples

                  + * + *

                  Set the queue's metadata to "queue:metadataMap"

                  + * + * {@codesnippet com.azure.storage.queue.queueClient.setMetadata#map} + * + *

                  Clear the queue's metadata

                  + * + * {@codesnippet com.azure.storage.queue.queueClient.clearMetadata#map} + * + * @param metadata Metadata to set on the queue + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the queue doesn't exist + */ + public VoidResponse setMetadata(Map metadata) { + return client.setMetadata(metadata).block(); + } + + /** + * Retrieves stored access policies specified on the queue. + * + *

                  Code Samples

                  + * + *

                  List the stored access policies

                  + * + * {@codesnippet com.azure.storage.queue.queueClient.getAccessPolicy} + * + * @return The stored access policies specified on the queue. + * @throws StorageErrorException If the queue doesn't exist + */ + public Iterable getAccessPolicy() { + return client.getAccessPolicy().toIterable(); + } + + /** + * Sets stored access policies on the queue. + * + *

                  Code Samples

                  + * + *

                  Set a read only stored access policy

                  + * + * {@codesnippet com.azure.storage.queue.queueClient.setAccessPolicy} + * + * @param permissions Access policies to set on the queue + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the queue doesn't exist, a stored access policy doesn't have all fields filled out, + * or the queue will have more than five policies. + */ + public VoidResponse setAccessPolicy(List permissions) { + return client.setAccessPolicy(permissions).block(); + } + + /** + * Deletes all messages in the queue. + * + *

                  Code Samples

                  + * + *

                  Clear the messages

                  + * + * {@codesnippet com.azure.storage.queue.queueClient.clearMessages} + * + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the queue doesn't exist + */ + public VoidResponse clearMessages() { + return client.clearMessages().block(); + } + + /** + * Enqueues a message that has a time-to-live of 7 days and is instantly visible. + * + *

                  Code Samples

                  + * + *

                  Enqueue a message of "Hello, Azure"

                  + * + * {@codesnippet com.azure.storage.queue.queueClient.enqueueMessage#string} + * + * @param messageText Message text + * @return A {@link EnqueuedMessage} value that contains the {@link EnqueuedMessage#messageId() messageId} and + * {@link EnqueuedMessage#popReceipt() popReceipt} that are used to interact with the message and other metadata + * about the enqueued message. + * @throws StorageErrorException If the queue doesn't exist + */ + public Response enqueueMessage(String messageText) { + return enqueueMessage(messageText, Duration.ofSeconds(0), Duration.ofDays(7)); + } + + /** + * Enqueues a message with a given time-to-live and a timeout period where the message is invisible in the queue. + * + *

                  Code Samples

                  + * + *

                  Add a message of "Hello, Azure" that has a timeout of 5 seconds

                  + * + * {@codesnippet com.azure.storage.queue.queueClient.enqueueMessage#string-duration-duration} + * + *

                  Add a message of "Goodbye, Azure" that has a time to live of 5 seconds

                  + * + * {@codesnippet com.azure.storage.queue.queueClient.enqueueMessageLiveTime#string-duration-duration} + * + * @param messageText Message text + * @param visibilityTimeout Optional. The timeout period for how long the message is invisible in the queue in seconds. + * If unset the value will default to 0 and the message will be instantly visible. The timeout must be between 0 + * seconds and 7 days. + * @param timeToLive Optional. How long the message will stay alive in the queue in seconds. If unset the value will + * default to 7 days, if -1 is passed the message will not expire. The time to live must be -1 or any positive number. + * @return A {@link EnqueuedMessage} value that contains the {@link EnqueuedMessage#messageId() messageId} and + * {@link EnqueuedMessage#popReceipt() popReceipt} that are used to interact with the message and other metadata + * about the enqueued message. + * @throws StorageErrorException If the queue doesn't exist or the {@code visibilityTimeout} or {@code timeToLive} + * are outside of the allowed limits. + */ + public Response enqueueMessage(String messageText, Duration visibilityTimeout, Duration timeToLive) { + return client.enqueueMessage(messageText, visibilityTimeout, timeToLive).block(); + } + + /** + * Retrieves the first message in the queue and hides it from other operations for 30 seconds. + * + *

                  Code Samples

                  + * + *

                  Dequeue a message

                  + * + * {@codesnippet com.azure.storage.queue.queueClient.dequeueMessages} + * + * @return The first {@link DequeuedMessage} in the queue, it contains + * {@link DequeuedMessage#messageId() messageId} and {@link DequeuedMessage#popReceipt() popReceipt} used to interact + * with the message, additionally it contains other metadata about the message. + * @throws StorageErrorException If the queue doesn't exist + */ + public Iterable dequeueMessages() { + return dequeueMessages(1, Duration.ofSeconds(30)); + } + + /** + * Retrieves up to the maximum number of messages from the queue and hides them from other operations for 30 seconds. + * + *

                  Code Samples

                  + * + *

                  Dequeue up to 5 messages

                  + * + * {@codesnippet com.azure.storage.queue.queueClient.dequeueMessages#integer} + * + * @param maxMessages Optional. Maximum number of messages to get, if there are less messages exist in the queue than requested + * all the messages will be returned. If left empty only 1 message will be retrieved, the allowed range is 1 to 32 + * messages. + * @return Up to {@code maxMessages} {@link DequeuedMessage DequeuedMessages} from the queue. Each DequeuedMessage contains + * {@link DequeuedMessage#messageId() messageId} and {@link DequeuedMessage#popReceipt() popReceipt} used to interact + * with the message and other metadata about the message. + * @throws StorageErrorException If the queue doesn't exist or {@code maxMessages} is outside of the allowed bounds + */ + public Iterable dequeueMessages(Integer maxMessages) { + return dequeueMessages(maxMessages, Duration.ofSeconds(30)); + } + + /** + * Retrieves up to the maximum number of messages from the queue and hides them from other operations for the + * timeout period. + * + *

                  Code Samples

                  + * + *

                  Dequeue up to 5 messages and give them a 60 second timeout period

                  + * + * {@codesnippet com.azure.storage.queue.queueClient.dequeueMessages#integer-duration} + * + * @param maxMessages Optional. Maximum number of messages to get, if there are less messages exist in the queue than requested + * all the messages will be returned. If left empty only 1 message will be retrieved, the allowed range is 1 to 32 + * messages. + * @param visibilityTimeout Optional. The timeout period for how long the message is invisible in the queue in seconds. + * If left empty the dequeued messages will be invisible for 30 seconds. The timeout must be between 1 second and 7 days. + * @return Up to {@code maxMessages} {@link DequeuedMessage DequeuedMessages} from the queue. Each DeqeuedMessage contains + * {@link DequeuedMessage#messageId() messageId} and {@link DequeuedMessage#popReceipt() popReceipt} used to interact + * with the message and other metadata about the message. + * @throws StorageErrorException If the queue doesn't exist or {@code maxMessages} or {@code visibilityTimeout} is + * outside of the allowed bounds + */ + public Iterable dequeueMessages(Integer maxMessages, Duration visibilityTimeout) { + return client.dequeueMessages(maxMessages, visibilityTimeout).toIterable(); + } + + /** + * Peeks the first message in the queue. + * + * Peeked messages don't contain the necessary information needed to interact with the message nor will it hide + * messages from other operations on the queue. + * + *

                  Code Samples

                  + * + *

                  Peek the first message

                  + * + * {@codesnippet com.azure.storage.queue.queueClient.peekMessages} + * + * @return A {@link PeekedMessage} that contains metadata about the message. + */ + public Iterable peekMessages() { + return peekMessages(null); + } + + /** + * Peek messages from the front of the queue up to the maximum number of messages. + * + * Peeked messages don't contain the necessary information needed to interact with the message nor will it hide + * messages from other operations on the queue. + * + *

                  Code Samples

                  + * + *

                  Peek up to the first five messages

                  + * + * {@codesnippet com.azure.storage.queue.queueClient.peekMessages#integer} + * + * @param maxMessages Optional. Maximum number of messages to peek, if there are less messages exist in the queue than requested + * all the messages will be peeked. If left empty only 1 message will be peeked, the allowed range is 1 to 32 + * messages. + * @return Up to {@code maxMessages} {@link PeekedMessage PeekedMessages} from the queue. Each PeekedMessage contains + * metadata about the message. + * @throws StorageErrorException If the queue doesn't exist or {@code maxMessages} is outside of the allowed bounds + */ + public Iterable peekMessages(Integer maxMessages) { + return client.peekMessages(maxMessages).toIterable(); + } + + /** + * Updates the specific message in the queue with a new message and resets the visibility timeout. + * + *

                  Code Samples

                  + * + *

                  Dequeue the first message and update it to "Hello again, Azure" and hide it for 5 seconds

                  + * + * {@codesnippet com.azure.storage.queue.queueClient.updateMessage} + * + * @param messageText Updated value for the message + * @param messageId Id of the message to update + * @param popReceipt Unique identifier that must match for the message to be updated + * @param visibilityTimeout The timeout period for how long the message is invisible in the queue in seconds. The + * timeout period must be between 1 second and 7 days. + * @return A {@link UpdatedMessage} that contains the new {@link UpdatedMessage#popReceipt() popReceipt} to interact + * with the message, additionally contains the updated metadata about the message. + * @throws StorageErrorException If the queue or messageId don't exist, the popReceipt doesn't match on the message, + * or the {@code visibilityTimeout} is outside the allowed bounds + */ + public Response updateMessage(String messageText, String messageId, String popReceipt, Duration visibilityTimeout) { + return client.updateMessage(messageText, messageId, popReceipt, visibilityTimeout).block(); + } + + /** + * Deletes the specified message in the queue + * + *

                  Code Samples

                  + * + *

                  Delete the first message

                  + * + * {@codesnippet com.azure.storage.queue.queueClient.deleteMessage} + * + * @param messageId Id of the message to deleted + * @param popReceipt Unique identifier that must match for the message to be deleted + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the queue or messageId don't exist or the popReceipt doesn't match on the message + */ + public VoidResponse deleteMessage(String messageId, String popReceipt) { + return client.deleteMessage(messageId, popReceipt).block(); + } +} diff --git a/storage/client/queue/src/main/java/com/azure/storage/queue/QueueClientBuilder.java b/storage/client/queue/src/main/java/com/azure/storage/queue/QueueClientBuilder.java new file mode 100644 index 0000000000000..d272cfc1bbf89 --- /dev/null +++ b/storage/client/queue/src/main/java/com/azure/storage/queue/QueueClientBuilder.java @@ -0,0 +1,344 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.queue; + +import com.azure.core.http.HttpClient; +import com.azure.core.http.HttpPipeline; +import com.azure.core.http.policy.AddDatePolicy; +import com.azure.core.http.policy.HttpLogDetailLevel; +import com.azure.core.http.policy.HttpLoggingPolicy; +import com.azure.core.http.policy.HttpPipelinePolicy; +import com.azure.core.http.policy.RequestIdPolicy; +import com.azure.core.http.policy.RetryPolicy; +import com.azure.core.http.policy.UserAgentPolicy; +import com.azure.core.implementation.http.policy.spi.HttpPolicyProviders; +import com.azure.core.implementation.util.ImplUtils; +import com.azure.core.util.configuration.Configuration; +import com.azure.core.util.configuration.ConfigurationManager; +import com.azure.core.util.logging.ClientLogger; +import com.azure.storage.common.credentials.SASTokenCredential; +import com.azure.storage.common.credentials.SharedKeyCredential; +import com.azure.storage.common.policy.SASTokenCredentialPolicy; +import com.azure.storage.common.policy.SharedKeyCredentialPolicy; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Objects; + +/** + * This class provides a fluent builder API to help aid the configuration and instantiation of the {@link QueueClient QueueClients} + * and {@link QueueAsyncClient QueueAsyncClients}, calling {@link QueueClientBuilder#buildClient() buildClient} constructs an + * instance of QueueClient and calling {@link QueueClientBuilder#buildAsyncClient() buildAsyncClient} constructs an instance of + * QueueAsyncClient. + * + *

                  The client needs the endpoint of the Azure Storage Queue service, name of the queue, and authorization credentials. + * {@link QueueClientBuilder#endpoint(String) endpoint} gives the builder the endpoint and may give the builder the + * {@link QueueClientBuilder#queueName(String) queueName} and a {@link SASTokenCredential} that authorizes the client.

                  + * + *

                  Instantiating a synchronous Queue Client with SAS token

                  + * {@codesnippet com.azure.storage.queue.queueClient.instantiation.sastoken} + * + *

                  Instantiating an Asynchronous Queue Client with SAS token

                  + * {@codesnippet com.azure.storage.queue.queueAsyncClient.instantiation.sastoken} + * + *

                  If the {@code endpoint} doesn't contain the queue name or {@code SASTokenCredential} they may be set using + * {@link QueueClientBuilder#queueName(String) queueName} and {@link QueueClientBuilder#credential(SASTokenCredential) credential}.

                  + * + *

                  Instantiating a synchronous Queue Client with credential

                  + * {@codesnippet com.azure.storage.queue.queueClient.instantiation.credential} + * + *

                  Instantiating an Asynchronous Queue Client with credential

                  + * {@codesnippet com.azure.storage.queue.queueAsyncClient.instantiation.credential} + * + *

                  Another way to authenticate the client is using a {@link SharedKeyCredential}. To create a SharedKeyCredential + * a connection string from the Storage Queue service must be used. Set the SharedKeyCredential with + * {@link QueueClientBuilder#connectionString(String) connectionString}. If the builder has both a SASTokenCredential and + * SharedKeyCredential the SharedKeyCredential will be preferred when authorizing requests sent to the service.

                  + * + *

                  Instantiating a synchronous Queue Client with connection string.

                  + * {@codesnippet com.azure.storage.queue.queueClient.instantiation.connectionstring} + * + *

                  Instantiating an Asynchronous Queue Client with connection string.

                  + * {@codesnippet com.azure.storage.queue.queueAsyncClient.instantiation.connectionstring} + * + * @see QueueClient + * @see QueueAsyncClient + * @see SASTokenCredential + * @see SharedKeyCredential + */ +public final class QueueClientBuilder { + private static final ClientLogger LOGGER = new ClientLogger(QueueClientBuilder.class); + private static final String ACCOUNT_NAME = "accountname"; + private final List policies; + + private URL endpoint; + private String queueName; + private SASTokenCredential sasTokenCredential; + private SharedKeyCredential sharedKeyCredential; + private HttpClient httpClient; + private HttpPipeline pipeline; + private HttpLogDetailLevel logLevel; + private RetryPolicy retryPolicy; + private Configuration configuration; + + /** + * Creates a builder instance that is able to configure and construct {@link QueueClient QueueClients} + * and {@link QueueAsyncClient QueueAsyncClients}. + */ + public QueueClientBuilder() { + retryPolicy = new RetryPolicy(); + logLevel = HttpLogDetailLevel.NONE; + policies = new ArrayList<>(); + + configuration = ConfigurationManager.getConfiguration(); + } + + /** + * Creates a {@link QueueClient} based on options set in the builder. Every time {@code buildClient()} is + * called a new instance of {@link QueueClient} is created. + * + *

                  + * If {@link QueueClientBuilder#pipeline(HttpPipeline) pipeline} is set, then the {@code pipeline}, + * {@link QueueClientBuilder#endpoint(String) endpoint}, and + * {@link QueueClientBuilder#queueName(String) queueName} are used to create the {@link QueueAsyncClient client}. + * All other builder settings are ignored. + *

                  + * + * @return A QueueClient with the options set from the builder. + * @throws NullPointerException If {@code endpoint} or {@code queueName} have not been set. + * @throws IllegalStateException If neither a {@link SharedKeyCredential} or {@link SASTokenCredential} has been set. + */ + public QueueClient buildClient() { + return new QueueClient(buildAsyncClient()); + } + + /** + * Creates a {@link QueueAsyncClient} based on options set in the builder. Every time {@code buildAsyncClient()} is + * called a new instance of {@link QueueAsyncClient} is created. + * + *

                  + * If {@link QueueClientBuilder#pipeline(HttpPipeline) pipeline} is set, then the {@code pipeline}, + * {@link QueueClientBuilder#endpoint(String) endpoint}, and + * {@link QueueClientBuilder#queueName(String) queueName} are used to create the {@link QueueAsyncClient client}. + * All other builder settings are ignored. + *

                  + * + * @return A QueueAsyncClient with the options set from the builder. + * @throws NullPointerException If {@code endpoint} or {@code queueName} have not been set. + * @throws IllegalArgumentException If neither a {@link SharedKeyCredential} or {@link SASTokenCredential} has been set. + */ + public QueueAsyncClient buildAsyncClient() { + Objects.requireNonNull(endpoint); + Objects.requireNonNull(queueName); + + if (sasTokenCredential == null && sharedKeyCredential == null) { + LOGGER.asError().log("Credentials are required for authorization"); + throw new IllegalArgumentException("Credentials are required for authorization"); + } + + if (pipeline != null) { + return new QueueAsyncClient(endpoint, pipeline, queueName); + } + // Closest to API goes first, closest to wire goes last. + final List policies = new ArrayList<>(); + + policies.add(new UserAgentPolicy(QueueConfiguration.NAME, QueueConfiguration.VERSION, configuration)); + policies.add(new RequestIdPolicy()); + policies.add(new AddDatePolicy()); + + if (sharedKeyCredential != null) { + policies.add(new SharedKeyCredentialPolicy(sharedKeyCredential)); + } else { + policies.add(new SASTokenCredentialPolicy(sasTokenCredential)); + } + + HttpPolicyProviders.addBeforeRetryPolicies(policies); + + policies.add(retryPolicy); + + policies.addAll(this.policies); + HttpPolicyProviders.addAfterRetryPolicies(policies); + policies.add(new HttpLoggingPolicy(logLevel)); + + HttpPipeline pipeline = HttpPipeline.builder() + .policies(policies.toArray(new HttpPipelinePolicy[0])) + .httpClient(httpClient) + .build(); + + return new QueueAsyncClient(endpoint, pipeline, queueName); + } + + /** + * Sets the endpoint for the Azure Storage Queue instance that the client will interact with. + * + *

                  The first path segment, if the endpoint contains path segments, will be assumed to be the name of the queue + * that the client will interact with.

                  + * + *

                  Query parameters of the endpoint will be parsed using {@link SASTokenCredential#fromQuery(String) fromQuery} in an + * attempt to generate a {@link SASTokenCredential} to authenticate requests sent to the service.

                  + * + * @param endpoint The URL of the Azure Storage Queue instance to send service requests to and receive responses from. + * @return the updated QueueClientBuilder object + * @throws IllegalArgumentException If {@code endpoint} isn't a proper URL + */ + public QueueClientBuilder endpoint(String endpoint) { + Objects.requireNonNull(endpoint); + try { + URL fullURL = new URL(endpoint); + this.endpoint = new URL(fullURL.getProtocol() + "://" + fullURL.getHost()); + + // Attempt to get the queue name from the URL passed + String[] pathSegments = fullURL.getPath().split("/", 2); + if (pathSegments.length == 2 && !ImplUtils.isNullOrEmpty(pathSegments[1])) { + this.queueName = pathSegments[1]; + } + + // Attempt to get the SAS token from the URL passed + SASTokenCredential credential = SASTokenCredential.fromQuery(fullURL.getQuery()); + if (credential != null) { + this.sasTokenCredential = credential; + } + } catch (MalformedURLException ex) { + LOGGER.asError().log("The Azure Storage Queue endpoint url is malformed. Endpoint: " + endpoint); + throw new IllegalArgumentException("The Azure Storage Queue endpoint url is malformed. Endpoint: " + endpoint); + } + + return this; + } + + /** + * Sets the name of the queue that the client will interact with. + * + * @param queueName Name of the queue + * @return the updated QueueClientBuilder object + * @throws NullPointerException If {@code queueName} is {@code null}. + */ + public QueueClientBuilder queueName(String queueName) { + this.queueName = Objects.requireNonNull(queueName); + return this; + } + + /** + * Sets the {@link SASTokenCredential} used to authenticate requests sent to the Queue. + * + * @param credential SAS token credential generated from the Storage account that authorizes requests + * @return the updated QueueClientBuilder object + * @throws NullPointerException If {@code credential} is {@code null}. + */ + public QueueClientBuilder credential(SASTokenCredential credential) { + this.sasTokenCredential = Objects.requireNonNull(credential); + return this; + } + + /** + * Sets the {@link SharedKeyCredential} used to authenticate requests sent to the Queue. + * + * @param credential Shared key credential can retrieve from the Storage account that authorizes requests + * @return the updated QueueServiceClientBuilder object + * @throws NullPointerException If {@code credential} is {@code null}. + */ + public QueueClientBuilder credential(SharedKeyCredential credential) { + this.sharedKeyCredential = Objects.requireNonNull(credential); + return this; + } + + /** + * Creates a {@link SharedKeyCredential} from the {@code connectionString} used to authenticate requests sent to the + * Queue service. + * + * @param connectionString Connection string from the Access Keys section in the Storage account + * @return the updated QueueClientBuilder object + * @throws NullPointerException If {@code connectionString} is {@code null}. + */ + public QueueClientBuilder connectionString(String connectionString) { + Objects.requireNonNull(connectionString); + this.sharedKeyCredential = SharedKeyCredential.fromConnectionString(connectionString); + getEndPointFromConnectionString(connectionString); + return this; + } + + private void getEndPointFromConnectionString(String connectionString) { + HashMap connectionStringPieces = new HashMap<>(); + for (String connectionStringPiece : connectionString.split(";")) { + String[] kvp = connectionStringPiece.split("=", 2); + connectionStringPieces.put(kvp[0].toLowerCase(Locale.ROOT), kvp[1]); + } + String accountName = connectionStringPieces.get(ACCOUNT_NAME); + try { + this.endpoint = new URL(String.format("https://%s.queue.core.windows.net", accountName)); + } catch (MalformedURLException e) { + LOGGER.asError().log("There is no valid account for the connection string. " + + "Connection String: %s", connectionString); + throw new IllegalArgumentException(String.format("There is no valid account for the connection string. " + + "Connection String: %s", connectionString)); + } + } + /** + * Sets the HTTP client to use for sending and receiving requests to and from the service. + * + * @param httpClient The HTTP client to use for requests. + * @return The updated QueueClientBuilder object. + * @throws NullPointerException If {@code httpClient} is {@code null}. + */ + public QueueClientBuilder httpClient(HttpClient httpClient) { + this.httpClient = Objects.requireNonNull(httpClient); + return this; + } + + /** + * Adds a policy to the set of existing policies that are executed after the {@link RetryPolicy}. + * + * @param pipelinePolicy The retry policy for service requests. + * @return The updated QueueClientBuilder object. + * @throws NullPointerException If {@code pipelinePolicy} is {@code null}. + */ + public QueueClientBuilder addPolicy(HttpPipelinePolicy pipelinePolicy) { + Objects.requireNonNull(pipelinePolicy); + this.policies.add(pipelinePolicy); + return this; + } + + /** + * Sets the logging level for HTTP requests and responses. + * + * @param logLevel The amount of logging output when sending and receiving HTTP requests/responses. + * @return The updated QueueClientBuilder object. + */ + public QueueClientBuilder httpLogDetailLevel(HttpLogDetailLevel logLevel) { + this.logLevel = logLevel; + return this; + } + + /** + * Sets the HTTP pipeline to use for the service client. + * + * If {@code pipeline} is set, all other settings are ignored, aside from {@link QueueClientBuilder#endpoint(String) endpoint} + * and {@link QueueClientBuilder#queueName(String) queueName} when building clients. + * + * @param pipeline The HTTP pipeline to use for sending service requests and receiving responses. + * @return The updated QueueClientBuilder object. + * @throws NullPointerException If {@code pipeline} is {@code null}. + */ + public QueueClientBuilder pipeline(HttpPipeline pipeline) { + Objects.requireNonNull(pipeline); + this.pipeline = pipeline; + return this; + } + + /** + * Sets the configuration store that is used during construction of the service client. + * + * The default configuration store is a clone of the {@link ConfigurationManager#getConfiguration() global + * configuration store}, use {@link Configuration#NONE} to bypass using configuration settings during construction. + * + * @param configuration The configuration store used to + * @return The updated QueueClientBuilder object. + */ + public QueueClientBuilder configuration(Configuration configuration) { + this.configuration = configuration; + return this; + } +} diff --git a/storage/client/queue/src/main/java/com/azure/storage/queue/QueueConfiguration.java b/storage/client/queue/src/main/java/com/azure/storage/queue/QueueConfiguration.java new file mode 100644 index 0000000000000..7a794525484f7 --- /dev/null +++ b/storage/client/queue/src/main/java/com/azure/storage/queue/QueueConfiguration.java @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.queue; + +/* + * Gets the SDK information for this library component. + */ +class QueueConfiguration { + //TODO: Eventually remove these hardcoded strings with https://github.com/Azure/azure-sdk-for-java/issues/3141 + static final String NAME = "azure-storage-queue"; + static final String VERSION = "12.0.0-preview.1"; +} diff --git a/storage/client/queue/src/main/java/com/azure/storage/queue/QueueServiceAsyncClient.java b/storage/client/queue/src/main/java/com/azure/storage/queue/QueueServiceAsyncClient.java new file mode 100644 index 0000000000000..e093f1d34b171 --- /dev/null +++ b/storage/client/queue/src/main/java/com/azure/storage/queue/QueueServiceAsyncClient.java @@ -0,0 +1,301 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.queue; + +import com.azure.core.http.HttpPipeline; +import com.azure.core.http.rest.Response; +import com.azure.core.http.rest.SimpleResponse; +import com.azure.core.http.rest.VoidResponse; +import com.azure.core.util.Context; +import com.azure.core.util.logging.ClientLogger; +import com.azure.storage.common.credentials.SASTokenCredential; +import com.azure.storage.common.credentials.SharedKeyCredential; +import com.azure.storage.queue.implementation.AzureQueueStorageBuilder; +import com.azure.storage.queue.implementation.AzureQueueStorageImpl; +import com.azure.storage.queue.models.CorsRule; +import com.azure.storage.queue.models.ListQueuesIncludeType; +import com.azure.storage.queue.models.ListQueuesSegmentResponse; +import com.azure.storage.queue.models.QueueItem; +import com.azure.storage.queue.models.QueuesSegmentOptions; +import com.azure.storage.queue.models.ServicesListQueuesSegmentResponse; +import com.azure.storage.queue.models.StorageErrorException; +import com.azure.storage.queue.models.StorageServiceProperties; +import com.azure.storage.queue.models.StorageServiceStats; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +/** + * This class provides a client that contains all the operations for interacting with a queue account in Azure Storage. + * Operations allowed by the client are creating, listing, and deleting queues, retrieving and updating properties of the account, + * and retrieving statistics of the account. + * + *

                  Instantiating an Asynchronous Queue Service Client

                  + * + * {@codesnippet com.azure.storage.queue.queueServiceAsyncClient.instantiation} + * + *

                  View {@link QueueServiceClientBuilder this} for additional ways to construct the client.

                  + * + * @see QueueServiceClientBuilder + * @see QueueServiceClient + * @see SharedKeyCredential + * @see SASTokenCredential + */ +public final class QueueServiceAsyncClient { + private static final ClientLogger LOGGER = new ClientLogger(QueueServiceAsyncClient.class); + private final AzureQueueStorageImpl client; + + /** + * Creates a QueueServiceAsyncClient that sends requests to the storage account at {@code endpoint}. + * Each service call goes through the {@code httpPipeline}. + * + * @param endpoint URL for the Storage Queue service + * @param httpPipeline HttpPipeline that the HTTP requests and response flow through + */ + QueueServiceAsyncClient(URL endpoint, HttpPipeline httpPipeline) { + this.client = new AzureQueueStorageBuilder().pipeline(httpPipeline) + .url(endpoint.toString()) + .build(); + } + + /** + * @return the URL of the storage queue + * @throws RuntimeException If the queue service is using a malformed URL. + */ + public URL getQueueServiceUrl() { + try { + return new URL(client.url()); + } catch (MalformedURLException ex) { + LOGGER.asError().log("Queue Service URL is malformed"); + throw new RuntimeException("Storage account URL is malformed"); + } + } + + /** + * Constructs a QueueAsyncClient that interacts with the specified queue. + * + * This will not create the queue in the storage account if it doesn't exist. + * + * @param queueName Name of the queue + * @return QueueAsyncClient that interacts with the specified queue + */ + public QueueAsyncClient getQueueAsyncClient(String queueName) { + return new QueueAsyncClient(client, queueName); + } + + /** + * Creates a queue in the storage account with the specified name and returns a QueueAsyncClient to interact + * with it. + * + *

                  Code Samples

                  + * + *

                  Create the queue "test"

                  + * + * {@codesnippet com.azure.storage.queue.queueServiceAsyncClient.createQueue#string} + * + * @param queueName Name of the queue + * @return A response containing the QueueAsyncClient and the status of creating the queue + * @throws StorageErrorException If a queue with the same name and different metadata already exists + */ + public Mono> createQueue(String queueName) { + return createQueue(queueName, null); + } + + /** + * Creates a queue in the storage account with the specified name and metadata and returns a QueueAsyncClient to + * interact with it. + * + *

                  Code Samples

                  + * + *

                  Create the queue "test" with metadata "queue:metadata"

                  + * + * {@codesnippet com.azure.storage.queue.queueServiceAsyncClient.createQueue#string-map} + * + * @param queueName Name of the queue + * @param metadata Metadata to associate with the queue + * @return A response containing the QueueAsyncClient and the status of creating the queue + * @throws StorageErrorException If a queue with the same name and different metadata already exists + */ + public Mono> createQueue(String queueName, Map metadata) { + QueueAsyncClient queueAsyncClient = new QueueAsyncClient(client, queueName); + + return queueAsyncClient.create(metadata) + .map(response -> new SimpleResponse<>(response, queueAsyncClient)); + } + + /** + * Deletes a queue in the storage account + * + *

                  Code Samples

                  + * + *

                  Delete the queue "test"

                  + * + * {@codesnippet com.azure.storage.queue.queueServiceAsyncClient.deleteQueue#string} + * + * @param queueName Name of the queue + * @return A response that only contains headers and response status code + * @throws StorageErrorException If the queue doesn't exist + */ + public Mono deleteQueue(String queueName) { + return new QueueAsyncClient(client, queueName).delete(); + } + + /** + * Lists all queues in the storage account without their metadata. + * + *

                  Code Samples

                  + * + *

                  List all queues in the account

                  + * + * {@codesnippet com.azure.storage.queue.queueServiceAsyncClient.listQueues} + * + * @return {@link QueueItem Queues} in the storage account + */ + public Flux listQueues() { + return listQueues(null, null); + } + + /** + * Lists the queues in the storage account that pass the filter. + * + * Pass true to {@link QueuesSegmentOptions#includeMetadata(boolean) includeMetadata} to have metadata returned for + * the queues. + * + *

                  Code Samples

                  + * + *

                  List all queues that begin with "azure"

                  + * + * {@codesnippet com.azure.storage.queue.queueServiceClient.listQueues#queueSergmentOptions} + * + * @param options Options for listing queues + * @return {@link QueueItem Queues} in the storage account that satisfy the filter requirements + */ + public Flux listQueues(QueuesSegmentOptions options) { + return listQueues(null, options); + } + + /** + * Lists the queues in the storage account that pass the filter starting at the specified marker. + * + * Pass true to {@link QueuesSegmentOptions#includeMetadata(boolean) includeMetadata} to have metadata returned for + * the queues. + * + * @param marker Starting point to list the queues + * @param options Options for listing queues + * @return {@link QueueItem Queues} in the storage account that satisfy the filter requirements + */ + Flux listQueues(String marker, QueuesSegmentOptions options) { + String prefix = null; + Integer maxResults = null; + final List include = new ArrayList<>(); + + if (options != null) { + prefix = options.prefix(); + maxResults = options.maxResults(); + if (options.includeMetadata()) { + include.add(ListQueuesIncludeType.fromString(ListQueuesIncludeType.METADATA.toString())); + } + } + + Mono result = client.services() + .listQueuesSegmentWithRestResponseAsync(prefix, marker, maxResults, include, null, null, Context.NONE); + + return result.flatMapMany(response -> extractAndFetchQueues(response, include, Context.NONE)); + } + + /* + * Helper function used to auto-enumerate through paged responses + */ + private Flux listQueues(ServicesListQueuesSegmentResponse response, List include, Context context) { + ListQueuesSegmentResponse value = response.value(); + Mono result = client.services() + .listQueuesSegmentWithRestResponseAsync(value.prefix(), value.marker(), value.maxResults(), include, null, null, context); + + return result.flatMapMany(r -> extractAndFetchQueues(r, include, context)); + } + + /* + * Helper function used to auto-enumerate though paged responses + */ + private Flux extractAndFetchQueues(ServicesListQueuesSegmentResponse response, List include, Context context) { + String nextPageLink = response.value().nextMarker(); + if (nextPageLink == null) { + return Flux.fromIterable(response.value().queueItems()); + } + + return Flux.fromIterable(response.value().queueItems()).concatWith(listQueues(response, include, context)); + } + + /** + * Retrieves the properties of the storage account's Queue service. The properties range from storage analytics and + * metric to CORS (Cross-Origin Resource Sharing). + * + *

                  Code Samples

                  + * + *

                  Retrieve Queue service properties

                  + * + * {@codesnippet com.azure.storage.queue.queueServiceAsyncClient.getProperties} + * + * @return Storage account Queue service properties + */ + public Mono> getProperties() { + return client.services().getPropertiesWithRestResponseAsync(Context.NONE) + .map(response -> new SimpleResponse<>(response, response.value())); + } + + /** + * Sets the properties for the storage account's Queue service. The properties range from storage analytics and + * metric to CORS (Cross-Origin Resource Sharing). + * + * To maintain the CORS in the Queue service pass a {@code null} value for {@link StorageServiceProperties#cors() CORS}. + * To disable all CORS in the Queue service pass an empty list for {@link StorageServiceProperties#cors() CORS}. + * + *

                  Code Sample

                  + * + *

                  Clear CORS in the Queue service

                  + * + * {@codesnippet com.azure.storage.queue.queueServiceAsyncClient.setProperties#storageServiceProperties} + * + *

                  Enable Minute and Hour Metrics

                  + * + * {@codesnippet com.azure.storage.queue.queueServiceAsyncClient.setPropertiesEnableMetrics#storageServiceProperties} + * + * @param properties Storage account Queue service properties + * @return A response that only contains headers and response status code + * @throws StorageErrorException When one of the following is true + *
                    + *
                  • A CORS rule is missing one of its fields
                  • + *
                  • More than five CORS rules will exist for the Queue service
                  • + *
                  • Size of all CORS rules exceeds 2KB
                  • + *
                  • + * Length of {@link CorsRule#allowedHeaders() allowed headers}, {@link CorsRule#exposedHeaders() exposed headers}, + * or {@link CorsRule#allowedOrigins() allowed origins} exceeds 256 characters. + *
                  • + *
                  • {@link CorsRule#allowedMethods() Allowed methods} isn't DELETE, GET, HEAD, MERGE, POST, OPTIONS, or PUT
                  • + *
                  + */ + public Mono setProperties(StorageServiceProperties properties) { + return client.services().setPropertiesWithRestResponseAsync(properties, Context.NONE) + .map(VoidResponse::new); + } + + /** + * Retrieves the geo replication information about the Queue service. + * + *

                  Code Samples

                  + * + *

                  Retrieve the geo replication information

                  + * + * {@codesnippet com.azure.storage.queue.queueServiceAsyncClient.getStatistics} + * + * @return The geo replication information about the Queue service + */ + public Mono> getStatistics() { + return client.services().getStatisticsWithRestResponseAsync(Context.NONE) + .map(response -> new SimpleResponse<>(response, response.value())); + } +} diff --git a/storage/client/queue/src/main/java/com/azure/storage/queue/QueueServiceClient.java b/storage/client/queue/src/main/java/com/azure/storage/queue/QueueServiceClient.java new file mode 100644 index 0000000000000..a002e5b12f872 --- /dev/null +++ b/storage/client/queue/src/main/java/com/azure/storage/queue/QueueServiceClient.java @@ -0,0 +1,234 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.queue; + +import com.azure.core.http.rest.Response; +import com.azure.core.http.rest.SimpleResponse; +import com.azure.core.http.rest.VoidResponse; +import com.azure.storage.common.credentials.SASTokenCredential; +import com.azure.storage.common.credentials.SharedKeyCredential; +import com.azure.storage.queue.models.CorsRule; +import com.azure.storage.queue.models.QueueItem; +import com.azure.storage.queue.models.QueuesSegmentOptions; +import com.azure.storage.queue.models.StorageErrorException; +import com.azure.storage.queue.models.StorageServiceProperties; +import com.azure.storage.queue.models.StorageServiceStats; +import java.net.URL; +import java.util.Map; + +/** + * This class provides a client that contains all the operations for interacting with a queue account in Azure Storage. + * Operations allowed by the client are creating, listing, and deleting queues, retrieving and updating properties of the account, + * and retrieving statistics of the account. + * + *

                  Instantiating an Synchronous Queue Service Client

                  + * + * {@codesnippet com.azure.storage.queue.queueServiceClient.instantiation} + * + *

                  View {@link QueueServiceClientBuilder this} for additional ways to construct the client.

                  + * + * @see QueueServiceClientBuilder + * @see QueueServiceAsyncClient + * @see SharedKeyCredential + * @see SASTokenCredential + */ +public final class QueueServiceClient { + private final QueueServiceAsyncClient client; + + /** + * Creates a QueueServiceClient that wraps a QueueServiceAsyncClient and blocks requests. + * + * @param client QueueServiceAsyncClient that is used to send requests + */ + QueueServiceClient(QueueServiceAsyncClient client) { + this.client = client; + } + + /** + * @return the URL of the storage queue + */ + public URL getQueueServiceUrl() { + return client.getQueueServiceUrl(); + } + + /** + * Constructs a QueueClient that interacts with the specified queue. + * + * This will not create the queue in the storage account if it doesn't exist. + * + * @param queueName Name of the queue + * @return QueueClient that interacts with the specified queue + */ + public QueueClient getQueueClient(String queueName) { + return new QueueClient(client.getQueueAsyncClient(queueName)); + } + + /** + * Creates a queue in the storage account with the specified name and returns a QueueClient to interact with it. + * + *

                  Code Samples

                  + * + *

                  Create the queue "test"

                  + * + * {@codesnippet com.azure.storage.queue.queueServiceClient.createQueue#string} + * + * @param queueName Name of the queue + * @return A response containing the QueueClient and the status of creating the queue + * @throws StorageErrorException If a queue with the same name and different metadata already exists + */ + public Response createQueue(String queueName) { + return createQueue(queueName, null); + } + + /** + * Creates a queue in the storage account with the specified name and metadata and returns a QueueClient to + * interact with it. + * + *

                  Code Samples

                  + * + *

                  Create the queue "test" with metadata "queue:metadata"

                  + * + * {@codesnippet com.azure.storage.queue.queueServiceClient.createQueue#string-map} + * + * @param queueName Name of the queue + * @param metadata Metadata to associate with the queue + * @return A response containing the QueueClient and the status of creating the queue + * @throws StorageErrorException If a queue with the same name and different metadata already exists + */ + public Response createQueue(String queueName, Map metadata) { + Response response = client.createQueue(queueName, metadata).block(); + + return new SimpleResponse<>(response, new QueueClient(response.value())); + } + + /** + * Deletes a queue in the storage account + * + *

                  Code Samples

                  + * + *

                  Delete the queue "test"

                  + * + * {@codesnippet com.azure.storage.queue.queueServiceClient.deleteQueue#string} + * + * @param queueName Name of the queue + * @return A response containing the status of deleting the queue + * @throws StorageErrorException If the queue doesn't exist + */ + public VoidResponse deleteQueue(String queueName) { + return client.deleteQueue(queueName).block(); + } + + /** + * Lists all queues in the storage account without their metadata. + * + *

                  Code Samples

                  + * + *

                  List all queues in the account

                  + * + * {@codesnippet com.azure.storage.queue.queueServiceClient.listQueues} + * + * @return {@link QueueItem Queues} in the storage account + */ + public Iterable listQueues() { + return listQueues(null, null); + } + + /** + * Lists the queues in the storage account that pass the filter. + * + * Pass true to {@link QueuesSegmentOptions#includeMetadata(boolean) includeMetadata} to have metadata returned for + * the queues. + * + *

                  Code Samples

                  + * + *

                  List all queues that begin with "azure"

                  + * + * {@codesnippet com.azure.storage.queue.queueServiceClient.listQueues#queueSergmentOptions} + * + * @param options Options for listing queues + * @return {@link QueueItem Queues} in the storage account that satisfy the filter requirements + */ + public Iterable listQueues(QueuesSegmentOptions options) { + return listQueues(null, options); + } + + /** + * Lists the queues in the storage account that pass the filter starting at the specified marker. + * + * Pass true to {@link QueuesSegmentOptions#includeMetadata(boolean) includeMetadata} to have metadata returned for + * the queues. + * + * @param marker Starting point to list the queues + * @param options Options for listing queues + * @return {@link QueueItem Queues} in the storage account that satisfy the filter requirements + */ + Iterable listQueues(String marker, QueuesSegmentOptions options) { + return client.listQueues(marker, options).toIterable(); + } + + /** + * Retrieves the properties of the storage account's Queue service. The properties range from storage analytics and + * metric to CORS (Cross-Origin Resource Sharing). + * + *

                  Code Samples

                  + * + *

                  Retrieve Queue service properties

                  + * + * {@codesnippet com.azure.storage.queue.queueServiceClient.getProperties} + * + * @return Storage account Queue service properties + */ + public Response getProperties() { + return client.getProperties().block(); + } + + /** + * Sets the properties for the storage account's Queue service. The properties range from storage analytics and + * metric to CORS (Cross-Origin Resource Sharing). + * + * To maintain the CORS in the Queue service pass a {@code null} value for {@link StorageServiceProperties#cors() CORS}. + * To disable all CORS in the Queue service pass an empty list for {@link StorageServiceProperties#cors() CORS}. + * + *

                  Code Sample

                  + * + *

                  Clear CORS in the Queue service

                  + * + * {@codesnippet com.azure.storage.queue.queueServiceClient.setProperties#storageServiceProperties} + * + *

                  Enable Minute and Hour Metrics

                  + * + * {@codesnippet com.azure.storage.queue.queueServiceClient.setPropertiesEnableMetrics#storageServiceProperties} + * + * @param properties Storage account Queue service properties + * @return A response that only contains headers and response status code + * @throws StorageErrorException When one of the following is true + *
                    + *
                  • A CORS rule is missing one of its fields
                  • + *
                  • More than five CORS rules will exist for the Queue service
                  • + *
                  • Size of all CORS rules exceeds 2KB
                  • + *
                  • + * Length of {@link CorsRule#allowedHeaders() allowed headers}, {@link CorsRule#exposedHeaders() exposed headers}, + * or {@link CorsRule#allowedOrigins() allowed origins} exceeds 256 characters. + *
                  • + *
                  • {@link CorsRule#allowedMethods() Allowed methods} isn't DELETE, GET, HEAD, MERGE, POST, OPTIONS, or PUT
                  • + *
                  + */ + public VoidResponse setProperties(StorageServiceProperties properties) { + return client.setProperties(properties).block(); + } + + /** + * Retrieves the geo replication information about the Queue service. + * + *

                  Code Samples

                  + * + *

                  Retrieve the geo replication information

                  + * + * {@codesnippet com.azure.storage.queue.queueServiceClient.getStatistics} + * + * @return The geo replication information about the Queue service + */ + public Response getStatistics() { + return client.getStatistics().block(); + } +} diff --git a/storage/client/queue/src/main/java/com/azure/storage/queue/QueueServiceClientBuilder.java b/storage/client/queue/src/main/java/com/azure/storage/queue/QueueServiceClientBuilder.java new file mode 100644 index 0000000000000..63b191256f5dd --- /dev/null +++ b/storage/client/queue/src/main/java/com/azure/storage/queue/QueueServiceClientBuilder.java @@ -0,0 +1,301 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.queue; + +import com.azure.core.http.HttpClient; +import com.azure.core.http.HttpPipeline; +import com.azure.core.http.policy.AddDatePolicy; +import com.azure.core.http.policy.HttpLogDetailLevel; +import com.azure.core.http.policy.HttpLoggingPolicy; +import com.azure.core.http.policy.HttpPipelinePolicy; +import com.azure.core.http.policy.RequestIdPolicy; +import com.azure.core.http.policy.RetryPolicy; +import com.azure.core.http.policy.UserAgentPolicy; +import com.azure.core.implementation.http.policy.spi.HttpPolicyProviders; +import com.azure.core.util.configuration.Configuration; +import com.azure.core.util.configuration.ConfigurationManager; +import com.azure.core.util.logging.ClientLogger; +import com.azure.storage.common.credentials.SASTokenCredential; +import com.azure.storage.common.credentials.SharedKeyCredential; +import com.azure.storage.common.policy.SASTokenCredentialPolicy; +import com.azure.storage.common.policy.SharedKeyCredentialPolicy; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * This class provides a fluent builder API to help aid the configuration and instantiation of the {@link QueueServiceClient queueServiceClients} + * and {@link QueueServiceAsyncClient queueServiceAsyncClients}, calling {@link QueueServiceClientBuilder#buildClient() buildClient} + * constructs an instance of QueueServiceClient and calling {@link QueueServiceClientBuilder#buildAsyncClient() buildAsyncClient} + * constructs an instance of QueueServiceAsyncClient. + * + *

                  The client needs the endpoint of the Azure Storage Queue service, name of the share, and authorization credential. + * {@link QueueServiceClientBuilder#endpoint(String) endpoint} gives the builder the endpoint and may give the builder the + * A {@link SASTokenCredential} that authorizes the client.

                  + * + *

                  Instantiating a synchronous Queue Service Client with SAS token

                  + * {@codesnippet com.azure.storage.queue.queueServiceClient.instantiation.sastoken} + * + *

                  Instantiating an Asynchronous Queue Service Client with SAS token

                  + * {@codesnippet com.azure.storage.queue.queueServiceAsyncClient.instantiation.sastoken} + * + *

                  If the {@code endpoint} doesn't contain {@code SASTokenCredential} they may be set using + * {@link QueueClientBuilder#credential(SASTokenCredential) credential}.

                  + * + *

                  Instantiating a synchronous Queue Service Client with SAS token

                  + * {@codesnippet com.azure.storage.queue.queueServiceAsyncClient.instantiation.credential} + * + *

                  Instantiating an Asynchronous Queue Service Client with SAS token

                  + * {@codesnippet com.azure.storage.queue.queueServiceAsyncClient.instantiation.credential} + * + *

                  If the {@code endpoint} doesn't contain the query parameters to construct a {@code SASTokenCredential} they may + * be set using {@link QueueServiceClientBuilder#credential(SASTokenCredential) credential}.

                  + * + *

                  Another way to authenticate the client is using a {@link SharedKeyCredential}. To create a SharedKeyCredential + * a connection string from the Storage Queue service must be used. Set the SharedKeyCredential with + * {@link QueueServiceClientBuilder#connectionString(String) connectionString}. If the builder has both a SASTokenCredential and + * SharedKeyCredential the SharedKeyCredential will be preferred when authorizing requests sent to the service.

                  + * + *

                  Instantiating a synchronous Queue Service Client with connection string.

                  + * {@codesnippet com.azure.storage.queue.queueServiceClient.instantiation.connectionstring} + * + *

                  Instantiating an Asynchronous Queue Service Client with connection string.

                  + * {@codesnippet com.azure.storage.queue.queueServiceAsyncClient.instantiation.connectionstring} + * + * @see QueueServiceClient + * @see QueueServiceAsyncClient + * @see SASTokenCredential + * @see SharedKeyCredential + */ +public final class QueueServiceClientBuilder { + private static final ClientLogger LOGGER = new ClientLogger(QueueServiceClientBuilder.class); + private final List policies; + + private URL endpoint; + private SASTokenCredential sasTokenCredential; + private SharedKeyCredential sharedKeyCredential; + private HttpClient httpClient; + private HttpPipeline pipeline; + private HttpLogDetailLevel logLevel; + private RetryPolicy retryPolicy; + private Configuration configuration; + + /** + * Creates a builder instance that is able to configure and construct {@link QueueServiceClient QueueServiceClients} + * and {@link QueueServiceAsyncClient QueueServiceAsyncClients}. + */ + public QueueServiceClientBuilder() { + retryPolicy = new RetryPolicy(); + logLevel = HttpLogDetailLevel.NONE; + policies = new ArrayList<>(); + configuration = ConfigurationManager.getConfiguration(); + } + + /** + * Creates a {@link QueueServiceAsyncClient} based on options set in the builder. Every time {@code buildAsyncClient()} is + * called a new instance of {@link QueueServiceAsyncClient} is created. + * + *

                  + * If {@link QueueServiceClientBuilder#pipeline(HttpPipeline) pipeline} is set, then the {@code pipeline} and + * {@link QueueServiceClientBuilder#endpoint(String) endpoint} are used to create the + * {@link QueueServiceAsyncClient client}. All other builder settings are ignored. + *

                  + * + * @return A QueueServiceAsyncClient with the options set from the builder. + * @throws NullPointerException If {@code endpoint} or {@code queueName} have not been set. + * @throws IllegalArgumentException If neither a {@link SharedKeyCredential} or {@link SASTokenCredential} has been set. + */ + public QueueServiceAsyncClient buildAsyncClient() { + Objects.requireNonNull(endpoint); + + if (sasTokenCredential == null && sharedKeyCredential == null) { + LOGGER.asError().log("Credentials are required for authorization"); + throw new IllegalArgumentException("Credentials are required for authorization"); + } + + if (pipeline != null) { + return new QueueServiceAsyncClient(endpoint, pipeline); + } + // Closest to API goes first, closest to wire goes last. + final List policies = new ArrayList<>(); + + policies.add(new UserAgentPolicy(QueueConfiguration.NAME, QueueConfiguration.VERSION, configuration)); + policies.add(new RequestIdPolicy()); + policies.add(new AddDatePolicy()); + + if (sharedKeyCredential != null) { + policies.add(new SharedKeyCredentialPolicy(sharedKeyCredential)); + } else { + policies.add(new SASTokenCredentialPolicy(sasTokenCredential)); + } + + HttpPolicyProviders.addBeforeRetryPolicies(policies); + + policies.add(retryPolicy); + + policies.addAll(this.policies); + HttpPolicyProviders.addAfterRetryPolicies(policies); + policies.add(new HttpLoggingPolicy(logLevel)); + + HttpPipeline pipeline = HttpPipeline.builder() + .policies(policies.toArray(new HttpPipelinePolicy[0])) + .httpClient(httpClient) + .build(); + + return new QueueServiceAsyncClient(endpoint, pipeline); + } + + /** + * Creates a {@link QueueServiceClient} based on options set in the builder. Every time {@code buildClient()} is + * called a new instance of {@link QueueServiceClient} is created. + * + *

                  + * If {@link QueueServiceClientBuilder#pipeline(HttpPipeline) pipeline} is set, then the {@code pipeline} and + * {@link QueueServiceClientBuilder#endpoint(String) endpoint} are used to create the + * {@link QueueServiceClient client}. All other builder settings are ignored. + *

                  + * + * @return A QueueServiceClient with the options set from the builder. + * @throws NullPointerException If {@code endpoint} or {@code queueName} have not been set. + * @throws IllegalStateException If neither a {@link SharedKeyCredential} or {@link SASTokenCredential} has been set. + */ + public QueueServiceClient buildClient() { + return new QueueServiceClient(buildAsyncClient()); + } + + + /** + * Sets the endpoint for the Azure Storage Queue instance that the client will interact with. + * + *

                  Query parameters of the endpoint will be parsed using {@link SASTokenCredential#fromQuery(String) fromQuery} in an + * attempt to generate a {@link SASTokenCredential} to authenticate requests sent to the service.

                  + * + * @param endpoint The URL of the Azure Storage Queue instance to send service requests to and receive responses from. + * @return the updated QueueServiceClientBuilder object + * @throws IllegalArgumentException If {@code endpoint} isn't a proper URL + */ + public QueueServiceClientBuilder endpoint(String endpoint) { + Objects.requireNonNull(endpoint); + try { + URL fullURL = new URL(endpoint); + this.endpoint = new URL(fullURL.getProtocol() + "://" + fullURL.getHost()); + + // Attempt to get the SAS token from the URL passed + SASTokenCredential credential = SASTokenCredential.fromQuery(fullURL.getQuery()); + if (credential != null) { + this.sasTokenCredential = credential; + } + } catch (MalformedURLException ex) { + LOGGER.asError().log("The Azure Storage Queue endpoint url is malformed."); + throw new IllegalArgumentException("The Azure Storage Queue endpoint url is malformed."); + } + + return this; + } + + /** + * Sets the {@link SASTokenCredential} used to authenticate requests sent to the Queue service. + * + * @param credential SAS token credential generated from the Storage account that authorizes requests + * @return the updated QueueServiceClientBuilder object + * @throws NullPointerException If {@code credential} is {@code null}. + */ + public QueueServiceClientBuilder credential(SASTokenCredential credential) { + this.sasTokenCredential = Objects.requireNonNull(credential); + return this; + } + + /** + * Sets the {@link SharedKeyCredential} used to authenticate requests sent to the Queue service. + * + * @param credential Shared key credential can retrieve from the Storage account that authorizes requests + * @return the updated QueueServiceClientBuilder object + * @throws NullPointerException If {@code credential} is {@code null}. + */ + public QueueServiceClientBuilder credential(SharedKeyCredential credential) { + this.sharedKeyCredential = Objects.requireNonNull(credential); + return this; + } + + + /** + * Creates a {@link SharedKeyCredential} from the {@code connectionString} used to authenticate requests sent to the + * Queue service. + * + * @param connectionString Connection string from the Access Keys section in the Storage account + * @return the updated QueueServiceClientBuilder object + * @throws NullPointerException If {@code connectionString} is {@code null}. + */ + public QueueServiceClientBuilder connectionString(String connectionString) { + Objects.requireNonNull(connectionString); + this.sharedKeyCredential = SharedKeyCredential.fromConnectionString(connectionString); + return this; + } + + /** + * Sets the HTTP client to use for sending and receiving requests to and from the service. + * + * @param httpClient The HTTP client to use for requests. + * @return The updated QueueServiceClientBuilder object. + * @throws NullPointerException If {@code httpClient} is {@code null}. + */ + public QueueServiceClientBuilder httpClient(HttpClient httpClient) { + this.httpClient = httpClient; + return this; + } + + /** + * Adds a policy to the set of existing policies that are executed after the {@link RetryPolicy}. + * + * @param pipelinePolicy The retry policy for service requests. + * @return The updated QueueServiceClientBuilder object. + * @throws NullPointerException If {@code pipelinePolicy} is {@code null}. + */ + public QueueServiceClientBuilder addPolicy(HttpPipelinePolicy pipelinePolicy) { + this.policies.add(pipelinePolicy); + return this; + } + + /** + * Sets the logging level for HTTP requests and responses. + * + * @param logLevel The amount of logging output when sending and receiving HTTP requests/responses. + * @return The updated QueueServiceClientBuilder object. + */ + public QueueServiceClientBuilder httpLogDetailLevel(HttpLogDetailLevel logLevel) { + this.logLevel = logLevel; + return this; + } + + /** + * Sets the HTTP pipeline to use for the service client. + * + * If {@code pipeline} is set, all other settings are ignored, aside from {@link QueueServiceClientBuilder#endpoint(String) endpoint} + * when building clients. + * + * @param pipeline The HTTP pipeline to use for sending service requests and receiving responses. + * @return The updated QueueServiceClientBuilder object. + * @throws NullPointerException If {@code pipeline} is {@code null}. + */ + public QueueServiceClientBuilder pipeline(HttpPipeline pipeline) { + Objects.requireNonNull(pipeline); + this.pipeline = pipeline; + return this; + } + + /** + * Sets the configuration store that is used during construction of the service client. + * + * The default configuration store is a clone of the {@link ConfigurationManager#getConfiguration() global + * configuration store}, use {@link Configuration#NONE} to bypass using configuration settings during construction. + * + * @param configuration The configuration store used to + * @return The updated QueueServiceClientBuilder object. + */ + public QueueServiceClientBuilder configuration(Configuration configuration) { + this.configuration = configuration; + return this; + } +} diff --git a/storage/client/queue/src/main/java/com/azure/storage/queue/README.md b/storage/client/queue/src/main/java/com/azure/storage/queue/README.md new file mode 100644 index 0000000000000..86ffe940daf0c --- /dev/null +++ b/storage/client/queue/src/main/java/com/azure/storage/queue/README.md @@ -0,0 +1,377 @@ +# Azure Storage Queue client library for Java +Azure Queue storage is a service for storing large numbers of messages that can be accessed from anywhere in the world via authenticated calls using HTTP or HTTPS. +A single queue message can be up to 64 KB in size, and a queue can contain millions of messages, up to the total capacity limit of a storage account. + +[Source code][source_code] | [Package (Maven)][package] | [API reference documentation][api_documentation] | [Product documentation][storage_docs] + +## Getting started + +### Prerequisites + +- [Java Development Kit (JDK)][jdk] with version 8 or above +- [Azure Subscription][azure_subscription] +- [Create Strorage Account][storage_account] + +### Adding the package to your product + +```xml + + com.azure + azure-storage + 12.0.0 + +``` + +### Create a Storage Account +To create a Storage Account you can use the Azure Portal or [Azure CLI][azure_cli]. + +```Powershell +az group create \ + --name storage-resource-group \ + --location westus +``` + +### Authenticate the client + +In order to interact with the Storage service (Blob, Queue, Message, MessageId, File) you'll need to create an instance of the Service Client class. +To make this possible you'll need the Account SAS (shared access signature) string of Storage account. Learn more at [SAS Token][sas_token] + +#### Get Credentials + +- **SAS Token** + +a. Use the [Azure CLI][azure_cli] snippet below to get the SAS token from the Storage account. + +```Powershell +az storage queue generate-sas + --name {queue name} + --expiry {date/time to expire SAS token} + --permission {permission to grant} + --connection-string {connection string of the storage account} +``` + +```Powershell +CONNECTION_STRING= + +az storage queue generate-sas + --name javasdksas + --expiry 2019-06-05 + --permission rpau + --connection-string $CONNECTION_STRING +``` +b. Alternatively, get the Account SAS Token from the Azure Portal. +``` +Go to your storage account -> Shared access signature -> Click on Generate SAS and connection string (after setup) +``` + +- **Shared Key Credential** + +a. Use account name and account key. Account name is your storage account name. +``` +// Here is where we get the key +Go to your storage account -> Access keys -> Key 1/ Key 2 -> Key +``` +b. Use the connection string +``` +// Here is where we get the key +Go to your storage account -> Access Keys -> Keys 1/ Key 2 -> Connection string +``` +## Key concepts +### URL format +Queues are addressable using the following URL format: +https://.queue.core.windows.net/ +The following URL addresses a queue in the diagram: +https://myaccount.queue.core.windows.net/images-to-download + +#### Resource URI Syntax +For the storage account, the base URI for queue operations includes the name of the account only: +```$xslt +https://myaccount.queue.core.windows.net +``` +For a queue, the base URI includes the name of the account and the name of the queue: +```$xslt +https://myaccount.queue.core.windows.net/myqueue +``` + +### Handling Exceptions + +```java +TODO +``` + +### Queue Names +Every queue within an account must have a unique name. The queue name must be a valid DNS name, and cannot be changed once created. Queue names must confirm to the following rules: +1. A queue name must start with a letter or number, and can only contain letters, numbers, and the dash (-) character. +1. The first and last letters in the queue name must be alphanumeric. The dash (-) character cannot be the first or last character. Consecutive dash characters are not permitted in the queue name. +1. All letters in a queue name must be lowercase. +1. A queue name must be from 3 through 63 characters long. + +### Queue Services +The queue service do operations on the queues in the storage account and manage the queue properties. + +### Queue Service Client + +The client performs the interactions with the Queue service, create or delete a queue, getting and setting Queue properties, list queues in account, and get queue statistics. An asynchronous, `QueueServiceAsyncClient`, and synchronous, `QueueClient`, client exists in the SDK allowing for selection of a client based on an application's use case. +Once you have the value of the SASToken you can create the queue service client with `${accountName}`, `${sasToken}`. +```Java +String queueServiceURL = String.format("https://%s.queue.core.windows.net/%s", accountName, sasToken) +QueueServiceClient queueServiceClient = QueueServiceClient.builder().endpoint(queueURL).build(); + +QueueClient newQueueServiceClient = queueServiceClient.createQueue("myqueue"); +``` + +or + +```Java +String queueServiceAsyncURL = String.format("https://%s.queue.core.windows.net/%s", accountName, sasToken) +QueueServiceAsyncClient queueServiceAsyncClient = QueueServiceAsyncClient.builder().endpoint(queueServiceAsyncURL).build(); +queueServiceAsyncClient.createQueue("newAsyncQueue").subscribe( + result -> { + // do something when new queue created + }, + error -> { + // do something if something wrong happened + }, + () -> { + // completed, do something + }); +``` + +### Queue +Azure Queue storage is a service for storing large numbers of messages that can be accessed from anywhere in the world via authenticated calls using HTTP or HTTPS. +A single queue message can be up to 64 KB in size, and a queue can contain millions of messages, up to the total capacity limit of a storage account. + +### QueueClient +Once you have the value of the SASToken you can create the queue service client with `${accountName}`, `${queueName}`, `${sasToken}`. +```Java +String queueURL = String.format("https://%s.queue.core.windows.net/%s%s", accountName, queueName, sasToken); +QueueClient queueClient = QueueClient.builder().endpoint(queueURL).build(); +// metadata is map of key-value pair, timeout is client side timeout +QueueClient newQueueClient = queueClient.create(metadata, timeout); +``` + +or + +```Java +String queueAsyncURL = String.format("https://%s.queue.core.windows.net/%s%s", accountName, queueAsyncName, sasToken) +QueueAsyncClient queueAsyncClient = QueueAsyncClient.builder().endpoint(queueAsyncURL).build(); +queueAsyncClient.create(metadata, timeout).subscribe( + result -> { + // do something when new queue created + }, + error -> { + // do something if something wrong happened + }, + () -> { + // completed, do something + }); +``` + +## Examples + +The following sections provide several code snippets covering some of the most common Configuration Service tasks, including: +- [Create a Queue](#Create-a-queue) +- [Delete a queue](#Delete-a-queue) +- [List the queues in account](#List-queues-in-account) +- [Get propertiesin Queue account](#Get-properties-in-queue-account) +- [Set propertiesin Queue account](#Set-properties-in-queue-account) +- [Get statistcs of queue](#Get-queue-service-statistics) +- [Enqueue message into a queue](#Enqueue-message-into-a-queue) +- [Update message into a queue](#Update-message-into-a-queue) +- [Peek messages into a queue](#Peek-messages-into-a-queue) +- [Dequeue messages from a queue](#Dequeue-messages-from-a-queue) +- [Delete message from a queue](#Delete-message-from-a-queue) +- [Get a Queue properties](#Get-a-queue-properties) +- [Set/Update a Queue metadata](#Set-a-queue-metadata) +### Create a queue + +Create a queue in the Storage Account. Throws StorageErrorException If the queue fails to be created. + +```Java +String queueServiceURL = String.format("https://%s.queue.core.windows.net/%s", accountName, sasToken) +QueueServiceClient queueServiceClient = QueueServiceClient.builder().endpoint(queueURL).build(); + +QueueClient newQueueServiceClient = queueServiceClient.createQueue("myqueue"); +``` +### Delete a queue + +Delete a queue in the Storage Account. Throws StorageErrorException If the queue fails to be deleted. +```Java +String queueServiceURL = String.format("https://%s.queue.core.windows.net/%s", accountName, sasToken) +QueueServiceClient queueServiceClient = QueueServiceClient.builder().endpoint(queueURL).build(); + +QueueClient newQueueServiceClient = queueServiceClient.deleteQueue("myqueue"); +``` + +### List queues in account + +List all the queues in account. +```Java +String queueServiceURL = String.format("https://%s.queue.core.windows.net/%s", accountName, sasToken) +QueueServiceClient queueServiceClient = QueueServiceClient.builder().endpoint(queueURL).build(); +// @param marker: Starting point to list the queues +// @param options: Filter for queue selection +queueServiceClient.listQueuesSegment(marker, options).forEach{ + queueItem -> {//do something} +}; +``` + +### Get properties in queue account + +Get queue properties in account, including properties for Storage Analytics and CORS (Cross-Origin Resource Sharing) rules. +```Java +String queueServiceURL = String.format("https://%s.queue.core.windows.net/%s", accountName, sasToken) +QueueServiceClient queueServiceClient = QueueServiceClient.builder().endpoint(queueURL).build(); + +Response properties = queueServiceClient.getProperties(); +``` + +### Set properties in queue account + +Set queue properties in account, including properties for Storage Analytics and CORS (Cross-Origin Resource Sharing) rules. +```Java +String queueServiceURL = String.format("https://%s.queue.core.windows.net/%s", accountName, sasToken) +QueueServiceClient queueServiceClient = QueueServiceClient.builder().endpoint(queueURL).build(); + +StorageServiceProperties properties = new StorageServiceProperties() { + // logging: some logging; + // HourMetrics: some metrics + // MinuteMetrics: some metrics + // Cors: some cors +} + +queueServiceClient.setProperties(properties); +``` + +### Get queue service statistics +he `Get Queue Service Stats` operation retrieves statistics related to replication for the Queue service. +It is only available on the secondary location endpoint when read-access geo-redundant replication is enabled for the storage account. +```Java +String queueServiceURL = String.format("https://%s.queue.core.windows.net/%s", accountName, sasToken) +QueueServiceClient queueServiceClient = QueueServiceClient.builder().endpoint(queueURL).build(); + +Response queueStats = queueServiceClient.getStatistics(); +``` + +### Enqueue message into a queue +The operation adds a new message to the back of the message queue. A visibility timeout can also be specified to make the message invisible until the visibility timeout expires. +A message must be in a format that can be included in an XML request with UTF-8 encoding. The encoded message can be up to 64 KB in size for versions 2011-08-18 and newer, or 8 KB in size for previous versions. +```Java +String queueURL = String.format("https://%s.queue.core.windows.net/%s%s", accountName, queueName, sasToken); +QueueClient queueClient = QueueClient.builder().endpoint(queueURL).build(); + +queueClient.enqueueMessage("myMessage"); +``` + +### Update messaged from a queue +The operation updates a message in the message queue. +```Java +String queueURL = String.format("https://%s.queue.core.windows.net/%s%s", accountName, queueName, sasToken); +QueueClient queueClient = QueueClient.builder().endpoint(queueURL).build(); +// @param messageId Id of the message +// @param popReceipt Unique identifier that must match the message for it to be updated +// @param visibilityTimeout How long the message will be invisible in the queue in seconds +queueClient.updateMessage(messageId, "new message", popReceipt, visibilityTimeout); +``` + +### Peek messages from a queue +The operation retrieves one or more messages from the front of the queue. +```Java +String queueURL = String.format("https://%s.queue.core.windows.net/%s%s", accountName, queueName, sasToken); +QueueClient queueClient = QueueClient.builder().endpoint(queueURL).build(); + +queueClient.peekMessages().forEach(message-> {print message.messageText();}); +``` + + +### Dequeue messages from a queue +The operation retrieves one or more messages from the front of the queue. +```Java +String queueURL = String.format("https://%s.queue.core.windows.net/%s%s", accountName, queueName, sasToken); +QueueClient queueClient = QueueClient.builder().endpoint(queueURL).build(); + +queueClient.dequeueMessage("myMessage").forEach(message-> {print message.messageText();}); +``` + + +### Delete message from a queue +The operation retrieves one or more messages from the front of the queue. +```Java +String queueURL = String.format("https://%s.queue.core.windows.net/%s%s", accountName, queueName, sasToken); +QueueClient queueClient = QueueClient.builder().endpoint(queueURL).build(); + +queueClient.deleteMessage(messageId, popReceipt); +``` + +### Get a queue properties +The operation retrieves user-defined metadata and queue properties on the specified queue. Metadata is associated with the queue as name-values pairs. +```Java +String queueURL = String.format("https://%s.queue.core.windows.net/%s%s", accountName, queueName, sasToken); +QueueClient queueClient = QueueClient.builder().endpoint(queueURL).build(); + +Response properties = queueClient.getProperties(); +``` + +### Set a queue metadata +The operation sets user-defined metadata on the specified queue. Metadata is associated with the queue as name-value pairs. +```Java +String queueURL = String.format("https://%s.queue.core.windows.net/%s%s", accountName, queueName, sasToken); +QueueClient queueClient = QueueClient.builder().endpoint(queueURL).build(); + +Map metadata = new HashMap<>() {{ + put("key1", "val1"); + put("key2", "val2"); +}}; +queueClient.setMetadata(metadata); +``` + + +## Troubleshooting + +## General + +When you interact with queue using this Java client library, errors returned by the service correspond to the same HTTP status codes returned for [REST API][storage_rest] requests. For example, if you try to retrieve a queue that doesn't exist in your Storage Account, a `404` error is returned, indicating `Not Found`. + +## Next steps + +### More Samples +- QueueServiceSample +- MessageSample +- QueueExceptionSample +- AsyncSample + +[Quickstart: Create a Java Spring app with App Configuration](https://docs.microsoft.com/en-us/azure/azure-app-configuration/quickstart-java-spring-app) + +## Contributing +This project welcomes contributions and suggestions. Most contributions require you to agree to a +Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us +the rights to use your contribution. For details, visit https://cla.microsoft.com. + +When you submit a pull request, a CLA-bot will automatically determine whether you need to provide +a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions +provided by the bot. You will only need to do this once across all repos using our CLA. + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or +contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +If you would like to become an active contributor to this project please follow the instructions provided in [Microsoft Azure Projects Contribution Guidelines](http://azure.github.io/guidelines.html). + +1. Fork it +2. Create your feature branch (`git checkout -b my-new-feature`) +3. Commit your changes (`git commit -am 'Add some feature'`) +4. Push to the branch (`git push origin my-new-feature`) +5. Create new Pull Request + + +[source_code]: to-be-continue +[package]: to-be-continue +[api_documentation]: https://docs.microsoft.com/en-us/rest/api/storageservices/queue-service-rest-api +[storage_docs]: https://docs.microsoft.com/en-us/azure/storage/queues/storage-queues-introduction +[jdk]: https://docs.microsoft.com/en-us/java/azure/java-supported-jdk-runtime?view=azure-java-stable +[maven]: https://maven.apache.org/ +[azure_subscription]: https://azure.microsoft.com/en-us/free/ +[storage_account]: https://docs.microsoft.com/en-us/azure/storage/common/storage-quickstart-create-account?tabs=azure-portal +[azure_cli]: https://docs.microsoft.com/cli/azure +[sas_token]: https://docs.microsoft.com/en-us/azure/storage/common/storage-dotnet-shared-access-signature-part-1 +[storage_rest]: https://docs.microsoft.com/en-us/rest/api/storageservices/queue-service-error-codes diff --git a/storage/client/queue/src/main/java/com/azure/storage/queue/implementation/AzureQueueStorageBuilder.java b/storage/client/queue/src/main/java/com/azure/storage/queue/implementation/AzureQueueStorageBuilder.java index 4f678dfe10832..f3582e10e1a44 100644 --- a/storage/client/queue/src/main/java/com/azure/storage/queue/implementation/AzureQueueStorageBuilder.java +++ b/storage/client/queue/src/main/java/com/azure/storage/queue/implementation/AzureQueueStorageBuilder.java @@ -75,9 +75,7 @@ public AzureQueueStorageImpl build() { if (this.url != null) { client.url(this.url); } - if (this.version != null) { - client.version(this.version); - } + client.version(this.version); return client; } } diff --git a/storage/client/queue/src/main/java/com/azure/storage/queue/implementation/MessageIdsImpl.java b/storage/client/queue/src/main/java/com/azure/storage/queue/implementation/MessageIdsImpl.java index 8a705fe7e4af7..f5e6c952f5de1 100644 --- a/storage/client/queue/src/main/java/com/azure/storage/queue/implementation/MessageIdsImpl.java +++ b/storage/client/queue/src/main/java/com/azure/storage/queue/implementation/MessageIdsImpl.java @@ -10,6 +10,7 @@ import com.azure.core.annotations.HeaderParam; import com.azure.core.annotations.Host; import com.azure.core.annotations.HostParam; +import com.azure.core.annotations.PathParam; import com.azure.core.annotations.PUT; import com.azure.core.annotations.QueryParam; import com.azure.core.annotations.Service; @@ -57,12 +58,12 @@ private interface MessageIdsService { @PUT("{queueName}/messages/{messageid}") @ExpectedResponses({204}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono update(@HostParam("url") String url, @BodyParam("application/xml; charset=utf-8") QueueMessage queueMessage, @QueryParam("popreceipt") String popReceipt, @QueryParam("visibilitytimeout") int visibilitytimeout, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-client-request-id") String requestId, Context context); + Mono update(@HostParam("url") String url, @PathParam("queueName") String queueName, @PathParam("messageid") String messageId, @BodyParam("application/xml; charset=utf-8") QueueMessage queueMessage, @QueryParam("popreceipt") String popReceipt, @QueryParam("visibilitytimeout") int visibilitytimeout, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-client-request-id") String requestId, Context context); @DELETE("{queueName}/messages/{messageid}") @ExpectedResponses({204}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono delete(@HostParam("url") String url, @QueryParam("popreceipt") String popReceipt, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-client-request-id") String requestId, Context context); + Mono delete(@HostParam("url") String url, @PathParam("queueName") String queueName, @PathParam("messageid") String messageId, @QueryParam("popreceipt") String popReceipt, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-client-request-id") String requestId, Context context); } /** @@ -75,10 +76,10 @@ private interface MessageIdsService { * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono updateWithRestResponseAsync(QueueMessage queueMessage, String popReceipt, int visibilitytimeout, Context context) { + public Mono updateWithRestResponseAsync(String queueName, String messageId, QueueMessage queueMessage, String popReceipt, int visibilitytimeout, Context context) { final Integer timeout = null; final String requestId = null; - return service.update(this.client.url(), queueMessage, popReceipt, visibilitytimeout, timeout, this.client.version(), requestId, context); + return service.update(this.client.url(), queueName, messageId, queueMessage, popReceipt, visibilitytimeout, timeout, this.client.version(), requestId, context); } /** @@ -93,8 +94,8 @@ public Mono updateWithRestResponseAsync(QueueMessage q * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono updateWithRestResponseAsync(QueueMessage queueMessage, String popReceipt, int visibilitytimeout, Integer timeout, String requestId, Context context) { - return service.update(this.client.url(), queueMessage, popReceipt, visibilitytimeout, timeout, this.client.version(), requestId, context); + public Mono updateWithRestResponseAsync(String queueName, String messageId, QueueMessage queueMessage, String popReceipt, int visibilitytimeout, Integer timeout, String requestId, Context context) { + return service.update(this.client.url(), queueName, messageId, queueMessage, popReceipt, visibilitytimeout, timeout, this.client.version(), requestId, context); } /** @@ -105,10 +106,10 @@ public Mono updateWithRestResponseAsync(QueueMessage q * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono deleteWithRestResponseAsync(String popReceipt, Context context) { + public Mono deleteWithRestResponseAsync(String queueName, String messageId, String popReceipt, Context context) { final Integer timeout = null; final String requestId = null; - return service.delete(this.client.url(), popReceipt, timeout, this.client.version(), requestId, context); + return service.delete(this.client.url(), queueName, messageId, popReceipt, timeout, this.client.version(), requestId, context); } /** @@ -121,7 +122,7 @@ public Mono deleteWithRestResponseAsync(String popRece * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono deleteWithRestResponseAsync(String popReceipt, Integer timeout, String requestId, Context context) { - return service.delete(this.client.url(), popReceipt, timeout, this.client.version(), requestId, context); + public Mono deleteWithRestResponseAsync(String queueName, String messageId, String popReceipt, Integer timeout, String requestId, Context context) { + return service.delete(this.client.url(), queueName, messageId, popReceipt, timeout, this.client.version(), requestId, context); } } diff --git a/storage/client/queue/src/main/java/com/azure/storage/queue/implementation/MessagesImpl.java b/storage/client/queue/src/main/java/com/azure/storage/queue/implementation/MessagesImpl.java index ee59aae16b63f..84f680cad1dba 100644 --- a/storage/client/queue/src/main/java/com/azure/storage/queue/implementation/MessagesImpl.java +++ b/storage/client/queue/src/main/java/com/azure/storage/queue/implementation/MessagesImpl.java @@ -11,6 +11,7 @@ import com.azure.core.annotations.HeaderParam; import com.azure.core.annotations.Host; import com.azure.core.annotations.HostParam; +import com.azure.core.annotations.PathParam; import com.azure.core.annotations.POST; import com.azure.core.annotations.QueryParam; import com.azure.core.annotations.Service; @@ -60,22 +61,22 @@ private interface MessagesService { @GET("{queueName}/messages") @ExpectedResponses({200}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono dequeue(@HostParam("url") String url, @QueryParam("numofmessages") Integer numberOfMessages, @QueryParam("visibilitytimeout") Integer visibilitytimeout, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-client-request-id") String requestId, Context context); + Mono dequeue(@HostParam("url") String url, @PathParam("queueName") String queueName, @QueryParam("numofmessages") Integer numberOfMessages, @QueryParam("visibilitytimeout") Integer visibilitytimeout, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-client-request-id") String requestId, Context context); @DELETE("{queueName}/messages") @ExpectedResponses({204}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono clear(@HostParam("url") String url, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-client-request-id") String requestId, Context context); + Mono clear(@HostParam("url") String url, @PathParam("queueName") String queueName, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-client-request-id") String requestId, Context context); @POST("{queueName}/messages") @ExpectedResponses({201}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono enqueue(@HostParam("url") String url, @BodyParam("application/xml; charset=utf-8") QueueMessage queueMessage, @QueryParam("visibilitytimeout") Integer visibilitytimeout, @QueryParam("messagettl") Integer messageTimeToLive, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-client-request-id") String requestId, Context context); + Mono enqueue(@HostParam("url") String url, @PathParam("queueName") String queueName, @BodyParam("application/xml; charset=utf-8") QueueMessage queueMessage, @QueryParam("visibilitytimeout") Integer visibilitytimeout, @QueryParam("messagettl") Integer messageTimeToLive, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-client-request-id") String requestId, Context context); @GET("{queueName}/messages") @ExpectedResponses({200}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono peek(@HostParam("url") String url, @QueryParam("numofmessages") Integer numberOfMessages, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-client-request-id") String requestId, @QueryParam("peekonly") String peekonly, Context context); + Mono peek(@HostParam("url") String url, @PathParam("queueName") String queueName, @QueryParam("numofmessages") Integer numberOfMessages, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-client-request-id") String requestId, @QueryParam("peekonly") String peekonly, Context context); } /** @@ -85,12 +86,12 @@ private interface MessagesService { * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono dequeueWithRestResponseAsync(Context context) { + public Mono dequeueWithRestResponseAsync(String queueName, Context context) { final Integer numberOfMessages = null; final Integer visibilitytimeout = null; final Integer timeout = null; final String requestId = null; - return service.dequeue(this.client.url(), numberOfMessages, visibilitytimeout, timeout, this.client.version(), requestId, context); + return service.dequeue(this.client.url(), queueName, numberOfMessages, visibilitytimeout, timeout, this.client.version(), requestId, context); } /** @@ -104,8 +105,8 @@ public Mono dequeueWithRestResponseAsync(Context contex * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono dequeueWithRestResponseAsync(Integer numberOfMessages, Integer visibilitytimeout, Integer timeout, String requestId, Context context) { - return service.dequeue(this.client.url(), numberOfMessages, visibilitytimeout, timeout, this.client.version(), requestId, context); + public Mono dequeueWithRestResponseAsync(String queueName, Integer numberOfMessages, Integer visibilitytimeout, Integer timeout, String requestId, Context context) { + return service.dequeue(this.client.url(), queueName, numberOfMessages, visibilitytimeout, timeout, this.client.version(), requestId, context); } /** @@ -115,10 +116,10 @@ public Mono dequeueWithRestResponseAsync(Integer number * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono clearWithRestResponseAsync(Context context) { + public Mono clearWithRestResponseAsync(String queueName, Context context) { final Integer timeout = null; final String requestId = null; - return service.clear(this.client.url(), timeout, this.client.version(), requestId, context); + return service.clear(this.client.url(), queueName, timeout, this.client.version(), requestId, context); } /** @@ -130,8 +131,8 @@ public Mono clearWithRestResponseAsync(Context context) { * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono clearWithRestResponseAsync(Integer timeout, String requestId, Context context) { - return service.clear(this.client.url(), timeout, this.client.version(), requestId, context); + public Mono clearWithRestResponseAsync(String queueName, Integer timeout, String requestId, Context context) { + return service.clear(this.client.url(), queueName, timeout, this.client.version(), requestId, context); } /** @@ -142,12 +143,12 @@ public Mono clearWithRestResponseAsync(Integer timeout, S * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono enqueueWithRestResponseAsync(QueueMessage queueMessage, Context context) { + public Mono enqueueWithRestResponseAsync(String queueName, QueueMessage queueMessage, Context context) { final Integer visibilitytimeout = null; final Integer messageTimeToLive = null; final Integer timeout = null; final String requestId = null; - return service.enqueue(this.client.url(), queueMessage, visibilitytimeout, messageTimeToLive, timeout, this.client.version(), requestId, context); + return service.enqueue(this.client.url(), queueName, queueMessage, visibilitytimeout, messageTimeToLive, timeout, this.client.version(), requestId, context); } /** @@ -162,8 +163,8 @@ public Mono enqueueWithRestResponseAsync(QueueMessage q * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono enqueueWithRestResponseAsync(QueueMessage queueMessage, Integer visibilitytimeout, Integer messageTimeToLive, Integer timeout, String requestId, Context context) { - return service.enqueue(this.client.url(), queueMessage, visibilitytimeout, messageTimeToLive, timeout, this.client.version(), requestId, context); + public Mono enqueueWithRestResponseAsync(String queueName, QueueMessage queueMessage, Integer visibilitytimeout, Integer messageTimeToLive, Integer timeout, String requestId, Context context) { + return service.enqueue(this.client.url(), queueName, queueMessage, visibilitytimeout, messageTimeToLive, timeout, this.client.version(), requestId, context); } /** @@ -173,12 +174,12 @@ public Mono enqueueWithRestResponseAsync(QueueMessage q * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono peekWithRestResponseAsync(Context context) { + public Mono peekWithRestResponseAsync(String queueName, Context context) { final Integer numberOfMessages = null; final Integer timeout = null; final String requestId = null; final String peekonly = "true"; - return service.peek(this.client.url(), numberOfMessages, timeout, this.client.version(), requestId, peekonly, context); + return service.peek(this.client.url(), queueName, numberOfMessages, timeout, this.client.version(), requestId, peekonly, context); } /** @@ -191,8 +192,8 @@ public Mono peekWithRestResponseAsync(Context context) { * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono peekWithRestResponseAsync(Integer numberOfMessages, Integer timeout, String requestId, Context context) { + public Mono peekWithRestResponseAsync(String queueName, Integer numberOfMessages, Integer timeout, String requestId, Context context) { final String peekonly = "true"; - return service.peek(this.client.url(), numberOfMessages, timeout, this.client.version(), requestId, peekonly, context); + return service.peek(this.client.url(), queueName, numberOfMessages, timeout, this.client.version(), requestId, peekonly, context); } } diff --git a/storage/client/queue/src/main/java/com/azure/storage/queue/implementation/QueuesImpl.java b/storage/client/queue/src/main/java/com/azure/storage/queue/implementation/QueuesImpl.java index 2eb2d4d1ec2e2..db933d31e4238 100644 --- a/storage/client/queue/src/main/java/com/azure/storage/queue/implementation/QueuesImpl.java +++ b/storage/client/queue/src/main/java/com/azure/storage/queue/implementation/QueuesImpl.java @@ -11,6 +11,7 @@ import com.azure.core.annotations.HeaderParam; import com.azure.core.annotations.Host; import com.azure.core.annotations.HostParam; +import com.azure.core.annotations.PathParam; import com.azure.core.annotations.PUT; import com.azure.core.annotations.QueryParam; import com.azure.core.annotations.Service; @@ -25,10 +26,9 @@ import com.azure.storage.queue.models.QueuesSetMetadataResponse; import com.azure.storage.queue.models.SignedIdentifier; import com.azure.storage.queue.models.StorageErrorException; -import reactor.core.publisher.Mono; - import java.util.List; import java.util.Map; +import reactor.core.publisher.Mono; /** * An instance of this class provides access to all the operations defined in @@ -65,32 +65,32 @@ private interface QueuesService { @PUT("{queueName}") @ExpectedResponses({201, 204}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono create(@HostParam("url") String url, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-meta-") Map metadata, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-client-request-id") String requestId, Context context); + Mono create(@HostParam("url") String url, @PathParam("queueName") String queueName, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-meta-") Map metadata, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-client-request-id") String requestId, Context context); @DELETE("{queueName}") @ExpectedResponses({204}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono delete(@HostParam("url") String url, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-client-request-id") String requestId, Context context); + Mono delete(@HostParam("url") String url, @PathParam("queueName") String queueName, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-client-request-id") String requestId, Context context); @GET("{queueName}") @ExpectedResponses({200}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono getProperties(@HostParam("url") String url, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-client-request-id") String requestId, @QueryParam("comp") String comp, Context context); + Mono getProperties(@HostParam("url") String url, @PathParam("queueName") String queueName, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-client-request-id") String requestId, @QueryParam("comp") String comp, Context context); @PUT("{queueName}") @ExpectedResponses({204}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono setMetadata(@HostParam("url") String url, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-meta-") Map metadata, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-client-request-id") String requestId, @QueryParam("comp") String comp, Context context); + Mono setMetadata(@HostParam("url") String url, @PathParam("queueName") String queueName, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-meta-") Map metadata, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-client-request-id") String requestId, @QueryParam("comp") String comp, Context context); @GET("{queueName}") @ExpectedResponses({200}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono getAccessPolicy(@HostParam("url") String url, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-client-request-id") String requestId, @QueryParam("comp") String comp, Context context); + Mono getAccessPolicy(@HostParam("url") String url, @PathParam("queueName") String queueName, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-client-request-id") String requestId, @QueryParam("comp") String comp, Context context); @PUT("{queueName}") @ExpectedResponses({204}) @UnexpectedResponseExceptionType(StorageErrorException.class) - Mono setAccessPolicy(@HostParam("url") String url, @BodyParam("application/xml; charset=utf-8") SignedIdentifiersWrapper queueAcl, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-client-request-id") String requestId, @QueryParam("comp") String comp, Context context); + Mono setAccessPolicy(@HostParam("url") String url, @PathParam("queueName") String queueName, @BodyParam("application/xml; charset=utf-8") SignedIdentifiersWrapper queueAcl, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-client-request-id") String requestId, @QueryParam("comp") String comp, Context context); } /** @@ -100,11 +100,11 @@ private interface QueuesService { * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono createWithRestResponseAsync(Context context) { + public Mono createWithRestResponseAsync(String queueName, Context context) { final Integer timeout = null; final Map metadata = null; final String requestId = null; - return service.create(this.client.url(), timeout, metadata, this.client.version(), requestId, context); + return service.create(this.client.url(), queueName, timeout, metadata, this.client.version(), requestId, context); } /** @@ -117,8 +117,8 @@ public Mono createWithRestResponseAsync(Context context) { * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono createWithRestResponseAsync(Integer timeout, Map metadata, String requestId, Context context) { - return service.create(this.client.url(), timeout, metadata, this.client.version(), requestId, context); + public Mono createWithRestResponseAsync(String queueName, Integer timeout, Map metadata, String requestId, Context context) { + return service.create(this.client.url(), queueName, timeout, metadata, this.client.version(), requestId, context); } /** @@ -128,10 +128,10 @@ public Mono createWithRestResponseAsync(Integer timeout, M * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono deleteWithRestResponseAsync(Context context) { + public Mono deleteWithRestResponseAsync(String queueName, Context context) { final Integer timeout = null; final String requestId = null; - return service.delete(this.client.url(), timeout, this.client.version(), requestId, context); + return service.delete(this.client.url(), queueName, timeout, this.client.version(), requestId, context); } /** @@ -143,8 +143,8 @@ public Mono deleteWithRestResponseAsync(Context context) { * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono deleteWithRestResponseAsync(Integer timeout, String requestId, Context context) { - return service.delete(this.client.url(), timeout, this.client.version(), requestId, context); + public Mono deleteWithRestResponseAsync(String queueName, Integer timeout, String requestId, Context context) { + return service.delete(this.client.url(), queueName, timeout, this.client.version(), requestId, context); } /** @@ -154,11 +154,11 @@ public Mono deleteWithRestResponseAsync(Integer timeout, S * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono getPropertiesWithRestResponseAsync(Context context) { + public Mono getPropertiesWithRestResponseAsync(String queueName, Context context) { final Integer timeout = null; final String requestId = null; final String comp = "metadata"; - return service.getProperties(this.client.url(), timeout, this.client.version(), requestId, comp, context); + return service.getProperties(this.client.url(), queueName, timeout, this.client.version(), requestId, comp, context); } /** @@ -170,9 +170,9 @@ public Mono getPropertiesWithRestResponseAsync(Cont * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono getPropertiesWithRestResponseAsync(Integer timeout, String requestId, Context context) { + public Mono getPropertiesWithRestResponseAsync(String queueName, Integer timeout, String requestId, Context context) { final String comp = "metadata"; - return service.getProperties(this.client.url(), timeout, this.client.version(), requestId, comp, context); + return service.getProperties(this.client.url(), queueName, timeout, this.client.version(), requestId, comp, context); } /** @@ -182,12 +182,12 @@ public Mono getPropertiesWithRestResponseAsync(Inte * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono setMetadataWithRestResponseAsync(Context context) { + public Mono setMetadataWithRestResponseAsync(String queueName, Context context) { final Integer timeout = null; final Map metadata = null; final String requestId = null; final String comp = "metadata"; - return service.setMetadata(this.client.url(), timeout, metadata, this.client.version(), requestId, comp, context); + return service.setMetadata(this.client.url(), queueName, timeout, metadata, this.client.version(), requestId, comp, context); } /** @@ -200,9 +200,9 @@ public Mono setMetadataWithRestResponseAsync(Context * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono setMetadataWithRestResponseAsync(Integer timeout, Map metadata, String requestId, Context context) { + public Mono setMetadataWithRestResponseAsync(String queueName, Integer timeout, Map metadata, String requestId, Context context) { final String comp = "metadata"; - return service.setMetadata(this.client.url(), timeout, metadata, this.client.version(), requestId, comp, context); + return service.setMetadata(this.client.url(), queueName, timeout, metadata, this.client.version(), requestId, comp, context); } /** @@ -212,11 +212,11 @@ public Mono setMetadataWithRestResponseAsync(Integer * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono getAccessPolicyWithRestResponseAsync(Context context) { + public Mono getAccessPolicyWithRestResponseAsync(String queueName, Context context) { final Integer timeout = null; final String requestId = null; final String comp = "acl"; - return service.getAccessPolicy(this.client.url(), timeout, this.client.version(), requestId, comp, context); + return service.getAccessPolicy(this.client.url(), queueName, timeout, this.client.version(), requestId, comp, context); } /** @@ -228,9 +228,9 @@ public Mono getAccessPolicyWithRestResponseAsync( * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono getAccessPolicyWithRestResponseAsync(Integer timeout, String requestId, Context context) { + public Mono getAccessPolicyWithRestResponseAsync(String queueName, Integer timeout, String requestId, Context context) { final String comp = "acl"; - return service.getAccessPolicy(this.client.url(), timeout, this.client.version(), requestId, comp, context); + return service.getAccessPolicy(this.client.url(), queueName, timeout, this.client.version(), requestId, comp, context); } /** @@ -240,12 +240,12 @@ public Mono getAccessPolicyWithRestResponseAsync( * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono setAccessPolicyWithRestResponseAsync(Context context) { + public Mono setAccessPolicyWithRestResponseAsync(String queueName, Context context) { final Integer timeout = null; final String requestId = null; final String comp = "acl"; SignedIdentifiersWrapper queueAclConverted = new SignedIdentifiersWrapper(null); - return service.setAccessPolicy(this.client.url(), queueAclConverted, timeout, this.client.version(), requestId, comp, context); + return service.setAccessPolicy(this.client.url(), queueName, queueAclConverted, timeout, this.client.version(), requestId, comp, context); } /** @@ -258,9 +258,9 @@ public Mono setAccessPolicyWithRestResponseAsync( * @throws IllegalArgumentException thrown if parameters fail the validation. * @return a Mono which performs the network request upon subscription. */ - public Mono setAccessPolicyWithRestResponseAsync(List queueAcl, Integer timeout, String requestId, Context context) { + public Mono setAccessPolicyWithRestResponseAsync(String queueName, List queueAcl, Integer timeout, String requestId, Context context) { final String comp = "acl"; SignedIdentifiersWrapper queueAclConverted = new SignedIdentifiersWrapper(queueAcl); - return service.setAccessPolicy(this.client.url(), queueAclConverted, timeout, this.client.version(), requestId, comp, context); + return service.setAccessPolicy(this.client.url(), queueName, queueAclConverted, timeout, this.client.version(), requestId, comp, context); } } diff --git a/storage/client/queue/src/main/java/com/azure/storage/queue/implementation/ServicesImpl.java b/storage/client/queue/src/main/java/com/azure/storage/queue/implementation/ServicesImpl.java index dc353d45f5243..1dc20d2b42fa8 100644 --- a/storage/client/queue/src/main/java/com/azure/storage/queue/implementation/ServicesImpl.java +++ b/storage/client/queue/src/main/java/com/azure/storage/queue/implementation/ServicesImpl.java @@ -25,9 +25,8 @@ import com.azure.storage.queue.models.ServicesSetPropertiesResponse; import com.azure.storage.queue.models.StorageErrorException; import com.azure.storage.queue.models.StorageServiceProperties; -import reactor.core.publisher.Mono; - import java.util.List; +import reactor.core.publisher.Mono; /** * An instance of this class provides access to all the operations defined in diff --git a/storage/client/queue/src/main/java/com/azure/storage/queue/models/DequeuedMessageItem.java b/storage/client/queue/src/main/java/com/azure/storage/queue/models/DequeuedMessage.java similarity index 85% rename from storage/client/queue/src/main/java/com/azure/storage/queue/models/DequeuedMessageItem.java rename to storage/client/queue/src/main/java/com/azure/storage/queue/models/DequeuedMessage.java index 7febd2c02758d..65e6e941c717a 100644 --- a/storage/client/queue/src/main/java/com/azure/storage/queue/models/DequeuedMessageItem.java +++ b/storage/client/queue/src/main/java/com/azure/storage/queue/models/DequeuedMessage.java @@ -1,6 +1,5 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -// Code generated by Microsoft (R) AutoRest Code Generator. package com.azure.storage.queue.models; @@ -14,7 +13,7 @@ * on a Queue. */ @JacksonXmlRootElement(localName = "QueueMessage") -public final class DequeuedMessageItem { +public final class DequeuedMessage { /* * The Id of the Message. */ @@ -71,9 +70,9 @@ public String messageId() { * Set the messageId property: The Id of the Message. * * @param messageId the messageId value to set. - * @return the DequeuedMessageItem object itself. + * @return the DequeuedMessage object itself. */ - public DequeuedMessageItem messageId(String messageId) { + public DequeuedMessage messageId(String messageId) { this.messageId = messageId; return this; } @@ -96,9 +95,9 @@ public OffsetDateTime insertionTime() { * the Queue. * * @param insertionTime the insertionTime value to set. - * @return the DequeuedMessageItem object itself. + * @return the DequeuedMessage object itself. */ - public DequeuedMessageItem insertionTime(OffsetDateTime insertionTime) { + public DequeuedMessage insertionTime(OffsetDateTime insertionTime) { if (insertionTime == null) { this.insertionTime = null; } else { @@ -125,9 +124,9 @@ public OffsetDateTime expirationTime() { * and be automatically deleted. * * @param expirationTime the expirationTime value to set. - * @return the DequeuedMessageItem object itself. + * @return the DequeuedMessage object itself. */ - public DequeuedMessageItem expirationTime(OffsetDateTime expirationTime) { + public DequeuedMessage expirationTime(OffsetDateTime expirationTime) { if (expirationTime == null) { this.expirationTime = null; } else { @@ -153,9 +152,9 @@ public String popReceipt() { * been dequeued by another client. * * @param popReceipt the popReceipt value to set. - * @return the DequeuedMessageItem object itself. + * @return the DequeuedMessage object itself. */ - public DequeuedMessageItem popReceipt(String popReceipt) { + public DequeuedMessage popReceipt(String popReceipt) { this.popReceipt = popReceipt; return this; } @@ -178,9 +177,9 @@ public OffsetDateTime timeNextVisible() { * become visible in the Queue. * * @param timeNextVisible the timeNextVisible value to set. - * @return the DequeuedMessageItem object itself. + * @return the DequeuedMessage object itself. */ - public DequeuedMessageItem timeNextVisible(OffsetDateTime timeNextVisible) { + public DequeuedMessage timeNextVisible(OffsetDateTime timeNextVisible) { if (timeNextVisible == null) { this.timeNextVisible = null; } else { @@ -204,9 +203,9 @@ public long dequeueCount() { * dequeued. * * @param dequeueCount the dequeueCount value to set. - * @return the DequeuedMessageItem object itself. + * @return the DequeuedMessage object itself. */ - public DequeuedMessageItem dequeueCount(long dequeueCount) { + public DequeuedMessage dequeueCount(long dequeueCount) { this.dequeueCount = dequeueCount; return this; } @@ -224,9 +223,9 @@ public String messageText() { * Set the messageText property: The content of the Message. * * @param messageText the messageText value to set. - * @return the DequeuedMessageItem object itself. + * @return the DequeuedMessage object itself. */ - public DequeuedMessageItem messageText(String messageText) { + public DequeuedMessage messageText(String messageText) { this.messageText = messageText; return this; } diff --git a/storage/client/queue/src/main/java/com/azure/storage/queue/models/MessagesDequeueResponse.java b/storage/client/queue/src/main/java/com/azure/storage/queue/models/MessagesDequeueResponse.java index 4ab4b09a4a620..c08c7ccc025c7 100644 --- a/storage/client/queue/src/main/java/com/azure/storage/queue/models/MessagesDequeueResponse.java +++ b/storage/client/queue/src/main/java/com/azure/storage/queue/models/MessagesDequeueResponse.java @@ -12,7 +12,7 @@ /** * Contains all response data for the dequeue operation. */ -public final class MessagesDequeueResponse extends ResponseBase> { +public final class MessagesDequeueResponse extends ResponseBase> { /** * Creates an instance of MessagesDequeueResponse. * @@ -22,7 +22,7 @@ public final class MessagesDequeueResponse extends ResponseBase value, MessagesDequeueHeaders headers) { + public MessagesDequeueResponse(HttpRequest request, int statusCode, HttpHeaders rawHeaders, List value, MessagesDequeueHeaders headers) { super(request, statusCode, rawHeaders, value, headers); } @@ -30,7 +30,7 @@ public MessagesDequeueResponse(HttpRequest request, int statusCode, HttpHeaders * @return the deserialized response body. */ @Override - public List value() { + public List value() { return super.value(); } } diff --git a/storage/client/queue/src/main/java/com/azure/storage/queue/models/MessagesPeekResponse.java b/storage/client/queue/src/main/java/com/azure/storage/queue/models/MessagesPeekResponse.java index 8ca8ed06fd99c..27ac646138908 100644 --- a/storage/client/queue/src/main/java/com/azure/storage/queue/models/MessagesPeekResponse.java +++ b/storage/client/queue/src/main/java/com/azure/storage/queue/models/MessagesPeekResponse.java @@ -12,7 +12,7 @@ /** * Contains all response data for the peek operation. */ -public final class MessagesPeekResponse extends ResponseBase> { +public final class MessagesPeekResponse extends ResponseBase> { /** * Creates an instance of MessagesPeekResponse. * @@ -22,7 +22,7 @@ public final class MessagesPeekResponse extends ResponseBase value, MessagesPeekHeaders headers) { + public MessagesPeekResponse(HttpRequest request, int statusCode, HttpHeaders rawHeaders, List value, MessagesPeekHeaders headers) { super(request, statusCode, rawHeaders, value, headers); } @@ -30,7 +30,7 @@ public MessagesPeekResponse(HttpRequest request, int statusCode, HttpHeaders raw * @return the deserialized response body. */ @Override - public List value() { + public List value() { return super.value(); } } diff --git a/storage/client/queue/src/main/java/com/azure/storage/queue/models/PeekedMessageItem.java b/storage/client/queue/src/main/java/com/azure/storage/queue/models/PeekedMessage.java similarity index 85% rename from storage/client/queue/src/main/java/com/azure/storage/queue/models/PeekedMessageItem.java rename to storage/client/queue/src/main/java/com/azure/storage/queue/models/PeekedMessage.java index d86bdab8c2305..edc9960d2d747 100644 --- a/storage/client/queue/src/main/java/com/azure/storage/queue/models/PeekedMessageItem.java +++ b/storage/client/queue/src/main/java/com/azure/storage/queue/models/PeekedMessage.java @@ -1,6 +1,5 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -// Code generated by Microsoft (R) AutoRest Code Generator. package com.azure.storage.queue.models; @@ -14,7 +13,7 @@ * on a Queue. */ @JacksonXmlRootElement(localName = "QueueMessage") -public final class PeekedMessageItem { +public final class PeekedMessage { /* * The Id of the Message. */ @@ -58,9 +57,9 @@ public String messageId() { * Set the messageId property: The Id of the Message. * * @param messageId the messageId value to set. - * @return the PeekedMessageItem object itself. + * @return the PeekedMessage object itself. */ - public PeekedMessageItem messageId(String messageId) { + public PeekedMessage messageId(String messageId) { this.messageId = messageId; return this; } @@ -83,9 +82,9 @@ public OffsetDateTime insertionTime() { * the Queue. * * @param insertionTime the insertionTime value to set. - * @return the PeekedMessageItem object itself. + * @return the PeekedMessage object itself. */ - public PeekedMessageItem insertionTime(OffsetDateTime insertionTime) { + public PeekedMessage insertionTime(OffsetDateTime insertionTime) { if (insertionTime == null) { this.insertionTime = null; } else { @@ -112,9 +111,9 @@ public OffsetDateTime expirationTime() { * and be automatically deleted. * * @param expirationTime the expirationTime value to set. - * @return the PeekedMessageItem object itself. + * @return the PeekedMessage object itself. */ - public PeekedMessageItem expirationTime(OffsetDateTime expirationTime) { + public PeekedMessage expirationTime(OffsetDateTime expirationTime) { if (expirationTime == null) { this.expirationTime = null; } else { @@ -138,9 +137,9 @@ public long dequeueCount() { * dequeued. * * @param dequeueCount the dequeueCount value to set. - * @return the PeekedMessageItem object itself. + * @return the PeekedMessage object itself. */ - public PeekedMessageItem dequeueCount(long dequeueCount) { + public PeekedMessage dequeueCount(long dequeueCount) { this.dequeueCount = dequeueCount; return this; } @@ -158,9 +157,9 @@ public String messageText() { * Set the messageText property: The content of the Message. * * @param messageText the messageText value to set. - * @return the PeekedMessageItem object itself. + * @return the PeekedMessage object itself. */ - public PeekedMessageItem messageText(String messageText) { + public PeekedMessage messageText(String messageText) { this.messageText = messageText; return this; } diff --git a/storage/client/queue/src/main/java/com/azure/storage/queue/models/QueueProperties.java b/storage/client/queue/src/main/java/com/azure/storage/queue/models/QueueProperties.java new file mode 100644 index 0000000000000..bb343ea96bd24 --- /dev/null +++ b/storage/client/queue/src/main/java/com/azure/storage/queue/models/QueueProperties.java @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.queue.models; + +import java.util.Map; + +/** + * Model class containing properties of a specific queue in the storage Queue service. + */ +public final class QueueProperties { + private final Map metadata; + private final int approximateMessagesCount; + + /** + * Creates an instance that contains properties of a queue. + * + * @param metadata Metadata associated with the queue + * @param approximateMessagesCount Approximate number of messages contained in the queue + */ + public QueueProperties(Map metadata, int approximateMessagesCount) { + this.metadata = metadata; + this.approximateMessagesCount = approximateMessagesCount; + } + + /** + * @return the user-defined metadata associated with the queue + */ + public Map metadata() { + return this.metadata; + } + + /** + * @return the approximate number of messages contained in the queue at the time of properties retrieval + */ + public int approximateMessagesCount() { + return approximateMessagesCount; + } +} diff --git a/storage/client/queue/src/main/java/com/azure/storage/queue/models/QueuesSegmentOptions.java b/storage/client/queue/src/main/java/com/azure/storage/queue/models/QueuesSegmentOptions.java new file mode 100644 index 0000000000000..d58b2f71b8d05 --- /dev/null +++ b/storage/client/queue/src/main/java/com/azure/storage/queue/models/QueuesSegmentOptions.java @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.queue.models; + +import com.azure.storage.queue.QueueServiceAsyncClient; +import com.azure.storage.queue.QueueServiceClient; + +/** + * A set of options for selecting queues from Storage Queue service. + * + *
                    + *
                  • + * Providing {@link QueuesSegmentOptions#prefix() prefix} will filter {@link QueueItem queues} that begin + * with the prefix. + *
                  • + *
                  • + * Providing {@link QueuesSegmentOptions#maxResults() maxResults} will limit the number of {@link QueueItem queues} + * returned in a single page. + *
                  • + *
                  • + * Setting {@link QueuesSegmentOptions#includeMetadata() includeMetadata} to true will include the metadata of + * each {@link QueueItem queue}, if false {@link QueueItem#metadata() metadata} for each queue will be {@code null}. + *
                  • + *
                  + * + * @see QueueServiceClient + * @see QueueServiceAsyncClient + */ +public final class QueuesSegmentOptions { + private boolean includeMetadata; + + private String prefix; + + private Integer maxResults; + + /** + * @return the status of including metadata when listing queues + */ + public boolean includeMetadata() { + return includeMetadata; + } + + /** + * Sets the status of including queue metadata when listing queues + * + * @param includeMetadata Flag indicating if metadata should be included in the listing + * @return An updated QueuesSegmentOptions object + */ + public QueuesSegmentOptions includeMetadata(boolean includeMetadata) { + this.includeMetadata = includeMetadata; + return this; + } + + /** + * @return the prefix the queue name must match to be included in the listing + */ + public String prefix() { + return prefix; + } + + /** + * Sets the prefix that a queue must match to be included in the listing + * + * @param prefix The prefix that queues must start with to pass the filter + * @return An updated QueuesSegmentOptions object + */ + public QueuesSegmentOptions prefix(String prefix) { + this.prefix = prefix; + return this; + } + + /** + * @return the maximum number of queues to include in a single response + */ + public Integer maxResults() { + return maxResults; + } + + /** + * Sets the maximum number of queues to include in a single response + * + * @param maxResults Maximum number of results to include in a single response + * @return An updated QueuesSegmentOptions object + */ + public QueuesSegmentOptions maxResults(Integer maxResults) { + this.maxResults = maxResults; + return this; + } +} diff --git a/storage/client/queue/src/main/java/com/azure/storage/queue/models/UpdatedMessage.java b/storage/client/queue/src/main/java/com/azure/storage/queue/models/UpdatedMessage.java new file mode 100644 index 0000000000000..fa6a4d5970153 --- /dev/null +++ b/storage/client/queue/src/main/java/com/azure/storage/queue/models/UpdatedMessage.java @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.queue.models; + +import java.time.OffsetDateTime; + +/** + * Response from the service when updating a message. Contains the information needed to continue working with + * the specific message. + */ +public final class UpdatedMessage { + private final String popReceipt; + private final OffsetDateTime timeNextVisible; + + /** + * Creates an instance of updated message information. + * + * @param popReceipt Unique identifier that verifies the operation on a message is valid + * @param timeNextVisible The next time the message will be visible to other operations in the queue + */ + public UpdatedMessage(String popReceipt, OffsetDateTime timeNextVisible) { + this.popReceipt = popReceipt; + this.timeNextVisible = timeNextVisible; + } + + /** + * @return the unique identifier used to verify that the operation is allowed on the message + */ + public String popReceipt() { + return popReceipt; + } + + /** + * @return the next time the message will be visible to other operations in the queue + */ + public OffsetDateTime timeNextVisible() { + return timeNextVisible; + } +} diff --git a/storage/client/queue/src/main/java/com/azure/storage/queue/package-info.java b/storage/client/queue/src/main/java/com/azure/storage/queue/package-info.java new file mode 100644 index 0000000000000..e6671d8292fac --- /dev/null +++ b/storage/client/queue/src/main/java/com/azure/storage/queue/package-info.java @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/** + * This package contains the classes to perform actions on Azure Storage Queue. + */ +package com.azure.storage.queue; diff --git a/storage/client/queue/src/samples/java/com/azure/storage/queue/AsyncSamples.java b/storage/client/queue/src/samples/java/com/azure/storage/queue/AsyncSamples.java new file mode 100644 index 0000000000000..39cc17db37ca2 --- /dev/null +++ b/storage/client/queue/src/samples/java/com/azure/storage/queue/AsyncSamples.java @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.queue; + +import static com.azure.storage.queue.SampleHelper.generateRandomName; + +/* + * This example mimics some arbitrary number of clients continuously sending messages up to a queue in a parallel and + * a server dequeuing the messages and processing them. + */ +public class AsyncSamples { + private static final String ACCOUNT_NAME = System.getenv("AZURE_STORAGE_ACCOUNT_NAME"); + private static final String SAS_TOKEN = System.getenv("PRIMARY_SAS_TOKEN"); + private static final String QUEUE_NAME = generateRandomName("async-call", 16); + + /** + * The main method shows how we do the basic operations of enqueueing and dequeueing messages on async queue client. + * @param args No args needed for main method. + */ + public static void main(String[] args) { + // Create an async queue client. + String queueURL = String.format("https://%s.queue.core.windows.net/%s%s", ACCOUNT_NAME, QUEUE_NAME, SAS_TOKEN); + QueueAsyncClient queueAsyncClient = new QueueClientBuilder().endpoint(queueURL).buildAsyncClient(); + + // Create a queue, enqueue two messages. + queueAsyncClient.create() + .doOnSuccess(response -> queueAsyncClient.enqueueMessage("This is message 1")) + .then(queueAsyncClient.enqueueMessage("This is message 2")) + .subscribe( + response -> { + System.out.println("Message successfully equeueed by queueAsyncClient. Message id:" + response.value().messageId()); + }, + err -> { + System.out.println("Error thrown when enqueue the message. Error message: " + err.getMessage()); + }, + () -> { + System.out.println("The enqueue has been completed."); + } + ); + } +} diff --git a/storage/client/queue/src/samples/java/com/azure/storage/queue/MessageSamples.java b/storage/client/queue/src/samples/java/com/azure/storage/queue/MessageSamples.java new file mode 100644 index 0000000000000..1372d45b9e808 --- /dev/null +++ b/storage/client/queue/src/samples/java/com/azure/storage/queue/MessageSamples.java @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.queue; + +import com.azure.core.http.rest.Response; +import com.azure.storage.queue.models.DequeuedMessage; + +import java.time.Duration; + +import static com.azure.storage.queue.SampleHelper.generateRandomName; + +public class MessageSamples { + private static final String ACCOUNT_NAME = System.getenv("AZURE_STORAGE_ACCOUNT_NAME"); + private static final String SAS_TOKEN = System.getenv("PRIMARY_SAS_TOKEN"); + + /** + * The main method illustrate the basic operations for enqueue and dequeue messages using sync client. + * @param args No args needed for main method. + * @throws InterruptedException If the Thread.sleep operation gets interrupted. + */ + public static void main(String[] args) throws InterruptedException { + // Build Queue Client using SAS Token + String queueServiceURL = String.format("https://%s.queue.core.windows.net/%s", ACCOUNT_NAME, SAS_TOKEN); + QueueServiceClient queueServiceClient = new QueueServiceClientBuilder().endpoint(queueServiceURL).buildClient(); + + // Create a queue client + Response queueClientResponse = queueServiceClient.createQueue(generateRandomName("enqueue", 16)); + QueueClient queueClient = queueClientResponse.value(); + // Using queue client to enqueue several "Hello World" messages into queue. + for (int i = 0; i < 3; i++) { + queueClient.enqueueMessage("Hello World"); + } + + // Enqueue json file into message. + // TODO + + // Get the total count of msg in the queue + int count = queueClient.getProperties().value().approximateMessagesCount(); + + // Peek all messages in queue. It is supposed to print "Hello World" 3 times. + queueClient.peekMessages(count).forEach( + peekedMessage -> { + System.out.println("Here is the msg: " + peekedMessage.messageText()); + } + ); + + // Dequeue all messages in queue and update the message "Hello World" to Hello, world!" + queueClient.dequeueMessages(count, Duration.ZERO).forEach( + queueMessage -> { + String msgToReplace = String.format("Hello, world!"); + queueClient.updateMessage(queueMessage.messageId(), msgToReplace, queueMessage.popReceipt(), Duration.ZERO); + } + ); + + // Delete the first available msg. + // Since there is no invisible time for above dequeue, the following if condition should be true. + if (queueClient.dequeueMessages().iterator().hasNext()) { + DequeuedMessage queueMessage = queueClient.dequeueMessages().iterator().next(); + queueClient.deleteMessage(queueMessage.messageId(), queueMessage.popReceipt()); + } else { + System.out.println("OOps, the messages disappear!"); + } + + // Clear all messages in the queue + // Sleep to guarantee we skip the default invisible time. + Thread.sleep(500); + queueClient.clearMessages(); + + // Finally, we delete the queue. + queueClient.delete(); + } +} diff --git a/storage/client/queue/src/samples/java/com/azure/storage/queue/QueueExceptionSamples.java b/storage/client/queue/src/samples/java/com/azure/storage/queue/QueueExceptionSamples.java new file mode 100644 index 0000000000000..d0ee4c8f2aed4 --- /dev/null +++ b/storage/client/queue/src/samples/java/com/azure/storage/queue/QueueExceptionSamples.java @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.queue; + +import com.azure.core.http.rest.Response; +import com.azure.storage.queue.models.StorageErrorCode; +import com.azure.storage.queue.models.StorageErrorException; + +import static com.azure.storage.queue.SampleHelper.generateRandomName; + +public class QueueExceptionSamples { + private static final String ACCOUNT_NAME = System.getenv("AZURE_STORAGE_ACCOUNT_NAME"); + private static final String SAS_TOKEN = System.getenv("PRIMARY_SAS_TOKEN"); + + /** + * The main method shows how to handle the storage exception. + * @param args No args needed for the main method. + * @throws RuntimeException If queueServiceClient failed to create a queue. + */ + public static void main(String[] args) { + // Create a queue service client. + String queueServiceURL = String.format("https://%s.queue.core.windows.net/%s", ACCOUNT_NAME, SAS_TOKEN); + QueueServiceClient queueServiceClient = new QueueServiceClientBuilder().endpoint(queueServiceURL).buildClient(); + + // Create queue client. + Response queueClientResponse; + try { + queueClientResponse = queueServiceClient.createQueue(generateRandomName("delete-not-exist", 16)); + System.out.println("Successfully create the queue! Status code: " + String.valueOf(queueClientResponse.statusCode())); + } catch (StorageErrorException e) { + System.out.println(String.format("Error creating a queue. Error message: %s", e.value().message())); + throw new RuntimeException(e); + } + QueueClient queueClient = queueClientResponse.value(); + queueClient.enqueueMessage("Hello, message 1!"); + queueClient.enqueueMessage("Hello, message 2!"); + + // Delete message with wrong message id. + try { + queueClientResponse.value().dequeueMessages().forEach( + msg -> { + queueClient.deleteMessage("wrong id", msg.popReceipt()); + } + ); + } catch (StorageErrorException e) { + if (e.getMessage().contains(StorageErrorCode.MESSAGE_NOT_FOUND.toString())) { + System.out.println("This is the error expected to throw"); + } else { + System.out.println("This is not the error we expect!"); + } + } + + // Delete message with wrong pop receipt. + try { + queueClient.dequeueMessages().forEach( + msg -> { + queueClient.deleteMessage(msg.messageId(), "Wrong Pop Receipt"); + } + ); + } catch (StorageErrorException e) { + if (e.getMessage().contains(StorageErrorCode.INVALID_QUERY_PARAMETER_VALUE.toString())) { + System.out.println("This is the error expected to throw"); + } else { + System.out.println("This is not the error we expect!"); + } + } + } +} diff --git a/storage/client/queue/src/samples/java/com/azure/storage/queue/QueueServiceSamples.java b/storage/client/queue/src/samples/java/com/azure/storage/queue/QueueServiceSamples.java new file mode 100644 index 0000000000000..ed1a9875a3d03 --- /dev/null +++ b/storage/client/queue/src/samples/java/com/azure/storage/queue/QueueServiceSamples.java @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.queue; + +import static com.azure.storage.queue.SampleHelper.generateRandomName; + +public class QueueServiceSamples { + private static final String ACCOUNT_NAME = System.getenv("AZURE_STORAGE_ACCOUNT_NAME"); + private static final String SAS_TOKEN = System.getenv("PRIMARY_SAS_TOKEN"); + + /** + * The main method illustrated the basic operations of creating and deleting queues using queue service sync client. + * @param args No args needed for main method. + */ + public static void main(String[] args) { + // Build Queue Service Client using SAS Token + String queueServiceURL = String.format("https://%s.queue.core.windows.net/%s", ACCOUNT_NAME, SAS_TOKEN); + QueueServiceClient queueServiceClient = new QueueServiceClientBuilder().endpoint(queueServiceURL).buildClient(); + queueServiceClient.createQueue(generateRandomName("create-queue", 16)); + + // Create another queue and list all queues, print the name and then delete the queue. + queueServiceClient.createQueue(generateRandomName("create-extra", 16)); + queueServiceClient.listQueues().forEach( + queueItem -> { + System.out.println("The queue name is: " + queueItem.name()); + queueServiceClient.deleteQueue(queueItem.name()); + } + ); + } +} diff --git a/storage/client/queue/src/samples/java/com/azure/storage/queue/SampleHelper.java b/storage/client/queue/src/samples/java/com/azure/storage/queue/SampleHelper.java new file mode 100644 index 0000000000000..094a52026ffe6 --- /dev/null +++ b/storage/client/queue/src/samples/java/com/azure/storage/queue/SampleHelper.java @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.queue; + +import java.util.UUID; + +class SampleHelper { + static String generateRandomName(String prefix, int length) { + int len = length > prefix.length() ? length - prefix.length() : 0; + return prefix + UUID.randomUUID().toString().substring(0, len); + } +} diff --git a/storage/client/queue/src/samples/java/com/azure/storage/queue/javadoc/QueueJavaDocCodeSamples.java b/storage/client/queue/src/samples/java/com/azure/storage/queue/javadoc/QueueJavaDocCodeSamples.java new file mode 100644 index 0000000000000..24b0405a49350 --- /dev/null +++ b/storage/client/queue/src/samples/java/com/azure/storage/queue/javadoc/QueueJavaDocCodeSamples.java @@ -0,0 +1,668 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.queue.javadoc; + +import com.azure.core.http.rest.Response; +import com.azure.core.http.rest.VoidResponse; +import com.azure.storage.common.credentials.SASTokenCredential; +import com.azure.storage.common.credentials.SharedKeyCredential; +import com.azure.storage.queue.QueueAsyncClient; +import com.azure.storage.queue.QueueClient; +import com.azure.storage.queue.QueueClientBuilder; +import com.azure.storage.queue.models.AccessPolicy; +import com.azure.storage.queue.models.DequeuedMessage; +import com.azure.storage.queue.models.EnqueuedMessage; +import com.azure.storage.queue.models.QueueProperties; +import com.azure.storage.queue.models.SignedIdentifier; +import com.azure.storage.queue.models.UpdatedMessage; +import java.time.Duration; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Contains code snippets when generating javadocs through doclets for {@link QueueClient} and {@link QueueAsyncClient}. + */ + +public class QueueJavaDocCodeSamples { + /** + * Generates code sample for creating a {@link QueueClient}. + */ + public void buildQueueClient() { + // BEGIN: com.azure.storage.queue.queueClient.instantiation + QueueClient client = new QueueClientBuilder() + .connectionString("connectionstring") + .endpoint("endpoint") + .buildClient(); + // END: com.azure.storage.queue.queueClient.instantiation + } + + /** + * Generates code sample for creating a {@link QueueAsyncClient}. + */ + public void buildQueueAsyncClient() { + // BEGIN: com.azure.storage.queue.queueAsyncClient.instantiation + QueueAsyncClient client = new QueueClientBuilder() + .connectionString("connectionstring") + .endpoint("endpoint") + .buildAsyncClient(); + // END: com.azure.storage.queue.queueAsyncClient.instantiation + } + + /** + * Generates code sample for creating a {@link QueueClient} with {@link QueueClient} + * @return An instance of {@link QueueClient} + */ + public QueueClient createClientWithSASToken() { + // BEGIN: com.azure.storage.queue.queueClient.instantiation.sastoken + QueueClient queueClient = new QueueClientBuilder() + .endpoint("https://${accountName}.queue.core.windows.net?${SASToken}") + .buildClient(); + // END: com.azure.storage.queue.queueClient.instantiation.sastoken + return queueClient; + } + + /** + * Generates code sample for creating a {@link QueueAsyncClient} with {@link SASTokenCredential} + * @return An instance of {@link QueueAsyncClient} + */ + public QueueAsyncClient createAsyncClientWithSASToken() { + // BEGIN: com.azure.storage.queue.queueAsyncClient.instantiation.sastoken + QueueAsyncClient queueAsyncClient = new QueueClientBuilder() + .endpoint("https://{accountName}.queue.core.windows.net?{SASToken}") + .buildAsyncClient(); + // END: com.azure.storage.queue.queueAsyncClient.instantiation.sastoken + return queueAsyncClient; + } + + /** + * Generates code sample for creating a {@link QueueClient} with {@link SASTokenCredential} + * @return An instance of {@link QueueClient} + */ + public QueueClient createClientWithCredential() { + // BEGIN: com.azure.storage.queue.queueClient.instantiation.credential + QueueClient queueClient = new QueueClientBuilder() + .endpoint("https://${accountName}.queue.core.windows.net") + .queueName("myqueue") + .credential(SASTokenCredential.fromQuery("{SASTokenQueryParams}")) + .buildClient(); + // END: com.azure.storage.queue.queueClient.instantiation.credential + return queueClient; + } + + /** + * Generates code sample for creating a {@link QueueAsyncClient} with {@link SASTokenCredential} + * @return An instance of {@link QueueAsyncClient} + */ + public QueueAsyncClient createAsyncClientWithCredential() { + // BEGIN: com.azure.storage.queue.queueAsyncClient.instantiation.credential + QueueAsyncClient queueAsyncClient = new QueueClientBuilder() + .endpoint("https://{accountName}.queue.core.windows.net") + .queueName("myqueue") + .credential(SASTokenCredential.fromQuery("{SASTokenQueryParams}")) + .buildAsyncClient(); + // END: com.azure.storage.queue.queueAsyncClient.instantiation.credential + return queueAsyncClient; + } + + /** + * Generates code sample for creating a {@link QueueClient} with {@code connectionString} which turns into {@link SharedKeyCredential} + * @return An instance of {@link QueueClient} + */ + public QueueClient createClientWithConnectionString() { + // BEGIN: com.azure.storage.queue.queueClient.instantiation.connectionstring + String connectionString = "DefaultEndpointsProtocol=https;AccountName={name};" + + "AccountKey={key};EndpointSuffix={core.windows.net}"; + QueueClient queueClient = new QueueClientBuilder() + .connectionString(connectionString) + .buildClient(); + // END: com.azure.storage.queue.queueClient.instantiation.connectionstring + return queueClient; + } + + /** + * Generates code sample for creating a {@link QueueAsyncClient} with {@code connectionString} which turns into {@link SharedKeyCredential} + * @return An instance of {@link QueueAsyncClient} + */ + public QueueAsyncClient createAsyncClientWithConnectionString() { + // BEGIN: com.azure.storage.queue.queueAsyncClient.instantiation.connectionstring + String connectionString = "DefaultEndpointsProtocol=https;AccountName={name};" + + "AccountKey={key};EndpointSuffix={core.windows.net}"; + QueueAsyncClient queueAsyncClient = new QueueClientBuilder() + .connectionString(connectionString) + .buildAsyncClient(); + // END: com.azure.storage.queue.queueAsyncClient.instantiation.connectionstring + return queueAsyncClient; + } + + /** + * Generates a code sample for using {@link QueueClient#create()} + */ + public void createQueue() { + QueueClient queueClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueClient.create + VoidResponse response = queueClient.create(); + System.out.println("Complete creating queue with status code: " + response.statusCode()); + // END: com.azure.storage.queue.queueClient.create + } + + /** + * Generates a code sample for using {@link QueueAsyncClient#create()} + */ + public void createQueueAsync() { + QueueAsyncClient queueAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueAsyncClient.create + queueAsyncClient.create().subscribe( + response -> { }, + error -> System.err.print(error.toString()), + () -> System.out.println("Complete creating the queue!") + ); + // END: com.azure.storage.queue.queueAsyncClient.create + } + + /** + * Generates a code sample for using {@link QueueClient#create(Map)} + */ + public void createQueueMaxOverload() { + QueueClient queueClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueClient.create#map + VoidResponse response = queueClient.create(Collections.singletonMap("queue", "metadataMap")); + System.out.println("Complete creating queue with status code: " + response.statusCode()); + // END: com.azure.storage.queue.queueClient.create#map + } + + /** + * Generates a code sample for using {@link QueueAsyncClient#create(Map)} + */ + public void createQueueAsyncMaxOverload() { + QueueAsyncClient queueAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueAsyncClient.create#map + queueAsyncClient.create(Collections.singletonMap("queue", "metadataMap")).subscribe( + response -> { }, + error -> System.err.print(error.toString()), + () -> System.out.println("Complete creating the queue!") + ); + // END: com.azure.storage.queue.queueAsyncClient.create#map + } + + /** + * Generates a code sample for using {@link QueueClient#enqueueMessage(String)} + */ + public void enqueueMessage() { + QueueClient queueClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueClient.enqueueMessage#string + Response response = queueClient.enqueueMessage("hello msg"); + System.out.println("Complete enqueuing the message with status code: " + response.statusCode()); + // END: com.azure.storage.queue.queueClient.enqueueMessage#string + } + + /** + * Generates a code sample for using {@link QueueAsyncClient#enqueueMessage(String)} + */ + public void enqueueMessageAsync() { + QueueAsyncClient queueAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueAsyncClient.enqueueMessage#string + queueAsyncClient.enqueueMessage("Hello, Azure").subscribe( + response -> { }, + error -> System.err.print(error.toString()), + () -> System.out.println("Complete enqueuing the message!") + ); + // END: com.azure.storage.queue.queueAsyncClient.enqueueMessage#string + } + + /** + * Generates a code sample for using {@link QueueClient#enqueueMessage(String, Duration, Duration)} + */ + public void enqueueMessageWithTimeoutOverload() { + QueueClient queueClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueClient.enqueueMessage#string-duration-duration + EnqueuedMessage enqueuedMessage = queueClient.enqueueMessage("Hello, Azure", + Duration.ofSeconds(5), null).value(); + System.out.printf("Message %s expires at %s", enqueuedMessage.messageId(), enqueuedMessage.expirationTime()); + // END: com.azure.storage.queue.queueClient.enqueueMessage#string-duration-duration + } + + /** + * Generates a code sample for using {@link QueueAsyncClient#enqueueMessage(String, Duration, Duration)} + */ + public void enqueueMessageAsyncWithTimeoutOverload() { + QueueAsyncClient queueAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueAsyncClient.enqueueMessage#string-duration-duration + queueAsyncClient.enqueueMessage("Hello, Azure", + Duration.ofSeconds(5), null).subscribe( + response -> System.out.printf("Message %s expires at %s", response.value().messageId(), + response.value().expirationTime()), + error -> System.err.print(error.toString()), + () -> System.out.println("Complete enqueuing the message!") + ); + // END: com.azure.storage.queue.queueAsyncClient.enqueueMessage#string-duration-duration + } + + /** + * Generates a code sample for using {@link QueueClient#enqueueMessage(String, Duration, Duration)} + */ + public void enqueueMessageWithLiveTimeOverload() { + QueueClient queueClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueClient.enqueueMessageLiveTime#string-duration-duration + EnqueuedMessage enqueuedMessage = queueClient.enqueueMessage("Goodbye, Azure", + null, Duration.ofSeconds(5)).value(); + System.out.printf("Message %s expires at %s", enqueuedMessage.messageId(), enqueuedMessage.expirationTime()); + // END: com.azure.storage.queue.queueClient.enqueueMessageLiveTime#string-duration-duration + } + + /** + * Generates a code sample for using {@link QueueAsyncClient#enqueueMessage(String, Duration, Duration)} + */ + public void enqueueMessageAsyncWithLiveTimeOverload() { + QueueAsyncClient queueAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueAsyncClient.enqueueMessageLiveTime#string-duration-duration + queueAsyncClient.enqueueMessage("Goodbye, Azure", + null, Duration.ofSeconds(5)).subscribe( + response -> System.out.printf("Message %s expires at %s", response.value().messageId(), + response.value().expirationTime()), + error -> System.err.print(error.toString()), + () -> System.out.println("Complete enqueuing the message!") + ); + // END: com.azure.storage.queue.queueAsyncClient.enqueueMessageLiveTime#string-duration-duration + } + + /** + * Generates a code sample for using {@link QueueClient#dequeueMessages()} + */ + public void dequeueMessage() { + QueueClient queueClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueClient.dequeueMessages + queueClient.dequeueMessages().forEach( + dequeuedMessage -> { + System.out.println("Complete dequeuing the message: " + dequeuedMessage.messageText()); + } + ); + // END: com.azure.storage.queue.queueClient.dequeueMessages + } + + + /** + * Generates a code sample for using {@link QueueAsyncClient#dequeueMessages()} + */ + public void dequeueMessageAsync() { + QueueAsyncClient queueAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueAsyncClient.dequeueMessages + queueAsyncClient.dequeueMessages().subscribe( + dequeuedMessage -> System.out.println("The message got from dequeue operation: " + + dequeuedMessage.messageText()), + error -> System.err.print(error.toString()), + () -> System.out.println("Complete dequeuing the message!") + ); + // END: com.azure.storage.queue.queueAsyncClient.dequeueMessages + } + + /** + * Generates a code sample for using {@link QueueClient#dequeueMessages(Integer)} + */ + public void dequeueMessageWithOverload() { + QueueClient queueClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueClient.dequeueMessages#integer + for (DequeuedMessage dequeuedMessage : queueClient.dequeueMessages(5)) { + System.out.printf("Dequeued %s and it becomes visible at %s", + dequeuedMessage.messageId(), dequeuedMessage.timeNextVisible()); + } + // END: com.azure.storage.queue.queueClient.dequeueMessages#integer + } + + /** + * Generates a code sample for using {@link QueueAsyncClient#dequeueMessages(Integer)} + */ + public void dequeueMessageAsyncWithOverload() { + QueueAsyncClient queueAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueAsyncClient.dequeueMessages#integer + queueAsyncClient.dequeueMessages(5).subscribe( + dequeuedMessage -> System.out.println("The message got from dequeue operation: " + + dequeuedMessage.messageText()), + error -> System.err.print(error.toString()), + () -> System.out.println("Complete dequeuing the message!") + ); + // END: com.azure.storage.queue.queueAsyncClient.dequeueMessages#integer + } + + /** + * Generates a code sample for using {@link QueueClient#dequeueMessages(Integer, Duration)} + */ + public void dequeueMessageMaxOverload() { + QueueClient queueClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueClient.dequeueMessages#integer-duration + for (DequeuedMessage dequeuedMessage : queueClient.dequeueMessages(5, Duration.ofSeconds(60))) { + System.out.printf("Dequeued %s and it becomes visible at %s", + dequeuedMessage.messageId(), dequeuedMessage.timeNextVisible()); + } + // END: com.azure.storage.queue.queueClient.dequeueMessages#integer-duration + } + + /** + * Generates a code sample for using {@link QueueAsyncClient#dequeueMessages(Integer, Duration)} + */ + public void dequeueMessageAsyncMaxOverload() { + QueueAsyncClient queueAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueAsyncClient.dequeueMessages#integer-duration + queueAsyncClient.dequeueMessages(5, Duration.ofSeconds(60)).subscribe( + dequeuedMessage -> System.out.println("The message got from dequeue operation: " + + dequeuedMessage.messageText()), + error -> System.err.print(error.toString()), + () -> System.out.println("Complete dequeuing the message!") + ); + // END: com.azure.storage.queue.queueAsyncClient.dequeueMessages#integer-duration + } + + /** + * Generates a code sample for using {@link QueueClient#peekMessages()} + */ + public void peekMessage() { + QueueClient queueClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueClient.peekMessages + queueClient.peekMessages().forEach( + peekedMessage -> { + System.out.println("Complete peeking the message: " + peekedMessage.messageText()); + } + ); + // END: com.azure.storage.queue.queueClient.peekMessages + } + + + /** + * Generates a code sample for using {@link QueueAsyncClient#peekMessages()} + */ + public void peekMessageAsync() { + QueueAsyncClient queueAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueAsyncClient.peekMessages + queueAsyncClient.peekMessages().subscribe( + peekMessages -> System.out.println("The message got from peek operation: " + peekMessages.messageText()), + error -> System.err.print(error.toString()), + () -> System.out.println("Complete peeking the message!") + ); + // END: com.azure.storage.queue.queueAsyncClient.peekMessages + } + + /** + * Generates a code sample for using {@link QueueClient#peekMessages(Integer)} + */ + public void peekMessageMaxOverload() { + QueueClient queueClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueClient.peekMessages#integer + queueClient.peekMessages(5).forEach( + peekMessage -> System.out.printf("Peeked message %s has been dequeued %d times", + peekMessage.messageId(), peekMessage.dequeueCount()) + ); + // END: com.azure.storage.queue.queueClient.peekMessages#integer + } + + + /** + * Generates a code sample for using {@link QueueAsyncClient#peekMessages(Integer)} + */ + public void peekMessageAsyncMaxOverload() { + QueueAsyncClient queueAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueAsyncClient.peekMessages#integer + queueAsyncClient.peekMessages(5).subscribe( + peekMessage -> System.out.printf("Peeked message %s has been dequeued %d times", + peekMessage.messageId(), peekMessage.dequeueCount()), + error -> System.err.print(error.toString()), + () -> System.out.println("Complete peeking the message!") + ); + // END: com.azure.storage.queue.queueAsyncClient.peekMessages#integer + } + + /** + * Generates a code sample for using {@link QueueClient#updateMessage(String, String, String, Duration)} + */ + public void updateMessage() { + QueueClient queueClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueClient.updateMessage + queueClient.dequeueMessages().forEach( + dequeuedMessage -> { + Response response = queueClient.updateMessage("newText", + dequeuedMessage.messageId(), dequeuedMessage.popReceipt(), null); + System.out.println("Complete updating the message with status code " + response.statusCode()); + } + ); + // END: com.azure.storage.queue.queueClient.updateMessage + } + + + /** + * Generates a code sample for using {@link QueueAsyncClient#updateMessage(String, String, String, Duration)} + */ + public void updateMessageAsync() { + QueueAsyncClient queueAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueAsyncClient.updateMessage + queueAsyncClient.dequeueMessages().subscribe( + dequeuedMessage -> { + queueAsyncClient.updateMessage("newText", dequeuedMessage.messageId(), + dequeuedMessage.popReceipt(), null).subscribe( + response -> { }, + updateError -> System.err.print(updateError.toString()), + () -> System.out.println("Complete updating the message!") + ); + }, + dequeueError -> System.err.print(dequeueError.toString()), + () -> System.out.println("Complete dequeueing the message!") + ); + // END: com.azure.storage.queue.queueAsyncClient.updateMessage + } + + /** + * Generates a code sample for using {@link QueueClient#deleteMessage(String, String)} + */ + public void deleteMessage() { + QueueClient queueClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueClient.deleteMessage + queueClient.dequeueMessages().forEach( + dequeuedMessage -> { + VoidResponse response = queueClient.deleteMessage(dequeuedMessage.messageId(), + dequeuedMessage.popReceipt()); + System.out.println("Complete deleting the message with status code " + response.statusCode()); + } + ); + // END: com.azure.storage.queue.queueClient.deleteMessage + } + + + /** + * Generates a code sample for using {@link QueueAsyncClient#deleteMessage(String, String)} + */ + public void deleteMessageAsync() { + QueueAsyncClient queueAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueAsyncClient.deleteMessage + queueAsyncClient.dequeueMessages().subscribe( + dequeuedMessage -> { + queueAsyncClient.deleteMessage(dequeuedMessage.messageId(), dequeuedMessage.popReceipt()).subscribe( + response -> { }, + deleteError -> System.err.print(deleteError.toString()), + () -> System.out.println("Complete deleting the message!") + ); + }, + dequeueError -> System.err.print(dequeueError.toString()), + () -> System.out.println("Complete dequeueing the message!") + ); + // END: com.azure.storage.queue.queueAsyncClient.deleteMessage + } + + /** + * Generates a code sample for using {@link QueueClient#delete()} + */ + public void deleteQueue() { + QueueClient queueClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueClient.delete + VoidResponse response = queueClient.delete(); + System.out.println("Complete deleting the queue with status code: " + response.statusCode()); + // END: com.azure.storage.queue.queueClient.delete + } + + + /** + * Generates a code sample for using {@link QueueAsyncClient#delete()} + */ + public void deleteQueueAsync() { + QueueAsyncClient queueAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueAsyncClient.delete + queueAsyncClient.delete().subscribe( + response -> System.out.println("Deleting the queue completed with status code: " + response.statusCode()) + ); + // END: com.azure.storage.queue.queueAsyncClient.delete + } + + /** + * Generates a code sample for using {@link QueueClient#getProperties()} + */ + public void getProperties() { + QueueClient queueClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueClient.getProperties + QueueProperties properties = queueClient.getProperties().value(); + System.out.printf("Metadata: %s, Approximate message count: %d", properties.metadata(), + properties.approximateMessagesCount()); + // END: com.azure.storage.queue.queueClient.getProperties + } + + /** + * Generates a code sample for using {@link QueueAsyncClient#getProperties()} + */ + public void getPropertiesAsync() { + QueueAsyncClient queueAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueAsyncClient.getProperties + queueAsyncClient.getProperties() + .subscribe(response -> { + QueueProperties properties = response.value(); + System.out.printf("Metadata: %s, Approximate message count: %d", properties.metadata(), + properties.approximateMessagesCount()); + }); + // END: com.azure.storage.queue.queueAsyncClient.getProperties + } + + /** + * Generate a code sample for using {@link QueueClient#setMetadata(Map)} to set metadata. + */ + public void setMetadata() { + QueueClient queueClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueClient.setMetadata#map + VoidResponse response = queueClient.setMetadata(Collections.singletonMap("queue", "metadataMap")); + System.out.printf("Setting metadata completed with status code %d", response.statusCode()); + // END: com.azure.storage.queue.queueClient.setMetadata#map + } + + /** + * Generate a code sample for using {@link QueueAsyncClient#setMetadata(Map)} to set metadata. + */ + public void setMetadataAsync() { + QueueAsyncClient queueAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueAsyncClient.setMetadata#map + queueAsyncClient.setMetadata(Collections.singletonMap("queue", "metadataMap")) + .subscribe(response -> System.out.printf("Setting metadata completed with status code %d", + response.statusCode())); + // END: com.azure.storage.queue.queueAsyncClient.setMetadata#map + } + + /** + * Generate a code sample for using {@link QueueClient#setMetadata(Map)} to clear metadata. + */ + public void clearMetadata() { + QueueClient queueClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueClient.clearMetadata#map + VoidResponse response = queueClient.setMetadata(null); + System.out.printf("Clearing metadata completed with status code %d", response.statusCode()); + // END: com.azure.storage.queue.queueClient.clearMetadata#map + } + + /** + * Generate a code sample for using {@link QueueAsyncClient#setMetadata(Map)} to clear metadata. + */ + public void clearMetadataAsync() { + QueueAsyncClient queueAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueAsyncClient.clearMetadata#map + queueAsyncClient.setMetadata(null) + .subscribe(response -> System.out.printf("Clearing metadata completed with status code %d", + response.statusCode())); + // END: com.azure.storage.queue.queueAsyncClient.clearMetadata#map + } + + /** + * Generates a code sample for using {@link QueueClient#getAccessPolicy()} + */ + public void getAccessPolicy() { + QueueClient queueClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueClient.getAccessPolicy + for (SignedIdentifier permission : queueClient.getAccessPolicy()) { + System.out.printf("Access policy %s allows these permissions: %s", permission.id(), + permission.accessPolicy().permission()); + } + // END: com.azure.storage.queue.queueClient.getAccessPolicy + } + + /** + * Generates a code sample for using {@link QueueAsyncClient#getAccessPolicy()} + */ + public void getAccessPolicyAsync() { + QueueAsyncClient queueAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueAsyncClient.getAccessPolicy + queueAsyncClient.getAccessPolicy() + .subscribe(result -> System.out.printf("Access policy %s allows these permissions: %s", + result.id(), result.accessPolicy().permission())); + // END: com.azure.storage.queue.queueAsyncClient.getAccessPolicy + } + + /** + * Generates a code sample for using {@link QueueClient#setAccessPolicy(List)} + */ + public void setAccessPolicy() { + QueueClient queueClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueClient.setAccessPolicy + AccessPolicy accessPolicy = new AccessPolicy().permission("r") + .start(OffsetDateTime.now(ZoneOffset.UTC)) + .expiry(OffsetDateTime.now(ZoneOffset.UTC).plusDays(10)); + SignedIdentifier permission = new SignedIdentifier().id("mypolicy").accessPolicy(accessPolicy); + VoidResponse response = queueClient.setAccessPolicy(Collections.singletonList(permission)); + System.out.printf("Setting access policies completed with status code %d", response.statusCode()); + // END: com.azure.storage.queue.queueClient.setAccessPolicy + } + + /** + * Generates a code sample for using {@link QueueAsyncClient#setAccessPolicy(List)} + */ + public void setAccessPolicyAsync() { + QueueAsyncClient queueAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueAsyncClient.setAccessPolicy + AccessPolicy accessPolicy = new AccessPolicy().permission("r") + .start(OffsetDateTime.now(ZoneOffset.UTC)) + .expiry(OffsetDateTime.now(ZoneOffset.UTC).plusDays(10)); + + SignedIdentifier permission = new SignedIdentifier().id("mypolicy").accessPolicy(accessPolicy); + queueAsyncClient.setAccessPolicy(Collections.singletonList(permission)) + .subscribe(response -> System.out.printf("Setting access policies completed with status code %d", + response.statusCode())); + // END: com.azure.storage.queue.queueAsyncClient.setAccessPolicy + } + + /** + * Generates a code sample for using {@link QueueClient#clearMessages()} + */ + public void clearMessages() { + QueueClient queueClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueClient.clearMessages + VoidResponse response = queueClient.clearMessages(); + System.out.printf("Clearing messages completed with status code %d", response.statusCode()); + // END: com.azure.storage.queue.queueClient.clearMessages + } + + + /** + * Generates a code sample for using {@link QueueAsyncClient#clearMessages()} + */ + public void clearMessagesAsync() { + QueueAsyncClient queueAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueAsyncClient.clearMessages + queueAsyncClient.clearMessages().subscribe( + response -> System.out.println("Clearing messages completed with status code: " + response.statusCode()) + ); + // END: com.azure.storage.queue.queueAsyncClient.clearMessages + } + +} diff --git a/storage/client/queue/src/samples/java/com/azure/storage/queue/javadoc/QueueServiceJavaDocCodeSamples.java b/storage/client/queue/src/samples/java/com/azure/storage/queue/javadoc/QueueServiceJavaDocCodeSamples.java new file mode 100644 index 0000000000000..3ed03621fbf76 --- /dev/null +++ b/storage/client/queue/src/samples/java/com/azure/storage/queue/javadoc/QueueServiceJavaDocCodeSamples.java @@ -0,0 +1,369 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.queue.javadoc; + +import com.azure.core.http.rest.Response; +import com.azure.core.http.rest.VoidResponse; +import com.azure.storage.common.credentials.SASTokenCredential; +import com.azure.storage.common.credentials.SharedKeyCredential; +import com.azure.storage.queue.QueueClient; +import com.azure.storage.queue.QueueServiceAsyncClient; +import com.azure.storage.queue.QueueServiceClient; +import com.azure.storage.queue.QueueServiceClientBuilder; +import com.azure.storage.queue.models.QueuesSegmentOptions; +import com.azure.storage.queue.models.StorageServiceProperties; +import com.azure.storage.queue.models.StorageServiceStats; +import java.util.Collections; +import java.util.Map; + +/** + * Contains code snippets when generating javadocs through doclets for {@link QueueServiceClient} and {@link QueueServiceAsyncClient}. + */ +public class QueueServiceJavaDocCodeSamples { + /** + * Generates code sample for creating a {@link QueueServiceClient}. + */ + public void buildQueueServiceClient() { + // BEGIN: com.azure.storage.queue.queueServiceClient.instantiation + QueueServiceClient client = new QueueServiceClientBuilder() + .connectionString("connectionstring") + .endpoint("endpoint") + .buildClient(); + // END: com.azure.storage.queue.queueServiceClient.instantiation + } + + /** + * Generates code sample for creating a {@link QueueServiceAsyncClient}. + */ + public void buildQueueServiceAsyncClient() { + // BEGIN: com.azure.storage.queue.queueServiceAsyncClient.instantiation + QueueServiceAsyncClient client = new QueueServiceClientBuilder() + .connectionString("connectionstring") + .endpoint("endpoint") + .buildAsyncClient(); + // END: com.azure.storage.queue.queueServiceAsyncClient.instantiation + } + + /** + * Generates code sample for creating a {@link QueueServiceClient} with {@link SASTokenCredential} + * @return An instance of {@link QueueServiceClient} + */ + public QueueServiceClient createClientWithSASToken() { + // BEGIN: com.azure.storage.queue.queueServiceClient.instantiation.sastoken + QueueServiceClient queueServiceClient = new QueueServiceClientBuilder() + .endpoint("https://${accountName}.queue.core.windows.net?${SASToken}") + .buildClient(); + // END: com.azure.storage.queue.queueServiceClient.instantiation.sastoken + return queueServiceClient; + } + + /** + * Generates code sample for creating a {@link QueueServiceAsyncClient} with {@link SASTokenCredential} + * @return An instance of {@link QueueServiceAsyncClient} + */ + public QueueServiceAsyncClient createAsyncClientWithSASToken() { + // BEGIN: com.azure.storage.queue.queueServiceAsyncClient.instantiation.sastoken + QueueServiceAsyncClient queueServiceAsyncClient = new QueueServiceClientBuilder() + .endpoint("https://{accountName}.queue.core.windows.net?{SASToken}") + .buildAsyncClient(); + // END: com.azure.storage.queue.queueServiceAsyncClient.instantiation.sastoken + return queueServiceAsyncClient; + } + + /** + * Generates code sample for creating a {@link QueueServiceClient} with {@link SASTokenCredential} + * @return An instance of {@link QueueServiceClient} + */ + public QueueServiceClient createClientWithCredential() { + // BEGIN: com.azure.storage.queue.queueServiceClient.instantiation.credential + QueueServiceClient queueServiceClient = new QueueServiceClientBuilder() + .endpoint("https://${accountName}.queue.core.windows.net") + .credential(SASTokenCredential.fromQuery("{SASTokenQueryParams}")) + .buildClient(); + // END: com.azure.storage.queue.queueServiceClient.instantiation.credential + return queueServiceClient; + } + + /** + * Generates code sample for creating a {@link QueueServiceAsyncClient} with {@link SASTokenCredential} + * @return An instance of {@link QueueServiceAsyncClient} + */ + public QueueServiceAsyncClient createAsyncClientWithCredential() { + // BEGIN: com.azure.storage.queue.queueServiceAsyncClient.instantiation.credential + QueueServiceAsyncClient queueServiceAsyncClient = new QueueServiceClientBuilder() + .endpoint("https://{accountName}.queue.core.windows.net") + .credential(SASTokenCredential.fromQuery("{SASTokenQueryParams}")) + .buildAsyncClient(); + // END: com.azure.storage.queue.queueServiceAsyncClient.instantiation.credential + return queueServiceAsyncClient; + } + + /** + * Generates code sample for creating a {@link QueueServiceClient} with {@code connectionString} which turns into {@link SharedKeyCredential} + * @return An instance of {@link QueueServiceClient} + */ + public QueueServiceClient createClientWithConnectionString() { + // BEGIN: com.azure.storage.queue.queueServiceClient.instantiation.connectionstring + String connectionString = "DefaultEndpointsProtocol=https;AccountName={name};" + + "AccountKey={key};EndpointSuffix={core.windows.net}"; + QueueServiceClient queueServiceClient = new QueueServiceClientBuilder() + .connectionString(connectionString) + .buildClient(); + // END: com.azure.storage.queue.queueServiceClient.instantiation.connectionstring + return queueServiceClient; + } + + /** + * Generates code sample for creating a {@link QueueServiceAsyncClient} with {@code connectionString} which turns into {@link SharedKeyCredential} + * @return An instance of {@link QueueServiceAsyncClient} + */ + public QueueServiceAsyncClient createAsyncClientWithConnectionString() { + // BEGIN: com.azure.storage.queue.queueServiceAsyncClient.instantiation.connectionstring + String connectionString = "DefaultEndpointsProtocol=https;AccountName={name};" + + "AccountKey={key};EndpointSuffix={core.windows.net}"; + QueueServiceAsyncClient queueServiceAsyncClient = new QueueServiceClientBuilder() + .connectionString(connectionString) + .buildAsyncClient(); + // END: com.azure.storage.queue.queueServiceAsyncClient.instantiation.connectionstring + return queueServiceAsyncClient; + } + + /** + * Generates a code sample for using {@link QueueServiceClient#createQueue(String)} + */ + public void createQueue() { + QueueServiceClient queueServiceClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueServiceClient.createQueue#string + Response response = queueServiceClient.createQueue("myqueue"); + System.out.println("Complete creating queue with status code: " + response.statusCode()); + // END: com.azure.storage.queue.queueServiceClient.createQueue#string + } + + /** + * Generates a code sample for using {@link QueueServiceAsyncClient#createQueue(String)} + */ + public void createQueueAsync() { + QueueServiceAsyncClient queueServiceAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueServiceAsyncClient.createQueue#string + queueServiceAsyncClient.createQueue("myqueue").subscribe( + response -> { }, + error -> System.err.print(error.toString()), + () -> System.out.println("Complete creating the queue!") + ); + // END: com.azure.storage.queue.queueServiceAsyncClient.createQueue#string + } + + /** + * Generates a code sample for using {@link QueueServiceClient#createQueue(String, Map)} + */ + public void createQueueMaxOverload() { + QueueServiceClient queueServiceClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueServiceClient.createQueue#string-map + Response response = queueServiceClient.createQueue("myqueue", + Collections.singletonMap("queue", "metadata")); + System.out.println("Complete creating queue with status code: " + response.statusCode()); + // END: com.azure.storage.queue.queueServiceClient.createQueue#string-map + } + + /** + * Generates a code sample for using {@link QueueServiceAsyncClient#createQueue(String, Map)} + */ + public void createQueueAsyncMaxOverload() { + QueueServiceAsyncClient queueServiceAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueServiceAsyncClient.createQueue#string-map + queueServiceAsyncClient.createQueue("myqueue", Collections.singletonMap("queue", "metadata")) + .subscribe( + response -> System.out.printf("Creating the queue with status code %d", response.statusCode()), + error -> System.err.print(error.toString()), + () -> System.out.println("Complete creating the queue!") + ); + // END: com.azure.storage.queue.queueServiceAsyncClient.createQueue#string-map + } + + /** + * Generates a code sample for using {@link QueueServiceClient#listQueues()} + */ + public void listQueues() { + QueueServiceClient queueServiceClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueServiceClient.listQueues + queueServiceClient.listQueues().forEach( + queueItem -> System.out.printf("Queue %s exists in the account", queueItem.name()) + ); + // END: com.azure.storage.queue.queueServiceClient.listQueues + } + + /** + * Generates a code sample for using {@link QueueServiceAsyncClient#listQueues()} + */ + public void listQueuesAsync() { + QueueServiceAsyncClient queueServiceAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueServiceAsyncClient.listQueues + queueServiceAsyncClient.listQueues().subscribe( + queueItem -> System.out.printf("Queue %s exists in the account", queueItem.name()), + error -> System.err.print(error.toString()), + () -> System.out.println("Complete listing the queues!") + ); + // END: com.azure.storage.queue.queueServiceAsyncClient.listQueues + } + + /** + * Generates a code sample for using {@link QueueServiceClient#listQueues(QueuesSegmentOptions)} )} + */ + public void listQueuesWithOverload() { + QueueServiceClient queueServiceClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueServiceClient.listQueues#queueSergmentOptions + queueServiceClient.listQueues(new QueuesSegmentOptions().prefix("azure")).forEach( + queueItem -> System.out.printf("Queue %s exists in the account and has metadata %s", + queueItem.name(), queueItem.metadata()) + ); + // END: com.azure.storage.queue.queueServiceClient.listQueues#queueSergmentOptions + } + + /** + * Generates a code sample for using {@link QueueServiceAsyncClient#listQueues(QueuesSegmentOptions)} + */ + public void listQueuesAsyncWithOverload() { + QueueServiceAsyncClient queueServiceAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueServiceAsyncClient.listQueues#queueSergmentOptions + queueServiceAsyncClient.listQueues(new QueuesSegmentOptions().prefix("azure")).subscribe( + queueItem -> System.out.printf("Queue %s exists in the account and has metadata %s", + queueItem.name(), queueItem.metadata()), + error -> System.err.print(error.toString()), + () -> System.out.println("Complete listing the queues!") + ); + // END: com.azure.storage.queue.queueServiceAsyncClient.listQueues#queueSergmentOptions + } + + /** + * Generates a code sample for using {@link QueueServiceClient#deleteQueue(String)} + */ + public void deleteQueue() { + QueueServiceClient queueServiceClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueServiceClient.deleteQueue#string + VoidResponse response = queueServiceClient.deleteQueue("myqueue"); + System.out.println("Complete deleting the queue with status code: " + response.statusCode()); + // END: com.azure.storage.queue.queueServiceClient.deleteQueue#string + } + + /** + * Generates a code sample for using {@link QueueServiceAsyncClient#deleteQueue(String)} + */ + public void deleteQueueAsync() { + QueueServiceAsyncClient queueServiceAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueServiceAsyncClient.deleteQueue#string + queueServiceAsyncClient.deleteQueue("myshare").subscribe( + response -> System.out.println("Deleting the queue completed with status code: " + response.statusCode()) + ); + // END: com.azure.storage.queue.queueServiceAsyncClient.deleteQueue#string + } + + /** + * Generates a code sample for using {@link QueueServiceClient#getProperties()} + */ + public void getProperties() { + QueueServiceClient queueServiceClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueServiceClient.getProperties + StorageServiceProperties properties = queueServiceClient.getProperties().value(); + System.out.printf("Hour metrics enabled: %b, Minute metrics enabled: %b", + properties.hourMetrics().enabled(), properties.minuteMetrics().enabled()); + // END: com.azure.storage.queue.queueServiceClient.getProperties + } + + /** + * Generates a code sample for using {@link QueueServiceAsyncClient#getProperties()} + */ + public void getPropertiesAsync() { + QueueServiceAsyncClient queueServiceAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueServiceAsyncClient.getProperties + queueServiceAsyncClient.getProperties() + .subscribe(response -> { + StorageServiceProperties properties = response.value(); + System.out.printf("Hour metrics enabled: %b, Minute metrics enabled: %b", + properties.hourMetrics().enabled(), properties.minuteMetrics().enabled()); + }); + // END: com.azure.storage.queue.queueServiceAsyncClient.getProperties + } + + /** + * Generates a code sample for using {@link QueueServiceClient#setProperties(StorageServiceProperties)} + */ + public void setProperties() { + QueueServiceClient queueServiceClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueServiceClient.setProperties#storageServiceProperties + StorageServiceProperties properties = queueServiceClient.getProperties().value(); + properties.cors(Collections.emptyList()); + + VoidResponse response = queueServiceClient.setProperties(properties); + System.out.printf("Setting Queue service properties completed with status code %d", response.statusCode()); + // END: com.azure.storage.queue.queueServiceClient.setProperties#storageServiceProperties + } + + /** + * Generates a code sample for using {@link QueueServiceAsyncClient#setProperties(StorageServiceProperties)} + */ + public void setPropertiesAsync() { + QueueServiceAsyncClient queueServiceAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueServiceAsyncClient.setProperties#storageServiceProperties + StorageServiceProperties properties = queueServiceAsyncClient.getProperties().block().value(); + queueServiceAsyncClient.setProperties(properties) + .subscribe(response -> System.out.printf("Setting Queue service properties completed with status code %d", + response.statusCode())); + // END: com.azure.storage.queue.queueServiceAsyncClient.setProperties#storageServiceProperties + } + + /** + * Generates a code sample for using {@link QueueServiceClient#setProperties(StorageServiceProperties)} with metrics enabled. + */ + public void setPropertiesEnableMetrics() { + QueueServiceClient queueServiceClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueServiceClient.setPropertiesEnableMetrics#storageServiceProperties + StorageServiceProperties properties = queueServiceClient.getProperties().value(); + properties.minuteMetrics().enabled(true); + properties.hourMetrics().enabled(true); + VoidResponse response = queueServiceClient.setProperties(properties); + System.out.printf("Setting Queue service properties completed with status code %d", response.statusCode()); + // END: com.azure.storage.queue.queueServiceClient.setPropertiesEnableMetrics#storageServiceProperties + } + + /** + * Generates a code sample for using {@link QueueServiceAsyncClient#setProperties(StorageServiceProperties)} with metrics enabled. + */ + public void setPropertiesAsyncEnableMetrics() { + QueueServiceAsyncClient queueServiceAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueServiceAsyncClient.setPropertiesEnableMetrics#storageServiceProperties + StorageServiceProperties properties = queueServiceAsyncClient.getProperties().block().value(); + properties.minuteMetrics().enabled(true); + properties.hourMetrics().enabled(true); + queueServiceAsyncClient.setProperties(properties) + .subscribe(response -> System.out.printf("Setting Queue service properties completed with status code %d", + response.statusCode())); + // END: com.azure.storage.queue.queueServiceAsyncClient.setPropertiesEnableMetrics#storageServiceProperties + } + + /** + * Generates a code sample for using {@link QueueServiceClient#getStatistics()} + */ + public void getStatistics() { + QueueServiceClient queueServiceClient = createClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueServiceClient.getStatistics + StorageServiceStats stats = queueServiceClient.getStatistics().value(); + System.out.printf("Geo replication status: %s, Last synced: %s", + stats.geoReplication().status(), stats.geoReplication().lastSyncTime()); + // END: com.azure.storage.queue.queueServiceClient.getStatistics + } + + /** + * Generates a code sample for using {@link QueueServiceAsyncClient#getStatistics()} + */ + public void getStatisticsAsync() { + QueueServiceAsyncClient queueServiceAsyncClient = createAsyncClientWithSASToken(); + // BEGIN: com.azure.storage.queue.queueServiceAsyncClient.getStatistics + queueServiceAsyncClient.getStatistics() + .subscribe(response -> { + StorageServiceStats stats = response.value(); + System.out.printf("Geo replication status: %s, Last synced: %s", + stats.geoReplication().status(), stats.geoReplication().lastSyncTime()); + }); + // END: com.azure.storage.queue.queueServiceAsyncClient.getStatistics + } +} diff --git a/storage/client/queue/src/test/java/com/azure/storage/queue/QueueAsyncClientTests.java b/storage/client/queue/src/test/java/com/azure/storage/queue/QueueAsyncClientTests.java new file mode 100644 index 0000000000000..f658e2aff324b --- /dev/null +++ b/storage/client/queue/src/test/java/com/azure/storage/queue/QueueAsyncClientTests.java @@ -0,0 +1,695 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.queue; + +import com.azure.core.http.HttpClient; +import com.azure.core.http.policy.HttpLogDetailLevel; +import com.azure.core.util.logging.ClientLogger; +import com.azure.storage.queue.models.AccessPolicy; +import com.azure.storage.queue.models.DequeuedMessage; +import com.azure.storage.queue.models.SignedIdentifier; +import com.azure.storage.queue.models.StorageErrorException; +import reactor.test.StepVerifier; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class QueueAsyncClientTests extends QueueClientTestsBase { + private final ClientLogger logger = new ClientLogger(QueueAsyncClientTests.class); + + private QueueAsyncClient client; + + @Override + protected void beforeTest() { + queueName = getQueueName(); + helper = new TestHelpers(); + + if (interceptorManager.isPlaybackMode()) { + client = helper.setupClient((connectionString, endpoint) -> new QueueClientBuilder() + .connectionString(connectionString) + .endpoint(endpoint) + .queueName(queueName) + .httpClient(interceptorManager.getPlaybackClient()) + .httpLogDetailLevel(HttpLogDetailLevel.BODY_AND_HEADERS) + .buildAsyncClient(), true, logger); + } else { + client = helper.setupClient((connectionString, endpoint) -> new QueueClientBuilder() + .connectionString(connectionString) + .endpoint(endpoint) + .queueName(queueName) + .httpClient(HttpClient.createDefault().wiretap(true)) + .httpLogDetailLevel(HttpLogDetailLevel.BODY_AND_HEADERS) + .addPolicy(interceptorManager.getRecordPolicy()) + .buildAsyncClient(), false, logger); + } + } + + @Override + protected void afterTest() { + try { + client.clearMessages().block(); + client.delete().block(); + } catch (StorageErrorException ex) { + // Queue already delete, that's what we wanted anyways. + } + } + + @Override + public void createWithSharedKey() { + + } + + @Override + public void createWithSASToken() { + // Need to find a way to get SAS tokens from the storage account + } + + @Override + public void createWithMetadata() { + Map metadata = new HashMap<>(); + metadata.put("metadata1", "value1"); + metadata.put("metadata2", "value2"); + + StepVerifier.create(client.create(metadata)) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(client.getProperties()) + .assertNext(response -> { + helper.assertResponseStatusCode(response, 200); + assertEquals(0, response.value().approximateMessagesCount()); + assertEquals(metadata, response.value().metadata()); + }) + .verifyComplete(); + } + + @Override + public void createTwiceSameMetadata() { + Map metadata = new HashMap<>(); + metadata.put("metadata1", "value1"); + metadata.put("metadata2", "value2"); + + StepVerifier.create(client.create(metadata)) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(client.create(metadata)) + .assertNext(response -> helper.assertResponseStatusCode(response, 204)) + .verifyComplete(); + } + + @Override + public void createTwiceDifferentMetadata() { + Map metadata = new HashMap<>(); + metadata.put("metadata1", "value1"); + metadata.put("metadata2", "value2"); + + StepVerifier.create(client.create()) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(client.create(metadata)) + .verifyErrorSatisfies(throwable -> helper.assertExceptionStatusCode(throwable, 409)); + } + + @Override + public void deleteExisting() { + StepVerifier.create(client.create()) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(client.enqueueMessage("This queue will be deleted")) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(client.delete()) + .assertNext(response -> helper.assertResponseStatusCode(response, 204)) + .verifyComplete(); + + helper.sleepInRecordMode(Duration.ofSeconds(30)); + + StepVerifier.create(client.enqueueMessage("This should fail")) + .verifyErrorSatisfies(throwable -> helper.assertExceptionStatusCode(throwable, 404)); + } + + @Override + public void deleteNonExistent() { + StepVerifier.create(client.delete()) + .verifyErrorSatisfies(throwable -> helper.assertExceptionStatusCode(throwable, 404)); + } + + @Override + public void getProperties() { + Map metadata = new HashMap<>(); + metadata.put("metadata1", "value1"); + metadata.put("metadata2", "value2"); + + StepVerifier.create(client.create(metadata)) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(client.getProperties()) + .assertNext(response -> { + helper.assertResponseStatusCode(response, 200); + assertEquals(0, response.value().approximateMessagesCount()); + assertEquals(metadata, response.value().metadata()); + }) + .verifyComplete(); + } + + @Override + public void getPropertiesQueueDoesNotExist() { + StepVerifier.create(client.getProperties()) + .verifyErrorSatisfies(throwable -> helper.assertExceptionStatusCode(throwable, 404)); + } + + @Override + public void setMetadata() { + Map metadata = new HashMap<>(); + metadata.put("metadata1", "value1"); + metadata.put("metadata2", "value2"); + + StepVerifier.create(client.create()) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(client.setMetadata(metadata)) + .assertNext(response -> helper.assertResponseStatusCode(response, 204)) + .verifyComplete(); + + StepVerifier.create(client.getProperties()) + .assertNext(response -> { + helper.assertResponseStatusCode(response, 200); + assertEquals(0, response.value().approximateMessagesCount()); + assertEquals(metadata, response.value().metadata()); + }) + .verifyComplete(); + } + + @Override + public void setMetadataQueueDoesNotExist() { + Map metadata = new HashMap<>(); + metadata.put("metadata1", "value1"); + metadata.put("metadata2", "value2"); + + StepVerifier.create(client.setMetadata(metadata)) + .verifyErrorSatisfies(throwable -> helper.assertExceptionStatusCode(throwable, 404)); + } + + @Override + public void setInvalidMetadata() { + Map badMetadata = Collections.singletonMap("", "bad metadata"); + + StepVerifier.create(client.create()) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(client.setMetadata(badMetadata)) + .verifyErrorSatisfies(throwable -> helper.assertExceptionStatusCode(throwable, 400)); + } + + @Override + public void deleteMetadata() { + Map metadata = new HashMap<>(); + metadata.put("metadata1", "value1"); + metadata.put("metadata2", "value2"); + + StepVerifier.create(client.create(metadata)) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(client.getProperties()) + .assertNext(response -> { + helper.assertResponseStatusCode(response, 200); + assertEquals(0, response.value().approximateMessagesCount()); + assertEquals(metadata, response.value().metadata()); + }) + .verifyComplete(); + + StepVerifier.create(client.setMetadata(null)) + .assertNext(response -> helper.assertResponseStatusCode(response, 204)) + .verifyComplete(); + + StepVerifier.create(client.getProperties()) + .assertNext(response -> { + helper.assertResponseStatusCode(response, 200); + assertEquals(Collections.EMPTY_MAP, response.value().metadata()); + }) + .verifyComplete(); + } + + @Override + public void getAccessPolicy() { + StepVerifier.create(client.create()) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(client.getAccessPolicy()) + .expectNextCount(0) + .verifyComplete(); + } + + @Override + public void getAccessPolicyQueueDoesNotExist() { + StepVerifier.create(client.getAccessPolicy()) + .verifyErrorSatisfies(throwable -> helper.assertExceptionStatusCode(throwable, 404)); + } + + @Override + public void setAccessPolicy() { + StepVerifier.create(client.create()) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + AccessPolicy accessPolicy = new AccessPolicy() + .permission("raup") + .start(OffsetDateTime.of(LocalDateTime.of(2000, 1, 1, 0, 0), ZoneOffset.UTC)) + .expiry(OffsetDateTime.of(LocalDateTime.of(2020, 1, 1, 0, 0), ZoneOffset.UTC)); + + SignedIdentifier permission = new SignedIdentifier() + .id("testpermission") + .accessPolicy(accessPolicy); + + StepVerifier.create(client.setAccessPolicy(Collections.singletonList(permission))) + .assertNext(response -> helper.assertResponseStatusCode(response, 204)) + .verifyComplete(); + + StepVerifier.create(client.getAccessPolicy()) + .assertNext(response -> helper.assertPermissionsAreEqual(permission, response)) + .verifyComplete(); + } + + @Override + public void setAccessPolicyQueueDoesNotExist() { + AccessPolicy accessPolicy = new AccessPolicy() + .permission("r") + .start(OffsetDateTime.now()) + .expiry(OffsetDateTime.now()); + + SignedIdentifier permission = new SignedIdentifier() + .id("test-permission") + .accessPolicy(accessPolicy); + + StepVerifier.create(client.setAccessPolicy(Collections.singletonList(permission))) + .verifyErrorSatisfies(throwable -> helper.assertExceptionStatusCode(throwable, 400)); + } + + @Override + public void setInvalidAccessPolicy() { + AccessPolicy accessPolicy = new AccessPolicy() + .permission("r") + .start(OffsetDateTime.of(LocalDateTime.of(2000, 1, 1, 0, 0), ZoneOffset.UTC)) + .expiry(OffsetDateTime.of(LocalDateTime.of(2020, 1, 1, 0, 0), ZoneOffset.UTC)); + + SignedIdentifier permission = new SignedIdentifier() + .id("theidofthispermissionislongerthanwhatisallowedbytheserviceandshouldfail") + .accessPolicy(accessPolicy); + + StepVerifier.create(client.create()) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(client.setAccessPolicy(Collections.singletonList(permission))) + .verifyErrorSatisfies(throwable -> helper.assertExceptionStatusCode(throwable, 400)); + } + + @Override + public void setTooManyAccessPolicies() { + AccessPolicy accessPolicy = new AccessPolicy() + .permission("r") + .start(OffsetDateTime.of(LocalDateTime.of(2000, 1, 1, 0, 0), ZoneOffset.UTC)) + .expiry(OffsetDateTime.of(LocalDateTime.of(2020, 1, 1, 0, 0), ZoneOffset.UTC)); + + List permissions = new ArrayList<>(); + for (int i = 0; i < 6; i++) { + permissions.add(new SignedIdentifier() + .id("policy" + i) + .accessPolicy(accessPolicy)); + } + + StepVerifier.create(client.create()) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(client.setAccessPolicy(permissions)) + .verifyErrorSatisfies(throwable -> helper.assertExceptionStatusCode(throwable, 400)); + } + + @Override + public void enqueueMessage() { + StepVerifier.create(client.create()) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + String messageText = "test message"; + StepVerifier.create(client.enqueueMessage(messageText)) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(client.peekMessages()) + .assertNext(peekedMessage -> assertEquals(messageText, peekedMessage.messageText())) + .verifyComplete(); + } + + @Override + public void enqueueEmptyMessage() { + StepVerifier.create(client.create()) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + String messageText = ""; + StepVerifier.create(client.enqueueMessage(messageText)) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(client.peekMessages()) + .assertNext(peekedMessage -> assertNull(peekedMessage.messageText())) + .verifyComplete(); + } + + @Override + public void enqueueShortTimeToLiveMessage() { + StepVerifier.create(client.create()) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + String messageText = "test message"; + StepVerifier.create(client.enqueueMessage(messageText, Duration.ofSeconds(0), Duration.ofSeconds(2))) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(client.peekMessages().delaySubscription(Duration.ofSeconds(5))) + .expectNextCount(0) + .verifyComplete(); + } + + @Override + public void enqueueQueueDoesNotExist() { + StepVerifier.create(client.enqueueMessage("this should fail")) + .verifyErrorSatisfies(throwable -> helper.assertExceptionStatusCode(throwable, 404)); + } + + @Override + public void dequeueMessage() { + StepVerifier.create(client.create()) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + String messageText = "test message"; + StepVerifier.create(client.enqueueMessage(messageText)) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(client.dequeueMessages()) + .assertNext(dequeuedMessage -> assertEquals(messageText, dequeuedMessage.messageText())) + .verifyComplete(); + } + + @Override + public void dequeueMultipleMessages() { + StepVerifier.create(client.create()) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + String messageText = "test message"; + String messageText2 = "test message 2"; + StepVerifier.create(client.enqueueMessage(messageText)) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(client.enqueueMessage(messageText2)) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(client.dequeueMessages(2)) + .assertNext(dequeuedMessage -> assertEquals(messageText, dequeuedMessage.messageText())) + .assertNext(dequeuedMessage -> assertEquals(messageText2, dequeuedMessage.messageText())) + .verifyComplete(); + } + + @Override + public void dequeueTooManyMessages() { + StepVerifier.create(client.create()) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(client.dequeueMessages(64)) + .verifyErrorSatisfies(throwable -> helper.assertExceptionStatusCode(throwable, 400)); + } + + @Override + public void dequeueQueueDoesNotExist() { + StepVerifier.create(client.dequeueMessages()) + .verifyErrorSatisfies(throwable -> helper.assertExceptionStatusCode(throwable, 404)); + } + + @Override + public void peekMessage() { + StepVerifier.create(client.create()) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + String messageText = "test message"; + StepVerifier.create(client.enqueueMessage(messageText)) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(client.peekMessages()) + .assertNext(peekedMessage -> assertEquals(messageText, peekedMessage.messageText())) + .verifyComplete(); + } + + @Override + public void peekMultipleMessages() { + StepVerifier.create(client.create()) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + String messageText = "test message"; + String messageText2 = "test message 2"; + StepVerifier.create(client.enqueueMessage(messageText)) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(client.enqueueMessage(messageText2)) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(client.peekMessages(2)) + .assertNext(peekedMessage -> assertEquals(messageText, peekedMessage.messageText())) + .assertNext(peekedMessage -> assertEquals(messageText2, peekedMessage.messageText())) + .verifyComplete(); + } + + @Override + public void peekTooManyMessages() { + StepVerifier.create(client.create()) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(client.peekMessages(64)) + .verifyErrorSatisfies(throwable -> helper.assertExceptionStatusCode(throwable, 400)); + } + + @Override + public void peekQueueDoesNotExist() { + StepVerifier.create(client.peekMessages()) + .verifyErrorSatisfies(throwable -> helper.assertExceptionStatusCode(throwable, 404)); + } + + @Override + public void clearMessages() { + StepVerifier.create(client.create()) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(client.enqueueMessage("test message")) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + StepVerifier.create(client.enqueueMessage("test message")) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + StepVerifier.create(client.enqueueMessage("test message")) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(client.getProperties()) + .assertNext(response -> { + helper.assertResponseStatusCode(response, 200); + assertEquals(3, response.value().approximateMessagesCount()); + }) + .verifyComplete(); + + StepVerifier.create(client.clearMessages()) + .assertNext(response -> helper.assertResponseStatusCode(response, 204)) + .verifyComplete(); + + StepVerifier.create(client.getProperties()) + .assertNext(response -> { + helper.assertResponseStatusCode(response, 200); + assertEquals(0, response.value().approximateMessagesCount()); + }) + .verifyComplete(); + } + + @Override + public void clearMessagesQueueDoesNotExist() { + StepVerifier.create(client.clearMessages()) + .verifyErrorSatisfies(throwable -> helper.assertExceptionStatusCode(throwable, 404)); + } + + @Override + public void deleteMessage() { + StepVerifier.create(client.create()) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + String messageText = "test message"; + StepVerifier.create(client.enqueueMessage(messageText)) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + DequeuedMessage dequeuedMessage = client.dequeueMessages().blockFirst(); + assertEquals(messageText, dequeuedMessage.messageText()); + StepVerifier.create(client.deleteMessage(dequeuedMessage.messageId(), dequeuedMessage.popReceipt())) + .assertNext(response -> helper.assertResponseStatusCode(response, 204)) + .verifyComplete(); + + StepVerifier.create(client.getProperties()) + .assertNext(response -> { + helper.assertResponseStatusCode(response, 200); + assertEquals(0, response.value().approximateMessagesCount()); + }) + .verifyComplete(); + } + + @Override + public void deleteMessageInvalidMessageId() { + StepVerifier.create(client.create()) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + String messageText = "test message"; + StepVerifier.create(client.enqueueMessage(messageText)) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + DequeuedMessage dequeuedMessage = new DequeuedMessage(); + StepVerifier.create(client.dequeueMessages()) + .assertNext(response -> { + assertEquals(messageText, response.messageText()); + dequeuedMessage.popReceipt(response.popReceipt()).messageId(response.messageId()); + }) + .verifyComplete(); + + StepVerifier.create(client.deleteMessage(dequeuedMessage.messageId() + "random", dequeuedMessage.popReceipt())) + .verifyErrorSatisfies(throwable -> helper.assertExceptionStatusCode(throwable, 404)); + } + + @Override + public void deleteMessageInvalidPopReceipt() { + StepVerifier.create(client.create()) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + String messageText = "test message"; + StepVerifier.create(client.enqueueMessage(messageText)) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + DequeuedMessage dequeuedMessage = new DequeuedMessage(); + StepVerifier.create(client.dequeueMessages()) + .assertNext(response -> { + assertEquals(messageText, response.messageText()); + dequeuedMessage.popReceipt(response.popReceipt()).messageId(response.messageId()); + }) + .verifyComplete(); + + StepVerifier.create(client.deleteMessage(dequeuedMessage.messageId(), dequeuedMessage.popReceipt() + "random")) + .verifyErrorSatisfies(throwable -> helper.assertExceptionStatusCode(throwable, 400)); + } + + @Override + public void deleteMessageQueueDoesNotExist() { + StepVerifier.create(client.deleteMessage("invalid", "call")) + .verifyErrorSatisfies(throwable -> helper.assertExceptionStatusCode(throwable, 404)); + } + + @Override + public void updateMessage() { + StepVerifier.create(client.create()) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + String messageText = "test message"; + StepVerifier.create(client.enqueueMessage(messageText)) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + DequeuedMessage dequeuedMessage = client.dequeueMessages().blockFirst(); + assertEquals(messageText, dequeuedMessage.messageText()); + + String updatedMessageText = "updated test message"; + StepVerifier.create(client.updateMessage(updatedMessageText, dequeuedMessage.messageId(), dequeuedMessage.popReceipt(), Duration.ofSeconds(1))) + .assertNext(response -> helper.assertResponseStatusCode(response, 204)) + .verifyComplete(); + + StepVerifier.create(client.peekMessages().delaySubscription(Duration.ofSeconds(2))) + .assertNext(response -> assertEquals(updatedMessageText, response.messageText())) + .verifyComplete(); + } + + @Override + public void updateMessageInvalidMessageId() { + StepVerifier.create(client.create()) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + String messageText = "test message"; + StepVerifier.create(client.enqueueMessage(messageText)) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + DequeuedMessage dequeuedMessage = client.dequeueMessages().blockFirst(); + assertEquals(messageText, dequeuedMessage.messageText()); + + String updatedMessageText = "updated test message"; + StepVerifier.create(client.updateMessage(updatedMessageText, dequeuedMessage.messageId() + "random", dequeuedMessage.popReceipt(), Duration.ofSeconds(1))) + .verifyErrorSatisfies(throwable -> helper.assertExceptionStatusCode(throwable, 404)); + } + + @Override + public void updateMessageInvalidPopReceipt() { + StepVerifier.create(client.create()) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + String messageText = "test message"; + StepVerifier.create(client.enqueueMessage(messageText)) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + DequeuedMessage dequeuedMessage = client.dequeueMessages().blockFirst(); + assertEquals(messageText, dequeuedMessage.messageText()); + + String updatedMessageText = "updated test message"; + StepVerifier.create(client.updateMessage(updatedMessageText, dequeuedMessage.messageId(), dequeuedMessage.popReceipt() + "random", Duration.ofSeconds(1))) + .verifyErrorSatisfies(throwable -> helper.assertExceptionStatusCode(throwable, 400)); + } + + @Override + public void updateMessageQueueDoesNotExist() { + StepVerifier.create(client.updateMessage("queue", "doesn't", "exist", Duration.ofSeconds(5))) + .verifyErrorSatisfies(throwable -> helper.assertExceptionStatusCode(throwable, 400)); + } +} diff --git a/storage/client/queue/src/test/java/com/azure/storage/queue/QueueClientTests.java b/storage/client/queue/src/test/java/com/azure/storage/queue/QueueClientTests.java new file mode 100644 index 0000000000000..c0da15213d8c7 --- /dev/null +++ b/storage/client/queue/src/test/java/com/azure/storage/queue/QueueClientTests.java @@ -0,0 +1,654 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.queue; + +import com.azure.core.http.HttpClient; +import com.azure.core.http.policy.HttpLogDetailLevel; +import com.azure.core.http.rest.Response; +import com.azure.core.util.logging.ClientLogger; +import com.azure.storage.queue.models.AccessPolicy; +import com.azure.storage.queue.models.DequeuedMessage; +import com.azure.storage.queue.models.PeekedMessage; +import com.azure.storage.queue.models.QueueProperties; +import com.azure.storage.queue.models.SignedIdentifier; +import com.azure.storage.queue.models.StorageErrorException; +import com.azure.storage.queue.models.UpdatedMessage; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +public class QueueClientTests extends QueueClientTestsBase { + private final ClientLogger logger = new ClientLogger(QueueClientTests.class); + + private QueueClient client; + + @Override + protected void beforeTest() { + queueName = getQueueName(); + helper = new TestHelpers(); + + if (interceptorManager.isPlaybackMode()) { + client = helper.setupClient((connectionString, endpoint) -> new QueueClientBuilder() + .connectionString(connectionString) + .endpoint(endpoint) + .queueName(queueName) + .httpClient(interceptorManager.getPlaybackClient()) + .httpLogDetailLevel(HttpLogDetailLevel.BODY_AND_HEADERS) + .buildClient(), true, logger); + } else { + client = helper.setupClient((connectionString, endpoint) -> new QueueClientBuilder() + .endpoint(endpoint) + .queueName(queueName) + .httpClient(HttpClient.createDefault().wiretap(true)) + .httpLogDetailLevel(HttpLogDetailLevel.BODY_AND_HEADERS) + .addPolicy(interceptorManager.getRecordPolicy()) + .buildClient(), false, logger); + } + } + + @Override + protected void afterTest() { + try { + client.clearMessages(); + client.delete(); + } catch (StorageErrorException ex) { + // Queue already delete, that's what we wanted anyways. + } + } + + @Override + public void createWithSharedKey() { + + } + + @Override + public void createWithSASToken() { + // Need to find a way to get SAS tokens from the storage account + } + + @Override + public void createWithMetadata() { + Map metadata = new HashMap<>(); + metadata.put("metadata1", "value1"); + metadata.put("metadata2", "value2"); + + helper.assertResponseStatusCode(client.create(metadata), 201); + + QueueProperties properties = client.getProperties().value(); + assertEquals(metadata, properties.metadata()); + } + + @Override + public void createTwiceSameMetadata() { + Map metadata = new HashMap<>(); + metadata.put("metadata1", "value1"); + metadata.put("metadata2", "value2"); + + helper.assertResponseStatusCode(client.create(metadata), 201); + helper.assertResponseStatusCode(client.create(metadata), 204); + } + + @Override + public void createTwiceDifferentMetadata() { + Map metadata = new HashMap<>(); + metadata.put("metadata1", "value1"); + metadata.put("metadata2", "value2"); + + helper.assertResponseStatusCode(client.create(), 201); + + try { + client.create(metadata); + fail("Creating a queue twice with different metadata values should throw an exception."); + } catch (Exception exception) { + helper.assertExceptionStatusCode(exception, 409); + } + } + + @Override + public void deleteExisting() { + helper.assertResponseStatusCode(client.create(), 201); + helper.assertResponseStatusCode(client.enqueueMessage("This queue will be deleted"), 201); + helper.assertResponseStatusCode(client.delete(), 204); + + helper.sleepInRecordMode(Duration.ofSeconds(30)); + + try { + client.enqueueMessage("This should fail"); + fail("Attempting to work with a queue that has been deleted should throw an exception."); + } catch (Exception exception) { + helper.assertExceptionStatusCode(exception, 404); + } + } + + @Override + public void deleteNonExistent() { + try { + client.delete(); + fail("Attempting to delete a queue that doesn't exist should throw an exception"); + } catch (Exception exception) { + helper.assertExceptionStatusCode(exception, 404); + } + } + + @Override + public void getProperties() { + Map metadata = new HashMap<>(); + metadata.put("metadata1", "value1"); + metadata.put("metadata2", "value2"); + + helper.assertResponseStatusCode(client.create(metadata), 201); + + Response response = client.getProperties(); + helper.assertResponseStatusCode(response, 200); + assertEquals(0, response.value().approximateMessagesCount()); + assertEquals(metadata, response.value().metadata()); + } + + @Override + public void getPropertiesQueueDoesNotExist() { + try { + client.getProperties(); + fail("Attempting to get properties of a queue that doesn't exist should throw an exception"); + } catch (Exception exception) { + helper.assertExceptionStatusCode(exception, 404); + } + } + + @Override + public void setMetadata() { + Map metadata = new HashMap<>(); + metadata.put("metadata1", "value1"); + metadata.put("metadata2", "value2"); + + helper.assertResponseStatusCode(client.create(), 201); + + helper.assertResponseStatusCode(client.setMetadata(metadata), 204); + + Response response = client.getProperties(); + helper.assertResponseStatusCode(response, 200); + assertEquals(0, response.value().approximateMessagesCount()); + assertEquals(metadata, response.value().metadata()); + } + + @Override + public void setMetadataQueueDoesNotExist() { + Map metadata = new HashMap<>(); + metadata.put("metadata1", "value1"); + metadata.put("metadata2", "value2"); + + try { + client.setMetadata(metadata); + fail("Attempting to set metadata on a queue that doesn't exist should throw an exception"); + } catch (Exception exception) { + helper.assertExceptionStatusCode(exception, 404); + } + } + + @Override + public void setInvalidMetadata() { + Map badMetadata = Collections.singletonMap("", "bad metadata"); + + helper.assertResponseStatusCode(client.create(), 201); + try { + client.setMetadata(badMetadata); + fail("Attempting to set invalid metadata on a queue that doesn't exist should throw an exception"); + } catch (Exception exception) { + helper.assertExceptionStatusCode(exception, 400); + } + } + + @Override + public void deleteMetadata() { + Map metadata = new HashMap<>(); + metadata.put("metadata1", "value1"); + metadata.put("metadata2", "value2"); + + helper.assertResponseStatusCode(client.create(metadata), 201); + + Response response = client.getProperties(); + helper.assertResponseStatusCode(response, 200); + assertEquals(0, response.value().approximateMessagesCount()); + assertEquals(metadata, response.value().metadata()); + + helper.assertResponseStatusCode(client.setMetadata(null), 204); + + response = client.getProperties(); + helper.assertResponseStatusCode(response, 200); + assertEquals(0, response.value().approximateMessagesCount()); + assertEquals(Collections.EMPTY_MAP, response.value().metadata()); + } + + @Override + public void getAccessPolicy() { + helper.assertResponseStatusCode(client.create(), 201); + + Iterable accessPolicies = client.getAccessPolicy(); + assertFalse(accessPolicies.iterator().hasNext()); + } + + @Override + public void getAccessPolicyQueueDoesNotExist() { + try { + client.getAccessPolicy().iterator().hasNext(); + fail("Attempting to get access policies on a queue that doesn't exist should throw an exception"); + } catch (Exception exception) { + helper.assertExceptionStatusCode(exception, 404); + } + } + + @Override + public void setAccessPolicy() { + helper.assertResponseStatusCode(client.create(), 201); + + AccessPolicy accessPolicy = new AccessPolicy() + .permission("raup") + .start(OffsetDateTime.of(LocalDateTime.of(2000, 1, 1, 0, 0), ZoneOffset.UTC)) + .expiry(OffsetDateTime.of(LocalDateTime.of(2020, 1, 1, 0, 0), ZoneOffset.UTC)); + + SignedIdentifier permission = new SignedIdentifier() + .id("testpermission") + .accessPolicy(accessPolicy); + + helper.assertResponseStatusCode(client.setAccessPolicy(Collections.singletonList(permission)), 204); + + Iterator accessPolicies = client.getAccessPolicy().iterator(); + helper.assertPermissionsAreEqual(permission, accessPolicies.next()); + assertFalse(accessPolicies.hasNext()); + } + + @Override + public void setAccessPolicyQueueDoesNotExist() { + AccessPolicy accessPolicy = new AccessPolicy() + .permission("r") + .start(OffsetDateTime.now()) + .expiry(OffsetDateTime.now()); + + SignedIdentifier permission = new SignedIdentifier() + .id("test-permission") + .accessPolicy(accessPolicy); + + try { + client.setAccessPolicy(Collections.singletonList(permission)); + fail("Attempting to set access policies on a queue that doesn't exist should throw an exception"); + } catch (Exception exception) { + helper.assertExceptionStatusCode(exception, 400); + } + } + + @Override + public void setInvalidAccessPolicy() { + AccessPolicy accessPolicy = new AccessPolicy() + .permission("r") + .start(OffsetDateTime.of(LocalDateTime.of(2000, 1, 1, 0, 0), ZoneOffset.UTC)) + .expiry(OffsetDateTime.of(LocalDateTime.of(2020, 1, 1, 0, 0), ZoneOffset.UTC)); + + SignedIdentifier permission = new SignedIdentifier() + .id("theidofthispermissionislongerthanwhatisallowedbytheserviceandshouldfail") + .accessPolicy(accessPolicy); + + helper.assertResponseStatusCode(client.create(), 201); + + try { + client.setAccessPolicy(Collections.singletonList(permission)); + fail("Attempting to set invalid access policies on a queue that doesn't exist should throw an exception"); + } catch (Exception exception) { + helper.assertExceptionStatusCode(exception, 400); + } + } + + @Override + public void setTooManyAccessPolicies() { + List permissions = new ArrayList<>(); + + AccessPolicy accessPolicy = new AccessPolicy() + .permission("r") + .start(OffsetDateTime.of(LocalDateTime.of(2000, 1, 1, 0, 0), ZoneOffset.UTC)) + .expiry(OffsetDateTime.of(LocalDateTime.of(2020, 1, 1, 0, 0), ZoneOffset.UTC)); + + for (int i = 0; i < 6; i++) { + permissions.add(new SignedIdentifier() + .id("policy" + i) + .accessPolicy(accessPolicy)); + } + + helper.assertResponseStatusCode(client.create(), 201); + + try { + client.setAccessPolicy(permissions); + fail("Attempting to set more than five access policies on a queue that doesn't exist should throw an exception"); + } catch (Exception exception) { + helper.assertExceptionStatusCode(exception, 400); + } + } + + @Override + public void enqueueMessage() { + helper.assertResponseStatusCode(client.create(), 201); + + String messageText = "test message"; + helper.assertResponseStatusCode(client.enqueueMessage(messageText), 201); + + Iterator response = client.peekMessages().iterator(); + assertEquals(messageText, response.next().messageText()); + assertFalse(response.hasNext()); + } + + @Override + public void enqueueEmptyMessage() { + helper.assertResponseStatusCode(client.create(), 201); + + String messageText = ""; + helper.assertResponseStatusCode(client.enqueueMessage(messageText), 201); + + Iterator response = client.peekMessages().iterator(); + assertNull(response.next().messageText()); + assertFalse(response.hasNext()); + } + + @Override + public void enqueueShortTimeToLiveMessage() { + helper.assertResponseStatusCode(client.create(), 201); + + String messageText = "test message"; + helper.assertResponseStatusCode(client.enqueueMessage(messageText, Duration.ofSeconds(0), Duration.ofSeconds(2)), 201); + + helper.sleepInRecordMode(Duration.ofSeconds(5)); + Iterator response = client.peekMessages().iterator(); + assertFalse(response.hasNext()); + } + + @Override + public void enqueueQueueDoesNotExist() { + try { + client.enqueueMessage("This should fail"); + fail("Attempting to enqueue a message on a queue that doesn't exist should throw an exception"); + } catch (Exception exception) { + helper.assertExceptionStatusCode(exception, 404); + } + } + + @Override + public void dequeueMessage() { + helper.assertResponseStatusCode(client.create(), 201); + + String messageText = "test message"; + helper.assertResponseStatusCode(client.enqueueMessage(messageText), 201); + + Iterator response = client.dequeueMessages().iterator(); + assertEquals(messageText, response.next().messageText()); + assertFalse(response.hasNext()); + } + + @Override + public void dequeueMultipleMessages() { + helper.assertResponseStatusCode(client.create(), 201); + + String messageText = "test message"; + String messageText2 = "test message 2"; + helper.assertResponseStatusCode(client.enqueueMessage(messageText), 201); + helper.assertResponseStatusCode(client.enqueueMessage(messageText2), 201); + + Iterator response = client.dequeueMessages(2).iterator(); + assertEquals(messageText, response.next().messageText()); + assertEquals(messageText2, response.next().messageText()); + assertFalse(response.hasNext()); + } + + @Override + public void dequeueTooManyMessages() { + helper.assertResponseStatusCode(client.create(), 201); + + try { + client.dequeueMessages(64).iterator().hasNext(); + fail("Attempting to get more than 32 messages from a queue should throw an exception"); + } catch (Exception exception) { + helper.assertExceptionStatusCode(exception, 400); + } + } + + @Override + public void dequeueQueueDoesNotExist() { + try { + client.dequeueMessages().iterator().hasNext(); + fail("Attempting to get messages from a queue that doesn't exist should throw an exception"); + } catch (Exception exception) { + helper.assertExceptionStatusCode(exception, 404); + } + } + + @Override + public void peekMessage() { + helper.assertResponseStatusCode(client.create(), 201); + + String messageText = "test message"; + helper.assertResponseStatusCode(client.enqueueMessage(messageText), 201); + + Iterator response = client.peekMessages().iterator(); + assertEquals(messageText, response.next().messageText()); + assertFalse(response.hasNext()); + } + + @Override + public void peekMultipleMessages() { + helper.assertResponseStatusCode(client.create(), 201); + + String messageText = "test message"; + String messageText2 = "test message 2"; + helper.assertResponseStatusCode(client.enqueueMessage(messageText), 201); + helper.assertResponseStatusCode(client.enqueueMessage(messageText2), 201); + + Iterator response = client.peekMessages(2).iterator(); + assertEquals(messageText, response.next().messageText()); + assertEquals(messageText2, response.next().messageText()); + assertFalse(response.hasNext()); + } + + @Override + public void peekTooManyMessages() { + helper.assertResponseStatusCode(client.create(), 201); + + try { + client.peekMessages(64).iterator().hasNext(); + fail("Attempting to peek more than 32 messages from a queue should throw an exception"); + } catch (Exception exception) { + helper.assertExceptionStatusCode(exception, 400); + } + } + + @Override + public void peekQueueDoesNotExist() { + try { + client.peekMessages().iterator().hasNext(); + fail("Attempting to peek messages from a queue that doesn't exist should throw an exception"); + } catch (Exception exception) { + helper.assertExceptionStatusCode(exception, 404); + } + } + + @Override + public void clearMessages() { + helper.assertResponseStatusCode(client.create(), 201); + + for (int i = 0; i < 3; i++) { + helper.assertResponseStatusCode(client.enqueueMessage("test message"), 201); + } + + Response response = client.getProperties(); + helper.assertResponseStatusCode(response, 200); + assertEquals(3, response.value().approximateMessagesCount()); + + helper.assertResponseStatusCode(client.clearMessages(), 204); + + response = client.getProperties(); + helper.assertResponseStatusCode(response, 200); + assertEquals(0, response.value().approximateMessagesCount()); + } + + @Override + public void clearMessagesQueueDoesNotExist() { + try { + client.clearMessages(); + fail("Attempting to clear messages of a queue that doesn't exist should throw an exception"); + } catch (Exception exception) { + helper.assertExceptionStatusCode(exception, 404); + } + } + + @Override + public void deleteMessage() { + helper.assertResponseStatusCode(client.create(), 201); + + String messageText = "test message"; + helper.assertResponseStatusCode(client.enqueueMessage(messageText), 201); + + Iterator response = client.dequeueMessages().iterator(); + DequeuedMessage message = response.next(); + assertFalse(response.hasNext()); + assertEquals(messageText, message.messageText()); + + helper.assertResponseStatusCode(client.deleteMessage(message.messageId(), message.popReceipt()), 204); + + Response propertiesResponse = client.getProperties(); + helper.assertResponseStatusCode(propertiesResponse, 200); + assertEquals(0, propertiesResponse.value().approximateMessagesCount()); + } + + @Override + public void deleteMessageInvalidMessageId() { + helper.assertResponseStatusCode(client.create(), 201); + + String messageText = "test message"; + helper.assertResponseStatusCode(client.enqueueMessage(messageText), 201); + + Iterator response = client.dequeueMessages().iterator(); + DequeuedMessage message = response.next(); + assertFalse(response.hasNext()); + assertEquals(messageText, message.messageText()); + + try { + client.deleteMessage(message.messageId() + "random", message.popReceipt()); + fail("Attempting to delete a message with an invalid ID should throw an exception."); + } catch (Exception exception) { + helper.assertExceptionStatusCode(exception, 404); + } + } + + @Override + public void deleteMessageInvalidPopReceipt() { + helper.assertResponseStatusCode(client.create(), 201); + + String messageText = "test message"; + helper.assertResponseStatusCode(client.enqueueMessage(messageText), 201); + + Iterator response = client.dequeueMessages().iterator(); + DequeuedMessage message = response.next(); + assertFalse(response.hasNext()); + assertEquals(messageText, message.messageText()); + + try { + client.deleteMessage(message.messageId(), message.popReceipt() + "random"); + fail("Attempting to delete a message with an invalid popReceipt should throw an exception."); + } catch (Exception exception) { + helper.assertExceptionStatusCode(exception, 400); + } + } + + @Override + public void deleteMessageQueueDoesNotExist() { + try { + client.deleteMessage("invalid", "call"); + fail("Attempting to delete a message from a queue that doesn't exist should throw an exception."); + } catch (Exception exception) { + helper.assertExceptionStatusCode(exception, 404); + } + } + + @Override + public void updateMessage() { + helper.assertResponseStatusCode(client.create(), 201); + + String messageText = "test message"; + helper.assertResponseStatusCode(client.enqueueMessage(messageText), 201); + + Iterator response = client.dequeueMessages().iterator(); + DequeuedMessage message = response.next(); + assertEquals(messageText, message.messageText()); + assertFalse(response.hasNext()); + + String updatedMessageText = "updated test message"; + Response updatedMessageResponse = client.updateMessage(updatedMessageText, message.messageId(), message.popReceipt(), Duration.ofSeconds(1)); + helper.assertResponseStatusCode(updatedMessageResponse, 204); + + helper.sleepInRecordMode(Duration.ofSeconds(2)); + + Iterator peekedMessageIterator = client.peekMessages().iterator(); + PeekedMessage peekedMessage = peekedMessageIterator.next(); + assertEquals(updatedMessageText, peekedMessage.messageText()); + assertFalse(peekedMessageIterator.hasNext()); + } + + @Override + public void updateMessageInvalidMessageId() { + helper.assertResponseStatusCode(client.create(), 201); + + String messageText = "test message"; + helper.assertResponseStatusCode(client.enqueueMessage(messageText), 201); + + Iterator response = client.dequeueMessages().iterator(); + DequeuedMessage message = response.next(); + assertEquals(messageText, message.messageText()); + assertFalse(response.hasNext()); + + String updatedMessageText = "updated test message"; + try { + client.updateMessage(updatedMessageText, message.messageId() + "random", message.popReceipt(), Duration.ofSeconds(1)); + fail("Attempting to update a message with an invalid ID should throw an exception."); + } catch (Exception exception) { + helper.assertExceptionStatusCode(exception, 404); + } + } + + @Override + public void updateMessageInvalidPopReceipt() { + helper.assertResponseStatusCode(client.create(), 201); + + String messageText = "test message"; + helper.assertResponseStatusCode(client.enqueueMessage(messageText), 201); + + Iterator response = client.dequeueMessages().iterator(); + DequeuedMessage message = response.next(); + assertEquals(messageText, message.messageText()); + assertFalse(response.hasNext()); + + String updatedMessageText = "updated test message"; + try { + client.updateMessage(updatedMessageText, message.messageId(), message.popReceipt() + "random", Duration.ofSeconds(1)); + fail("Attempting to update a message with an invalid popReceipt should throw an exception."); + } catch (Exception exception) { + helper.assertExceptionStatusCode(exception, 400); + } + } + + @Override + public void updateMessageQueueDoesNotExist() { + try { + client.updateMessage("queue", "doesn't", "exist", Duration.ofSeconds(5)); + fail("Attempting to update a message on a queue that doesn't exist should throw an exception."); + } catch (Exception exception) { + helper.assertExceptionStatusCode(exception, 400); + } + } +} diff --git a/storage/client/queue/src/test/java/com/azure/storage/queue/QueueClientTestsBase.java b/storage/client/queue/src/test/java/com/azure/storage/queue/QueueClientTestsBase.java new file mode 100644 index 0000000000000..b83d9a1459bbc --- /dev/null +++ b/storage/client/queue/src/test/java/com/azure/storage/queue/QueueClientTestsBase.java @@ -0,0 +1,148 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.queue; + +import com.azure.core.test.TestBase; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; + +public abstract class QueueClientTestsBase extends TestBase { + String queueName; + TestHelpers helper; + + @Rule + public TestName testName = new TestName(); + + @Override + public String testName() { + return testName.getMethodName(); + } + + String getQueueName() { + return testResourceNamer.randomName("queue", 16).toLowerCase(); + } + + @Test + public abstract void createWithSharedKey(); + + @Test + public abstract void createWithSASToken(); + + @Test + public abstract void createWithMetadata(); + + @Test + public abstract void createTwiceSameMetadata(); + + @Test + public abstract void createTwiceDifferentMetadata(); + + @Test + public abstract void deleteExisting(); + + @Test + public abstract void deleteNonExistent(); + + @Test + public abstract void getProperties(); + + @Test + public abstract void getPropertiesQueueDoesNotExist(); + + @Test + public abstract void setMetadata(); + + @Test + public abstract void setMetadataQueueDoesNotExist(); + + @Test + public abstract void setInvalidMetadata(); + + @Test + public abstract void deleteMetadata(); + + @Test + public abstract void getAccessPolicy(); + + @Test + public abstract void getAccessPolicyQueueDoesNotExist(); + + @Test + public abstract void setAccessPolicy(); + + @Test + public abstract void setAccessPolicyQueueDoesNotExist(); + + @Test + public abstract void setInvalidAccessPolicy(); + + @Test + public abstract void setTooManyAccessPolicies(); + + @Test + public abstract void enqueueMessage(); + + @Test + public abstract void enqueueEmptyMessage(); + + @Test + public abstract void enqueueShortTimeToLiveMessage(); + + @Test + public abstract void enqueueQueueDoesNotExist(); + + @Test + public abstract void dequeueMessage(); + + @Test + public abstract void dequeueMultipleMessages(); + + @Test + public abstract void dequeueTooManyMessages(); + + @Test + public abstract void dequeueQueueDoesNotExist(); + + @Test + public abstract void peekMessage(); + + @Test + public abstract void peekMultipleMessages(); + + @Test + public abstract void peekTooManyMessages(); + + @Test + public abstract void peekQueueDoesNotExist(); + + @Test + public abstract void clearMessages(); + + @Test + public abstract void clearMessagesQueueDoesNotExist(); + + @Test + public abstract void deleteMessage(); + + @Test + public abstract void deleteMessageInvalidMessageId(); + + @Test + public abstract void deleteMessageInvalidPopReceipt(); + + @Test + public abstract void deleteMessageQueueDoesNotExist(); + + @Test + public abstract void updateMessage(); + + @Test + public abstract void updateMessageInvalidMessageId(); + + @Test + public abstract void updateMessageInvalidPopReceipt(); + + @Test + public abstract void updateMessageQueueDoesNotExist(); +} diff --git a/storage/client/queue/src/test/java/com/azure/storage/queue/QueueServiceAsyncClientTests.java b/storage/client/queue/src/test/java/com/azure/storage/queue/QueueServiceAsyncClientTests.java new file mode 100644 index 0000000000000..8d0b89aa9be07 --- /dev/null +++ b/storage/client/queue/src/test/java/com/azure/storage/queue/QueueServiceAsyncClientTests.java @@ -0,0 +1,250 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.queue; + +import com.azure.core.http.HttpClient; +import com.azure.core.http.policy.HttpLogDetailLevel; +import com.azure.core.util.logging.ClientLogger; +import com.azure.storage.queue.models.Logging; +import com.azure.storage.queue.models.Metrics; +import com.azure.storage.queue.models.QueueItem; +import com.azure.storage.queue.models.QueuesSegmentOptions; +import com.azure.storage.queue.models.RetentionPolicy; +import com.azure.storage.queue.models.StorageErrorException; +import com.azure.storage.queue.models.StorageServiceProperties; +import reactor.test.StepVerifier; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; + +import static org.junit.Assert.assertEquals; + +public class QueueServiceAsyncClientTests extends QueueServiceClientTestsBase { + private final ClientLogger logger = new ClientLogger(QueueServiceAsyncClientTests.class); + + private QueueServiceAsyncClient serviceClient; + + @Override + protected void beforeTest() { + queueName = getQueueName(); + helper = new TestHelpers(); + + if (interceptorManager.isPlaybackMode()) { + serviceClient = helper.setupClient((connectionString, endpoint) -> new QueueServiceClientBuilder() + .connectionString(connectionString) + .endpoint(endpoint) + .httpClient(interceptorManager.getPlaybackClient()) + .httpLogDetailLevel(HttpLogDetailLevel.BODY_AND_HEADERS) + .buildAsyncClient(), true, logger); + } else { + serviceClient = helper.setupClient((connectionString, endpoint) -> new QueueServiceClientBuilder() + .connectionString(connectionString) + .endpoint(endpoint) + .httpClient(HttpClient.createDefault().wiretap(true)) + .httpLogDetailLevel(HttpLogDetailLevel.BODY_AND_HEADERS) + .addPolicy(interceptorManager.getRecordPolicy()) + .buildAsyncClient(), false, logger); + } + } + + @Override + protected void afterTest() { + serviceClient.listQueues(new QueuesSegmentOptions().prefix(queueName)) + .collectList() + .block() + .forEach(queue -> { + QueueAsyncClient client = serviceClient.getQueueAsyncClient(queue.name()); + try { + client.clearMessages().then(client.delete()).block(); + } catch (StorageErrorException ex) { + // Queue already delete, that's what we wanted anyways. + } + }); + } + + @Override + public void getQueueDoesNotCreateAQueue() { + StepVerifier.create(serviceClient.getQueueAsyncClient(queueName).enqueueMessage("Expecting an exception")); + } + + @Override + public void createQueue() { + StepVerifier.create(serviceClient.createQueue(queueName).block().value().enqueueMessage("Testing service client creating a queue")) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + } + + @Override + public void createQueueWithMetadata() { + Map metadata = new HashMap<>(); + metadata.put("metadata1", "value1"); + metadata.put("metadata2", "value2"); + QueueAsyncClient client = serviceClient.createQueue(queueName, metadata).block().value(); + + StepVerifier.create(client.getProperties()) + .assertNext(response -> { + assertEquals(metadata, response.value().metadata()); + }) + .verifyComplete(); + } + + @Override + public void createQueueTwiceSameMetadata() { + final String messageText = "Testing service client creating the same queue twice does not modify the queue"; + Map metadata = new HashMap<>(); + metadata.put("metadata1", "value1"); + metadata.put("metadata2", "value2"); + + StepVerifier.create(serviceClient.createQueue(queueName, metadata).block().value().enqueueMessage(messageText)) + .assertNext(response -> helper.assertResponseStatusCode(response, 201)) + .verifyComplete(); + + StepVerifier.create(serviceClient.createQueue(queueName, metadata).block().value().peekMessages()) + .assertNext(response -> assertEquals(messageText, response.messageText())) + .verifyComplete(); + } + + @Override + public void createQueueTwiceDifferentMetadata() { + Map metadata = new HashMap<>(); + metadata.put("metadata1", "value1"); + metadata.put("metadata2", "value2"); + + try { + serviceClient.createQueue(queueName); + serviceClient.createQueue(queueName, metadata); + } catch (Exception exception) { + } + } + + @Override + public void deleteExistingQueue() { + QueueAsyncClient client = serviceClient.createQueue(queueName).block().value(); + serviceClient.deleteQueue(queueName).block(); + + StepVerifier.create(client.enqueueMessage("Expecting an exception")); + } + + @Override + public void deleteNonExistentQueue() { + try { + serviceClient.deleteQueue(queueName); + } catch (Exception exception) { + } + } + + @Override + public void listQueues() { + LinkedList testQueues = new LinkedList<>(); + for (int i = 0; i < 3; i++) { + QueueItem queue = new QueueItem().name(queueName + i); + testQueues.add(queue); + serviceClient.createQueue(queue.name(), queue.metadata()).block(); + } + + StepVerifier.create(serviceClient.listQueues(defaultSegmentOptions())) + .assertNext(result -> helper.assertQueuesAreEqual(testQueues.pop(), result)) + .assertNext(result -> helper.assertQueuesAreEqual(testQueues.pop(), result)) + .assertNext(result -> helper.assertQueuesAreEqual(testQueues.pop(), result)) + .verifyComplete(); + } + + @Override + public void listQueuesIncludeMetadata() { + Map metadata = new HashMap<>(); + metadata.put("metadata1", "value1"); + metadata.put("metadata2", "value2"); + + LinkedList testQueues = new LinkedList<>(); + for (int i = 0; i < 3; i++) { + QueueItem queue = new QueueItem().name(queueName + i); + if (i % 2 == 0) { + queue.metadata(metadata); + } + + testQueues.add(queue); + serviceClient.createQueue(queue.name(), queue.metadata()).block(); + } + + StepVerifier.create(serviceClient.listQueues(defaultSegmentOptions().includeMetadata(true))) + .assertNext(result -> helper.assertQueuesAreEqual(testQueues.pop(), result)) + .assertNext(result -> helper.assertQueuesAreEqual(testQueues.pop(), result)) + .assertNext(result -> helper.assertQueuesAreEqual(testQueues.pop(), result)) + .verifyComplete(); + } + + @Override + public void listQueuesWithPrefix() { + LinkedList testQueues = new LinkedList<>(); + for (int i = 0; i < 3; i++) { + QueueItem queue = new QueueItem(); + if (i % 2 == 0) { + queue.name(queueName + "prefix" + i); + testQueues.add(queue); + } else { + queue.name(queueName + i); + } + + serviceClient.createQueue(queue.name(), queue.metadata()).block(); + } + + StepVerifier.create(serviceClient.listQueues(defaultSegmentOptions().prefix(queueName + "prefix"))) + .assertNext(result -> helper.assertQueuesAreEqual(testQueues.pop(), result)) + .assertNext(result -> helper.assertQueuesAreEqual(testQueues.pop(), result)) + .verifyComplete(); + } + + @Override + public void listQueuesWithLimit() { + LinkedList testQueues = new LinkedList<>(); + for (int i = 0; i < 3; i++) { + QueueItem queue = new QueueItem().name(queueName + i); + testQueues.add(queue); + serviceClient.createQueue(queue.name(), queue.metadata()).block(); + } + + StepVerifier.create(serviceClient.listQueues(defaultSegmentOptions().maxResults(2))) + .verifyComplete(); + } + + @Override + public void setProperties() { + StorageServiceProperties originalProperties = serviceClient.getProperties().block().value(); + + RetentionPolicy retentionPolicy = new RetentionPolicy().enabled(true) + .days(3); + + Logging logging = new Logging().version("1.0") + .delete(true) + .write(true) + .retentionPolicy(retentionPolicy); + + Metrics metrics = new Metrics().enabled(true) + .includeAPIs(false) + .retentionPolicy(retentionPolicy) + .version("1.0"); + + StorageServiceProperties updatedProperties = new StorageServiceProperties().logging(logging) + .hourMetrics(metrics) + .minuteMetrics(metrics) + .cors(new ArrayList<>()); + + StepVerifier.create(serviceClient.setProperties(updatedProperties)) + .assertNext(response -> helper.assertResponseStatusCode(response, 202)) + .verifyComplete(); + + StepVerifier.create(serviceClient.getProperties()) + .assertNext(response -> helper.assertQueueServicePropertiesAreEqual(updatedProperties, response.value())) + .verifyComplete(); + + StepVerifier.create(serviceClient.setProperties(originalProperties)) + .assertNext(response -> helper.assertResponseStatusCode(response, 202)) + .verifyComplete(); + + StepVerifier.create(serviceClient.getProperties()) + .assertNext(response -> helper.assertQueueServicePropertiesAreEqual(originalProperties, response.value())) + .verifyComplete(); + } +} diff --git a/storage/client/queue/src/test/java/com/azure/storage/queue/QueueServiceClientTests.java b/storage/client/queue/src/test/java/com/azure/storage/queue/QueueServiceClientTests.java new file mode 100644 index 0000000000000..c6587872c6fb7 --- /dev/null +++ b/storage/client/queue/src/test/java/com/azure/storage/queue/QueueServiceClientTests.java @@ -0,0 +1,257 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.queue; + +import com.azure.core.http.HttpClient; +import com.azure.core.http.policy.HttpLogDetailLevel; +import com.azure.core.http.rest.Response; +import com.azure.core.http.rest.VoidResponse; +import com.azure.core.util.logging.ClientLogger; +import com.azure.storage.queue.models.EnqueuedMessage; +import com.azure.storage.queue.models.Logging; +import com.azure.storage.queue.models.Metrics; +import com.azure.storage.queue.models.PeekedMessage; +import com.azure.storage.queue.models.QueueItem; +import com.azure.storage.queue.models.QueueProperties; +import com.azure.storage.queue.models.QueuesSegmentOptions; +import com.azure.storage.queue.models.RetentionPolicy; +import com.azure.storage.queue.models.StorageErrorException; +import com.azure.storage.queue.models.StorageServiceProperties; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +public class QueueServiceClientTests extends QueueServiceClientTestsBase { + private final ClientLogger logger = new ClientLogger(QueueServiceClientTests.class); + + private QueueServiceClient serviceClient; + + @Override + protected void beforeTest() { + queueName = getQueueName(); + helper = new TestHelpers(); + + if (interceptorManager.isPlaybackMode()) { + serviceClient = helper.setupClient((connectionString, endpoint) -> new QueueServiceClientBuilder() + .connectionString(connectionString) + .endpoint(endpoint) + .httpClient(interceptorManager.getPlaybackClient()) + .httpLogDetailLevel(HttpLogDetailLevel.BODY_AND_HEADERS) + .buildClient(), true, logger); + } else { + serviceClient = helper.setupClient((connectionString, endpoint) -> new QueueServiceClientBuilder() + .connectionString(connectionString) + .endpoint(endpoint) + .httpClient(HttpClient.createDefault().wiretap(true)) + .httpLogDetailLevel(HttpLogDetailLevel.BODY_AND_HEADERS) + .addPolicy(interceptorManager.getRecordPolicy()) + .buildClient(), false, logger); + } + } + + @Override + protected void afterTest() { + serviceClient.listQueues(new QueuesSegmentOptions().prefix(queueName)) + .forEach(queueToDelete -> { + try { + QueueClient client = serviceClient.getQueueClient(queueToDelete.name()); + client.clearMessages(); + client.delete(); + } catch (StorageErrorException ex) { + // Queue already delete, that's what we wanted anyways. + } + }); + } + + @Override + public void getQueueDoesNotCreateAQueue() { + try { + serviceClient.getQueueClient(queueName).enqueueMessage("Expecting an exception"); + fail("getQueueClient doesn't create a queue in Azure Storage."); + } catch (Exception exception) { + helper.assertExceptionStatusCode(exception, 404); + } + } + + @Override + public void createQueue() { + QueueClient client = serviceClient.createQueue(queueName).value(); + Response response = client.enqueueMessage("Testing service client creating a queue"); + helper.assertResponseStatusCode(response, 201); + } + + @Override + public void createQueueWithMetadata() { + Map metadata = new HashMap<>(); + metadata.put("metadata1", "value1"); + metadata.put("metadata2", "value2"); + QueueClient client = serviceClient.createQueue(queueName, metadata).value(); + + Response propertiesResponse = client.getProperties(); + helper.assertResponseStatusCode(propertiesResponse, 200); + assertEquals(metadata, propertiesResponse.value().metadata()); + } + + @Override + public void createQueueTwiceSameMetadata() { + final String messageText = "Testing service client creating the same queue twice does not modify the queue"; + Map metadata = new HashMap<>(); + metadata.put("metadata1", "value1"); + metadata.put("metadata2", "value2"); + + EnqueuedMessage enqueuedMessage = serviceClient.createQueue(queueName, metadata).value().enqueueMessage(messageText).value(); + assertNotNull(enqueuedMessage); + + PeekedMessage peekedMessage = serviceClient.createQueue(queueName, metadata).value().peekMessages().iterator().next(); + assertEquals(messageText, peekedMessage.messageText()); + } + + @Override + public void createQueueTwiceDifferentMetadata() { + Map metadata = new HashMap<>(); + metadata.put("metadata1", "value1"); + metadata.put("metadata2", "value2"); + + try { + serviceClient.createQueue(queueName); + serviceClient.createQueue(queueName, metadata); + fail("Creating a queue twice with different metadata should throw an exception."); + } catch (Exception exception) { + helper.assertExceptionStatusCode(exception, 409); + } + } + + @Override + public void deleteExistingQueue() { + QueueClient client = serviceClient.createQueue(queueName).value(); + serviceClient.deleteQueue(queueName); + + try { + client.enqueueMessage("Expecting an exception"); + fail("Attempting to enqueue a message on a client that has been delete should throw an exception."); + } catch (Exception exception) { + helper.assertExceptionStatusCode(exception, 404); + } + } + + @Override + public void deleteNonExistentQueue() { + try { + serviceClient.deleteQueue(queueName); + fail("Attempting to delete a queue that doesn't exist should throw an exception."); + } catch (Exception exception) { + helper.assertExceptionStatusCode(exception, 404); + } + } + + @Override + public void listQueues() { + LinkedList testQueues = new LinkedList<>(); + for (int i = 0; i < 3; i++) { + QueueItem queue = new QueueItem().name(queueName + i); + testQueues.add(queue); + serviceClient.createQueue(queue.name(), queue.metadata()); + } + + for (QueueItem queue : serviceClient.listQueues(defaultSegmentOptions())) { + helper.assertQueuesAreEqual(testQueues.pop(), queue); + } + } + + @Override + public void listQueuesIncludeMetadata() { + Map metadata = new HashMap<>(); + metadata.put("metadata1", "value1"); + metadata.put("metadata2", "value2"); + + LinkedList testQueues = new LinkedList<>(); + for (int i = 0; i < 3; i++) { + QueueItem queue = new QueueItem().name(queueName + i); + if (i % 2 == 0) { + queue.metadata(metadata); + } + + testQueues.add(queue); + serviceClient.createQueue(queue.name(), queue.metadata()); + } + + for (QueueItem queue : serviceClient.listQueues(defaultSegmentOptions().includeMetadata(true))) { + helper.assertQueuesAreEqual(testQueues.pop(), queue); + } + } + + @Override + public void listQueuesWithPrefix() { + LinkedList testQueues = new LinkedList<>(); + for (int i = 0; i < 3; i++) { + QueueItem queue = new QueueItem(); + if (i % 2 == 0) { + queue.name(queueName + "prefix" + i); + testQueues.add(queue); + } else { + queue.name(queueName + i); + } + + serviceClient.createQueue(queue.name(), queue.metadata()); + } + + for (QueueItem queue : serviceClient.listQueues(defaultSegmentOptions().prefix(queueName + "prefix"))) { + helper.assertQueuesAreEqual(testQueues.pop(), queue); + } + } + + @Override + public void listQueuesWithLimit() { + LinkedList testQueues = new LinkedList<>(); + for (int i = 0; i < 3; i++) { + QueueItem queue = new QueueItem().name(queueName + i); + testQueues.add(queue); + serviceClient.createQueue(queue.name(), queue.metadata()); + } + + for (QueueItem queue : serviceClient.listQueues(defaultSegmentOptions().maxResults(2))) { + helper.assertQueuesAreEqual(testQueues.pop(), queue); + } + } + + @Override + public void setProperties() { + StorageServiceProperties originalProperties = serviceClient.getProperties().value(); + + RetentionPolicy retentionPolicy = new RetentionPolicy().enabled(true) + .days(3); + + Logging logging = new Logging().version("1.0") + .delete(true) + .write(true) + .retentionPolicy(retentionPolicy); + + Metrics metrics = new Metrics().enabled(true) + .includeAPIs(false) + .retentionPolicy(retentionPolicy) + .version("1.0"); + + StorageServiceProperties updatedProperties = new StorageServiceProperties().logging(logging) + .hourMetrics(metrics) + .minuteMetrics(metrics) + .cors(new ArrayList<>()); + + VoidResponse setResponse = serviceClient.setProperties(updatedProperties); + helper.assertResponseStatusCode(setResponse, 202); + + Response getResponse = serviceClient.getProperties(); + helper.assertQueueServicePropertiesAreEqual(updatedProperties, getResponse.value()); + + setResponse = serviceClient.setProperties(originalProperties); + helper.assertResponseStatusCode(setResponse, 202); + + getResponse = serviceClient.getProperties(); + helper.assertQueueServicePropertiesAreEqual(originalProperties, getResponse.value()); + } +} diff --git a/storage/client/queue/src/test/java/com/azure/storage/queue/QueueServiceClientTestsBase.java b/storage/client/queue/src/test/java/com/azure/storage/queue/QueueServiceClientTestsBase.java new file mode 100644 index 0000000000000..3b86144d6f225 --- /dev/null +++ b/storage/client/queue/src/test/java/com/azure/storage/queue/QueueServiceClientTestsBase.java @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.queue; + +import com.azure.core.test.TestBase; +import com.azure.storage.queue.models.QueuesSegmentOptions; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; + +public abstract class QueueServiceClientTestsBase extends TestBase { + String queueName; + TestHelpers helper; + + @Rule + public TestName testName = new TestName(); + + @Override + public String testName() { + return testName.getMethodName(); + } + + String getQueueName() { + return testResourceNamer.randomName("queue", 16).toLowerCase(); + } + + @Test + public abstract void getQueueDoesNotCreateAQueue(); + + @Test + public abstract void createQueue(); + + @Test + public abstract void createQueueWithMetadata(); + + @Test + public abstract void createQueueTwiceSameMetadata(); + + @Test + public abstract void createQueueTwiceDifferentMetadata(); + + @Test + public abstract void deleteExistingQueue(); + + @Test + public abstract void deleteNonExistentQueue(); + + @Test + public abstract void listQueues(); + + @Test + public abstract void listQueuesIncludeMetadata(); + + @Test + public abstract void listQueuesWithPrefix(); + + // TODO (alzimmer): This test is off for now until we determine how to handle paging with limited results + //@Test + public abstract void listQueuesWithLimit(); + + @Test + public abstract void setProperties(); + + QueuesSegmentOptions defaultSegmentOptions() { + return new QueuesSegmentOptions().prefix(queueName); + } +} diff --git a/storage/client/queue/src/test/java/com/azure/storage/queue/TestHelpers.java b/storage/client/queue/src/test/java/com/azure/storage/queue/TestHelpers.java new file mode 100644 index 0000000000000..bac3f552860fa --- /dev/null +++ b/storage/client/queue/src/test/java/com/azure/storage/queue/TestHelpers.java @@ -0,0 +1,161 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.storage.queue; + +import com.azure.core.http.rest.Response; +import com.azure.core.implementation.util.ImplUtils; +import com.azure.core.util.configuration.ConfigurationManager; +import com.azure.core.util.logging.ClientLogger; +import com.azure.storage.queue.models.CorsRule; +import com.azure.storage.queue.models.Logging; +import com.azure.storage.queue.models.Metrics; +import com.azure.storage.queue.models.QueueItem; +import com.azure.storage.queue.models.RetentionPolicy; +import com.azure.storage.queue.models.SignedIdentifier; +import com.azure.storage.queue.models.StorageErrorException; +import com.azure.storage.queue.models.StorageServiceProperties; + +import java.time.Duration; +import java.util.List; +import java.util.function.BiFunction; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * Contains helper methods for unit tests. + */ +class TestHelpers { + private final String azureStorageConnectionString = "AZURE_STORAGE_CONNECTION_STRING"; + private final String azureStorageQueueEndpoint = "AZURE_STORAGE_QUEUE_ENDPOINT"; + + T setupClient(BiFunction clientBuilder, boolean isPlaybackMode, ClientLogger logger) { + String connectionString = "DefaultEndpointsProtocol=https;AccountName=teststorage;AccountKey=atestaccountkey;EndpointSuffix=core.windows.net"; + String queueEndpoint = "https://teststorage.queue.core.windows.net/"; + + if (!isPlaybackMode) { + connectionString = ConfigurationManager.getConfiguration().get(azureStorageConnectionString); + queueEndpoint = ConfigurationManager.getConfiguration().get(azureStorageQueueEndpoint); + } + + if (ImplUtils.isNullOrEmpty(connectionString) && ImplUtils.isNullOrEmpty(queueEndpoint)) { + logger.asWarning().log("{} and {} must be set to build the testing client", azureStorageConnectionString, azureStorageQueueEndpoint); + fail(); + return null; + } + + return clientBuilder.apply(connectionString, queueEndpoint); + } + + void assertQueuesAreEqual(QueueItem expected, QueueItem actual) { + if (expected == null) { + assertNull(actual); + } else { + assertEquals(expected.name(), actual.name()); + + if (expected.metadata() != null && !ImplUtils.isNullOrEmpty(actual.metadata())) { + assertEquals(expected.metadata(), actual.metadata()); + } + } + } + + void assertQueueServicePropertiesAreEqual(StorageServiceProperties expected, StorageServiceProperties actual) { + if (expected == null) { + assertNull(actual); + } else { + assertMetricsAreEqual(expected.hourMetrics(), actual.hourMetrics()); + assertMetricsAreEqual(expected.minuteMetrics(), actual.minuteMetrics()); + assertLoggingAreEqual(expected.logging(), actual.logging()); + assertCorsAreEqual(expected.cors(), actual.cors()); + } + } + + private void assertMetricsAreEqual(Metrics expected, Metrics actual) { + if (expected == null) { + assertNull(actual); + } else { + assertEquals(expected.enabled(), actual.enabled()); + assertEquals(expected.includeAPIs(), actual.includeAPIs()); + assertEquals(expected.version(), actual.version()); + assertRetentionPoliciesAreEqual(expected.retentionPolicy(), actual.retentionPolicy()); + } + } + + private void assertLoggingAreEqual(Logging expected, Logging actual) { + if (expected == null) { + assertNull(actual); + } else { + assertEquals(expected.read(), actual.read()); + assertEquals(expected.write(), actual.write()); + assertEquals(expected.delete(), actual.delete()); + assertEquals(expected.version(), actual.version()); + assertRetentionPoliciesAreEqual(expected.retentionPolicy(), actual.retentionPolicy()); + } + } + + private void assertRetentionPoliciesAreEqual(RetentionPolicy expected, RetentionPolicy actual) { + if (expected == null) { + assertNull(actual); + } else { + assertEquals(expected.days(), actual.days()); + assertEquals(expected.enabled(), actual.enabled()); + } + } + + private void assertCorsAreEqual(List expected, List actual) { + if (expected == null) { + assertTrue(ImplUtils.isNullOrEmpty(actual)); + } else { + assertEquals(expected.size(), actual.size()); + for (int i = 0; i < expected.size(); i++) { + assertCorRulesAreEqual(expected.get(i), actual.get(i)); + } + } + } + + private void assertCorRulesAreEqual(CorsRule expected, CorsRule actual) { + if (expected == null) { + assertNull(actual); + } else { + assertEquals(expected.allowedHeaders(), actual.allowedHeaders()); + assertEquals(expected.allowedMethods(), actual.allowedMethods()); + assertEquals(expected.allowedOrigins(), actual.allowedOrigins()); + assertEquals(expected.exposedHeaders(), actual.exposedHeaders()); + assertEquals(expected.maxAgeInSeconds(), actual.maxAgeInSeconds()); + } + } + + void assertPermissionsAreEqual(SignedIdentifier expected, SignedIdentifier actual) { + assertEquals(expected.id(), actual.id()); + assertEquals(expected.accessPolicy().permission(), actual.accessPolicy().permission()); + assertEquals(expected.accessPolicy().start(), actual.accessPolicy().start()); + assertEquals(expected.accessPolicy().expiry(), actual.accessPolicy().expiry()); + } + + void assertResponseStatusCode(Response response, int expectedStatusCode) { + assertEquals(expectedStatusCode, response.statusCode()); + } + + void assertExceptionStatusCode(Throwable throwable, int expectedStatusCode) { + assertTrue(throwable instanceof StorageErrorException); + StorageErrorException storageErrorException = (StorageErrorException) throwable; + assertEquals(expectedStatusCode, storageErrorException.response().statusCode()); + } + + void sleepInRecordMode(Duration duration) { + String azureTestMode = ConfigurationManager.getConfiguration().get("AZURE_TEST_MODE"); + if ("RECORD".equalsIgnoreCase(azureTestMode)) { + sleep(duration); + } + } + + void sleep(Duration duration) { + try { + Thread.sleep(duration.toMillis()); + } catch (InterruptedException ex) { + // Ignore the error + } + } +} diff --git a/storage/client/queue/src/test/resources/session-records/clearMessages.json b/storage/client/queue/src/test/resources/session-records/clearMessages.json new file mode 100644 index 0000000000000..dab05b23fcfd5 --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/clearMessages.json @@ -0,0 +1,161 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue39460410", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd222863-4003-0028-6775-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:00 GMT" + } + }, { + "Method" : "POST", + "Uri" : "https://sima.queue.core.windows.net/queue39460410/messages?visibilitytimeout=0&messagettl=604800", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd2228a8-4003-0028-1b75-37b82d000000", + "Body" : "23ac390a-a15c-4036-b933-506b593e817aWed, 10 Jul 2019 23:18:01 GMTWed, 17 Jul 2019 23:18:01 GMTAgAAAAMAAAAAAAAAeGrWt3U31QE=Wed, 10 Jul 2019 23:18:01 GMT", + "Date" : "Wed, 10 Jul 2019 23:18:00 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "POST", + "Uri" : "https://sima.queue.core.windows.net/queue39460410/messages?visibilitytimeout=0&messagettl=604800", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd2228d0-4003-0028-3a75-37b82d000000", + "Body" : "0820c842-8ea6-466f-801e-8fdb0240f573Wed, 10 Jul 2019 23:18:01 GMTWed, 17 Jul 2019 23:18:01 GMTAgAAAAMAAAAAAAAARoLet3U31QE=Wed, 10 Jul 2019 23:18:01 GMT", + "Date" : "Wed, 10 Jul 2019 23:18:00 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "POST", + "Uri" : "https://sima.queue.core.windows.net/queue39460410/messages?visibilitytimeout=0&messagettl=604800", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd2228f8-4003-0028-5975-37b82d000000", + "Body" : "1a2b254b-4f28-4dba-8a56-5947173e08faWed, 10 Jul 2019 23:18:01 GMTWed, 17 Jul 2019 23:18:01 GMTAgAAAAMAAAAAAAAA5kvmt3U31QE=Wed, 10 Jul 2019 23:18:01 GMT", + "Date" : "Wed, 10 Jul 2019 23:18:00 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue39460410?comp=metadata", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "Content-Length" : "0", + "x-ms-approximate-messages-count" : "3", + "StatusCode" : "200", + "x-ms-request-id" : "dd222917-4003-0028-7175-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:00 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue39460410/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd22292f-4003-0028-0775-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:00 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue39460410?comp=metadata", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "Content-Length" : "0", + "x-ms-approximate-messages-count" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "dd222940-4003-0028-1575-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:01 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue39460410/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd22295c-4003-0028-2975-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:01 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue39460410", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd222973-4003-0028-3e75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:01 GMT" + } + } ], + "variables" : [ "queue39460410" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/clearMessagesQueueDoesNotExist.json b/storage/client/queue/src/test/resources/session-records/clearMessagesQueueDoesNotExist.json new file mode 100644 index 0000000000000..9fb11f03c6960 --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/clearMessagesQueueDoesNotExist.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue0499487b/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "dd223222-4003-0028-3175-37b82d000000", + "Body" : "QueueNotFoundThe specified queue does not exist.\nRequestId:dd223222-4003-0028-3175-37b82d000000\nTime:2019-07-10T23:18:05.7976908Z", + "Date" : "Wed, 10 Jul 2019 23:18:05 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue0499487b/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "dd223243-4003-0028-4e75-37b82d000000", + "Body" : "QueueNotFoundThe specified queue does not exist.\nRequestId:dd223243-4003-0028-4e75-37b82d000000\nTime:2019-07-10T23:18:05.8547315Z", + "Date" : "Wed, 10 Jul 2019 23:18:05 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "queue0499487b" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/createQueue.json b/storage/client/queue/src/test/resources/session-records/createQueue.json new file mode 100644 index 0000000000000..51d4b6d64f5b5 --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/createQueue.json @@ -0,0 +1,90 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue09262a67", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "0d29065a-6003-009e-2f76-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:47 GMT" + } + }, { + "Method" : "POST", + "Uri" : "https://sima.queue.core.windows.net/queue09262a67/messages?visibilitytimeout=0&messagettl=604800", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "0d290667-6003-009e-3b76-37b6d5000000", + "Body" : "94fd0809-1708-49c0-91ea-d4cf2141af7fWed, 10 Jul 2019 23:24:47 GMTWed, 17 Jul 2019 23:24:47 GMTAgAAAAMAAAAAAAAAREDwqXY31QE=Wed, 10 Jul 2019 23:24:47 GMT", + "Date" : "Wed, 10 Jul 2019 23:24:47 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net?prefix=queue09262a67&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "0d29067f-6003-009e-5376-37b6d5000000", + "Body" : "queue09262a67queue09262a67", + "Date" : "Wed, 10 Jul 2019 23:24:47 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue09262a67/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "0d290694-6003-009e-6676-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:47 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue09262a67", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "0d2906a1-6003-009e-7376-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:47 GMT" + } + } ], + "variables" : [ "queue09262a67" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/createQueueTwiceDifferentMetadata.json b/storage/client/queue/src/test/resources/session-records/createQueueTwiceDifferentMetadata.json new file mode 100644 index 0000000000000..e7297afd23b18 --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/createQueueTwiceDifferentMetadata.json @@ -0,0 +1,90 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue2172697c", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "0d290a57-6003-009e-6676-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:49 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue2172697c", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueAlreadyExists", + "retry-after" : "0", + "Content-Length" : "222", + "StatusCode" : "409", + "x-ms-request-id" : "0d290a73-6003-009e-7e76-37b6d5000000", + "Body" : "QueueAlreadyExistsThe specified queue already exists.\nRequestId:0d290a73-6003-009e-7e76-37b6d5000000\nTime:2019-07-10T23:24:49.9481623Z", + "Date" : "Wed, 10 Jul 2019 23:24:49 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net?prefix=queue2172697c&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "0d290a84-6003-009e-0f76-37b6d5000000", + "Body" : "queue2172697cqueue2172697c", + "Date" : "Wed, 10 Jul 2019 23:24:49 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue2172697c/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "0d290a9d-6003-009e-2776-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:50 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue2172697c", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "0d290aa4-6003-009e-2d76-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:50 GMT" + } + } ], + "variables" : [ "queue2172697c" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/createQueueTwiceSameMetadata.json b/storage/client/queue/src/test/resources/session-records/createQueueTwiceSameMetadata.json new file mode 100644 index 0000000000000..5f637cb224b88 --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/createQueueTwiceSameMetadata.json @@ -0,0 +1,125 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue3315117d", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "0d29094b-6003-009e-0a76-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:49 GMT" + } + }, { + "Method" : "POST", + "Uri" : "https://sima.queue.core.windows.net/queue3315117d/messages?visibilitytimeout=0&messagettl=604800", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "0d29096a-6003-009e-2176-37b6d5000000", + "Body" : "e59cbb06-dc9a-4fd8-89c9-a62bec05127eWed, 10 Jul 2019 23:24:49 GMTWed, 17 Jul 2019 23:24:49 GMTAgAAAAMAAAAAAAAAh80Wq3Y31QE=Wed, 10 Jul 2019 23:24:49 GMT", + "Date" : "Wed, 10 Jul 2019 23:24:49 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue3315117d", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "0d290979-6003-009e-2d76-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:49 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue3315117d/messages?peekonly=true", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "0d29099f-6003-009e-4b76-37b6d5000000", + "Body" : "e59cbb06-dc9a-4fd8-89c9-a62bec05127eWed, 10 Jul 2019 23:24:49 GMTWed, 17 Jul 2019 23:24:49 GMT0Testing service client creating the same queue twice does not modify the queue", + "Date" : "Wed, 10 Jul 2019 23:24:49 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net?prefix=queue3315117d&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "0d2909d4-6003-009e-7a76-37b6d5000000", + "Body" : "queue3315117dqueue3315117d", + "Date" : "Wed, 10 Jul 2019 23:24:49 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue3315117d/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "0d2909eb-6003-009e-0e76-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:49 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue3315117d", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "0d2909f9-6003-009e-1b76-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:49 GMT" + } + } ], + "variables" : [ "queue3315117d" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/createQueueWithMetadata.json b/storage/client/queue/src/test/resources/session-records/createQueueWithMetadata.json new file mode 100644 index 0000000000000..3c8381cb87770 --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/createQueueWithMetadata.json @@ -0,0 +1,91 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue2741227a", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "0d290770-6003-009e-1d76-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:48 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue2741227a?comp=metadata", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "Content-Length" : "0", + "x-ms-approximate-messages-count" : "0", + "x-ms-meta-metadata1" : "value1", + "StatusCode" : "200", + "x-ms-request-id" : "0d290788-6003-009e-3176-37b6d5000000", + "x-ms-meta-metadata2" : "value2", + "Date" : "Wed, 10 Jul 2019 23:24:48 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net?prefix=queue2741227a&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "0d2907ab-6003-009e-4e76-37b6d5000000", + "Body" : "queue2741227aqueue2741227a", + "Date" : "Wed, 10 Jul 2019 23:24:48 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue2741227a/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "0d2907b9-6003-009e-5976-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:48 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue2741227a", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "0d2907c4-6003-009e-6276-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:48 GMT" + } + } ], + "variables" : [ "queue2741227a" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/createTwiceDifferentMetadata.json b/storage/client/queue/src/test/resources/session-records/createTwiceDifferentMetadata.json new file mode 100644 index 0000000000000..2c1103e32db9c --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/createTwiceDifferentMetadata.json @@ -0,0 +1,71 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue0432116e", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd226070-4003-0028-4675-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:37 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue0432116e", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueAlreadyExists", + "retry-after" : "0", + "Content-Length" : "222", + "StatusCode" : "409", + "x-ms-request-id" : "dd22608d-4003-0028-5f75-37b82d000000", + "Body" : "QueueAlreadyExistsThe specified queue already exists.\nRequestId:dd22608d-4003-0028-5f75-37b82d000000\nTime:2019-07-10T23:18:38.0176580Z", + "Date" : "Wed, 10 Jul 2019 23:18:37 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue0432116e/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd22609f-4003-0028-7175-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:37 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue0432116e", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd2260a4-4003-0028-7675-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:37 GMT" + } + } ], + "variables" : [ "queue0432116e" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/createTwiceSameMetadata.json b/storage/client/queue/src/test/resources/session-records/createTwiceSameMetadata.json new file mode 100644 index 0000000000000..67e8f6692f9ce --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/createTwiceSameMetadata.json @@ -0,0 +1,68 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue1490420b", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd225ee9-4003-0028-6675-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:36 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue1490420b", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd225f04-4003-0028-7875-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:36 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue1490420b/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd225f21-4003-0028-1175-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:36 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue1490420b", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd225f32-4003-0028-2175-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:36 GMT" + } + } ], + "variables" : [ "queue1490420b" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/createWithMetadata.json b/storage/client/queue/src/test/resources/session-records/createWithMetadata.json new file mode 100644 index 0000000000000..e81225ed944ad --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/createWithMetadata.json @@ -0,0 +1,72 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue98908a15", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd2231c8-4003-0028-6375-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:05 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue98908a15?comp=metadata", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "Content-Length" : "0", + "x-ms-approximate-messages-count" : "0", + "x-ms-meta-metadata1" : "value1", + "StatusCode" : "200", + "x-ms-request-id" : "dd2231df-4003-0028-7475-37b82d000000", + "x-ms-meta-metadata2" : "value2", + "Date" : "Wed, 10 Jul 2019 23:18:05 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue98908a15/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd2231f6-4003-0028-0975-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:05 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue98908a15", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd223209-4003-0028-1b75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:05 GMT" + } + } ], + "variables" : [ "queue98908a15" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/createWithSASToken.json b/storage/client/queue/src/test/resources/session-records/createWithSASToken.json new file mode 100644 index 0000000000000..b0f0da481c199 --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/createWithSASToken.json @@ -0,0 +1,23 @@ +{ + "networkCallRecords" : [ { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue99626420/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "dd222843-4003-0028-5275-37b82d000000", + "Body" : "QueueNotFoundThe specified queue does not exist.\nRequestId:dd222843-4003-0028-5275-37b82d000000\nTime:2019-07-10T23:18:01.0102891Z", + "Date" : "Wed, 10 Jul 2019 23:18:00 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "queue99626420" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/createWithSharedKey.json b/storage/client/queue/src/test/resources/session-records/createWithSharedKey.json new file mode 100644 index 0000000000000..45ad59b25dadb --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/createWithSharedKey.json @@ -0,0 +1,23 @@ +{ + "networkCallRecords" : [ { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue46849fbc/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "dd226200-4003-0028-1275-37b82d000000", + "Body" : "QueueNotFoundThe specified queue does not exist.\nRequestId:dd226200-4003-0028-1275-37b82d000000\nTime:2019-07-10T23:18:39.1594759Z", + "Date" : "Wed, 10 Jul 2019 23:18:38 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "queue46849fbc" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/deleteExisting.json b/storage/client/queue/src/test/resources/session-records/deleteExisting.json new file mode 100644 index 0000000000000..ffaa890d17294 --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/deleteExisting.json @@ -0,0 +1,94 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue69325b7f", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd223362-4003-0028-4f75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:05 GMT" + } + }, { + "Method" : "POST", + "Uri" : "https://sima.queue.core.windows.net/queue69325b7f/messages?visibilitytimeout=0&messagettl=604800", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd223387-4003-0028-7075-37b82d000000", + "Body" : "23c03c48-1eb9-48c9-aded-b6c6adb39e9eWed, 10 Jul 2019 23:18:06 GMTWed, 17 Jul 2019 23:18:06 GMTAgAAAAMAAAAAAAAA1NHmunU31QE=Wed, 10 Jul 2019 23:18:06 GMT", + "Date" : "Wed, 10 Jul 2019 23:18:05 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue69325b7f", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd223398-4003-0028-0175-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:05 GMT" + } + }, { + "Method" : "POST", + "Uri" : "https://sima.queue.core.windows.net/queue69325b7f/messages?visibilitytimeout=0&messagettl=604800", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "dd225eb4-4003-0028-3c75-37b82d000000", + "Body" : "QueueNotFoundThe specified queue does not exist.\nRequestId:dd225eb4-4003-0028-3c75-37b82d000000\nTime:2019-07-10T23:18:36.4715552Z", + "Date" : "Wed, 10 Jul 2019 23:18:35 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue69325b7f/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "dd225ecd-4003-0028-5175-37b82d000000", + "Body" : "QueueNotFoundThe specified queue does not exist.\nRequestId:dd225ecd-4003-0028-5175-37b82d000000\nTime:2019-07-10T23:18:36.5175891Z", + "Date" : "Wed, 10 Jul 2019 23:18:36 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "queue69325b7f" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/deleteExistingQueue.json b/storage/client/queue/src/test/resources/session-records/deleteExistingQueue.json new file mode 100644 index 0000000000000..414ed34da046c --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/deleteExistingQueue.json @@ -0,0 +1,75 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue15727eac", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "0d290577-6003-009e-6976-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:46 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue15727eac", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "0d2905e7-6003-009e-4776-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:46 GMT" + } + }, { + "Method" : "POST", + "Uri" : "https://sima.queue.core.windows.net/queue15727eac/messages?visibilitytimeout=0&messagettl=604800", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "0d290618-6003-009e-7176-37b6d5000000", + "Body" : "QueueNotFoundThe specified queue does not exist.\nRequestId:0d290618-6003-009e-7176-37b6d5000000\nTime:2019-07-10T23:24:47.1091308Z", + "Date" : "Wed, 10 Jul 2019 23:24:47 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net?prefix=queue15727eac&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "0d290643-6003-009e-1b76-37b6d5000000", + "Body" : "queue15727eac", + "Date" : "Wed, 10 Jul 2019 23:24:47 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "queue15727eac" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/deleteMessage.json b/storage/client/queue/src/test/resources/session-records/deleteMessage.json new file mode 100644 index 0000000000000..4682bf4a1a131 --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/deleteMessage.json @@ -0,0 +1,124 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue1154048f", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd226162-4003-0028-1e75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:38 GMT" + } + }, { + "Method" : "POST", + "Uri" : "https://sima.queue.core.windows.net/queue1154048f/messages?visibilitytimeout=0&messagettl=604800", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd226172-4003-0028-2a75-37b82d000000", + "Body" : "5d20d094-2ec7-4544-9ad1-86fc3aa14140Wed, 10 Jul 2019 23:18:38 GMTWed, 17 Jul 2019 23:18:38 GMTAgAAAAMAAAAAAAAAskA9znU31QE=Wed, 10 Jul 2019 23:18:38 GMT", + "Date" : "Wed, 10 Jul 2019 23:18:38 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue1154048f/messages?numofmessages=1&visibilitytimeout=30", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "dd226183-4003-0028-3675-37b82d000000", + "Body" : "5d20d094-2ec7-4544-9ad1-86fc3aa14140Wed, 10 Jul 2019 23:18:38 GMTWed, 17 Jul 2019 23:18:38 GMTAgAAAAMAAAAAAAAAOIYm4HU31QE=Wed, 10 Jul 2019 23:19:08 GMT1test message", + "Date" : "Wed, 10 Jul 2019 23:18:38 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue1154048f/messages/5d20d094-2ec7-4544-9ad1-86fc3aa14140?popreceipt=AgAAAAMAAAAAAAAAOIYm4HU31QE%3d", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd226190-4003-0028-3f75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:38 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue1154048f?comp=metadata", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "Content-Length" : "0", + "x-ms-approximate-messages-count" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "dd2261a5-4003-0028-4e75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:38 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue1154048f/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd2261ba-4003-0028-5e75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:38 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue1154048f", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd2261ce-4003-0028-6c75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:38 GMT" + } + } ], + "variables" : [ "queue1154048f" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/deleteMessageInvalidMessageId.json b/storage/client/queue/src/test/resources/session-records/deleteMessageInvalidMessageId.json new file mode 100644 index 0000000000000..4eccb534a46f3 --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/deleteMessageInvalidMessageId.json @@ -0,0 +1,109 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue96938aad", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd221ea5-4003-0028-1075-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:17:53 GMT" + } + }, { + "Method" : "POST", + "Uri" : "https://sima.queue.core.windows.net/queue96938aad/messages?visibilitytimeout=0&messagettl=604800", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd221eb9-4003-0028-2375-37b82d000000", + "Body" : "17aa480c-b8aa-47ce-9fdb-753a00853819Wed, 10 Jul 2019 23:17:54 GMTWed, 17 Jul 2019 23:17:54 GMTAgAAAAMAAAAAAAAALPXXs3U31QE=Wed, 10 Jul 2019 23:17:54 GMT", + "Date" : "Wed, 10 Jul 2019 23:17:54 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue96938aad/messages?numofmessages=1&visibilitytimeout=30", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "dd221ecb-4003-0028-3575-37b82d000000", + "Body" : "17aa480c-b8aa-47ce-9fdb-753a00853819Wed, 10 Jul 2019 23:17:54 GMTWed, 17 Jul 2019 23:17:54 GMTAgAAAAMAAAAAAAAA4IjBxXU31QE=Wed, 10 Jul 2019 23:18:24 GMT1test message", + "Date" : "Wed, 10 Jul 2019 23:17:54 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue96938aad/messages/17aa480c-b8aa-47ce-9fdb-753a00853819random?popreceipt=AgAAAAMAAAAAAAAA4IjBxXU31QE%3d", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "MessageNotFound", + "retry-after" : "0", + "Content-Length" : "221", + "StatusCode" : "404", + "x-ms-request-id" : "dd221ee5-4003-0028-4d75-37b82d000000", + "Body" : "MessageNotFoundThe specified message does not exist.\nRequestId:dd221ee5-4003-0028-4d75-37b82d000000\nTime:2019-07-10T23:17:54.6327585Z", + "Date" : "Wed, 10 Jul 2019 23:17:54 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue96938aad/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd221ef8-4003-0028-5f75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:17:54 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue96938aad", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd221f17-4003-0028-7c75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:17:54 GMT" + } + } ], + "variables" : [ "queue96938aad" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/deleteMessageInvalidPopReceipt.json b/storage/client/queue/src/test/resources/session-records/deleteMessageInvalidPopReceipt.json new file mode 100644 index 0000000000000..01154b0b743de --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/deleteMessageInvalidPopReceipt.json @@ -0,0 +1,109 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue67327535", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd221e11-4003-0028-0575-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:17:53 GMT" + } + }, { + "Method" : "POST", + "Uri" : "https://sima.queue.core.windows.net/queue67327535/messages?visibilitytimeout=0&messagettl=604800", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd221e1c-4003-0028-0f75-37b82d000000", + "Body" : "a848cec9-d421-43a6-ac39-6a941d76d21bWed, 10 Jul 2019 23:17:54 GMTWed, 17 Jul 2019 23:17:54 GMTAgAAAAMAAAAAAAAAJDmgs3U31QE=Wed, 10 Jul 2019 23:17:54 GMT", + "Date" : "Wed, 10 Jul 2019 23:17:53 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue67327535/messages?numofmessages=1&visibilitytimeout=30", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "dd221e2b-4003-0028-1e75-37b82d000000", + "Body" : "a848cec9-d421-43a6-ac39-6a941d76d21bWed, 10 Jul 2019 23:17:54 GMTWed, 17 Jul 2019 23:17:54 GMTAgAAAAMAAAAAAAAANWmKxXU31QE=Wed, 10 Jul 2019 23:18:24 GMT1test message", + "Date" : "Wed, 10 Jul 2019 23:17:53 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue67327535/messages/a848cec9-d421-43a6-ac39-6a941d76d21b?popreceipt=AgAAAAMAAAAAAAAANWmKxXU31QE%3drandom", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "InvalidQueryParameterValue", + "retry-after" : "0", + "Content-Length" : "444", + "StatusCode" : "400", + "x-ms-request-id" : "dd221e4c-4003-0028-3c75-37b82d000000", + "Body" : "InvalidQueryParameterValueValue for one of the query parameters specified in the request URI is invalid.\nRequestId:dd221e4c-4003-0028-3c75-37b82d000000\nTime:2019-07-10T23:17:54.2735030ZpopreceiptAgAAAAMAAAAAAAAANWmKxXU31QE=randomInvalid pop receipt format", + "Date" : "Wed, 10 Jul 2019 23:17:53 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue67327535/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd221e6a-4003-0028-5575-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:17:53 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue67327535", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd221e7b-4003-0028-6675-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:17:53 GMT" + } + } ], + "variables" : [ "queue67327535" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/deleteMessageQueueDoesNotExist.json b/storage/client/queue/src/test/resources/session-records/deleteMessageQueueDoesNotExist.json new file mode 100644 index 0000000000000..c03dedb5c4887 --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/deleteMessageQueueDoesNotExist.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue055108e6/messages/invalid?popreceipt=call", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "dd226381-4003-0028-5575-37b82d000000", + "Body" : "QueueNotFoundThe specified queue does not exist.\nRequestId:dd226381-4003-0028-5575-37b82d000000\nTime:2019-07-10T23:18:40.1101585Z", + "Date" : "Wed, 10 Jul 2019 23:18:39 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue055108e6/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "dd226390-4003-0028-6375-37b82d000000", + "Body" : "QueueNotFoundThe specified queue does not exist.\nRequestId:dd226390-4003-0028-6375-37b82d000000\nTime:2019-07-10T23:18:40.1591931Z", + "Date" : "Wed, 10 Jul 2019 23:18:39 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "queue055108e6" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/deleteMetadata.json b/storage/client/queue/src/test/resources/session-records/deleteMetadata.json new file mode 100644 index 0000000000000..8ba83a6d8d215 --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/deleteMetadata.json @@ -0,0 +1,106 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue679519e2", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd2263ff-4003-0028-4975-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:39 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue679519e2?comp=metadata", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "Content-Length" : "0", + "x-ms-approximate-messages-count" : "0", + "x-ms-meta-metadata1" : "value1", + "StatusCode" : "200", + "x-ms-request-id" : "dd226409-4003-0028-5275-37b82d000000", + "x-ms-meta-metadata2" : "value2", + "Date" : "Wed, 10 Jul 2019 23:18:40 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue679519e2?comp=metadata", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd226418-4003-0028-6075-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:40 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue679519e2?comp=metadata", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "Content-Length" : "0", + "x-ms-approximate-messages-count" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "dd226420-4003-0028-6875-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:40 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue679519e2/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd226434-4003-0028-7b75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:40 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue679519e2", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd22644c-4003-0028-1275-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:40 GMT" + } + } ], + "variables" : [ "queue679519e2" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/deleteNonExistent.json b/storage/client/queue/src/test/resources/session-records/deleteNonExistent.json new file mode 100644 index 0000000000000..2fa6701c17acb --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/deleteNonExistent.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue04551c5d", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "dd226108-4003-0028-4e75-37b82d000000", + "Body" : "QueueNotFoundThe specified queue does not exist.\nRequestId:dd226108-4003-0028-4e75-37b82d000000\nTime:2019-07-10T23:18:38.4209499Z", + "Date" : "Wed, 10 Jul 2019 23:18:37 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue04551c5d/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "dd226119-4003-0028-5e75-37b82d000000", + "Body" : "QueueNotFoundThe specified queue does not exist.\nRequestId:dd226119-4003-0028-5e75-37b82d000000\nTime:2019-07-10T23:18:38.4689812Z", + "Date" : "Wed, 10 Jul 2019 23:18:37 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "queue04551c5d" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/deleteNonExistentQueue.json b/storage/client/queue/src/test/resources/session-records/deleteNonExistentQueue.json new file mode 100644 index 0000000000000..bf9105b3ccdc4 --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/deleteNonExistentQueue.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue82016a1d", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "0d290bd9-6003-009e-4476-37b6d5000000", + "Body" : "QueueNotFoundThe specified queue does not exist.\nRequestId:0d290bd9-6003-009e-4476-37b6d5000000\nTime:2019-07-10T23:24:50.9128531Z", + "Date" : "Wed, 10 Jul 2019 23:24:50 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net?prefix=queue82016a1d&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "0d290bf6-6003-009e-5b76-37b6d5000000", + "Body" : "queue82016a1d", + "Date" : "Wed, 10 Jul 2019 23:24:50 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "queue82016a1d" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/dequeueMessage.json b/storage/client/queue/src/test/resources/session-records/dequeueMessage.json new file mode 100644 index 0000000000000..6473031c5a50f --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/dequeueMessage.json @@ -0,0 +1,90 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue679346d1", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd221d34-4003-0028-3d75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:17:52 GMT" + } + }, { + "Method" : "POST", + "Uri" : "https://sima.queue.core.windows.net/queue679346d1/messages?visibilitytimeout=0&messagettl=604800", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd221d8c-4003-0028-0a75-37b82d000000", + "Body" : "b6c999ca-9091-4109-b579-c4bca39d216dWed, 10 Jul 2019 23:17:53 GMTWed, 17 Jul 2019 23:17:53 GMTAgAAAAMAAAAAAAAA9bc/s3U31QE=Wed, 10 Jul 2019 23:17:53 GMT", + "Date" : "Wed, 10 Jul 2019 23:17:53 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue679346d1/messages?numofmessages=1&visibilitytimeout=30", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "dd221da4-4003-0028-2275-37b82d000000", + "Body" : "b6c999ca-9091-4109-b579-c4bca39d216dWed, 10 Jul 2019 23:17:53 GMTWed, 17 Jul 2019 23:17:53 GMTAgAAAAMAAAAAAAAA11EwxXU31QE=Wed, 10 Jul 2019 23:18:23 GMT1test message", + "Date" : "Wed, 10 Jul 2019 23:17:53 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue679346d1/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd221da9-4003-0028-2775-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:17:53 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue679346d1", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd221db5-4003-0028-3275-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:17:53 GMT" + } + } ], + "variables" : [ "queue679346d1" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/dequeueMultipleMessages.json b/storage/client/queue/src/test/resources/session-records/dequeueMultipleMessages.json new file mode 100644 index 0000000000000..104a637b766db --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/dequeueMultipleMessages.json @@ -0,0 +1,109 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue6570759e", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd226228-4003-0028-2f75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:38 GMT" + } + }, { + "Method" : "POST", + "Uri" : "https://sima.queue.core.windows.net/queue6570759e/messages?visibilitytimeout=0&messagettl=604800", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd22623f-4003-0028-4475-37b82d000000", + "Body" : "3d48ff69-e261-4bea-839c-ca0edfc2af4cWed, 10 Jul 2019 23:18:39 GMTWed, 17 Jul 2019 23:18:39 GMTAgAAAAMAAAAAAAAAYwGHznU31QE=Wed, 10 Jul 2019 23:18:39 GMT", + "Date" : "Wed, 10 Jul 2019 23:18:38 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "POST", + "Uri" : "https://sima.queue.core.windows.net/queue6570759e/messages?visibilitytimeout=0&messagettl=604800", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd226254-4003-0028-5375-37b82d000000", + "Body" : "fd6a25b1-b3d3-4743-b5b3-072add1a4a86Wed, 10 Jul 2019 23:18:39 GMTWed, 17 Jul 2019 23:18:39 GMTAgAAAAMAAAAAAAAAz3yOznU31QE=Wed, 10 Jul 2019 23:18:39 GMT", + "Date" : "Wed, 10 Jul 2019 23:18:38 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue6570759e/messages?numofmessages=2&visibilitytimeout=30", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "dd226269-4003-0028-6475-37b82d000000", + "Body" : "3d48ff69-e261-4bea-839c-ca0edfc2af4cWed, 10 Jul 2019 23:18:39 GMTWed, 17 Jul 2019 23:18:39 GMTAgAAAAMAAAAAAAAAWsJ34HU31QE=Wed, 10 Jul 2019 23:19:09 GMT1test messagefd6a25b1-b3d3-4743-b5b3-072add1a4a86Wed, 10 Jul 2019 23:18:39 GMTWed, 17 Jul 2019 23:18:39 GMTAgAAAAMAAAAAAAAAWsJ34HU31QE=Wed, 10 Jul 2019 23:19:09 GMT1test message 2", + "Date" : "Wed, 10 Jul 2019 23:18:38 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue6570759e/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd226296-4003-0028-0975-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:38 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue6570759e", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd2262a9-4003-0028-1975-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:38 GMT" + } + } ], + "variables" : [ "queue6570759e" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/dequeueQueueDoesNotExist.json b/storage/client/queue/src/test/resources/session-records/dequeueQueueDoesNotExist.json new file mode 100644 index 0000000000000..3bef9c37aaf6e --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/dequeueQueueDoesNotExist.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue39822dce/messages?numofmessages=1&visibilitytimeout=30", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "dd2260e6-4003-0028-3075-37b82d000000", + "Body" : "QueueNotFoundThe specified queue does not exist.\nRequestId:dd2260e6-4003-0028-3075-37b82d000000\nTime:2019-07-10T23:18:38.3148712Z", + "Date" : "Wed, 10 Jul 2019 23:18:37 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue39822dce/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "dd2260f8-4003-0028-4175-37b82d000000", + "Body" : "QueueNotFoundThe specified queue does not exist.\nRequestId:dd2260f8-4003-0028-4175-37b82d000000\nTime:2019-07-10T23:18:38.3619056Z", + "Date" : "Wed, 10 Jul 2019 23:18:37 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "queue39822dce" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/dequeueTooManyMessages.json b/storage/client/queue/src/test/resources/session-records/dequeueTooManyMessages.json new file mode 100644 index 0000000000000..757caa567efa5 --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/dequeueTooManyMessages.json @@ -0,0 +1,71 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue03015bde", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd2226b1-4003-0028-0675-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:17:59 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue03015bde/messages?numofmessages=64&visibilitytimeout=30", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "OutOfRangeQueryParameterValue", + "retry-after" : "0", + "Content-Length" : "456", + "StatusCode" : "400", + "x-ms-request-id" : "dd2226cd-4003-0028-1d75-37b82d000000", + "Body" : "OutOfRangeQueryParameterValueOne of the query parameters specified in the request URI is outside the permissible range.\nRequestId:dd2226cd-4003-0028-1d75-37b82d000000\nTime:2019-07-10T23:18:00.2767678Znumofmessages64132", + "Date" : "Wed, 10 Jul 2019 23:17:59 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue03015bde/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd2226e0-4003-0028-2e75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:17:59 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue03015bde", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd2226fd-4003-0028-4a75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:17:59 GMT" + } + } ], + "variables" : [ "queue03015bde" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/enqueueEmptyMessage.json b/storage/client/queue/src/test/resources/session-records/enqueueEmptyMessage.json new file mode 100644 index 0000000000000..ed0aaef6517fc --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/enqueueEmptyMessage.json @@ -0,0 +1,90 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue76420ef8", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd2262ce-4003-0028-3475-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:39 GMT" + } + }, { + "Method" : "POST", + "Uri" : "https://sima.queue.core.windows.net/queue76420ef8/messages?visibilitytimeout=0&messagettl=604800", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd2262e1-4003-0028-4275-37b82d000000", + "Body" : "e1efc34f-03c7-4037-9597-6955381e8889Wed, 10 Jul 2019 23:18:39 GMTWed, 17 Jul 2019 23:18:39 GMTAgAAAAMAAAAAAAAAl6+7znU31QE=Wed, 10 Jul 2019 23:18:39 GMT", + "Date" : "Wed, 10 Jul 2019 23:18:39 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue76420ef8/messages?peekonly=true", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "dd2262ef-4003-0028-5075-37b82d000000", + "Body" : "e1efc34f-03c7-4037-9597-6955381e8889Wed, 10 Jul 2019 23:18:39 GMTWed, 17 Jul 2019 23:18:39 GMT0", + "Date" : "Wed, 10 Jul 2019 23:18:39 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue76420ef8/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd2262fe-4003-0028-5e75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:39 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue76420ef8", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd22630f-4003-0028-6f75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:39 GMT" + } + } ], + "variables" : [ "queue76420ef8" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/enqueueMessage.json b/storage/client/queue/src/test/resources/session-records/enqueueMessage.json new file mode 100644 index 0000000000000..ff3e72aedaacf --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/enqueueMessage.json @@ -0,0 +1,90 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue17032768", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd222a23-4003-0028-4b75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:01 GMT" + } + }, { + "Method" : "POST", + "Uri" : "https://sima.queue.core.windows.net/queue17032768/messages?visibilitytimeout=0&messagettl=604800", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd222a4a-4003-0028-6c75-37b82d000000", + "Body" : "7422f1b3-c1b1-4385-a945-6365e2e01caaWed, 10 Jul 2019 23:18:01 GMTWed, 17 Jul 2019 23:18:01 GMTAgAAAAMAAAAAAAAAQm1FuHU31QE=Wed, 10 Jul 2019 23:18:01 GMT", + "Date" : "Wed, 10 Jul 2019 23:18:01 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue17032768/messages?peekonly=true", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "dd222a74-4003-0028-1275-37b82d000000", + "Body" : "7422f1b3-c1b1-4385-a945-6365e2e01caaWed, 10 Jul 2019 23:18:01 GMTWed, 17 Jul 2019 23:18:01 GMT0test message", + "Date" : "Wed, 10 Jul 2019 23:18:01 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue17032768/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd222aa3-4003-0028-3975-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:01 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue17032768", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd222ac4-4003-0028-5675-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:01 GMT" + } + } ], + "variables" : [ "queue17032768" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/enqueueQueueDoesNotExist.json b/storage/client/queue/src/test/resources/session-records/enqueueQueueDoesNotExist.json new file mode 100644 index 0000000000000..9433eea40c38b --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/enqueueQueueDoesNotExist.json @@ -0,0 +1,43 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://sima.queue.core.windows.net/queue39910e47/messages?visibilitytimeout=0&messagettl=604800", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "dd2229a6-4003-0028-6475-37b82d000000", + "Body" : "QueueNotFoundThe specified queue does not exist.\nRequestId:dd2229a6-4003-0028-6475-37b82d000000\nTime:2019-07-10T23:18:01.6777634Z", + "Date" : "Wed, 10 Jul 2019 23:18:01 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue39910e47/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "dd2229bd-4003-0028-7575-37b82d000000", + "Body" : "QueueNotFoundThe specified queue does not exist.\nRequestId:dd2229bd-4003-0028-7575-37b82d000000\nTime:2019-07-10T23:18:01.7277985Z", + "Date" : "Wed, 10 Jul 2019 23:18:01 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "queue39910e47" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/enqueueShortTimeToLiveMessage.json b/storage/client/queue/src/test/resources/session-records/enqueueShortTimeToLiveMessage.json new file mode 100644 index 0000000000000..bb6b2172664f2 --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/enqueueShortTimeToLiveMessage.json @@ -0,0 +1,90 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue69359315", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd221f37-4003-0028-1c75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:17:54 GMT" + } + }, { + "Method" : "POST", + "Uri" : "https://sima.queue.core.windows.net/queue69359315/messages?visibilitytimeout=0&messagettl=2", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd221f43-4003-0028-2775-37b82d000000", + "Body" : "c96e7db1-1d4a-4743-b165-a5835968722aWed, 10 Jul 2019 23:17:54 GMTWed, 10 Jul 2019 23:17:56 GMTAgAAAAMAAAAAAAAApcYOtHU31QE=Wed, 10 Jul 2019 23:17:54 GMT", + "Date" : "Wed, 10 Jul 2019 23:17:54 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue69359315/messages?peekonly=true", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "dd222634-4003-0028-1675-37b82d000000", + "Body" : "", + "Date" : "Wed, 10 Jul 2019 23:17:59 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue69359315/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd22264c-4003-0028-2c75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:17:59 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue69359315", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd222666-4003-0028-4375-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:17:59 GMT" + } + } ], + "variables" : [ "queue69359315" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/getAccessPolicy.json b/storage/client/queue/src/test/resources/session-records/getAccessPolicy.json new file mode 100644 index 0000000000000..79f9317b619d1 --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/getAccessPolicy.json @@ -0,0 +1,71 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue276184e3", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd222bac-4003-0028-2175-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:02 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue276184e3?comp=acl", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "dd222bc0-4003-0028-3175-37b82d000000", + "Body" : "", + "Date" : "Wed, 10 Jul 2019 23:18:02 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue276184e3/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd222be3-4003-0028-4d75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:02 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue276184e3", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd222bfe-4003-0028-6375-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:02 GMT" + } + } ], + "variables" : [ "queue276184e3" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/getAccessPolicyQueueDoesNotExist.json b/storage/client/queue/src/test/resources/session-records/getAccessPolicyQueueDoesNotExist.json new file mode 100644 index 0000000000000..8c772f655188e --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/getAccessPolicyQueueDoesNotExist.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue66823709?comp=acl", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "dd22604d-4003-0028-2a75-37b82d000000", + "Body" : "QueueNotFoundThe specified queue does not exist.\nRequestId:dd22604d-4003-0028-2a75-37b82d000000\nTime:2019-07-10T23:18:37.8315254Z", + "Date" : "Wed, 10 Jul 2019 23:18:37 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue66823709/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "dd226056-4003-0028-3275-37b82d000000", + "Body" : "QueueNotFoundThe specified queue does not exist.\nRequestId:dd226056-4003-0028-3275-37b82d000000\nTime:2019-07-10T23:18:37.8795608Z", + "Date" : "Wed, 10 Jul 2019 23:18:37 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "queue66823709" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/getProperties.json b/storage/client/queue/src/test/resources/session-records/getProperties.json new file mode 100644 index 0000000000000..a7c0a0ec5332d --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/getProperties.json @@ -0,0 +1,72 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue36601253", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd2227b8-4003-0028-6075-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:00 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue36601253?comp=metadata", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "Content-Length" : "0", + "x-ms-approximate-messages-count" : "0", + "x-ms-meta-metadata1" : "value1", + "StatusCode" : "200", + "x-ms-request-id" : "dd2227d1-4003-0028-7675-37b82d000000", + "x-ms-meta-metadata2" : "value2", + "Date" : "Wed, 10 Jul 2019 23:18:00 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue36601253/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd2227f9-4003-0028-1775-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:00 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue36601253", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd222807-4003-0028-2575-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:00 GMT" + } + } ], + "variables" : [ "queue36601253" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/getPropertiesQueueDoesNotExist.json b/storage/client/queue/src/test/resources/session-records/getPropertiesQueueDoesNotExist.json new file mode 100644 index 0000000000000..fe98b22946df5 --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/getPropertiesQueueDoesNotExist.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue7527481d?comp=metadata", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "dd222681-4003-0028-5b75-37b82d000000", + "Body" : "QueueNotFoundThe specified queue does not exist.\nRequestId:dd222681-4003-0028-5b75-37b82d000000\nTime:2019-07-10T23:18:00.1126512Z", + "Date" : "Wed, 10 Jul 2019 23:17:59 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue7527481d/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "dd22269d-4003-0028-7375-37b82d000000", + "Body" : "QueueNotFoundThe specified queue does not exist.\nRequestId:dd22269d-4003-0028-7375-37b82d000000\nTime:2019-07-10T23:18:00.1626872Z", + "Date" : "Wed, 10 Jul 2019 23:17:59 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "queue7527481d" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/getQueueDoesNotCreateAQueue.json b/storage/client/queue/src/test/resources/session-records/getQueueDoesNotCreateAQueue.json new file mode 100644 index 0000000000000..7776d4fed4783 --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/getQueueDoesNotCreateAQueue.json @@ -0,0 +1,43 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://sima.queue.core.windows.net/queue48695d7f/messages?visibilitytimeout=0&messagettl=604800", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "0d290a18-6003-009e-3676-37b6d5000000", + "Body" : "QueueNotFoundThe specified queue does not exist.\nRequestId:0d290a18-6003-009e-3676-37b6d5000000\nTime:2019-07-10T23:24:49.7390111Z", + "Date" : "Wed, 10 Jul 2019 23:24:49 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net?prefix=queue48695d7f&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "0d290a2f-6003-009e-4876-37b6d5000000", + "Body" : "queue48695d7f", + "Date" : "Wed, 10 Jul 2019 23:24:49 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "queue48695d7f" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/listQueues.json b/storage/client/queue/src/test/resources/session-records/listQueues.json new file mode 100644 index 0000000000000..361f47abd400a --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/listQueues.json @@ -0,0 +1,186 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue455724b60", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "0d2907f4-6003-009e-0976-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:48 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue455724b61", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "0d290805-6003-009e-1376-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:48 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue455724b62", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "0d29080b-6003-009e-1876-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:48 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net?prefix=queue455724b6&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "0d290816-6003-009e-2276-37b6d5000000", + "Body" : "queue455724b6queue455724b60queue455724b61queue455724b62", + "Date" : "Wed, 10 Jul 2019 23:24:48 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net?prefix=queue455724b6&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "0d29083e-6003-009e-4676-37b6d5000000", + "Body" : "queue455724b6queue455724b60queue455724b61queue455724b62", + "Date" : "Wed, 10 Jul 2019 23:24:48 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue455724b60/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "0d290871-6003-009e-6e76-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:48 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue455724b60", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "0d290896-6003-009e-0676-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:48 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue455724b61/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "0d2908bc-6003-009e-2476-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:48 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue455724b61", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "0d2908df-6003-009e-3e76-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:49 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue455724b62/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "0d2908fd-6003-009e-5676-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:49 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue455724b62", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "0d29091f-6003-009e-6a76-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:49 GMT" + } + } ], + "variables" : [ "queue455724b6" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/listQueuesIncludeMetadata.json b/storage/client/queue/src/test/resources/session-records/listQueuesIncludeMetadata.json new file mode 100644 index 0000000000000..a0e836cf26701 --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/listQueuesIncludeMetadata.json @@ -0,0 +1,186 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue58738ae80", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "0d290c20-6003-009e-7b76-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:51 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue58738ae81", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "0d290c34-6003-009e-0a76-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:51 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue58738ae82", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "0d290c4e-6003-009e-1f76-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:51 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net?prefix=queue58738ae8&include=metadata&comp=list", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "0d290c6b-6003-009e-3376-37b6d5000000", + "Body" : "queue58738ae8queue58738ae80value1value2queue58738ae81queue58738ae82value1value2", + "Date" : "Wed, 10 Jul 2019 23:24:51 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net?prefix=queue58738ae8&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "0d290c8e-6003-009e-5176-37b6d5000000", + "Body" : "queue58738ae8queue58738ae80queue58738ae81queue58738ae82", + "Date" : "Wed, 10 Jul 2019 23:24:51 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue58738ae80/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "0d290caa-6003-009e-6876-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:51 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue58738ae80", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "0d290cc1-6003-009e-7a76-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:51 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue58738ae81/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "0d290cde-6003-009e-1176-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:51 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue58738ae81", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "0d290cf2-6003-009e-2376-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:51 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue58738ae82/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "0d290d14-6003-009e-4076-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:51 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue58738ae82", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "0d290d2b-6003-009e-5476-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:51 GMT" + } + } ], + "variables" : [ "queue58738ae8" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/listQueuesWithPrefix.json b/storage/client/queue/src/test/resources/session-records/listQueuesWithPrefix.json new file mode 100644 index 0000000000000..ff107058e29fc --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/listQueuesWithPrefix.json @@ -0,0 +1,186 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue423382efprefix0", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "0d290acd-6003-009e-5476-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:50 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue423382ef1", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "0d290ae3-6003-009e-6876-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:50 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue423382efprefix2", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "0d290afd-6003-009e-8076-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:50 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net?prefix=queue423382efprefix&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "0d290b0d-6003-009e-0f76-37b6d5000000", + "Body" : "queue423382efprefixqueue423382efprefix0queue423382efprefix2", + "Date" : "Wed, 10 Jul 2019 23:24:50 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net?prefix=queue423382ef&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "0d290b16-6003-009e-1776-37b6d5000000", + "Body" : "queue423382efqueue423382ef1queue423382efprefix0queue423382efprefix2", + "Date" : "Wed, 10 Jul 2019 23:24:50 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue423382ef1/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "0d290b38-6003-009e-3576-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:50 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue423382ef1", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "0d290b4c-6003-009e-4776-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:50 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue423382efprefix0/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "0d290b60-6003-009e-5976-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:50 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue423382efprefix0", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "0d290b71-6003-009e-6676-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:50 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue423382efprefix2/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "0d290b81-6003-009e-7576-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:50 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue423382efprefix2", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "0d290b9a-6003-009e-0b76-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:50 GMT" + } + } ], + "variables" : [ "queue423382ef" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/peekMessage.json b/storage/client/queue/src/test/resources/session-records/peekMessage.json new file mode 100644 index 0000000000000..540c460785a11 --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/peekMessage.json @@ -0,0 +1,90 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue54577e60", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd225fa8-4003-0028-0d75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:36 GMT" + } + }, { + "Method" : "POST", + "Uri" : "https://sima.queue.core.windows.net/queue54577e60/messages?visibilitytimeout=0&messagettl=604800", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd225fb8-4003-0028-1c75-37b82d000000", + "Body" : "53096a37-63bf-45d9-9172-4c8713ff282eWed, 10 Jul 2019 23:18:37 GMTWed, 17 Jul 2019 23:18:37 GMTAgAAAAMAAAAAAAAATsJgzXU31QE=Wed, 10 Jul 2019 23:18:37 GMT", + "Date" : "Wed, 10 Jul 2019 23:18:36 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue54577e60/messages?peekonly=true", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "dd225fd2-4003-0028-3375-37b82d000000", + "Body" : "53096a37-63bf-45d9-9172-4c8713ff282eWed, 10 Jul 2019 23:18:37 GMTWed, 17 Jul 2019 23:18:37 GMT0test message", + "Date" : "Wed, 10 Jul 2019 23:18:36 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue54577e60/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd225fe6-4003-0028-4775-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:36 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue54577e60", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd225ff7-4003-0028-5875-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:37 GMT" + } + } ], + "variables" : [ "queue54577e60" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/peekMultipleMessages.json b/storage/client/queue/src/test/resources/session-records/peekMultipleMessages.json new file mode 100644 index 0000000000000..5ea0a89b15f98 --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/peekMultipleMessages.json @@ -0,0 +1,109 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue17925938", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd222afc-4003-0028-0875-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:01 GMT" + } + }, { + "Method" : "POST", + "Uri" : "https://sima.queue.core.windows.net/queue17925938/messages?visibilitytimeout=0&messagettl=604800", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd222b1e-4003-0028-2375-37b82d000000", + "Body" : "86c3b192-2c4f-4bd4-89bf-07553c141ac0Wed, 10 Jul 2019 23:18:02 GMTWed, 17 Jul 2019 23:18:02 GMTAgAAAAMAAAAAAAAAI8dyuHU31QE=Wed, 10 Jul 2019 23:18:02 GMT", + "Date" : "Wed, 10 Jul 2019 23:18:01 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "POST", + "Uri" : "https://sima.queue.core.windows.net/queue17925938/messages?visibilitytimeout=0&messagettl=604800", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd222b4a-4003-0028-4775-37b82d000000", + "Body" : "1b2d50b7-3540-4dd4-a480-8549b5207fe7Wed, 10 Jul 2019 23:18:02 GMTWed, 17 Jul 2019 23:18:02 GMTAgAAAAMAAAAAAAAAqWl6uHU31QE=Wed, 10 Jul 2019 23:18:02 GMT", + "Date" : "Wed, 10 Jul 2019 23:18:01 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue17925938/messages?numofmessages=2&peekonly=true", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "dd222b67-4003-0028-6175-37b82d000000", + "Body" : "86c3b192-2c4f-4bd4-89bf-07553c141ac0Wed, 10 Jul 2019 23:18:02 GMTWed, 17 Jul 2019 23:18:02 GMT0test message1b2d50b7-3540-4dd4-a480-8549b5207fe7Wed, 10 Jul 2019 23:18:02 GMTWed, 17 Jul 2019 23:18:02 GMT0test message 2", + "Date" : "Wed, 10 Jul 2019 23:18:01 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue17925938/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd222b7e-4003-0028-7675-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:01 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue17925938", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd222b8e-4003-0028-0675-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:01 GMT" + } + } ], + "variables" : [ "queue17925938" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/peekQueueDoesNotExist.json b/storage/client/queue/src/test/resources/session-records/peekQueueDoesNotExist.json new file mode 100644 index 0000000000000..c9de737c16179 --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/peekQueueDoesNotExist.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue5442745c/messages?peekonly=true", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "dd221dd8-4003-0028-5075-37b82d000000", + "Body" : "QueueNotFoundThe specified queue does not exist.\nRequestId:dd221dd8-4003-0028-5075-37b82d000000\nTime:2019-07-10T23:17:53.9252561Z", + "Date" : "Wed, 10 Jul 2019 23:17:53 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue5442745c/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "dd221df7-4003-0028-6d75-37b82d000000", + "Body" : "QueueNotFoundThe specified queue does not exist.\nRequestId:dd221df7-4003-0028-6d75-37b82d000000\nTime:2019-07-10T23:17:54.0093158Z", + "Date" : "Wed, 10 Jul 2019 23:17:53 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "queue5442745c" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/peekTooManyMessages.json b/storage/client/queue/src/test/resources/session-records/peekTooManyMessages.json new file mode 100644 index 0000000000000..f653e97e1e0ce --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/peekTooManyMessages.json @@ -0,0 +1,71 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue80227035", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd22612f-4003-0028-7275-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:38 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue80227035/messages?numofmessages=64&peekonly=true", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "OutOfRangeQueryParameterValue", + "retry-after" : "0", + "Content-Length" : "456", + "StatusCode" : "400", + "x-ms-request-id" : "dd226138-4003-0028-7a75-37b82d000000", + "Body" : "OutOfRangeQueryParameterValueOne of the query parameters specified in the request URI is outside the permissible range.\nRequestId:dd226138-4003-0028-7a75-37b82d000000\nTime:2019-07-10T23:18:38.5780592Znumofmessages64132", + "Date" : "Wed, 10 Jul 2019 23:18:38 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue80227035/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd22613d-4003-0028-7f75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:38 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue80227035", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd226149-4003-0028-0975-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:38 GMT" + } + } ], + "variables" : [ "queue80227035" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/setAccessPolicy.json b/storage/client/queue/src/test/resources/session-records/setAccessPolicy.json new file mode 100644 index 0000000000000..ac6e9881a669f --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/setAccessPolicy.json @@ -0,0 +1,88 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue656812ca", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd225f53-4003-0028-4075-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:36 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue656812ca?comp=acl", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd225f6a-4003-0028-5575-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:36 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue656812ca?comp=acl", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "dd225f7e-4003-0028-6675-37b82d000000", + "Body" : "testpermission2000-01-01T00:00:00.0000000Z2020-01-01T00:00:00.0000000Zraup", + "Date" : "Wed, 10 Jul 2019 23:18:36 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue656812ca/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd225f8c-4003-0028-7475-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:36 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue656812ca", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd225f98-4003-0028-7e75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:36 GMT" + } + } ], + "variables" : [ "queue656812ca" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/setAccessPolicyQueueDoesNotExist.json b/storage/client/queue/src/test/resources/session-records/setAccessPolicyQueueDoesNotExist.json new file mode 100644 index 0000000000000..b2b42bce18c37 --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/setAccessPolicyQueueDoesNotExist.json @@ -0,0 +1,43 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue01052e33?comp=acl", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "InvalidXmlDocument", + "retry-after" : "0", + "Content-Length" : "294", + "StatusCode" : "400", + "x-ms-request-id" : "dd22645d-4003-0028-2375-37b82d000000", + "Body" : "InvalidXmlDocumentXML specified is not syntactically valid.\nRequestId:dd22645d-4003-0028-2375-37b82d000000\nTime:2019-07-10T23:18:40.8086534Z00", + "Date" : "Wed, 10 Jul 2019 23:18:40 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue01052e33/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "dd22646a-4003-0028-2f75-37b82d000000", + "Body" : "QueueNotFoundThe specified queue does not exist.\nRequestId:dd22646a-4003-0028-2f75-37b82d000000\nTime:2019-07-10T23:18:40.8566884Z", + "Date" : "Wed, 10 Jul 2019 23:18:40 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "queue01052e33" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/setInvalidAccessPolicy.json b/storage/client/queue/src/test/resources/session-records/setInvalidAccessPolicy.json new file mode 100644 index 0000000000000..fe186f2b4ac1c --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/setInvalidAccessPolicy.json @@ -0,0 +1,72 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue424460ef", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd226338-4003-0028-1475-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:39 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue424460ef?comp=acl", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "InvalidXmlDocument", + "retry-after" : "0", + "Content-Length" : "371", + "StatusCode" : "400", + "x-ms-request-id" : "dd22634c-4003-0028-2575-37b82d000000", + "Body" : "InvalidXmlDocumentXML specified is not syntactically valid.\nRequestId:dd22634c-4003-0028-2575-37b82d000000\nTime:2019-07-10T23:18:39.9250246Z1304Signed identifier ID cannot be empty or over 64 characters in length", + "Date" : "Wed, 10 Jul 2019 23:18:39 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue424460ef/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd22635d-4003-0028-3675-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:39 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue424460ef", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd226363-4003-0028-3a75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:39 GMT" + } + } ], + "variables" : [ "queue424460ef" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/setInvalidMetadata.json b/storage/client/queue/src/test/resources/session-records/setInvalidMetadata.json new file mode 100644 index 0000000000000..eb9faafed36f9 --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/setInvalidMetadata.json @@ -0,0 +1,71 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue38693e14", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd22272a-4003-0028-7175-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:17:59 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue38693e14?comp=metadata", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "EmptyMetadataKey", + "retry-after" : "0", + "Content-Length" : "285", + "StatusCode" : "400", + "x-ms-request-id" : "dd22274f-4003-0028-0b75-37b82d000000", + "Body" : "EmptyMetadataKeyThe key for one of the metadata key-value pairs is empty.\nRequestId:dd22274f-4003-0028-0b75-37b82d000000\nTime:2019-07-10T23:18:00.5359517Zbad metadata", + "Date" : "Wed, 10 Jul 2019 23:18:00 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue38693e14/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd222779-4003-0028-2875-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:00 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue38693e14", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd222798-4003-0028-4375-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:00 GMT" + } + } ], + "variables" : [ "queue38693e14" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/setMetadata.json b/storage/client/queue/src/test/resources/session-records/setMetadata.json new file mode 100644 index 0000000000000..5d60ce89a352d --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/setMetadata.json @@ -0,0 +1,88 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue625780b6", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd2263a4-4003-0028-7675-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:39 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue625780b6?comp=metadata", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd2263c0-4003-0028-0f75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:39 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue625780b6?comp=metadata", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "Content-Length" : "0", + "x-ms-approximate-messages-count" : "0", + "x-ms-meta-metadata1" : "value1", + "StatusCode" : "200", + "x-ms-request-id" : "dd2263d2-4003-0028-2075-37b82d000000", + "x-ms-meta-metadata2" : "value2", + "Date" : "Wed, 10 Jul 2019 23:18:39 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue625780b6/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd2263dd-4003-0028-2a75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:39 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue625780b6", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd2263ea-4003-0028-3775-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:39 GMT" + } + } ], + "variables" : [ "queue625780b6" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/setMetadataQueueDoesNotExist.json b/storage/client/queue/src/test/resources/session-records/setMetadataQueueDoesNotExist.json new file mode 100644 index 0000000000000..4d7536d402ced --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/setMetadataQueueDoesNotExist.json @@ -0,0 +1,42 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue570099c0?comp=metadata", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "dd2260d2-4003-0028-2175-37b82d000000", + "Body" : "QueueNotFoundThe specified queue does not exist.\nRequestId:dd2260d2-4003-0028-2175-37b82d000000\nTime:2019-07-10T23:18:38.2057923Z", + "Date" : "Wed, 10 Jul 2019 23:18:37 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue570099c0/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "dd2260d9-4003-0028-2775-37b82d000000", + "Body" : "QueueNotFoundThe specified queue does not exist.\nRequestId:dd2260d9-4003-0028-2775-37b82d000000\nTime:2019-07-10T23:18:38.2538267Z", + "Date" : "Wed, 10 Jul 2019 23:18:37 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "queue570099c0" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/setProperties.json b/storage/client/queue/src/test/resources/session-records/setProperties.json new file mode 100644 index 0000000000000..3c832f439b708 --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/setProperties.json @@ -0,0 +1,114 @@ +{ + "networkCallRecords" : [ { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net?restype=service&comp=properties", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "0d2906b5-6003-009e-0476-37b6d5000000", + "Body" : "1.0falsefalsefalsefalse1.0truetruetrue71.0falsefalse", + "Date" : "Wed, 10 Jul 2019 23:24:47 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net?restype=service&comp=properties", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "0d2906d0-6003-009e-1a76-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:47 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net?restype=service&comp=properties", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "0d290712-6003-009e-4e76-37b6d5000000", + "Body" : "1.0falsetruetruetrue31.0truefalsetrue31.0truefalsetrue3", + "Date" : "Wed, 10 Jul 2019 23:24:47 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net?restype=service&comp=properties", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "0d290721-6003-009e-5b76-37b6d5000000", + "Date" : "Wed, 10 Jul 2019 23:24:48 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net?restype=service&comp=properties", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "0d290739-6003-009e-7076-37b6d5000000", + "Body" : "1.0falsefalsefalsefalse1.0truetruetrue71.0falsefalse", + "Date" : "Wed, 10 Jul 2019 23:24:48 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net?prefix=queue49487893&include=&comp=list", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "0d29074d-6003-009e-0176-37b6d5000000", + "Body" : "queue49487893", + "Date" : "Wed, 10 Jul 2019 23:24:48 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "queue49487893" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/setTooManyAccessPolicies.json b/storage/client/queue/src/test/resources/session-records/setTooManyAccessPolicies.json new file mode 100644 index 0000000000000..adee338b14141 --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/setTooManyAccessPolicies.json @@ -0,0 +1,72 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue265747a2", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd22600f-4003-0028-7075-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:37 GMT" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue265747a2?comp=acl", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "InvalidXmlDocument", + "retry-after" : "0", + "Content-Length" : "294", + "StatusCode" : "400", + "x-ms-request-id" : "dd226022-4003-0028-0175-37b82d000000", + "Body" : "InvalidXmlDocumentXML specified is not syntactically valid.\nRequestId:dd226022-4003-0028-0175-37b82d000000\nTime:2019-07-10T23:18:37.6694120Z00", + "Date" : "Wed, 10 Jul 2019 23:18:37 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue265747a2/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd226034-4003-0028-1175-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:37 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue265747a2", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd226043-4003-0028-2075-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:37 GMT" + } + } ], + "variables" : [ "queue265747a2" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/updateMessage.json b/storage/client/queue/src/test/resources/session-records/updateMessage.json new file mode 100644 index 0000000000000..46e184bda045c --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/updateMessage.json @@ -0,0 +1,128 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue4236461c", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd222c30-4003-0028-0f75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:02 GMT" + } + }, { + "Method" : "POST", + "Uri" : "https://sima.queue.core.windows.net/queue4236461c/messages?visibilitytimeout=0&messagettl=604800", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd222c40-4003-0028-1c75-37b82d000000", + "Body" : "c3781181-9103-468e-b645-f6dbb100bd9cWed, 10 Jul 2019 23:18:02 GMTWed, 17 Jul 2019 23:18:02 GMTAgAAAAMAAAAAAAAAjQ/SuHU31QE=Wed, 10 Jul 2019 23:18:02 GMT", + "Date" : "Wed, 10 Jul 2019 23:18:02 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue4236461c/messages?numofmessages=1&visibilitytimeout=30", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "dd222c62-4003-0028-3b75-37b82d000000", + "Body" : "c3781181-9103-468e-b645-f6dbb100bd9cWed, 10 Jul 2019 23:18:02 GMTWed, 17 Jul 2019 23:18:02 GMTAgAAAAMAAAAAAAAAEAO9ynU31QE=Wed, 10 Jul 2019 23:18:32 GMT1test message", + "Date" : "Wed, 10 Jul 2019 23:18:02 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue4236461c/messages/c3781181-9103-468e-b645-f6dbb100bd9c?popreceipt=AgAAAAMAAAAAAAAAEAO9ynU31QE%3d&visibilitytimeout=1", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-time-next-visible" : "Wed, 10 Jul 2019 23:18:04 GMT", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd222c7f-4003-0028-5475-37b82d000000", + "x-ms-popreceipt" : "AwAAAAMAAAAAAAAAZuN9uXU31QEBAAAA", + "Date" : "Wed, 10 Jul 2019 23:18:02 GMT" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue4236461c/messages?peekonly=true", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "dd2230d0-4003-0028-1b75-37b82d000000", + "Body" : "c3781181-9103-468e-b645-f6dbb100bd9cWed, 10 Jul 2019 23:18:02 GMTWed, 17 Jul 2019 23:18:02 GMT1updated test message", + "Date" : "Wed, 10 Jul 2019 23:18:04 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue4236461c/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd2230e9-4003-0028-2e75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:04 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue4236461c", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd223113-4003-0028-4f75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:04 GMT" + } + } ], + "variables" : [ "queue4236461c" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/updateMessageInvalidMessageId.json b/storage/client/queue/src/test/resources/session-records/updateMessageInvalidMessageId.json new file mode 100644 index 0000000000000..eae58afb9650c --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/updateMessageInvalidMessageId.json @@ -0,0 +1,110 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue10019121", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd223139-4003-0028-6d75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:04 GMT" + } + }, { + "Method" : "POST", + "Uri" : "https://sima.queue.core.windows.net/queue10019121/messages?visibilitytimeout=0&messagettl=604800", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd223159-4003-0028-0475-37b82d000000", + "Body" : "e3dbec12-5e3c-47f4-abd9-fbdf6e44746dWed, 10 Jul 2019 23:18:05 GMTWed, 17 Jul 2019 23:18:05 GMTAgAAAAMAAAAAAAAA7UtBunU31QE=Wed, 10 Jul 2019 23:18:05 GMT", + "Date" : "Wed, 10 Jul 2019 23:18:04 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue10019121/messages?numofmessages=1&visibilitytimeout=30", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "dd223169-4003-0028-1475-37b82d000000", + "Body" : "e3dbec12-5e3c-47f4-abd9-fbdf6e44746dWed, 10 Jul 2019 23:18:05 GMTWed, 17 Jul 2019 23:18:05 GMTAgAAAAMAAAAAAAAAc5EqzHU31QE=Wed, 10 Jul 2019 23:18:35 GMT1test message", + "Date" : "Wed, 10 Jul 2019 23:18:04 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue10019121/messages/e3dbec12-5e3c-47f4-abd9-fbdf6e44746drandom?popreceipt=AgAAAAMAAAAAAAAAc5EqzHU31QE%3d&visibilitytimeout=1", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "MessageNotFound", + "retry-after" : "0", + "Content-Length" : "221", + "StatusCode" : "404", + "x-ms-request-id" : "dd22317f-4003-0028-2775-37b82d000000", + "Body" : "MessageNotFoundThe specified message does not exist.\nRequestId:dd22317f-4003-0028-2775-37b82d000000\nTime:2019-07-10T23:18:05.3974051Z", + "Date" : "Wed, 10 Jul 2019 23:18:04 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue10019121/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd223195-4003-0028-3975-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:04 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue10019121", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd2231a9-4003-0028-4975-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:05 GMT" + } + } ], + "variables" : [ "queue10019121" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/updateMessageInvalidPopReceipt.json b/storage/client/queue/src/test/resources/session-records/updateMessageInvalidPopReceipt.json new file mode 100644 index 0000000000000..f7795c878f27f --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/updateMessageInvalidPopReceipt.json @@ -0,0 +1,110 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue41380e30", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd22326c-4003-0028-6c75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:05 GMT" + } + }, { + "Method" : "POST", + "Uri" : "https://sima.queue.core.windows.net/queue41380e30/messages?visibilitytimeout=0&messagettl=604800", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "StatusCode" : "201", + "x-ms-request-id" : "dd22329c-4003-0028-1475-37b82d000000", + "Body" : "8900ad74-7404-4cd2-8b4b-41e70995dc1fWed, 10 Jul 2019 23:18:06 GMTWed, 17 Jul 2019 23:18:06 GMTAgAAAAMAAAAAAAAA/TyvunU31QE=Wed, 10 Jul 2019 23:18:06 GMT", + "Date" : "Wed, 10 Jul 2019 23:18:05 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "GET", + "Uri" : "https://sima.queue.core.windows.net/queue41380e30/messages?numofmessages=1&visibilitytimeout=30", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "retry-after" : "0", + "StatusCode" : "200", + "x-ms-request-id" : "dd2232d5-4003-0028-4575-37b82d000000", + "Body" : "8900ad74-7404-4cd2-8b4b-41e70995dc1fWed, 10 Jul 2019 23:18:06 GMTWed, 17 Jul 2019 23:18:06 GMTAgAAAAMAAAAAAAAA+kWZzHU31QE=Wed, 10 Jul 2019 23:18:36 GMT1test message", + "Date" : "Wed, 10 Jul 2019 23:18:05 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue41380e30/messages/8900ad74-7404-4cd2-8b4b-41e70995dc1f?popreceipt=AgAAAAMAAAAAAAAA%2bkWZzHU31QE%3drandom&visibilitytimeout=1", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "InvalidQueryParameterValue", + "retry-after" : "0", + "Content-Length" : "444", + "StatusCode" : "400", + "x-ms-request-id" : "dd2232ff-4003-0028-6d75-37b82d000000", + "Body" : "InvalidQueryParameterValueValue for one of the query parameters specified in the request URI is invalid.\nRequestId:dd2232ff-4003-0028-6d75-37b82d000000\nTime:2019-07-10T23:18:06.1209188ZpopreceiptAgAAAAMAAAAAAAAA+kWZzHU31QE=randomInvalid pop receipt format", + "Date" : "Wed, 10 Jul 2019 23:18:05 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue41380e30/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd22331d-4003-0028-0a75-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:05 GMT" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue41380e30", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "dd223333-4003-0028-2075-37b82d000000", + "Date" : "Wed, 10 Jul 2019 23:18:05 GMT" + } + } ], + "variables" : [ "queue41380e30" ] +} \ No newline at end of file diff --git a/storage/client/queue/src/test/resources/session-records/updateMessageQueueDoesNotExist.json b/storage/client/queue/src/test/resources/session-records/updateMessageQueueDoesNotExist.json new file mode 100644 index 0000000000000..9341ea4a79897 --- /dev/null +++ b/storage/client/queue/src/test/resources/session-records/updateMessageQueueDoesNotExist.json @@ -0,0 +1,43 @@ +{ + "networkCallRecords" : [ { + "Method" : "PUT", + "Uri" : "https://sima.queue.core.windows.net/queue77530b22/messages/doesn't?popreceipt=exist&visibilitytimeout=5", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0", + "Content-Type" : "application/xml; charset=utf-8" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "InvalidQueryParameterValue", + "retry-after" : "0", + "Content-Length" : "415", + "StatusCode" : "400", + "x-ms-request-id" : "dd2229e1-4003-0028-1475-37b82d000000", + "Body" : "InvalidQueryParameterValueValue for one of the query parameters specified in the request URI is invalid.\nRequestId:dd2229e1-4003-0028-1475-37b82d000000\nTime:2019-07-10T23:18:01.7878412ZpopreceiptexistInvalid pop receipt format", + "Date" : "Wed, 10 Jul 2019 23:18:01 GMT", + "Content-Type" : "application/xml" + } + }, { + "Method" : "DELETE", + "Uri" : "https://sima.queue.core.windows.net/queue77530b22/messages", + "Headers" : { + "x-ms-version" : "2018-03-28", + "User-Agent" : "azsdk-java-storage-queue/1.0.0-SNAPSHOT 1.8.0_201; Windows 10 10.0" + }, + "Response" : { + "x-ms-version" : "2018-03-28", + "Server" : "Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0", + "x-ms-error-code" : "QueueNotFound", + "retry-after" : "0", + "Content-Length" : "217", + "StatusCode" : "404", + "x-ms-request-id" : "dd2229fb-4003-0028-2a75-37b82d000000", + "Body" : "QueueNotFoundThe specified queue does not exist.\nRequestId:dd2229fb-4003-0028-2a75-37b82d000000\nTime:2019-07-10T23:18:01.8368763Z", + "Date" : "Wed, 10 Jul 2019 23:18:01 GMT", + "Content-Type" : "application/xml" + } + } ], + "variables" : [ "queue77530b22" ] +} \ No newline at end of file