diff --git a/sdk/storage/azure-storage-file-share/CHANGELOG.md b/sdk/storage/azure-storage-file-share/CHANGELOG.md index 7d314d9e8466b..1f69d2178a0ae 100644 --- a/sdk/storage/azure-storage-file-share/CHANGELOG.md +++ b/sdk/storage/azure-storage-file-share/CHANGELOG.md @@ -3,6 +3,8 @@ ## 12.33.0-beta.1 (Unreleased) ### Features Added +- Added support for retrieving file and directory properties by file ID +- Added support for retrieving a file's hard links by file ID on SMB shares. - Added support for change feed on premium file shares. Added `ShareCreateOptions.setChangeFeedEnabled`, `setChangeFeedRetentionInDays` and `ShareSetPropertiesOptions.setChangeFeedEnabled`, `setChangeFeedRetentionInDays` to enable and configure change feed. diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareAsyncClient.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareAsyncClient.java index 1900beac97b1b..0cd2790f5cc0d 100644 --- a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareAsyncClient.java +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareAsyncClient.java @@ -21,6 +21,7 @@ import com.azure.storage.common.implementation.SasImplUtils; import com.azure.storage.common.implementation.StorageImplUtils; import com.azure.storage.file.share.implementation.AzureFileStorageImpl; +import com.azure.storage.file.share.implementation.ShareErrors; import com.azure.storage.file.share.implementation.models.SharePermission; import com.azure.storage.file.share.implementation.util.ModelHelper; import com.azure.storage.file.share.implementation.util.ShareSasImplUtil; @@ -173,6 +174,27 @@ public ShareDirectoryAsyncClient getDirectoryClient(String directoryName) { serviceVersion, sasToken); } + /** + * Creates a directory client that addresses a directory by its file ID. + * + *

Call {@link ShareDirectoryAsyncClient#getProperties()} on the returned client to retrieve properties without + * knowing the directory path. Obtain the ID from {@link FileSmbProperties#getFileId()} in a path-based properties + * response. Other service operations require a path-addressed client.

+ * + *
{@code
+     * Mono properties = shareAsyncClient.getDirectoryClientByFileId(fileId).getProperties();
+     * }
+ * + * @param fileId The file ID of the directory. + * @return A client for interacting with the directory by ID. + * @throws IllegalArgumentException If {@code fileId} is null or blank. + */ + public ShareDirectoryAsyncClient getDirectoryClientByFileId(String fileId) { + ShareErrors.validateFileId(fileId); + return new ShareDirectoryAsyncClient(azureFileStorageClient, shareName, "", fileId, snapshot, accountName, + serviceVersion, sasToken); + } + /** * Constructs a {@link ShareFileAsyncClient} that interacts with the specified file. * @@ -187,6 +209,28 @@ public ShareFileAsyncClient getFileClient(String filePath) { serviceVersion, sasToken); } + /** + * Creates a file client that addresses a file by its file ID. + * + *

Call {@link ShareFileAsyncClient#getProperties()} on the returned client to retrieve properties without knowing + * the file path. Obtain the ID from {@link FileSmbProperties#getFileId()} in a path-based properties response. + * The returned client supports property retrieval and {@link ShareFileAsyncClient#getFileLinks()} (SMB only). + * Other service operations require a path-addressed client.

+ * + *
{@code
+     * Mono properties = shareAsyncClient.getFileClientByFileId(fileId).getProperties();
+     * }
+ * + * @param fileId The file ID of the file. + * @return A client for interacting with the file by ID. + * @throws IllegalArgumentException If {@code fileId} is null or blank. + */ + public ShareFileAsyncClient getFileClientByFileId(String fileId) { + ShareErrors.validateFileId(fileId); + return new ShareFileAsyncClient(azureFileStorageClient, shareName, "", fileId, snapshot, accountName, + serviceVersion, sasToken); + } + /** * Creates a new {@link ShareAsyncClient} linked to the {@code snapshot} of this share resource. * diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareClient.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareClient.java index ad7b672c3d7b5..24097375a410e 100644 --- a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareClient.java +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareClient.java @@ -22,6 +22,7 @@ import com.azure.storage.common.implementation.SasImplUtils; import com.azure.storage.common.implementation.StorageImplUtils; import com.azure.storage.file.share.implementation.AzureFileStorageImpl; +import com.azure.storage.file.share.implementation.ShareErrors; import com.azure.storage.file.share.implementation.models.SharePermission; import com.azure.storage.file.share.implementation.models.ShareSignedIdentifierWrapper; import com.azure.storage.file.share.implementation.models.ShareStats; @@ -180,6 +181,27 @@ public ShareDirectoryClient getDirectoryClient(String directoryName) { serviceVersion, sasToken); } + /** + * Creates a directory client that addresses a directory by its file ID. + * + *

Call {@link ShareDirectoryClient#getProperties()} on the returned client to retrieve properties without + * knowing the directory path. Obtain the ID from {@link FileSmbProperties#getFileId()} in a path-based properties + * response. Other service operations require a path-addressed client.

+ * + *
{@code
+     * ShareDirectoryProperties properties = shareClient.getDirectoryClientByFileId(fileId).getProperties();
+     * }
+ * + * @param fileId The file ID of the directory. + * @return A client for interacting with the directory by ID. + * @throws IllegalArgumentException If {@code fileId} is null or blank. + */ + public ShareDirectoryClient getDirectoryClientByFileId(String fileId) { + ShareErrors.validateFileId(fileId); + return new ShareDirectoryClient(azureFileStorageClient, shareName, "", fileId, snapshot, accountName, + serviceVersion, sasToken); + } + /** * Constructs a {@link ShareFileClient} that interacts with the specified file. * @@ -196,6 +218,30 @@ public ShareFileClient getFileClient(String filePath) { azureFileStorageClient, shareName, filePath, snapshot, accountName, serviceVersion, sasToken); } + /** + * Creates a file client that addresses a file by its file ID. + * + *

Call {@link ShareFileClient#getProperties()} on the returned client to retrieve properties without knowing + * the file path. Obtain the ID from {@link FileSmbProperties#getFileId()} in a path-based properties response. + * The returned client supports property retrieval and {@link ShareFileClient#getFileLinks()} (SMB only). + * Other service operations require a path-addressed client.

+ * + *
{@code
+     * ShareFileProperties properties = shareClient.getFileClientByFileId(fileId).getProperties();
+     * }
+ * + * @param fileId The file ID of the file. + * @return A client for interacting with the file by ID. + * @throws IllegalArgumentException If {@code fileId} is null or blank. + */ + public ShareFileClient getFileClientByFileId(String fileId) { + ShareErrors.validateFileId(fileId); + ShareFileAsyncClient asyncClient = new ShareFileAsyncClient(azureFileStorageClient, shareName, "", fileId, + snapshot, accountName, serviceVersion, sasToken); + return new ShareFileClient(asyncClient, azureFileStorageClient, shareName, "", fileId, snapshot, accountName, + serviceVersion, sasToken); + } + /** * Creates a new {@link ShareAsyncClient} linked to the {@code snapshot} of this share resource. * diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareDirectoryAsyncClient.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareDirectoryAsyncClient.java index 575d1d9f9b912..5b273b90b4614 100644 --- a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareDirectoryAsyncClient.java +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareDirectoryAsyncClient.java @@ -23,6 +23,7 @@ import com.azure.storage.common.implementation.SasImplUtils; import com.azure.storage.common.implementation.StorageImplUtils; import com.azure.storage.file.share.implementation.AzureFileStorageImpl; +import com.azure.storage.file.share.implementation.ShareErrors; import com.azure.storage.file.share.implementation.models.CopyFileSmbInfo; import com.azure.storage.file.share.implementation.models.DestinationLeaseAccessConditions; import com.azure.storage.file.share.implementation.models.ListFilesIncludeType; @@ -69,6 +70,10 @@ * 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. * + *

Clients addressed by file ID support property retrieval. Other service operations, including existence checks, + * and child-client creation throw {@link IllegalStateException} or signal it through the returned publisher. + * Use a path-addressed client for these operations.

+ * *

Instantiating an Asynchronous Directory Client

* * @@ -93,6 +98,7 @@ public class ShareDirectoryAsyncClient { private final AzureFileStorageImpl azureFileStorageClient; private final String shareName; private final String directoryPath; + private final String fileId; private final String snapshot; private final String accountName; private final ShareServiceVersion serviceVersion; @@ -110,10 +116,17 @@ public class ShareDirectoryAsyncClient { */ ShareDirectoryAsyncClient(AzureFileStorageImpl azureFileStorageClient, String shareName, String directoryPath, String snapshot, String accountName, ShareServiceVersion serviceVersion, AzureSasCredential sasToken) { + this(azureFileStorageClient, shareName, directoryPath, "", snapshot, accountName, serviceVersion, sasToken); + } + + ShareDirectoryAsyncClient(AzureFileStorageImpl azureFileStorageClient, String shareName, String directoryPath, + String fileId, String snapshot, String accountName, ShareServiceVersion serviceVersion, + AzureSasCredential sasToken) { Objects.requireNonNull(shareName, "'shareName' cannot be null."); Objects.requireNonNull(directoryPath); this.shareName = shareName; this.directoryPath = directoryPath; + this.fileId = fileId; this.snapshot = snapshot; this.azureFileStorageClient = azureFileStorageClient; this.accountName = accountName; @@ -123,8 +136,9 @@ public class ShareDirectoryAsyncClient { ShareDirectoryAsyncClient(ShareDirectoryAsyncClient directoryAsyncClient) { this(directoryAsyncClient.azureFileStorageClient, directoryAsyncClient.shareName, - Utility.urlEncode(directoryAsyncClient.directoryPath), directoryAsyncClient.snapshot, - directoryAsyncClient.accountName, directoryAsyncClient.serviceVersion, directoryAsyncClient.sasToken); + Utility.urlEncode(directoryAsyncClient.directoryPath), directoryAsyncClient.fileId, + directoryAsyncClient.snapshot, directoryAsyncClient.accountName, directoryAsyncClient.serviceVersion, + directoryAsyncClient.sasToken); } /** @@ -133,14 +147,26 @@ public class ShareDirectoryAsyncClient { * @return the URL of the storage directory client */ public String getDirectoryUrl() { - StringBuilder directoryUrlString = new StringBuilder(azureFileStorageClient.getUrl()).append("/") - .append(shareName) - .append("/") - .append(directoryPath); + String directoryUrlString = azureFileStorageClient.getUrl() + "/" + shareName; + if (fileId.isEmpty()) { + directoryUrlString += "/" + directoryPath; + } if (snapshot != null) { - directoryUrlString.append("?sharesnapshot=").append(snapshot); + directoryUrlString = Utility.appendQueryParameter(directoryUrlString, "sharesnapshot", snapshot); + } + if (!fileId.isEmpty()) { + directoryUrlString = Utility.appendQueryParameter(directoryUrlString, "fileid", Utility.urlEncode(fileId)); } - return directoryUrlString.toString(); + return directoryUrlString; + } + + /** + * Gets the file ID used to address the directory, or an empty string when the client is path-addressed. + * + * @return The file ID. + */ + public String getFileId() { + return fileId; } /** @@ -160,8 +186,10 @@ public ShareServiceVersion getServiceVersion() { * * @param fileName Name of the file * @return a ShareFileAsyncClient that interacts with the specified share + * @throws IllegalStateException If this client is addressed by file ID. */ public ShareFileAsyncClient getFileClient(String fileName) { + ShareErrors.validatePathOperation(fileId, "getFileClient"); String filePath = directoryPath + "/" + fileName; // Support for root directory if (directoryPath.isEmpty()) { @@ -179,8 +207,10 @@ public ShareFileAsyncClient getFileClient(String fileName) { * * @param subdirectoryName Name of the directory * @return a ShareDirectoryAsyncClient that interacts with the specified directory + * @throws IllegalStateException If this client is addressed by file ID. */ public ShareDirectoryAsyncClient getSubdirectoryClient(String subdirectoryName) { + ShareErrors.validatePathOperation(fileId, "getSubdirectoryClient"); StringBuilder directoryPathBuilder = new StringBuilder().append(this.directoryPath); if (!this.directoryPath.isEmpty() && !this.directoryPath.endsWith("/")) { directoryPathBuilder.append("/"); @@ -202,6 +232,7 @@ public ShareDirectoryAsyncClient getSubdirectoryClient(String subdirectoryName) * * * @return Flag indicating existence of the directory. + * @throws IllegalStateException If this client is addressed by file ID, emitted through the returned publisher. */ @ServiceMethod(returns = ReturnType.SINGLE) public Mono exists() { @@ -220,6 +251,7 @@ public Mono exists() { * * * @return Flag indicating existence of the directory. + * @throws IllegalStateException If this client is addressed by file ID, emitted through the returned publisher. */ @ServiceMethod(returns = ReturnType.SINGLE) public Mono> existsWithResponse() { @@ -231,6 +263,7 @@ public Mono> existsWithResponse() { } Mono> existsWithResponse(Context context) { + ShareErrors.validatePathOperation(fileId, "exists"); return this.getPropertiesWithResponse(context) .map(cp -> (Response) new SimpleResponse<>(cp, true)) .onErrorResume(ModelHelper::checkDoesNotExistStatusCode, t -> { @@ -356,6 +389,7 @@ public Mono> createWithResponse(ShareDirectoryCreat Mono> createWithResponse(FileSmbProperties smbProperties, String filePermission, FilePermissionFormat filePermissionFormat, FilePosixProperties posixProperties, Map metadata, FilePropertySemantics filePropertySemantics, Context context) { + ShareErrors.validatePathOperation(fileId, "create"); context = context == null ? Context.NONE : context; smbProperties = smbProperties == null ? new FileSmbProperties() : smbProperties; posixProperties = posixProperties == null ? new FilePosixProperties() : posixProperties; @@ -523,6 +557,7 @@ public Mono> deleteWithResponse() { } Mono> deleteWithResponse(Context context) { + ShareErrors.validatePathOperation(fileId, "delete"); context = context == null ? Context.NONE : context; return azureFileStorageClient.getDirectories() .deleteNoCustomHeadersWithResponseAsync(shareName, directoryPath, null, context); @@ -611,6 +646,11 @@ Mono> deleteIfExistsWithResponse(Context context) { * *

Retrieve directory properties

* + *

This operation accepts path-addressed and file-ID-addressed clients. To retrieve properties without knowing + * the directory path, use {@link ShareAsyncClient#getDirectoryClientByFileId(String)}. Service availability determines + * whether a file-ID request succeeds; the client does not check the share protocol. If the service does not + * support property retrieval by ID for the share, use a path-addressed client.

+ * * *
      * shareDirectoryAsyncClient.getProperties().subscribe(properties -> {
@@ -661,6 +701,11 @@ public Mono> getPropertiesWithResponse() {
 
     Mono> getPropertiesWithResponse(Context context) {
         context = context == null ? Context.NONE : context;
+        if (!fileId.isEmpty()) {
+            return azureFileStorageClient.getDirectories()
+                .getPropertiesByFileIdWithResponseAsync(shareName, fileId, snapshot, null, null, context)
+                .map(ModelHelper::mapShareDirectoryPropertiesResponse);
+        }
         return azureFileStorageClient.getDirectories()
             .getPropertiesWithResponseAsync(shareName, directoryPath, snapshot, null, context)
             .map(ModelHelper::mapShareDirectoryPropertiesResponse);
@@ -770,6 +815,7 @@ public Mono> setPropertiesWithResponse(ShareDirecto
 
     Mono> setPropertiesWithResponse(FileSmbProperties smbProperties, String filePermission,
         FilePermissionFormat filePermissionFormat, FilePosixProperties posixProperties, Context context) {
+        ShareErrors.validatePathOperation(fileId, "setProperties");
         context = context == null ? Context.NONE : context;
         smbProperties = smbProperties == null ? new FileSmbProperties() : smbProperties;
         posixProperties = posixProperties == null ? new FilePosixProperties() : posixProperties;
@@ -870,6 +916,7 @@ public Mono> setMetadataWithResponse(Map
 
     Mono> setMetadataWithResponse(Map metadata,
         Context context) {
+        ShareErrors.validatePathOperation(fileId, "setMetadata");
         context = context == null ? Context.NONE : context;
         return azureFileStorageClient.getDirectories()
             .setMetadataWithResponseAsync(shareName, directoryPath, null, metadata, context)
@@ -977,6 +1024,7 @@ public PagedFlux listFilesAndDirectories(ShareListFilesAndDirecto
 
     PagedFlux listFilesAndDirectoriesWithOptionalTimeout(ShareListFilesAndDirectoriesOptions options,
         Duration timeout, Context context) {
+        ShareErrors.validatePathOperation(fileId, "listFilesAndDirectories");
         final ShareListFilesAndDirectoriesOptions modifiedOptions
             = options == null ? new ShareListFilesAndDirectoriesOptions() : options;
 
@@ -1046,6 +1094,7 @@ public PagedFlux listHandles(Integer maxResultPerPage, boolean recur
 
     PagedFlux listHandlesWithOptionalTimeout(Integer maxResultPerPage, boolean recursive, Duration timeout,
         Context context) {
+        ShareErrors.validatePathOperation(fileId, "listHandles");
         Function>> retriever = marker -> StorageImplUtils
             .applyOptionalTimeout(this.azureFileStorageClient.getDirectories()
                 .listHandlesWithResponseAsync(shareName, directoryPath, marker, maxResultPerPage, null, snapshot,
@@ -1118,6 +1167,7 @@ public Mono> forceCloseHandleWithResponse(String hand
     }
 
     Mono> forceCloseHandleWithResponse(String handleId, Context context) {
+        ShareErrors.validatePathOperation(fileId, "forceCloseHandle");
         return this.azureFileStorageClient.getDirectories()
             .forceCloseHandlesWithResponseAsync(shareName, directoryPath, handleId, null, null, snapshot, false,
                 context)
@@ -1161,6 +1211,7 @@ public Mono forceCloseAllHandles(boolean recursive) {
     }
 
     PagedFlux forceCloseAllHandlesWithTimeout(boolean recursive, Duration timeout, Context context) {
+        ShareErrors.validatePathOperation(fileId, "forceCloseAllHandles");
         Function>> retriever = marker -> StorageImplUtils
             .applyOptionalTimeout(this.azureFileStorageClient.getDirectories()
                 .forceCloseHandlesWithResponseAsync(shareName, directoryPath, "*", null, marker, snapshot, recursive,
@@ -1242,6 +1293,7 @@ public Mono> renameWithResponse(ShareFileRen
     }
 
     Mono> renameWithResponse(ShareFileRenameOptions options, Context context) {
+        ShareErrors.validatePathOperation(fileId, "rename");
         StorageImplUtils.assertNotNull("options", options);
         context = context == null ? Context.NONE : context;
 
@@ -2156,6 +2208,7 @@ public String generateSas(ShareServiceSasSignatureValues shareServiceSasSignatur
     @Deprecated
     public String generateSas(ShareServiceSasSignatureValues shareServiceSasSignatureValues,
         Consumer stringToSignHandler, Context context) {
+        ShareErrors.validatePathOperation(fileId, "generateSas");
         return new ShareSasImplUtil(shareServiceSasSignatureValues, getShareName(), getDirectoryPath())
             .generateSas(SasImplUtils.extractSharedKeyCredential(getHttpPipeline()), stringToSignHandler, context);
     }
diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareDirectoryClient.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareDirectoryClient.java
index 416626a7113e8..69f94cc0001cd 100644
--- a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareDirectoryClient.java
+++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareDirectoryClient.java
@@ -20,14 +20,15 @@
 import com.azure.core.util.CoreUtils;
 import com.azure.core.util.logging.ClientLogger;
 import com.azure.storage.common.StorageSharedKeyCredential;
+import com.azure.storage.common.Utility;
 import com.azure.storage.common.implementation.SasImplUtils;
 import com.azure.storage.common.implementation.StorageImplUtils;
 import com.azure.storage.file.share.implementation.AzureFileStorageImpl;
+import com.azure.storage.file.share.implementation.ShareErrors;
 import com.azure.storage.file.share.implementation.models.CopyFileSmbInfo;
 import com.azure.storage.file.share.implementation.models.DestinationLeaseAccessConditions;
 import com.azure.storage.file.share.implementation.models.DirectoriesCreateHeaders;
 import com.azure.storage.file.share.implementation.models.DirectoriesForceCloseHandlesHeaders;
-import com.azure.storage.file.share.implementation.models.DirectoriesGetPropertiesHeaders;
 import com.azure.storage.file.share.implementation.models.DirectoriesListHandlesHeaders;
 import com.azure.storage.file.share.implementation.models.DirectoriesSetMetadataHeaders;
 import com.azure.storage.file.share.implementation.models.DirectoriesSetPropertiesHeaders;
@@ -75,6 +76,9 @@
  * 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.
  *
+ * 

Clients addressed by file ID support property retrieval. Other service operations, including existence checks, + * and child-client creation throw {@link IllegalStateException}. Use a path-addressed client for these operations.

+ * *

Instantiating an Synchronous Directory Client

* * @@ -100,6 +104,7 @@ public class ShareDirectoryClient { private final AzureFileStorageImpl azureFileStorageClient; private final String shareName; private final String directoryPath; + private final String fileId; private final String snapshot; private final String accountName; private final ShareServiceVersion serviceVersion; @@ -118,24 +123,34 @@ public class ShareDirectoryClient { */ ShareDirectoryClient(AzureFileStorageImpl azureFileStorageClient, String shareName, String directoryPath, String snapshot, String accountName, ShareServiceVersion serviceVersion, AzureSasCredential sasToken) { + this(azureFileStorageClient, shareName, directoryPath, "", snapshot, accountName, serviceVersion, sasToken); + } + + ShareDirectoryClient(AzureFileStorageImpl azureFileStorageClient, String shareName, String directoryPath, + String fileId, String snapshot, String accountName, ShareServiceVersion serviceVersion, + AzureSasCredential sasToken) { Objects.requireNonNull(shareName, "'shareName' cannot be null."); Objects.requireNonNull(directoryPath); this.shareName = shareName; this.directoryPath = directoryPath; + this.fileId = fileId; this.snapshot = snapshot; this.azureFileStorageClient = azureFileStorageClient; this.accountName = accountName; this.serviceVersion = serviceVersion; this.sasToken = sasToken; - StringBuilder directoryUrlString = new StringBuilder(azureFileStorageClient.getUrl()).append("/") - .append(shareName) - .append("/") - .append(directoryPath); + String directoryUrlString = azureFileStorageClient.getUrl() + "/" + shareName; + if (fileId.isEmpty()) { + directoryUrlString += "/" + directoryPath; + } if (snapshot != null) { - directoryUrlString.append("?sharesnapshot=").append(snapshot); + directoryUrlString = Utility.appendQueryParameter(directoryUrlString, "sharesnapshot", snapshot); + } + if (!fileId.isEmpty()) { + directoryUrlString = Utility.appendQueryParameter(directoryUrlString, "fileid", Utility.urlEncode(fileId)); } - this.directoryUrl = directoryUrlString.toString(); + this.directoryUrl = directoryUrlString; } /** @@ -147,6 +162,15 @@ public String getDirectoryUrl() { return this.directoryUrl; } + /** + * Gets the file ID used to address the directory, or an empty string when the client is path-addressed. + * + * @return The file ID. + */ + public String getFileId() { + return fileId; + } + /** * Gets the service version the client is using. * @@ -164,8 +188,10 @@ public ShareServiceVersion getServiceVersion() { * * @param fileName Name of the file * @return a ShareFileClient that interacts with the specified share + * @throws IllegalStateException If this client is addressed by file ID. */ public ShareFileClient getFileClient(String fileName) { + ShareErrors.validatePathOperation(fileId, "getFileClient"); String filePath = directoryPath + "/" + fileName; // Support for root directory if (directoryPath.isEmpty()) { @@ -184,8 +210,10 @@ public ShareFileClient getFileClient(String fileName) { * * @param subdirectoryName Name of the directory * @return a ShareDirectoryClient that interacts with the specified directory + * @throws IllegalStateException If this client is addressed by file ID. */ public ShareDirectoryClient getSubdirectoryClient(String subdirectoryName) { + ShareErrors.validatePathOperation(fileId, "getSubdirectoryClient"); boolean needPathDelimiter = !this.directoryPath.isEmpty() && !this.directoryPath.endsWith("/"); String subDirectoryPath = this.directoryPath + (needPathDelimiter ? "/" : "") + subdirectoryName; return new ShareDirectoryClient(azureFileStorageClient, shareName, subDirectoryPath, snapshot, accountName, @@ -204,6 +232,7 @@ public ShareDirectoryClient getSubdirectoryClient(String subdirectoryName) { * * * @return Flag indicating existence of the directory. + * @throws IllegalStateException If this client is addressed by file ID. */ @ServiceMethod(returns = ReturnType.SINGLE) public Boolean exists() { @@ -225,9 +254,11 @@ public Boolean exists() { * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. * @param context Additional context that is passed through the Http pipeline during the service call. * @return Flag indicating existence of the directory. + * @throws IllegalStateException If this client is addressed by file ID. */ @ServiceMethod(returns = ReturnType.SINGLE) public Response existsWithResponse(Duration timeout, Context context) { + ShareErrors.validatePathOperation(fileId, "exists"); try { Response response = getPropertiesWithResponse(timeout, context); return new SimpleResponse<>(response, true); @@ -342,6 +373,7 @@ public Response createWithResponse(FileSmbProperties smbProp @ServiceMethod(returns = ReturnType.SINGLE) public Response createWithResponse(ShareDirectoryCreateOptions options, Duration timeout, Context context) { + ShareErrors.validatePathOperation(fileId, "create"); Context finalContext = context == null ? Context.NONE : context; ShareDirectoryCreateOptions finalOptions = options == null ? new ShareDirectoryCreateOptions() : options; @@ -494,6 +526,7 @@ public void delete() { */ @ServiceMethod(returns = ReturnType.SINGLE) public Response deleteWithResponse(Duration timeout, Context context) { + ShareErrors.validatePathOperation(fileId, "delete"); Context finalContext = context == null ? Context.NONE : context; Callable> operation = () -> this.azureFileStorageClient.getDirectories() .deleteNoCustomHeadersWithResponse(shareName, directoryPath, null, finalContext); @@ -578,6 +611,11 @@ public Response deleteIfExistsWithResponse(Duration timeout, Context co * *

Retrieve directory properties

* + *

This operation accepts path-addressed and file-ID-addressed clients. To retrieve properties without knowing + * the directory path, use {@link ShareClient#getDirectoryClientByFileId(String)}. Service availability determines + * whether a file-ID request succeeds; the client does not check the share protocol. If the service does not + * support property retrieval by ID for the share, use a path-addressed client.

+ * * *
      * ShareDirectoryProperties response = shareDirectoryClient.getProperties();
@@ -623,9 +661,11 @@ public ShareDirectoryProperties getProperties() {
     @ServiceMethod(returns = ReturnType.SINGLE)
     public Response getPropertiesWithResponse(Duration timeout, Context context) {
         Context finalContext = context == null ? Context.NONE : context;
-        Callable> operation
-            = () -> this.azureFileStorageClient.getDirectories()
-                .getPropertiesWithResponse(shareName, directoryPath, snapshot, null, finalContext);
+        Callable> operation = fileId.isEmpty()
+            ? () -> this.azureFileStorageClient.getDirectories()
+                .getPropertiesWithResponse(shareName, directoryPath, snapshot, null, finalContext)
+            : () -> this.azureFileStorageClient.getDirectories()
+                .getPropertiesByFileIdWithResponse(shareName, fileId, snapshot, null, null, finalContext);
 
         return ModelHelper
             .mapShareDirectoryPropertiesResponse(sendRequest(operation, timeout, ShareStorageException.class));
@@ -724,6 +764,7 @@ public Response setPropertiesWithResponse(FileSmbProperties
     @ServiceMethod(returns = ReturnType.SINGLE)
     public Response setPropertiesWithResponse(ShareDirectorySetPropertiesOptions options,
         Duration timeout, Context context) {
+        ShareErrors.validatePathOperation(fileId, "setProperties");
         Context finalContext = context == null ? Context.NONE : context;
         ShareDirectorySetPropertiesOptions finalOptions
             = options == null ? new ShareDirectorySetPropertiesOptions() : options;
@@ -831,6 +872,7 @@ public ShareDirectorySetMetadataInfo setMetadata(Map metadata) {
     @ServiceMethod(returns = ReturnType.SINGLE)
     public Response setMetadataWithResponse(Map metadata,
         Duration timeout, Context context) {
+        ShareErrors.validatePathOperation(fileId, "setMetadata");
         Context finalContext = context == null ? Context.NONE : context;
         Callable> operation
             = () -> this.azureFileStorageClient.getDirectories()
@@ -941,6 +983,7 @@ public PagedIterable listFilesAndDirectories(String prefix, Integ
     @ServiceMethod(returns = ReturnType.COLLECTION)
     public PagedIterable listFilesAndDirectories(ShareListFilesAndDirectoriesOptions options,
         Duration timeout, Context context) {
+        ShareErrors.validatePathOperation(fileId, "listFilesAndDirectories");
         Context finalContext = context == null ? Context.NONE : context;
 
         final ShareListFilesAndDirectoriesOptions modifiedOptions
@@ -1016,6 +1059,7 @@ public PagedIterable listHandles(Integer maxResultsPerPage, boolean
 
     PagedIterable listHandlesWithOptionalTimeout(Integer maxResultPerPage, boolean recursive,
         Duration timeout, Context context) {
+        ShareErrors.validatePathOperation(fileId, "listHandles");
         Context finalContext = context == null ? Context.NONE : context;
         Function> retriever = (marker) -> {
             Callable> operation
@@ -1092,6 +1136,7 @@ public CloseHandlesInfo forceCloseHandle(String handleId) {
      */
     @ServiceMethod(returns = ReturnType.SINGLE)
     public Response forceCloseHandleWithResponse(String handleId, Duration timeout, Context context) {
+        ShareErrors.validatePathOperation(fileId, "forceCloseHandle");
         Context finalContext = context == null ? Context.NONE : context;
 
         Callable> operation
@@ -1135,6 +1180,7 @@ public Response forceCloseHandleWithResponse(String handleId,
      */
     @ServiceMethod(returns = ReturnType.SINGLE)
     public CloseHandlesInfo forceCloseAllHandles(boolean recursive, Duration timeout, Context context) {
+        ShareErrors.validatePathOperation(fileId, "forceCloseAllHandles");
         Context finalContext = context == null ? Context.NONE : context;
 
         Function> retriever = (marker) -> {
@@ -1221,6 +1267,7 @@ public ShareDirectoryClient rename(String destinationPath) {
     @ServiceMethod(returns = ReturnType.SINGLE)
     public Response renameWithResponse(ShareFileRenameOptions options, Duration timeout,
         Context context) {
+        ShareErrors.validatePathOperation(fileId, "rename");
         StorageImplUtils.assertNotNull("options", options);
         Context finalContext = context == null ? Context.NONE : context;
 
@@ -2056,6 +2103,7 @@ public String generateSas(ShareServiceSasSignatureValues shareServiceSasSignatur
     @Deprecated
     public String generateSas(ShareServiceSasSignatureValues shareServiceSasSignatureValues,
         Consumer stringToSignHandler, Context context) {
+        ShareErrors.validatePathOperation(fileId, "generateSas");
         return new ShareSasImplUtil(shareServiceSasSignatureValues, getShareName(), getDirectoryPath())
             .generateSas(SasImplUtils.extractSharedKeyCredential(getHttpPipeline()), stringToSignHandler, context);
     }
diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareFileAsyncClient.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareFileAsyncClient.java
index c6e2bde18e29c..67dc5c906881f 100644
--- a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareFileAsyncClient.java
+++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareFileAsyncClient.java
@@ -36,6 +36,7 @@
 import com.azure.storage.common.implementation.StorageImplUtils;
 import com.azure.storage.common.implementation.UploadUtils;
 import com.azure.storage.file.share.implementation.AzureFileStorageImpl;
+import com.azure.storage.file.share.implementation.ShareErrors;
 import com.azure.storage.file.share.implementation.models.CopyFileSmbInfo;
 import com.azure.storage.file.share.implementation.models.DestinationLeaseAccessConditions;
 import com.azure.storage.file.share.implementation.models.FilesDownloadHeaders;
@@ -60,6 +61,7 @@
 import com.azure.storage.file.share.models.ShareFileDownloadHeaders;
 import com.azure.storage.file.share.models.ShareFileHttpHeaders;
 import com.azure.storage.file.share.models.ShareFileInfo;
+import com.azure.storage.file.share.models.ShareFileLinks;
 import com.azure.storage.file.share.models.ShareFileMetadataInfo;
 import com.azure.storage.file.share.models.ShareFilePermission;
 import com.azure.storage.file.share.models.ShareFileProperties;
@@ -125,6 +127,10 @@
  * 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.
  *
+ * 

Use {@link ShareAsyncClient#getFileClientByFileId(String)} to retrieve properties without knowing the file path. + * File-ID clients support {@link #getProperties()} and {@link #getFileLinks()} (SMB only). + * Other service operations require a path-addressed client.

+ * *

Instantiating an Asynchronous File Client

* * @@ -149,6 +155,7 @@ public class ShareFileAsyncClient { private final AzureFileStorageImpl azureFileStorageClient; private final String shareName; private final String filePath; + private final String fileId; private final String snapshot; private final String accountName; private final ShareServiceVersion serviceVersion; @@ -164,11 +171,17 @@ public class ShareFileAsyncClient { * @param snapshot The snapshot of the share */ ShareFileAsyncClient(AzureFileStorageImpl azureFileStorageClient, String shareName, String filePath, + String snapshot, String accountName, ShareServiceVersion serviceVersion, AzureSasCredential sasToken) { + this(azureFileStorageClient, shareName, filePath, "", snapshot, accountName, serviceVersion, sasToken); + } + + ShareFileAsyncClient(AzureFileStorageImpl azureFileStorageClient, String shareName, String filePath, String fileId, String snapshot, String accountName, ShareServiceVersion serviceVersion, AzureSasCredential sasToken) { Objects.requireNonNull(shareName, "'shareName' cannot be null."); Objects.requireNonNull(filePath, "'filePath' cannot be null."); this.shareName = shareName; this.filePath = filePath; + this.fileId = fileId; this.snapshot = snapshot; this.azureFileStorageClient = azureFileStorageClient; this.accountName = accountName; @@ -178,8 +191,8 @@ public class ShareFileAsyncClient { ShareFileAsyncClient(ShareFileAsyncClient fileAsyncClient) { this(fileAsyncClient.azureFileStorageClient, fileAsyncClient.shareName, - Utility.urlEncode(fileAsyncClient.filePath), fileAsyncClient.snapshot, fileAsyncClient.accountName, - fileAsyncClient.serviceVersion, fileAsyncClient.sasToken); + Utility.urlEncode(fileAsyncClient.filePath), fileAsyncClient.fileId, fileAsyncClient.snapshot, + fileAsyncClient.accountName, fileAsyncClient.serviceVersion, fileAsyncClient.sasToken); } /** @@ -197,14 +210,17 @@ public String getAccountUrl() { * @return the URL of the storage file client */ public String getFileUrl() { - StringBuilder fileUrlstring = new StringBuilder(azureFileStorageClient.getUrl()).append("/") - .append(shareName) - .append("/") - .append(filePath); + String fileUrlstring = azureFileStorageClient.getUrl() + "/" + shareName; + if (fileId.isEmpty()) { + fileUrlstring += "/" + filePath; + } if (snapshot != null) { - fileUrlstring.append("?sharesnapshot=").append(snapshot); + fileUrlstring = Utility.appendQueryParameter(fileUrlstring, "sharesnapshot", snapshot); + } + if (!fileId.isEmpty()) { + fileUrlstring = Utility.appendQueryParameter(fileUrlstring, "fileid", Utility.urlEncode(fileId)); } - return fileUrlstring.toString(); + return fileUrlstring; } /** @@ -216,10 +232,53 @@ public ShareServiceVersion getServiceVersion() { return serviceVersion; } + /** + * Gets the file ID used to address the file, or an empty string when the client is path-addressed. + * + * @return The file ID. + */ + public String getFileId() { + return fileId; + } + AzureSasCredential getSasToken() { return sasToken; } + /** + * SMB only. Gets the hard links to this file. This operation is available only for a file-ID-addressed client. + * + * @param requestConditions Conditions to apply to the request. + * @return A response containing the file properties and hard links. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> getFileLinksWithResponse(ShareRequestConditions requestConditions) { + try { + ShareErrors.assertFileIdAddressed(fileId, "getFileLinks"); + return withContext(context -> getFileLinksWithResponse(requestConditions, context)); + } catch (RuntimeException exception) { + return monoError(LOGGER, exception); + } + } + + Mono> getFileLinksWithResponse(ShareRequestConditions requestConditions, Context context) { + ShareRequestConditions conditions + = requestConditions == null ? new ShareRequestConditions() : requestConditions; + return azureFileStorageClient.getFiles() + .getHardLinksWithResponseAsync(shareName, fileId, snapshot, null, null, conditions.getLeaseId(), context) + .map(ModelHelper::getFileLinksResponse); + } + + /** + * SMB only. Gets the file links. This operation is available only for a file-ID-addressed client. + * + * @return The file properties and hard links. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono getFileLinks() { + return getFileLinksWithResponse(null).flatMap(FluxUtil::toMono); + } + /** * Determines if the file this client represents exists in the cloud. * @@ -261,6 +320,7 @@ public Mono> existsWithResponse() { } Mono> existsWithResponse(Context context) { + ShareErrors.validatePathOperation(fileId, "exists"); return this.getPropertiesWithResponse(null, context) .map(cp -> (Response) new SimpleResponse<>(cp, true)) .onErrorResume(ModelHelper::checkDoesNotExistStatusCode, t -> { @@ -464,6 +524,7 @@ Mono> createWithResponse(long maxSize, ShareFileHttpHead FileSmbProperties smbProperties, String filePermission, FilePermissionFormat filePermissionFormat, FilePosixProperties filePosixProperties, Map metadata, ShareRequestConditions requestConditions, FilePropertySemantics filePropertySemantics, BinaryData binaryData, Context context) { + ShareErrors.validatePathOperation(fileId, "create"); final Context contextLocal = context == null ? Context.NONE : context; final ShareRequestConditions requestConditionsLocal = requestConditions == null ? new ShareRequestConditions() : requestConditions; @@ -652,6 +713,7 @@ public PollerFlux beginCopy(String sourceUrl, FileSmbPr */ public PollerFlux beginCopy(String sourceUrl, ShareFileCopyOptions options, Duration pollInterval) { + ShareErrors.validatePathOperation(fileId, "beginCopy"); final ShareRequestConditions finalRequestConditions = options.getDestinationRequestConditions() == null ? new ShareRequestConditions() @@ -878,6 +940,7 @@ public Mono> abortCopyWithResponse(String copyId, ShareRequestCon Mono> abortCopyWithResponse(String copyId, ShareRequestConditions requestConditions, Context context) { + ShareErrors.validatePathOperation(fileId, "abortCopy"); requestConditions = requestConditions == null ? new ShareRequestConditions() : requestConditions; return azureFileStorageClient.getFiles() .abortCopyNoCustomHeadersWithResponseAsync(shareName, filePath, copyId, null, @@ -1007,6 +1070,7 @@ public Mono> downloadToFileWithResponse(String dow Mono> downloadToFileWithResponse(String downloadFilePath, ShareFileRange range, ShareRequestConditions requestConditions, Context context) { + ShareErrors.validatePathOperation(fileId, "downloadToFile"); return Mono.using(() -> channelSetup(downloadFilePath, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW), channel -> getPropertiesWithResponse(requestConditions, context) .flatMap(response -> downloadResponseInChunk(response, channel, range, requestConditions, context)), @@ -1185,6 +1249,7 @@ public Mono downloadWithResponse(ShareFileDownlo } Mono downloadWithResponse(ShareFileDownloadOptions options, Context context) { + ShareErrors.validatePathOperation(fileId, "download"); options = options == null ? new ShareFileDownloadOptions() : options; ShareFileRange range = options.getRange() == null ? new ShareFileRange(0) : options.getRange(); ShareRequestConditions requestConditions @@ -1349,6 +1414,7 @@ public Mono> deleteWithResponse(ShareRequestConditions requestCon } Mono> deleteWithResponse(ShareRequestConditions requestConditions, Context context) { + ShareErrors.validatePathOperation(fileId, "delete"); requestConditions = requestConditions == null ? new ShareRequestConditions() : requestConditions; return azureFileStorageClient.getFiles() .deleteNoCustomHeadersWithResponseAsync(shareName, filePath, null, requestConditions.getLeaseId(), context); @@ -1445,6 +1511,11 @@ Mono> deleteIfExistsWithResponse(ShareRequestConditions reques * *

Retrieve file properties

* + *

This operation accepts path-addressed and file-ID-addressed clients. To address a file by ID, use + * {@link ShareAsyncClient#getFileClientByFileId(String)}. Service availability determines whether a file-ID request + * succeeds; the client does not check the share protocol. If the service does not support property retrieval by ID + * for the share, use a path-addressed client.

+ * * *
      * shareFileAsyncClient.getProperties()
@@ -1530,6 +1601,12 @@ Mono> getPropertiesWithResponse(ShareRequestCondit
         Context context) {
         requestConditions = requestConditions == null ? new ShareRequestConditions() : requestConditions;
         context = context == null ? Context.NONE : context;
+        if (!fileId.isEmpty()) {
+            return azureFileStorageClient.getFiles()
+                .getPropertiesByFileIdWithResponseAsync(shareName, fileId, snapshot, null, null,
+                    requestConditions.getLeaseId(), context)
+                .map(ModelHelper::getPropertiesResponse);
+        }
         return azureFileStorageClient.getFiles()
             .getPropertiesWithResponseAsync(shareName, filePath, snapshot, null, requestConditions.getLeaseId(),
                 context)
@@ -1779,6 +1856,7 @@ public Mono> setPropertiesWithResponse(ShareFileSetPrope
     Mono> setPropertiesWithResponse(long newFileSize, ShareFileHttpHeaders httpHeaders,
         FileSmbProperties smbProperties, String filePermission, FilePermissionFormat filePermissionFormat,
         FilePosixProperties fileposixProperties, ShareRequestConditions requestConditions, Context context) {
+        ShareErrors.validatePathOperation(fileId, "setProperties");
         context = context == null ? Context.NONE : context;
         requestConditions = requestConditions == null ? new ShareRequestConditions() : requestConditions;
         smbProperties = smbProperties == null ? new FileSmbProperties() : smbProperties;
@@ -1922,6 +2000,7 @@ public Mono> setMetadataWithResponse(Map> setMetadataWithResponse(Map metadata,
         ShareRequestConditions requestConditions, Context context) {
+        ShareErrors.validatePathOperation(fileId, "setMetadata");
         requestConditions = requestConditions == null ? new ShareRequestConditions() : requestConditions;
         context = context == null ? Context.NONE : context;
         try {
@@ -2132,6 +2211,7 @@ public Mono> uploadWithResponse(ShareFileUploadOpt
     }
 
     Mono> uploadWithResponse(ShareFileUploadOptions options, Context context) {
+        ShareErrors.validatePathOperation(fileId, "upload");
         try {
             StorageImplUtils.assertNotNull("options", options);
             ShareRequestConditions validatedRequestConditions = options.getRequestConditions() == null
@@ -2301,6 +2381,7 @@ public Mono> uploadRangeWithResponse(ShareFileUplo
      * One-shot upload range.
      */
     Mono> uploadRangeWithResponse(ShareFileUploadRangeOptions options, Context context) {
+        ShareErrors.validatePathOperation(fileId, "uploadRange");
         ShareRequestConditions requestConditions
             = options.getRequestConditions() == null ? new ShareRequestConditions() : options.getRequestConditions();
         long rangeOffset = (options.getOffset() == null) ? 0L : options.getOffset();
@@ -2465,6 +2546,7 @@ public Mono> uploadRangeFromUrlWithRes
 
     Mono>
         uploadRangeFromUrlWithResponse(ShareFileUploadRangeFromUrlOptions options, Context context) {
+        ShareErrors.validatePathOperation(fileId, "uploadRangeFromUrl");
         ShareRequestConditions modifiedRequestConditions = options.getDestinationRequestConditions() == null
             ? new ShareRequestConditions()
             : options.getDestinationRequestConditions();
@@ -2588,6 +2670,7 @@ public Mono> clearRangeWithResponse(long length, l
 
     Mono> clearRangeWithResponse(long length, long offset,
         ShareRequestConditions requestConditions, Context context) {
+        ShareErrors.validatePathOperation(fileId, "clearRange");
         requestConditions = requestConditions == null ? new ShareRequestConditions() : requestConditions;
         ShareFileRange range = new ShareFileRange(offset, offset + length - 1);
         context = context == null ? Context.NONE : context;
@@ -2622,6 +2705,7 @@ Mono> clearRangeWithResponse(long length, long off
      * @param uploadFilePath The path where store the source file to upload
      * @return An empty response.
      * @throws UncheckedIOException If an I/O error occurs.
+     * @throws IllegalStateException If this client is addressed by file ID, emitted through the returned publisher.
      */
     @ServiceMethod(returns = ReturnType.SINGLE)
     public Mono uploadFromFile(String uploadFilePath) {
@@ -2655,10 +2739,12 @@ public Mono uploadFromFile(String uploadFilePath) {
      * @param requestConditions {@link ShareRequestConditions}
      * @return An empty response.
      * @throws UncheckedIOException If an I/O error occurs.
+     * @throws IllegalStateException If this client is addressed by file ID, emitted through the returned publisher.
      */
     @ServiceMethod(returns = ReturnType.SINGLE)
     public Mono uploadFromFile(String uploadFilePath, ShareRequestConditions requestConditions) {
         try {
+            ShareErrors.validatePathOperation(fileId, "uploadFromFile");
             return Mono.using(() -> channelSetup(uploadFilePath, StandardOpenOption.READ),
                 channel -> Flux.fromIterable(sliceFile(uploadFilePath))
                     .flatMap(chunk -> uploadWithResponse(
@@ -2958,6 +3044,7 @@ PagedFlux listAllRangesInternal(ShareFileRange range, ShareR
     Mono> listRangesWithResponse(ShareFileRange range,
         ShareRequestConditions requestConditions, String previousSnapshot, Boolean supportRename, String marker,
         Integer maxResultsPerPage, Context context) {
+        ShareErrors.validatePathOperation(fileId, "listRanges");
 
         ShareRequestConditions finalRequestConditions
             = requestConditions == null ? new ShareRequestConditions() : requestConditions;
@@ -3024,6 +3111,7 @@ public PagedFlux listHandles(Integer maxResultsPerPage) {
     }
 
     PagedFlux listHandlesWithOptionalTimeout(Integer maxResultsPerPage, Duration timeout, Context context) {
+        ShareErrors.validatePathOperation(fileId, "listHandles");
         Function>> retriever = marker -> StorageImplUtils
             .applyOptionalTimeout(this.azureFileStorageClient.getFiles()
                 .listHandlesWithResponseAsync(shareName, filePath, marker, maxResultsPerPage, null, snapshot, context),
@@ -3095,6 +3183,7 @@ public Mono> forceCloseHandleWithResponse(String hand
     }
 
     Mono> forceCloseHandleWithResponse(String handleId, Context context) {
+        ShareErrors.validatePathOperation(fileId, "forceCloseHandle");
         context = context == null ? Context.NONE : context;
         return azureFileStorageClient.getFiles()
             .forceCloseHandlesWithResponseAsync(shareName, filePath, handleId, null, null, snapshot, context)
@@ -3136,6 +3225,7 @@ public Mono forceCloseAllHandles() {
     }
 
     PagedFlux forceCloseAllHandlesWithOptionalTimeout(Duration timeout, Context context) {
+        ShareErrors.validatePathOperation(fileId, "forceCloseAllHandles");
         Function>> retriever = marker -> StorageImplUtils
             .applyOptionalTimeout(
                 this.azureFileStorageClient.getFiles()
@@ -3221,6 +3311,7 @@ public Mono> renameWithResponse(ShareFileRenameOp
     }
 
     Mono> renameWithResponse(ShareFileRenameOptions options, Context context) {
+        ShareErrors.validatePathOperation(fileId, "rename");
         StorageImplUtils.assertNotNull("options", options);
         context = context == null ? Context.NONE : context;
 
@@ -3439,6 +3530,7 @@ public String generateSas(ShareServiceSasSignatureValues shareServiceSasSignatur
      */
     public String generateSas(ShareServiceSasSignatureValues shareServiceSasSignatureValues,
         Consumer stringToSignHandler, Context context) {
+        ShareErrors.validatePathOperation(fileId, "generateSas");
         return new ShareSasImplUtil(shareServiceSasSignatureValues, getShareName(), getFilePath())
             .generateSas(SasImplUtils.extractSharedKeyCredential(getHttpPipeline()), stringToSignHandler, context);
     }
@@ -3490,6 +3582,7 @@ public Mono> createHardLinkWithResponse(ShareFileCreateH
 
     Mono> createHardLinkWithResponse(String targetFile,
         ShareRequestConditions requestConditions, Context context) {
+        ShareErrors.validatePathOperation(fileId, "createHardLink");
         context = context == null ? Context.NONE : context;
         requestConditions = requestConditions == null ? new ShareRequestConditions() : requestConditions;
         return this.azureFileStorageClient.getFiles()
@@ -3545,6 +3638,7 @@ public Mono> createSymbolicLinkWithResponse(ShareFileCre
     Mono> createSymbolicLinkWithResponse(String linkText, Map metadata,
         OffsetDateTime fileCreationTime, OffsetDateTime fileLastWriteTime, String owner, String group,
         ShareRequestConditions requestConditions, Context context) {
+        ShareErrors.validatePathOperation(fileId, "createSymbolicLink");
         context = context == null ? Context.NONE : context;
         requestConditions = requestConditions == null ? new ShareRequestConditions() : requestConditions;
         String fileCreationTimeString = FileSmbProperties.parseFileSMBDate(fileCreationTime);
@@ -3596,6 +3690,7 @@ public Mono> getSymbolicLinkWithResponse() {
     }
 
     Mono> getSymbolicLinkWithResponse(Context context) {
+        ShareErrors.validatePathOperation(fileId, "getSymbolicLink");
         context = context == null ? Context.NONE : context;
         return this.azureFileStorageClient.getFiles()
             .getSymbolicLinkWithResponseAsync(shareName, filePath, null, snapshot, null, context)
@@ -3630,6 +3725,7 @@ public String generateUserDelegationSas(ShareServiceSasSignatureValues shareServ
      */
     public String generateUserDelegationSas(ShareServiceSasSignatureValues shareServiceSasSignatureValues,
         UserDelegationKey userDelegationKey, Consumer stringToSignHandler, Context context) {
+        ShareErrors.validatePathOperation(fileId, "generateUserDelegationSas");
         return new ShareSasImplUtil(shareServiceSasSignatureValues, getShareName(), getFilePath())
             .generateUserDelegationSas(userDelegationKey, accountName, stringToSignHandler, context);
     }
diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareFileClient.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareFileClient.java
index 81febcdf9130d..c97912c2fa606 100644
--- a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareFileClient.java
+++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareFileClient.java
@@ -33,15 +33,18 @@
 import com.azure.storage.common.implementation.StorageSeekableByteChannel;
 import com.azure.storage.common.implementation.UploadUtils;
 import com.azure.storage.file.share.implementation.AzureFileStorageImpl;
+import com.azure.storage.file.share.implementation.ShareErrors;
 import com.azure.storage.file.share.implementation.models.CopyFileSmbInfo;
 import com.azure.storage.file.share.implementation.models.DestinationLeaseAccessConditions;
 import com.azure.storage.file.share.implementation.models.FilesCreateHardLinkHeaders;
 import com.azure.storage.file.share.implementation.models.FilesCreateHeaders;
 import com.azure.storage.file.share.implementation.models.FilesCreateSymbolicLinkHeaders;
 import com.azure.storage.file.share.implementation.models.FilesForceCloseHandlesHeaders;
+import com.azure.storage.file.share.implementation.models.FilesGetHardLinksHeaders;
 import com.azure.storage.file.share.implementation.models.FilesGetPropertiesHeaders;
 import com.azure.storage.file.share.implementation.models.FilesGetRangeListHeaders;
 import com.azure.storage.file.share.implementation.models.FilesGetSymbolicLinkHeaders;
+import com.azure.storage.file.share.implementation.models.HardLinkList;
 import com.azure.storage.file.share.implementation.models.FilesListHandlesHeaders;
 import com.azure.storage.file.share.implementation.models.FilesSetHttpHeadersHeaders;
 import com.azure.storage.file.share.implementation.models.FilesSetMetadataHeaders;
@@ -66,6 +69,7 @@
 import com.azure.storage.file.share.models.ShareFileDownloadResponse;
 import com.azure.storage.file.share.models.ShareFileHttpHeaders;
 import com.azure.storage.file.share.models.ShareFileInfo;
+import com.azure.storage.file.share.models.ShareFileLinks;
 import com.azure.storage.file.share.models.ShareFileMetadataInfo;
 import com.azure.storage.file.share.models.ShareFilePermission;
 import com.azure.storage.file.share.models.ShareFileProperties;
@@ -119,6 +123,10 @@
  * 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.
  *
+ * 

Use {@link ShareClient#getFileClientByFileId(String)} to retrieve properties without knowing the file path. + * File-ID clients support {@link #getProperties()} and {@link #getFileLinks()} (SMB only). + * Other service operations require a path-addressed client.

+ * *

Instantiating a synchronous File Client

* * @@ -144,6 +152,7 @@ public class ShareFileClient { private final AzureFileStorageImpl azureFileStorageClient; private final String shareName; private final String filePath; + private final String fileId; private final String snapshot; private final String accountName; private final ShareServiceVersion serviceVersion; @@ -163,25 +172,36 @@ public class ShareFileClient { ShareFileClient(ShareFileAsyncClient shareFileAsyncClient, AzureFileStorageImpl azureFileStorageClient, String shareName, String filePath, String snapshot, String accountName, ShareServiceVersion serviceVersion, AzureSasCredential sasToken) { + this(shareFileAsyncClient, azureFileStorageClient, shareName, filePath, "", snapshot, accountName, + serviceVersion, sasToken); + } + + ShareFileClient(ShareFileAsyncClient shareFileAsyncClient, AzureFileStorageImpl azureFileStorageClient, + String shareName, String filePath, String fileId, String snapshot, String accountName, + ShareServiceVersion serviceVersion, AzureSasCredential sasToken) { this.shareFileAsyncClient = shareFileAsyncClient; Objects.requireNonNull(shareName, "'shareName' cannot be null."); Objects.requireNonNull(filePath, "'filePath' cannot be null."); this.shareName = shareName; this.filePath = filePath; + this.fileId = fileId; this.snapshot = snapshot; this.azureFileStorageClient = azureFileStorageClient; this.accountName = accountName; this.serviceVersion = serviceVersion; this.sasToken = sasToken; - StringBuilder fileUrlstring = new StringBuilder(azureFileStorageClient.getUrl()).append("/") - .append(shareName) - .append("/") - .append(filePath); + String fileUrlstring = azureFileStorageClient.getUrl() + "/" + shareName; + if (fileId.isEmpty()) { + fileUrlstring += "/" + filePath; + } if (snapshot != null) { - fileUrlstring.append("?sharesnapshot=").append(snapshot); + fileUrlstring = Utility.appendQueryParameter(fileUrlstring, "sharesnapshot", snapshot); } - this.fileUrlString = fileUrlstring.toString(); + if (!fileId.isEmpty()) { + fileUrlstring = Utility.appendQueryParameter(fileUrlstring, "fileid", Utility.urlEncode(fileId)); + } + this.fileUrlString = fileUrlstring; } /** @@ -202,6 +222,15 @@ public String getFileUrl() { return this.fileUrlString; } + /** + * Gets the file ID used to address the file, or an empty string when the client is path-addressed. + * + * @return The file ID. + */ + public String getFileId() { + return fileId; + } + /** * Gets the service version the client is using. * @@ -211,6 +240,56 @@ public ShareServiceVersion getServiceVersion() { return this.serviceVersion; } + /** + * SMB only. Gets the hard links to this file. This operation is available only for a file-ID-addressed client. + * + * @return A response containing the file properties and hard links. + */ + public Response getFileLinksWithResponse() { + return getFileLinksWithResponse(null, null, Context.NONE); + } + + /** + * SMB only. Gets the hard links to this file. This operation is available only for a file-ID-addressed client. + * + * @param requestConditions Conditions to apply to the request. + * @return A response containing the file properties and hard links. + */ + public Response getFileLinksWithResponse(ShareRequestConditions requestConditions) { + return getFileLinksWithResponse(requestConditions, null, Context.NONE); + } + + /** + * SMB only. Gets the hard links to this file. This operation is available only for a file-ID-addressed client. + * + * @param requestConditions Conditions to apply to the request. + * @param timeout An optional timeout applied to the operation. + * @param context Additional context passed through the HTTP pipeline. + * @return A response containing the file properties and hard links. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response getFileLinksWithResponse(ShareRequestConditions requestConditions, Duration timeout, + Context context) { + ShareErrors.assertFileIdAddressed(fileId, "getFileLinks"); + ShareRequestConditions conditions + = requestConditions == null ? new ShareRequestConditions() : requestConditions; + Context finalContext = context == null ? Context.NONE : context; + Callable> operation = () -> azureFileStorageClient + .getFiles() + .getHardLinksWithResponse(shareName, fileId, snapshot, null, null, conditions.getLeaseId(), finalContext); + return ModelHelper.getFileLinksResponse(sendRequest(operation, timeout, ShareStorageException.class)); + } + + /** + * SMB only. Gets the file links. This operation is available only for a file-ID-addressed client. + * + * @return The file properties and hard links. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public ShareFileLinks getFileLinks() { + return getFileLinksWithResponse().getValue(); + } + /** * Opens a file input stream to download the file. * @@ -229,6 +308,7 @@ public final StorageFileInputStream openInputStream() { * @throws ShareStorageException If a storage service error occurred. */ public final StorageFileInputStream openInputStream(ShareFileRange range) { + ShareErrors.validatePathOperation(fileId, "openInputStream"); return new StorageFileInputStream(shareFileAsyncClient, range.getStart(), range.getEnd() == null ? null : (range.getEnd() - range.getStart() + 1)); } @@ -253,6 +333,7 @@ public final StorageFileOutputStream getFileOutputStream() { * @throws ShareStorageException If a storage service error occurred. */ public final StorageFileOutputStream getFileOutputStream(long offset) { + ShareErrors.validatePathOperation(fileId, "getFileOutputStream"); return new StorageFileOutputStream(shareFileAsyncClient, offset); } @@ -262,6 +343,7 @@ public final StorageFileOutputStream getFileOutputStream(long offset) { * @return The opened channel. */ public SeekableByteChannel getFileSeekableByteChannelWrite(ShareFileSeekableByteChannelWriteOptions options) { + ShareErrors.validatePathOperation(fileId, "getFileSeekableByteChannelWrite"); Objects.requireNonNull(options, "'options' cannot be null."); if (options.isOverwriteMode()) { @@ -282,6 +364,7 @@ public SeekableByteChannel getFileSeekableByteChannelWrite(ShareFileSeekableByte * @return The opened channel. */ public SeekableByteChannel getFileSeekableByteChannelRead(ShareFileSeekableByteChannelReadOptions options) { + ShareErrors.validatePathOperation(fileId, "getFileSeekableByteChannelRead"); ShareRequestConditions conditions = options != null ? options.getRequestConditions() : null; Long configuredChunkSize = options != null ? options.getChunkSizeInBytes() : null; int chunkSize @@ -326,6 +409,7 @@ public Boolean exists() { */ @ServiceMethod(returns = ReturnType.SINGLE) public Response existsWithResponse(Duration timeout, Context context) { + ShareErrors.validatePathOperation(fileId, "exists"); try { Response response = getPropertiesWithResponse(timeout, context); return new SimpleResponse<>(response, true); @@ -531,6 +615,7 @@ public Response createWithResponse(long maxSize, ShareFileHttpHea @ServiceMethod(returns = ReturnType.SINGLE) public Response createWithResponse(ShareFileCreateOptions options, Duration timeout, Context context) { + ShareErrors.validatePathOperation(fileId, "create"); StorageImplUtils.assertNotNull("options", options); Context finalContext = context == null ? Context.NONE : context; ShareRequestConditions requestConditions @@ -721,6 +806,7 @@ public SyncPoller beginCopy(String sourceUrl, FileSmbPr */ public SyncPoller beginCopy(String sourceUrl, ShareFileCopyOptions options, Duration pollInterval) { + ShareErrors.validatePathOperation(fileId, "beginCopy"); final ShareRequestConditions finalRequestConditions = options.getDestinationRequestConditions() == null ? new ShareRequestConditions() : options.getDestinationRequestConditions(); @@ -936,6 +1022,7 @@ public Response abortCopyWithResponse(String copyId, Duration timeout, Con @ServiceMethod(returns = ReturnType.SINGLE) public Response abortCopyWithResponse(String copyId, ShareRequestConditions requestConditions, Duration timeout, Context context) { + ShareErrors.validatePathOperation(fileId, "abortCopy"); Context finalContext = context == null ? Context.NONE : context; ShareRequestConditions finalRequestConditions = requestConditions == null ? new ShareRequestConditions() : requestConditions; @@ -1310,6 +1397,7 @@ public Response deleteWithResponse(Duration timeout, Context context) { @ServiceMethod(returns = ReturnType.SINGLE) public Response deleteWithResponse(ShareRequestConditions requestConditions, Duration timeout, Context context) { + ShareErrors.validatePathOperation(fileId, "delete"); Context finalContext = context == null ? Context.NONE : context; ShareRequestConditions finalRequestConditions = requestConditions == null ? new ShareRequestConditions() : requestConditions; @@ -1398,6 +1486,11 @@ public Response deleteIfExistsWithResponse(ShareRequestConditions reque * *

Retrieve file properties

* + *

This operation accepts path-addressed and file-ID-addressed clients. To address a file by ID, use + * {@link ShareClient#getFileClientByFileId(String)}. Service availability determines whether a file-ID request + * succeeds; the client does not check the share protocol. If the service does not support property retrieval by ID + * for the share, use a path-addressed client.

+ * * *
      * ShareFileProperties properties = fileClient.getProperties();
@@ -1480,9 +1573,13 @@ public Response getPropertiesWithResponse(ShareRequestCondi
         Context finalContext = context == null ? Context.NONE : context;
         ShareRequestConditions finalRequestConditions
             = requestConditions == null ? new ShareRequestConditions() : requestConditions;
-        Callable> operation = () -> this.azureFileStorageClient.getFiles()
-            .getPropertiesWithResponse(shareName, filePath, snapshot, null, finalRequestConditions.getLeaseId(),
-                finalContext);
+        Callable> operation = fileId.isEmpty()
+            ? () -> this.azureFileStorageClient.getFiles()
+                .getPropertiesWithResponse(shareName, filePath, snapshot, null, finalRequestConditions.getLeaseId(),
+                    finalContext)
+            : () -> this.azureFileStorageClient.getFiles()
+                .getPropertiesByFileIdWithResponse(shareName, fileId, snapshot, null, null,
+                    finalRequestConditions.getLeaseId(), finalContext);
 
         return ModelHelper.getPropertiesResponse(sendRequest(operation, timeout, ShareStorageException.class));
     }
@@ -1716,6 +1813,7 @@ public Response setPropertiesWithResponse(long newFileSize, Share
     @ServiceMethod(returns = ReturnType.SINGLE)
     public Response setPropertiesWithResponse(ShareFileSetPropertiesOptions options, Duration timeout,
         Context context) {
+        ShareErrors.validatePathOperation(fileId, "setProperties");
         StorageImplUtils.assertNotNull("options", options);
         Context finalContext = context == null ? Context.NONE : context;
         ShareRequestConditions finalRequestConditions
@@ -1869,6 +1967,7 @@ public Response setMetadataWithResponse(Map setMetadataWithResponse(Map metadata,
         ShareRequestConditions requestConditions, Duration timeout, Context context) {
+        ShareErrors.validatePathOperation(fileId, "setMetadata");
         Context finalContext = context == null ? Context.NONE : context;
         ShareRequestConditions finalRequestConditions
             = requestConditions == null ? new ShareRequestConditions() : requestConditions;
@@ -2276,6 +2375,7 @@ public Response uploadRangeFromUrlWithResponse(
     @ServiceMethod(returns = ReturnType.SINGLE)
     public Response
         uploadRangeFromUrlWithResponse(ShareFileUploadRangeFromUrlOptions options, Duration timeout, Context context) {
+        ShareErrors.validatePathOperation(fileId, "uploadRangeFromUrl");
         ShareRequestConditions finalRequestConditions = options.getDestinationRequestConditions() == null
             ? new ShareRequestConditions()
             : options.getDestinationRequestConditions();
@@ -2390,6 +2490,7 @@ public Response clearRangeWithResponse(long length, long of
     @ServiceMethod(returns = ReturnType.SINGLE)
     public Response clearRangeWithResponse(long length, long offset,
         ShareRequestConditions requestConditions, Duration timeout, Context context) {
+        ShareErrors.validatePathOperation(fileId, "clearRange");
         ShareRequestConditions finalRequestConditions
             = requestConditions == null ? new ShareRequestConditions() : requestConditions;
         ShareFileRange range = new ShareFileRange(offset, offset + length - 1);
@@ -2420,6 +2521,7 @@ public Response clearRangeWithResponse(long length, long of
      * Azure Docs Upload.

* * @param uploadFilePath The path where store the source file to upload + * @throws IllegalStateException If this client is addressed by file ID. */ @ServiceMethod(returns = ReturnType.SINGLE) public void uploadFromFile(String uploadFilePath) { @@ -2447,6 +2549,7 @@ public void uploadFromFile(String uploadFilePath) { * * @param uploadFilePath The path where store the source file to upload * @param requestConditions {@link ShareRequestConditions} + * @throws IllegalStateException If this client is addressed by file ID. */ @ServiceMethod(returns = ReturnType.SINGLE) public void uploadFromFile(String uploadFilePath, ShareRequestConditions requestConditions) { @@ -2546,6 +2649,7 @@ public PagedIterable listRanges(ShareFileRange range, Duration t @ServiceMethod(returns = ReturnType.COLLECTION) public PagedIterable listRanges(ShareFileRange range, ShareRequestConditions requestConditions, Duration timeout, Context context) { + ShareErrors.validatePathOperation(fileId, "listRanges"); Context finalContext = context == null ? Context.NONE : context; ShareRequestConditions finalRequestConditions = requestConditions == null ? new ShareRequestConditions() : requestConditions; @@ -2735,6 +2839,7 @@ public ShareFileRangeList listRangesDiff(String previousSnapshot) { @ServiceMethod(returns = ReturnType.SINGLE) public Response listRangesDiffWithResponse(ShareFileListRangesDiffOptions options, Duration timeout, Context context) { + ShareErrors.validatePathOperation(fileId, "listRangesDiff"); Context finalContext = context == null ? Context.NONE : context; ShareRequestConditions requestConditions = options.getRequestConditions() == null ? new ShareRequestConditions() : options.getRequestConditions(); @@ -2798,6 +2903,7 @@ public PagedIterable listHandles() { */ @ServiceMethod(returns = ReturnType.COLLECTION) public PagedIterable listHandles(Integer maxResultsPerPage, Duration timeout, Context context) { + ShareErrors.validatePathOperation(fileId, "listHandles"); Context finalContext = context == null ? Context.NONE : context; try { Callable> operation @@ -2876,6 +2982,7 @@ public CloseHandlesInfo forceCloseHandle(String handleId) { */ @ServiceMethod(returns = ReturnType.SINGLE) public Response forceCloseHandleWithResponse(String handleId, Duration timeout, Context context) { + ShareErrors.validatePathOperation(fileId, "forceCloseHandle"); Context finalContext = context == null ? Context.NONE : context; Callable> operation = () -> azureFileStorageClient.getFiles() .forceCloseHandlesWithResponse(shareName, filePath, handleId, null, null, snapshot, finalContext); @@ -2913,6 +3020,7 @@ public Response forceCloseHandleWithResponse(String handleId, */ @ServiceMethod(returns = ReturnType.SINGLE) public CloseHandlesInfo forceCloseAllHandles(Duration timeout, Context context) { + ShareErrors.validatePathOperation(fileId, "forceCloseAllHandles"); Context finalContext = context == null ? Context.NONE : context; try { Callable> operation @@ -3001,6 +3109,7 @@ public ShareFileClient rename(String destinationPath) { @ServiceMethod(returns = ReturnType.SINGLE) public Response renameWithResponse(ShareFileRenameOptions options, Duration timeout, Context context) { + ShareErrors.validatePathOperation(fileId, "rename"); StorageImplUtils.assertNotNull("options", options); Context finalContext = context == null ? Context.NONE : context; ShareRequestConditions sourceRequestConditions = options.getSourceRequestConditions() == null @@ -3223,6 +3332,7 @@ public String generateSas(ShareServiceSasSignatureValues shareServiceSasSignatur */ public String generateSas(ShareServiceSasSignatureValues shareServiceSasSignatureValues, Consumer stringToSignHandler, Context context) { + ShareErrors.validatePathOperation(fileId, "generateSas"); return new ShareSasImplUtil(shareServiceSasSignatureValues, getShareName(), getFilePath()) .generateSas(SasImplUtils.extractSharedKeyCredential(getHttpPipeline()), stringToSignHandler, context); } @@ -3267,6 +3377,7 @@ public ShareFileInfo createHardLink(String targetFile) { @ServiceMethod(returns = ReturnType.SINGLE) public Response createHardLinkWithResponse(ShareFileCreateHardLinkOptions options, Duration timeout, Context context) { + ShareErrors.validatePathOperation(fileId, "createHardLink"); StorageImplUtils.assertNotNull("options", options); Context finalContext = context == null ? Context.NONE : context; ShareRequestConditions requestConditions @@ -3318,6 +3429,7 @@ public ShareFileInfo createSymbolicLink(String linkText) { */ public Response createSymbolicLinkWithResponse(ShareFileCreateSymbolicLinkOptions options, Duration timeout, Context context) { + ShareErrors.validatePathOperation(fileId, "createSymbolicLink"); StorageImplUtils.assertNotNull("options", options); Context finalContext = context == null ? Context.NONE : context; ShareRequestConditions requestConditions @@ -3368,6 +3480,7 @@ public ShareFileSymbolicLinkInfo getSymbolicLink() { * describing the symbolic link. */ public Response getSymbolicLinkWithResponse(Duration timeout, Context context) { + ShareErrors.validatePathOperation(fileId, "getSymbolicLink"); Context finalContext = context == null ? Context.NONE : context; Callable> operation @@ -3405,6 +3518,7 @@ public String generateUserDelegationSas(ShareServiceSasSignatureValues shareServ */ public String generateUserDelegationSas(ShareServiceSasSignatureValues shareServiceSasSignatureValues, UserDelegationKey userDelegationKey, Consumer stringToSignHandler, Context context) { + ShareErrors.validatePathOperation(fileId, "generateUserDelegationSas"); return new ShareSasImplUtil(shareServiceSasSignatureValues, getShareName(), getFilePath()) .generateUserDelegationSas(userDelegationKey, accountName, stringToSignHandler, context); } @@ -3412,6 +3526,7 @@ public String generateUserDelegationSas(ShareServiceSasSignatureValues shareServ ResponseBase listRangesWithResponse(ShareFileRange range, ShareRequestConditions requestConditions, String previousSnapshot, Boolean supportRename, String marker, Integer maxResultsPerPage, Duration timeout, Context context) { + ShareErrors.validatePathOperation(fileId, "listRanges"); ShareRequestConditions finalRequestConditions = requestConditions == null ? new ShareRequestConditions() : requestConditions; String rangeString = range == null ? null : range.toString(); diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareFileClientBuilder.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareFileClientBuilder.java index fe1fbc329ade4..44dbb88d833bd 100644 --- a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareFileClientBuilder.java +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareFileClientBuilder.java @@ -35,6 +35,7 @@ import com.azure.storage.common.policy.RequestRetryOptions; import com.azure.storage.common.sas.CommonSasQueryParameters; import com.azure.storage.file.share.implementation.AzureFileStorageImpl; +import com.azure.storage.file.share.implementation.ShareErrors; import com.azure.storage.file.share.implementation.util.BuilderHelper; import com.azure.storage.file.share.models.ShareAudience; import com.azure.storage.file.share.models.ShareTokenIntent; @@ -162,6 +163,7 @@ public class ShareFileClientBuilder implements TokenCredentialTrait * - *

Query parameters of the endpoint will be parsed in an attempt to generate a SAS token to authenticate - * requests sent to the service.

+ *

The {@code sharesnapshot} and {@code fileid} query parameters are parsed from the endpoint. Other query + * parameters are parsed in an attempt to generate a SAS token 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. @@ -340,10 +344,19 @@ public ShareFileClientBuilder endpoint(String endpoint) { this.shareSnapshot = snapshotArray[0]; } + if (length >= 2) { + this.fileId = ""; + } + String[] fileIdArray = queryParamsMap.remove("fileid"); + if (fileIdArray != null) { + ShareErrors.validateFileId(fileIdArray[0]); + this.fileId = fileIdArray[0]; + this.resourcePath = ""; + } + // TODO (gapra): What happens if a user has custom queries? // Attempt to get the SAS token from the URL passed - String sasToken - = new CommonSasQueryParameters(SasImplUtils.parseQueryString(fullUrl.getQuery()), false).encode(); + String sasToken = new CommonSasQueryParameters(queryParamsMap, false).encode(); if (!CoreUtils.isNullOrEmpty(sasToken)) { sasToken(sasToken); } @@ -367,6 +380,30 @@ public ShareFileClientBuilder shareName(String shareName) { return this; } + /** + * Sets the file ID used to address a file. + * + *

Call {@link ShareFileClient#getProperties()} or {@link ShareFileAsyncClient#getProperties()} on the + * resulting client to retrieve properties without supplying the file path. File-ID clients also support + * {@link ShareFileClient#getFileLinks()} and {@link ShareFileAsyncClient#getFileLinks()} (SMB only). + * Other service operations require a path-addressed client.

+ * + *

Setting a file ID clears the resource path. The file ID can also be read from the {@code fileid} query + * parameter when supplied in the endpoint. File IDs are supported only for file clients; building a directory + * client with a file ID configured throws {@link IllegalStateException}. Use {@link #resourcePath(String)} to + * clear the file ID and address a directory by path.

+ * + * @param fileId The file ID of the file. + * @return the updated ShareFileClientBuilder object + * @throws IllegalArgumentException If {@code fileId} is null or blank. + */ + public ShareFileClientBuilder fileId(String fileId) { + ShareErrors.validateFileId(fileId); + this.fileId = fileId; + this.resourcePath = ""; + return this; + } + /** * Sets the shareSnapshot that the constructed clients will interact with. This shareSnapshot must be linked to the * share that has been specified in the builder. @@ -381,7 +418,8 @@ public ShareFileClientBuilder snapshot(String snapshot) { } /** - * Sets the file that the constructed clients will interact with + * Sets the file or directory path that the constructed clients will interact with. Calling this method clears any + * file ID previously set with {@link #fileId(String)}. * * @param resourcePath Path of the file (or directory). * @return the updated ShareFileClientBuilder object @@ -389,6 +427,7 @@ public ShareFileClientBuilder snapshot(String snapshot) { */ public ShareFileClientBuilder resourcePath(String resourcePath) { this.resourcePath = resourcePath; + this.fileId = ""; return this; } diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/DirectoriesImpl.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/DirectoriesImpl.java index f166775a06c68..466f68f0d3c69 100644 --- a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/DirectoriesImpl.java +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/DirectoriesImpl.java @@ -631,6 +631,54 @@ Response renameNoCustomHeadersSync(@HostParam("url") String url, @PathPara @HeaderParam("x-ms-source-allow-trailing-dot") Boolean allowSourceTrailingDot, @HeaderParam("x-ms-file-request-intent") ShareTokenIntent fileRequestIntent, @HeaderParam("Accept") String accept, Context context); + + @Get("/{shareName}") + @ExpectedResponses({ 200 }) + @UnexpectedResponseExceptionType(ShareStorageExceptionInternal.class) + Mono> getPropertiesByFileId(@HostParam("url") String url, + @PathParam("shareName") String shareName, @QueryParam("restype") String restype, + @QueryParam("fileid") String fileId, @QueryParam("sharesnapshot") String sharesnapshot, + @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, + @HeaderParam("x-ms-client-request-id") String requestId, + @HeaderParam("x-ms-allow-trailing-dot") Boolean allowTrailingDot, + @HeaderParam("x-ms-file-request-intent") ShareTokenIntent fileRequestIntent, + @HeaderParam("Accept") String accept, Context context); + + @Get("/{shareName}") + @ExpectedResponses({ 200 }) + @UnexpectedResponseExceptionType(ShareStorageExceptionInternal.class) + Mono> getPropertiesByFileIdNoCustomHeaders(@HostParam("url") String url, + @PathParam("shareName") String shareName, @QueryParam("restype") String restype, + @QueryParam("fileid") String fileId, @QueryParam("sharesnapshot") String sharesnapshot, + @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, + @HeaderParam("x-ms-client-request-id") String requestId, + @HeaderParam("x-ms-allow-trailing-dot") Boolean allowTrailingDot, + @HeaderParam("x-ms-file-request-intent") ShareTokenIntent fileRequestIntent, + @HeaderParam("Accept") String accept, Context context); + + @Get("/{shareName}") + @ExpectedResponses({ 200 }) + @UnexpectedResponseExceptionType(ShareStorageExceptionInternal.class) + ResponseBase getPropertiesByFileIdSync(@HostParam("url") String url, + @PathParam("shareName") String shareName, @QueryParam("restype") String restype, + @QueryParam("fileid") String fileId, @QueryParam("sharesnapshot") String sharesnapshot, + @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, + @HeaderParam("x-ms-client-request-id") String requestId, + @HeaderParam("x-ms-allow-trailing-dot") Boolean allowTrailingDot, + @HeaderParam("x-ms-file-request-intent") ShareTokenIntent fileRequestIntent, + @HeaderParam("Accept") String accept, Context context); + + @Get("/{shareName}") + @ExpectedResponses({ 200 }) + @UnexpectedResponseExceptionType(ShareStorageExceptionInternal.class) + Response getPropertiesByFileIdNoCustomHeadersSync(@HostParam("url") String url, + @PathParam("shareName") String shareName, @QueryParam("restype") String restype, + @QueryParam("fileid") String fileId, @QueryParam("sharesnapshot") String sharesnapshot, + @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, + @HeaderParam("x-ms-client-request-id") String requestId, + @HeaderParam("x-ms-allow-trailing-dot") Boolean allowTrailingDot, + @HeaderParam("x-ms-file-request-intent") ShareTokenIntent fileRequestIntent, + @HeaderParam("Accept") String accept, Context context); } /** @@ -3767,4 +3815,262 @@ public Response renameNoCustomHeadersWithResponse(String shareName, String throw ModelHelper.mapToShareStorageException(internalException); } } + + /** + * Returns all system properties for the directory identified by its file ID. The data returned does not include the + * files in the directory or any subdirectories. + * + * @param shareName The name of the target share. + * @param fileId The file ID of the target file or 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://learn.microsoft.com/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations">Setting + * Timeouts for File Service Operations.</a>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws ShareStorageExceptionInternal thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the {@link ResponseBase} on successful completion of {@link Mono}. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> getPropertiesByFileIdWithResponseAsync( + String shareName, String fileId, String sharesnapshot, Integer timeout, String requestId) { + return FluxUtil + .withContext(context -> getPropertiesByFileIdWithResponseAsync(shareName, fileId, sharesnapshot, timeout, + requestId, context)) + .onErrorMap(ShareStorageExceptionInternal.class, ModelHelper::mapToShareStorageException); + } + + /** + * Returns all system properties for the directory identified by its file ID. The data returned does not include the + * files in the directory or any subdirectories. + * + * @param shareName The name of the target share. + * @param fileId The file ID of the target file or 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://learn.microsoft.com/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations">Setting + * Timeouts for File Service Operations.</a>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws ShareStorageExceptionInternal thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the {@link ResponseBase} on successful completion of {@link Mono}. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> getPropertiesByFileIdWithResponseAsync( + String shareName, String fileId, String sharesnapshot, Integer timeout, String requestId, Context context) { + final String restype = "directory"; + final String accept = "application/xml"; + return service + .getPropertiesByFileId(this.client.getUrl(), shareName, restype, fileId, sharesnapshot, timeout, + this.client.getVersion(), requestId, this.client.isAllowTrailingDot(), + this.client.getFileRequestIntent(), accept, context) + .onErrorMap(ShareStorageExceptionInternal.class, ModelHelper::mapToShareStorageException); + } + + /** + * Returns all system properties for the directory identified by its file ID. The data returned does not include the + * files in the directory or any subdirectories. + * + * @param shareName The name of the target share. + * @param fileId The file ID of the target file or 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://learn.microsoft.com/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations">Setting + * Timeouts for File Service Operations.</a>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws ShareStorageExceptionInternal thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return A {@link Mono} that completes when a successful response is received. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono getPropertiesByFileIdAsync(String shareName, String fileId, String sharesnapshot, Integer timeout, + String requestId) { + return getPropertiesByFileIdWithResponseAsync(shareName, fileId, sharesnapshot, timeout, requestId) + .onErrorMap(ShareStorageExceptionInternal.class, ModelHelper::mapToShareStorageException) + .flatMap(ignored -> Mono.empty()); + } + + /** + * Returns all system properties for the directory identified by its file ID. The data returned does not include the + * files in the directory or any subdirectories. + * + * @param shareName The name of the target share. + * @param fileId The file ID of the target file or 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://learn.microsoft.com/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations">Setting + * Timeouts for File Service Operations.</a>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws ShareStorageExceptionInternal thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return A {@link Mono} that completes when a successful response is received. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono getPropertiesByFileIdAsync(String shareName, String fileId, String sharesnapshot, Integer timeout, + String requestId, Context context) { + return getPropertiesByFileIdWithResponseAsync(shareName, fileId, sharesnapshot, timeout, requestId, context) + .onErrorMap(ShareStorageExceptionInternal.class, ModelHelper::mapToShareStorageException) + .flatMap(ignored -> Mono.empty()); + } + + /** + * Returns all system properties for the directory identified by its file ID. The data returned does not include the + * files in the directory or any subdirectories. + * + * @param shareName The name of the target share. + * @param fileId The file ID of the target file or 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://learn.microsoft.com/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations">Setting + * Timeouts for File Service Operations.</a>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws ShareStorageExceptionInternal thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the {@link Response} on successful completion of {@link Mono}. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> getPropertiesByFileIdNoCustomHeadersWithResponseAsync(String shareName, String fileId, + String sharesnapshot, Integer timeout, String requestId) { + return FluxUtil + .withContext(context -> getPropertiesByFileIdNoCustomHeadersWithResponseAsync(shareName, fileId, + sharesnapshot, timeout, requestId, context)) + .onErrorMap(ShareStorageExceptionInternal.class, ModelHelper::mapToShareStorageException); + } + + /** + * Returns all system properties for the directory identified by its file ID. The data returned does not include the + * files in the directory or any subdirectories. + * + * @param shareName The name of the target share. + * @param fileId The file ID of the target file or 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://learn.microsoft.com/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations">Setting + * Timeouts for File Service Operations.</a>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws ShareStorageExceptionInternal thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the {@link Response} on successful completion of {@link Mono}. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> getPropertiesByFileIdNoCustomHeadersWithResponseAsync(String shareName, String fileId, + String sharesnapshot, Integer timeout, String requestId, Context context) { + final String restype = "directory"; + final String accept = "application/xml"; + return service + .getPropertiesByFileIdNoCustomHeaders(this.client.getUrl(), shareName, restype, fileId, sharesnapshot, + timeout, this.client.getVersion(), requestId, this.client.isAllowTrailingDot(), + this.client.getFileRequestIntent(), accept, context) + .onErrorMap(ShareStorageExceptionInternal.class, ModelHelper::mapToShareStorageException); + } + + /** + * Returns all system properties for the directory identified by its file ID. The data returned does not include the + * files in the directory or any subdirectories. + * + * @param shareName The name of the target share. + * @param fileId The file ID of the target file or 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://learn.microsoft.com/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations">Setting + * Timeouts for File Service Operations.</a>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws ShareStorageExceptionInternal thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the {@link ResponseBase}. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public ResponseBase getPropertiesByFileIdWithResponse(String shareName, + String fileId, String sharesnapshot, Integer timeout, String requestId, Context context) { + try { + final String restype = "directory"; + final String accept = "application/xml"; + return service.getPropertiesByFileIdSync(this.client.getUrl(), shareName, restype, fileId, sharesnapshot, + timeout, this.client.getVersion(), requestId, this.client.isAllowTrailingDot(), + this.client.getFileRequestIntent(), accept, context); + } catch (ShareStorageExceptionInternal internalException) { + throw ModelHelper.mapToShareStorageException(internalException); + } + } + + /** + * Returns all system properties for the directory identified by its file ID. The data returned does not include the + * files in the directory or any subdirectories. + * + * @param shareName The name of the target share. + * @param fileId The file ID of the target file or 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://learn.microsoft.com/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations">Setting + * Timeouts for File Service Operations.</a>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws ShareStorageExceptionInternal thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public void getPropertiesByFileId(String shareName, String fileId, String sharesnapshot, Integer timeout, + String requestId) { + getPropertiesByFileIdWithResponse(shareName, fileId, sharesnapshot, timeout, requestId, Context.NONE); + } + + /** + * Returns all system properties for the directory identified by its file ID. The data returned does not include the + * files in the directory or any subdirectories. + * + * @param shareName The name of the target share. + * @param fileId The file ID of the target file or 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://learn.microsoft.com/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations">Setting + * Timeouts for File Service Operations.</a>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws ShareStorageExceptionInternal thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the {@link Response}. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response getPropertiesByFileIdNoCustomHeadersWithResponse(String shareName, String fileId, + String sharesnapshot, Integer timeout, String requestId, Context context) { + try { + final String restype = "directory"; + final String accept = "application/xml"; + return service.getPropertiesByFileIdNoCustomHeadersSync(this.client.getUrl(), shareName, restype, fileId, + sharesnapshot, timeout, this.client.getVersion(), requestId, this.client.isAllowTrailingDot(), + this.client.getFileRequestIntent(), accept, context); + } catch (ShareStorageExceptionInternal internalException) { + throw ModelHelper.mapToShareStorageException(internalException); + } + } } diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/FilesImpl.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/FilesImpl.java index e2391f3e79237..b0f43d2413ee3 100644 --- a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/FilesImpl.java +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/FilesImpl.java @@ -38,6 +38,7 @@ import com.azure.storage.file.share.implementation.models.FilesDeleteHeaders; import com.azure.storage.file.share.implementation.models.FilesDownloadHeaders; import com.azure.storage.file.share.implementation.models.FilesForceCloseHandlesHeaders; +import com.azure.storage.file.share.implementation.models.FilesGetHardLinksHeaders; import com.azure.storage.file.share.implementation.models.FilesGetPropertiesHeaders; import com.azure.storage.file.share.implementation.models.FilesGetRangeListHeaders; import com.azure.storage.file.share.implementation.models.FilesGetSymbolicLinkHeaders; @@ -49,6 +50,7 @@ import com.azure.storage.file.share.implementation.models.FilesStartCopyHeaders; import com.azure.storage.file.share.implementation.models.FilesUploadRangeFromURLHeaders; import com.azure.storage.file.share.implementation.models.FilesUploadRangeHeaders; +import com.azure.storage.file.share.implementation.models.HardLinkList; import com.azure.storage.file.share.implementation.models.ListHandlesResponse; import com.azure.storage.file.share.implementation.models.ShareFileRangeWriteType; import com.azure.storage.file.share.implementation.models.ShareStorageExceptionInternal; @@ -104,6 +106,102 @@ public final class FilesImpl { @ServiceInterface(name = "AzureFileStorageFiles") public interface FilesService { + @Get("/{shareName}") + @ExpectedResponses({ 200 }) + @UnexpectedResponseExceptionType(ShareStorageExceptionInternal.class) + Mono> getHardLinks(@HostParam("url") String url, + @PathParam("shareName") String shareName, @QueryParam("comp") String comp, + @QueryParam("sharesnapshot") String sharesnapshot, @QueryParam("fileid") String fileId, + @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, + @HeaderParam("x-ms-client-request-id") String requestId, @HeaderParam("x-ms-lease-id") String leaseId, + @HeaderParam("x-ms-allow-trailing-dot") Boolean allowTrailingDot, + @HeaderParam("x-ms-file-request-intent") ShareTokenIntent fileRequestIntent, + @HeaderParam("Accept") String accept, Context context); + + @Get("/{shareName}") + @ExpectedResponses({ 200 }) + @UnexpectedResponseExceptionType(ShareStorageExceptionInternal.class) + Mono> getHardLinksNoCustomHeaders(@HostParam("url") String url, + @PathParam("shareName") String shareName, @QueryParam("comp") String comp, + @QueryParam("sharesnapshot") String sharesnapshot, @QueryParam("fileid") String fileId, + @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, + @HeaderParam("x-ms-client-request-id") String requestId, @HeaderParam("x-ms-lease-id") String leaseId, + @HeaderParam("x-ms-allow-trailing-dot") Boolean allowTrailingDot, + @HeaderParam("x-ms-file-request-intent") ShareTokenIntent fileRequestIntent, + @HeaderParam("Accept") String accept, Context context); + + @Get("/{shareName}") + @ExpectedResponses({ 200 }) + @UnexpectedResponseExceptionType(ShareStorageExceptionInternal.class) + ResponseBase getHardLinksSync(@HostParam("url") String url, + @PathParam("shareName") String shareName, @QueryParam("comp") String comp, + @QueryParam("sharesnapshot") String sharesnapshot, @QueryParam("fileid") String fileId, + @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, + @HeaderParam("x-ms-client-request-id") String requestId, @HeaderParam("x-ms-lease-id") String leaseId, + @HeaderParam("x-ms-allow-trailing-dot") Boolean allowTrailingDot, + @HeaderParam("x-ms-file-request-intent") ShareTokenIntent fileRequestIntent, + @HeaderParam("Accept") String accept, Context context); + + @Get("/{shareName}") + @ExpectedResponses({ 200 }) + @UnexpectedResponseExceptionType(ShareStorageExceptionInternal.class) + Response getHardLinksNoCustomHeadersSync(@HostParam("url") String url, + @PathParam("shareName") String shareName, @QueryParam("comp") String comp, + @QueryParam("sharesnapshot") String sharesnapshot, @QueryParam("fileid") String fileId, + @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, + @HeaderParam("x-ms-client-request-id") String requestId, @HeaderParam("x-ms-lease-id") String leaseId, + @HeaderParam("x-ms-allow-trailing-dot") Boolean allowTrailingDot, + @HeaderParam("x-ms-file-request-intent") ShareTokenIntent fileRequestIntent, + @HeaderParam("Accept") String accept, Context context); + + @Head("/{shareName}") + @ExpectedResponses({ 200 }) + @UnexpectedResponseExceptionType(ShareStorageExceptionInternal.class) + Mono> getPropertiesByFileId(@HostParam("url") String url, + @PathParam("shareName") String shareName, @QueryParam("fileid") String fileId, + @QueryParam("sharesnapshot") String sharesnapshot, @QueryParam("timeout") Integer timeout, + @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-client-request-id") String requestId, + @HeaderParam("x-ms-lease-id") String leaseId, + @HeaderParam("x-ms-allow-trailing-dot") Boolean allowTrailingDot, + @HeaderParam("x-ms-file-request-intent") ShareTokenIntent fileRequestIntent, + @HeaderParam("Accept") String accept, Context context); + + @Head("/{shareName}") + @ExpectedResponses({ 200 }) + @UnexpectedResponseExceptionType(ShareStorageExceptionInternal.class) + Mono> getPropertiesByFileIdNoCustomHeaders(@HostParam("url") String url, + @PathParam("shareName") String shareName, @QueryParam("fileid") String fileId, + @QueryParam("sharesnapshot") String sharesnapshot, @QueryParam("timeout") Integer timeout, + @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-client-request-id") String requestId, + @HeaderParam("x-ms-lease-id") String leaseId, + @HeaderParam("x-ms-allow-trailing-dot") Boolean allowTrailingDot, + @HeaderParam("x-ms-file-request-intent") ShareTokenIntent fileRequestIntent, + @HeaderParam("Accept") String accept, Context context); + + @Head("/{shareName}") + @ExpectedResponses({ 200 }) + @UnexpectedResponseExceptionType(ShareStorageExceptionInternal.class) + ResponseBase getPropertiesByFileIdSync(@HostParam("url") String url, + @PathParam("shareName") String shareName, @QueryParam("fileid") String fileId, + @QueryParam("sharesnapshot") String sharesnapshot, @QueryParam("timeout") Integer timeout, + @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-client-request-id") String requestId, + @HeaderParam("x-ms-lease-id") String leaseId, + @HeaderParam("x-ms-allow-trailing-dot") Boolean allowTrailingDot, + @HeaderParam("x-ms-file-request-intent") ShareTokenIntent fileRequestIntent, + @HeaderParam("Accept") String accept, Context context); + + @Head("/{shareName}") + @ExpectedResponses({ 200 }) + @UnexpectedResponseExceptionType(ShareStorageExceptionInternal.class) + Response getPropertiesByFileIdNoCustomHeadersSync(@HostParam("url") String url, + @PathParam("shareName") String shareName, @QueryParam("fileid") String fileId, + @QueryParam("sharesnapshot") String sharesnapshot, @QueryParam("timeout") Integer timeout, + @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-client-request-id") String requestId, + @HeaderParam("x-ms-lease-id") String leaseId, + @HeaderParam("x-ms-allow-trailing-dot") Boolean allowTrailingDot, + @HeaderParam("x-ms-file-request-intent") ShareTokenIntent fileRequestIntent, + @HeaderParam("Accept") String accept, Context context); + @Put("/{shareName}/{fileName}") @ExpectedResponses({ 201 }) @UnexpectedResponseExceptionType(ShareStorageExceptionInternal.class) @@ -1513,6 +1611,544 @@ Response createHardLinkNoCustomHeadersSync(@HostParam("url") String url, @HeaderParam("Accept") String accept, Context context); } + /** + * Returns the hard links of a file, along with all user-defined metadata, content properties, and system properties + * for the file. SMB only. + * + * @param shareName The name of the target share. + * @param fileId The file ID of the file whose hard links are returned. + * @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://learn.microsoft.com/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations">Setting + * Timeouts for File Service Operations.</a>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @param leaseId If specified, the operation only succeeds if the resource's lease is active and matches this ID. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws ShareStorageExceptionInternal thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the list of hard links for a file along with {@link ResponseBase} on successful completion of + * {@link Mono}. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> getHardLinksWithResponseAsync(String shareName, + String fileId, String sharesnapshot, Integer timeout, String requestId, String leaseId) { + return FluxUtil + .withContext(context -> getHardLinksWithResponseAsync(shareName, fileId, sharesnapshot, timeout, requestId, + leaseId, context)) + .onErrorMap(ShareStorageExceptionInternal.class, ModelHelper::mapToShareStorageException); + } + + /** + * Returns the hard links of a file, along with all user-defined metadata, content properties, and system properties + * for the file. SMB only. + * + * @param shareName The name of the target share. + * @param fileId The file ID of the file whose hard links are returned. + * @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://learn.microsoft.com/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations">Setting + * Timeouts for File Service Operations.</a>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @param leaseId If specified, the operation only succeeds if the resource's lease is active and matches this ID. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws ShareStorageExceptionInternal thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the list of hard links for a file along with {@link ResponseBase} on successful completion of + * {@link Mono}. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> getHardLinksWithResponseAsync(String shareName, + String fileId, String sharesnapshot, Integer timeout, String requestId, String leaseId, Context context) { + final String comp = "hardlinks"; + final String accept = "application/xml"; + return service + .getHardLinks(this.client.getUrl(), shareName, comp, sharesnapshot, fileId, timeout, + this.client.getVersion(), requestId, leaseId, this.client.isAllowTrailingDot(), + this.client.getFileRequestIntent(), accept, context) + .onErrorMap(ShareStorageExceptionInternal.class, ModelHelper::mapToShareStorageException); + } + + /** + * Returns the hard links of a file, along with all user-defined metadata, content properties, and system properties + * for the file. SMB only. + * + * @param shareName The name of the target share. + * @param fileId The file ID of the file whose hard links are returned. + * @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://learn.microsoft.com/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations">Setting + * Timeouts for File Service Operations.</a>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @param leaseId If specified, the operation only succeeds if the resource's lease is active and matches this ID. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws ShareStorageExceptionInternal thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the list of hard links for a file on successful completion of {@link Mono}. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono getHardLinksAsync(String shareName, String fileId, String sharesnapshot, Integer timeout, + String requestId, String leaseId) { + return getHardLinksWithResponseAsync(shareName, fileId, sharesnapshot, timeout, requestId, leaseId) + .onErrorMap(ShareStorageExceptionInternal.class, ModelHelper::mapToShareStorageException) + .flatMap(res -> Mono.justOrEmpty(res.getValue())); + } + + /** + * Returns the hard links of a file, along with all user-defined metadata, content properties, and system properties + * for the file. SMB only. + * + * @param shareName The name of the target share. + * @param fileId The file ID of the file whose hard links are returned. + * @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://learn.microsoft.com/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations">Setting + * Timeouts for File Service Operations.</a>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @param leaseId If specified, the operation only succeeds if the resource's lease is active and matches this ID. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws ShareStorageExceptionInternal thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the list of hard links for a file on successful completion of {@link Mono}. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono getHardLinksAsync(String shareName, String fileId, String sharesnapshot, Integer timeout, + String requestId, String leaseId, Context context) { + return getHardLinksWithResponseAsync(shareName, fileId, sharesnapshot, timeout, requestId, leaseId, context) + .onErrorMap(ShareStorageExceptionInternal.class, ModelHelper::mapToShareStorageException) + .flatMap(res -> Mono.justOrEmpty(res.getValue())); + } + + /** + * Returns the hard links of a file, along with all user-defined metadata, content properties, and system properties + * for the file. SMB only. + * + * @param shareName The name of the target share. + * @param fileId The file ID of the file whose hard links are returned. + * @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://learn.microsoft.com/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations">Setting + * Timeouts for File Service Operations.</a>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @param leaseId If specified, the operation only succeeds if the resource's lease is active and matches this ID. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws ShareStorageExceptionInternal thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the list of hard links for a file along with {@link Response} on successful completion of {@link Mono}. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> getHardLinksNoCustomHeadersWithResponseAsync(String shareName, String fileId, + String sharesnapshot, Integer timeout, String requestId, String leaseId) { + return FluxUtil + .withContext(context -> getHardLinksNoCustomHeadersWithResponseAsync(shareName, fileId, sharesnapshot, + timeout, requestId, leaseId, context)) + .onErrorMap(ShareStorageExceptionInternal.class, ModelHelper::mapToShareStorageException); + } + + /** + * Returns the hard links of a file, along with all user-defined metadata, content properties, and system properties + * for the file. SMB only. + * + * @param shareName The name of the target share. + * @param fileId The file ID of the file whose hard links are returned. + * @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://learn.microsoft.com/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations">Setting + * Timeouts for File Service Operations.</a>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @param leaseId If specified, the operation only succeeds if the resource's lease is active and matches this ID. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws ShareStorageExceptionInternal thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the list of hard links for a file along with {@link Response} on successful completion of {@link Mono}. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> getHardLinksNoCustomHeadersWithResponseAsync(String shareName, String fileId, + String sharesnapshot, Integer timeout, String requestId, String leaseId, Context context) { + final String comp = "hardlinks"; + final String accept = "application/xml"; + return service + .getHardLinksNoCustomHeaders(this.client.getUrl(), shareName, comp, sharesnapshot, fileId, timeout, + this.client.getVersion(), requestId, leaseId, this.client.isAllowTrailingDot(), + this.client.getFileRequestIntent(), accept, context) + .onErrorMap(ShareStorageExceptionInternal.class, ModelHelper::mapToShareStorageException); + } + + /** + * Returns the hard links of a file, along with all user-defined metadata, content properties, and system properties + * for the file. SMB only. + * + * @param shareName The name of the target share. + * @param fileId The file ID of the file whose hard links are returned. + * @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://learn.microsoft.com/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations">Setting + * Timeouts for File Service Operations.</a>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @param leaseId If specified, the operation only succeeds if the resource's lease is active and matches this ID. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws ShareStorageExceptionInternal thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the list of hard links for a file along with {@link ResponseBase}. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public ResponseBase getHardLinksWithResponse(String shareName, + String fileId, String sharesnapshot, Integer timeout, String requestId, String leaseId, Context context) { + try { + final String comp = "hardlinks"; + final String accept = "application/xml"; + return service.getHardLinksSync(this.client.getUrl(), shareName, comp, sharesnapshot, fileId, timeout, + this.client.getVersion(), requestId, leaseId, this.client.isAllowTrailingDot(), + this.client.getFileRequestIntent(), accept, context); + } catch (ShareStorageExceptionInternal internalException) { + throw ModelHelper.mapToShareStorageException(internalException); + } + } + + /** + * Returns the hard links of a file, along with all user-defined metadata, content properties, and system properties + * for the file. SMB only. + * + * @param shareName The name of the target share. + * @param fileId The file ID of the file whose hard links are returned. + * @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://learn.microsoft.com/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations">Setting + * Timeouts for File Service Operations.</a>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @param leaseId If specified, the operation only succeeds if the resource's lease is active and matches this ID. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws ShareStorageExceptionInternal thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the list of hard links for a file. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public HardLinkList getHardLinks(String shareName, String fileId, String sharesnapshot, Integer timeout, + String requestId, String leaseId) { + try { + return getHardLinksWithResponse(shareName, fileId, sharesnapshot, timeout, requestId, leaseId, Context.NONE) + .getValue(); + } catch (ShareStorageExceptionInternal internalException) { + throw ModelHelper.mapToShareStorageException(internalException); + } + } + + /** + * Returns the hard links of a file, along with all user-defined metadata, content properties, and system properties + * for the file. SMB only. + * + * @param shareName The name of the target share. + * @param fileId The file ID of the file whose hard links are returned. + * @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://learn.microsoft.com/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations">Setting + * Timeouts for File Service Operations.</a>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @param leaseId If specified, the operation only succeeds if the resource's lease is active and matches this ID. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws ShareStorageExceptionInternal thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the list of hard links for a file along with {@link Response}. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response getHardLinksNoCustomHeadersWithResponse(String shareName, String fileId, + String sharesnapshot, Integer timeout, String requestId, String leaseId, Context context) { + try { + final String comp = "hardlinks"; + final String accept = "application/xml"; + return service.getHardLinksNoCustomHeadersSync(this.client.getUrl(), shareName, comp, sharesnapshot, fileId, + timeout, this.client.getVersion(), requestId, leaseId, this.client.isAllowTrailingDot(), + this.client.getFileRequestIntent(), accept, context); + } catch (ShareStorageExceptionInternal internalException) { + throw ModelHelper.mapToShareStorageException(internalException); + } + } + + /** + * Returns all user-defined metadata, standard HTTP properties, and system properties for the file identified by its + * file ID. It does not return the content of the file. + * + * @param shareName The name of the target share. + * @param fileId The file ID of the target file or 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://learn.microsoft.com/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations">Setting + * Timeouts for File Service Operations.</a>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @param leaseId If specified, the operation only succeeds if the resource's lease is active and matches this ID. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws ShareStorageExceptionInternal thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the {@link ResponseBase} on successful completion of {@link Mono}. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> getPropertiesByFileIdWithResponseAsync(String shareName, + String fileId, String sharesnapshot, Integer timeout, String requestId, String leaseId) { + return FluxUtil + .withContext(context -> getPropertiesByFileIdWithResponseAsync(shareName, fileId, sharesnapshot, timeout, + requestId, leaseId, context)) + .onErrorMap(ShareStorageExceptionInternal.class, ModelHelper::mapToShareStorageException); + } + + /** + * Returns all user-defined metadata, standard HTTP properties, and system properties for the file identified by its + * file ID. It does not return the content of the file. + * + * @param shareName The name of the target share. + * @param fileId The file ID of the target file or 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://learn.microsoft.com/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations">Setting + * Timeouts for File Service Operations.</a>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @param leaseId If specified, the operation only succeeds if the resource's lease is active and matches this ID. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws ShareStorageExceptionInternal thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the {@link ResponseBase} on successful completion of {@link Mono}. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> getPropertiesByFileIdWithResponseAsync(String shareName, + String fileId, String sharesnapshot, Integer timeout, String requestId, String leaseId, Context context) { + final String accept = "application/xml"; + return service + .getPropertiesByFileId(this.client.getUrl(), shareName, fileId, sharesnapshot, timeout, + this.client.getVersion(), requestId, leaseId, this.client.isAllowTrailingDot(), + this.client.getFileRequestIntent(), accept, context) + .onErrorMap(ShareStorageExceptionInternal.class, ModelHelper::mapToShareStorageException); + } + + /** + * Returns all user-defined metadata, standard HTTP properties, and system properties for the file identified by its + * file ID. It does not return the content of the file. + * + * @param shareName The name of the target share. + * @param fileId The file ID of the target file or 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://learn.microsoft.com/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations">Setting + * Timeouts for File Service Operations.</a>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @param leaseId If specified, the operation only succeeds if the resource's lease is active and matches this ID. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws ShareStorageExceptionInternal thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return A {@link Mono} that completes when a successful response is received. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono getPropertiesByFileIdAsync(String shareName, String fileId, String sharesnapshot, Integer timeout, + String requestId, String leaseId) { + return getPropertiesByFileIdWithResponseAsync(shareName, fileId, sharesnapshot, timeout, requestId, leaseId) + .onErrorMap(ShareStorageExceptionInternal.class, ModelHelper::mapToShareStorageException) + .flatMap(ignored -> Mono.empty()); + } + + /** + * Returns all user-defined metadata, standard HTTP properties, and system properties for the file identified by its + * file ID. It does not return the content of the file. + * + * @param shareName The name of the target share. + * @param fileId The file ID of the target file or 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://learn.microsoft.com/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations">Setting + * Timeouts for File Service Operations.</a>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @param leaseId If specified, the operation only succeeds if the resource's lease is active and matches this ID. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws ShareStorageExceptionInternal thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return A {@link Mono} that completes when a successful response is received. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono getPropertiesByFileIdAsync(String shareName, String fileId, String sharesnapshot, Integer timeout, + String requestId, String leaseId, Context context) { + return getPropertiesByFileIdWithResponseAsync(shareName, fileId, sharesnapshot, timeout, requestId, leaseId, + context).onErrorMap(ShareStorageExceptionInternal.class, ModelHelper::mapToShareStorageException) + .flatMap(ignored -> Mono.empty()); + } + + /** + * Returns all user-defined metadata, standard HTTP properties, and system properties for the file identified by its + * file ID. It does not return the content of the file. + * + * @param shareName The name of the target share. + * @param fileId The file ID of the target file or 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://learn.microsoft.com/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations">Setting + * Timeouts for File Service Operations.</a>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @param leaseId If specified, the operation only succeeds if the resource's lease is active and matches this ID. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws ShareStorageExceptionInternal thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the {@link Response} on successful completion of {@link Mono}. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> getPropertiesByFileIdNoCustomHeadersWithResponseAsync(String shareName, String fileId, + String sharesnapshot, Integer timeout, String requestId, String leaseId) { + return FluxUtil + .withContext(context -> getPropertiesByFileIdNoCustomHeadersWithResponseAsync(shareName, fileId, + sharesnapshot, timeout, requestId, leaseId, context)) + .onErrorMap(ShareStorageExceptionInternal.class, ModelHelper::mapToShareStorageException); + } + + /** + * Returns all user-defined metadata, standard HTTP properties, and system properties for the file identified by its + * file ID. It does not return the content of the file. + * + * @param shareName The name of the target share. + * @param fileId The file ID of the target file or 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://learn.microsoft.com/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations">Setting + * Timeouts for File Service Operations.</a>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @param leaseId If specified, the operation only succeeds if the resource's lease is active and matches this ID. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws ShareStorageExceptionInternal thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the {@link Response} on successful completion of {@link Mono}. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> getPropertiesByFileIdNoCustomHeadersWithResponseAsync(String shareName, String fileId, + String sharesnapshot, Integer timeout, String requestId, String leaseId, Context context) { + final String accept = "application/xml"; + return service + .getPropertiesByFileIdNoCustomHeaders(this.client.getUrl(), shareName, fileId, sharesnapshot, timeout, + this.client.getVersion(), requestId, leaseId, this.client.isAllowTrailingDot(), + this.client.getFileRequestIntent(), accept, context) + .onErrorMap(ShareStorageExceptionInternal.class, ModelHelper::mapToShareStorageException); + } + + /** + * Returns all user-defined metadata, standard HTTP properties, and system properties for the file identified by its + * file ID. It does not return the content of the file. + * + * @param shareName The name of the target share. + * @param fileId The file ID of the target file or 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://learn.microsoft.com/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations">Setting + * Timeouts for File Service Operations.</a>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @param leaseId If specified, the operation only succeeds if the resource's lease is active and matches this ID. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws ShareStorageExceptionInternal thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the {@link ResponseBase}. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public ResponseBase getPropertiesByFileIdWithResponse(String shareName, + String fileId, String sharesnapshot, Integer timeout, String requestId, String leaseId, Context context) { + try { + final String accept = "application/xml"; + return service.getPropertiesByFileIdSync(this.client.getUrl(), shareName, fileId, sharesnapshot, timeout, + this.client.getVersion(), requestId, leaseId, this.client.isAllowTrailingDot(), + this.client.getFileRequestIntent(), accept, context); + } catch (ShareStorageExceptionInternal internalException) { + throw ModelHelper.mapToShareStorageException(internalException); + } + } + + /** + * Returns all user-defined metadata, standard HTTP properties, and system properties for the file identified by its + * file ID. It does not return the content of the file. + * + * @param shareName The name of the target share. + * @param fileId The file ID of the target file or 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://learn.microsoft.com/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations">Setting + * Timeouts for File Service Operations.</a>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @param leaseId If specified, the operation only succeeds if the resource's lease is active and matches this ID. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws ShareStorageExceptionInternal thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public void getPropertiesByFileId(String shareName, String fileId, String sharesnapshot, Integer timeout, + String requestId, String leaseId) { + getPropertiesByFileIdWithResponse(shareName, fileId, sharesnapshot, timeout, requestId, leaseId, Context.NONE); + } + + /** + * Returns all user-defined metadata, standard HTTP properties, and system properties for the file identified by its + * file ID. It does not return the content of the file. + * + * @param shareName The name of the target share. + * @param fileId The file ID of the target file or 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://learn.microsoft.com/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations">Setting + * Timeouts for File Service Operations.</a>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @param leaseId If specified, the operation only succeeds if the resource's lease is active and matches this ID. + * @param context The context to associate with this operation. + * @throws IllegalArgumentException thrown if parameters fail the validation. + * @throws ShareStorageExceptionInternal thrown if the request is rejected by server. + * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. + * @return the {@link Response}. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response getPropertiesByFileIdNoCustomHeadersWithResponse(String shareName, String fileId, + String sharesnapshot, Integer timeout, String requestId, String leaseId, Context context) { + try { + final String accept = "application/xml"; + return service.getPropertiesByFileIdNoCustomHeadersSync(this.client.getUrl(), shareName, fileId, + sharesnapshot, timeout, this.client.getVersion(), requestId, leaseId, this.client.isAllowTrailingDot(), + this.client.getFileRequestIntent(), accept, context); + } catch (ShareStorageExceptionInternal internalException) { + throw ModelHelper.mapToShareStorageException(internalException); + } + } + /** * Creates a new file or replaces a file. Can also initialize the file with content. * diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/ShareErrors.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/ShareErrors.java new file mode 100644 index 0000000000000..2a7090c1dda4d --- /dev/null +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/ShareErrors.java @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.file.share.implementation; + +import com.azure.core.util.CoreUtils; +import com.azure.core.util.logging.ClientLogger; + +/** + * Shared checks for operations that require a particular resource addressing mode. + */ +public final class ShareErrors { + private static final ClientLogger LOGGER = new ClientLogger(ShareErrors.class); + + private ShareErrors() { + } + + /** + * Validates a file ID accepted by a client factory or builder. + * + * @param fileId The ID to validate. + * @throws IllegalArgumentException If the ID is null or blank. + */ + public static void validateFileId(String fileId) { + if (fileId == null) { + throw LOGGER.logExceptionAsError(new IllegalArgumentException("'fileId' cannot be null.")); + } + if (fileId.trim().isEmpty()) { + throw LOGGER.logExceptionAsError(new IllegalArgumentException("'fileId' cannot be empty.")); + } + } + + /** + * Throws if the client does not address its resource by file ID. + * + * @param fileId The client's file ID. + * @param operationName The operation name to report on failure. + * @throws IllegalStateException If the client is not file-ID-addressed. + */ + public static void assertFileIdAddressed(String fileId, String operationName) { + if (CoreUtils.isNullOrEmpty(fileId)) { + throw LOGGER.logExceptionAsError( + new IllegalStateException(operationName + " requires a file-ID-addressed client.")); + } + } + + /** + * Validates that the client can perform a path-based operation. + * + * @param fileId The client's file ID. + * @param operationName The operation name to report on failure. + * @throws IllegalStateException If the client is file-ID-addressed. + */ + public static void validatePathOperation(String fileId, String operationName) { + assertNotFileIdAddressed(fileId, operationName); + } + + /** + * Throws if the client addresses its resource by file ID. + * + * @param fileId The client's file ID. + * @param operationName The operation name to report on failure. + * @throws IllegalStateException If the client is file-ID-addressed. + */ + public static void assertNotFileIdAddressed(String fileId, String operationName) { + if (!CoreUtils.isNullOrEmpty(fileId)) { + throw LOGGER.logExceptionAsError( + new IllegalStateException(operationName + " is not supported for a file-ID-addressed client.")); + } + } +} diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/accesshelpers/ShareDirectoryPropertiesHelper.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/accesshelpers/ShareDirectoryPropertiesHelper.java index 7dd95ad83c29a..8ea3694fb668d 100644 --- a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/accesshelpers/ShareDirectoryPropertiesHelper.java +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/accesshelpers/ShareDirectoryPropertiesHelper.java @@ -34,10 +34,12 @@ public interface ShareDirectoryPropertiesAccessor { * encrypted using the specified algorithm. Otherwise, the value is set to false. * @param smbProperties The SMB properties of the directory. * @param posixProperties the NFS properties of the directory. + * @param fileName The name returned by the service. * @return A new instance of {@link ShareDirectoryProperties}. */ ShareDirectoryProperties create(Map metadata, String eTag, OffsetDateTime lastModified, - boolean isServerEncrypted, FileSmbProperties smbProperties, FilePosixProperties posixProperties); + boolean isServerEncrypted, FileSmbProperties smbProperties, FilePosixProperties posixProperties, + String fileName); } /** @@ -59,11 +61,12 @@ public static void setAccessor(ShareDirectoryPropertiesAccessor accessor) { * encrypted using the specified algorithm. Otherwise, the value is set to false. * @param smbProperties The SMB properties of the directory. * @param posixProperties the NFS properties of the directory. + * @param fileName The name returned by the service. * @return A new instance of {@link ShareDirectoryProperties}. */ public static ShareDirectoryProperties create(Map metadata, String eTag, OffsetDateTime lastModified, boolean isServerEncrypted, FileSmbProperties smbProperties, - FilePosixProperties posixProperties) { + FilePosixProperties posixProperties, String fileName) { // This looks odd but is necessary, it is possible to engage the access helper before anywhere else in the // application accesses ShareDirectoryProperties which triggers the accessor to be configured. So, if the accessor // is null this effectively pokes the class to set up the accessor. @@ -72,6 +75,7 @@ public static ShareDirectoryProperties create(Map metadata, Stri } assert accessor != null; - return accessor.create(metadata, eTag, lastModified, isServerEncrypted, smbProperties, posixProperties); + return accessor.create(metadata, eTag, lastModified, isServerEncrypted, smbProperties, posixProperties, + fileName); } } diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/accesshelpers/ShareFilePropertiesHelper.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/accesshelpers/ShareFilePropertiesHelper.java index 7e6c151276cc8..31ae579a728d1 100644 --- a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/accesshelpers/ShareFilePropertiesHelper.java +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/accesshelpers/ShareFilePropertiesHelper.java @@ -37,6 +37,7 @@ public interface ShareFilePropertiesAccessor { * @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 contentLanguage The content language specified for the file. * @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. @@ -67,15 +68,16 @@ public interface ShareFilePropertiesAccessor { * completely encrypted using the specified algorithm. Otherwise, the value is set to false. * @param smbProperties The SMB properties of the file. * @param posixProperties The NFS properties of the file. + * @param fileName The name of the file when returned by a file-ID request. * @return A new instance of {@link ShareFileProperties}. */ ShareFileProperties create(String eTag, OffsetDateTime lastModified, Map metadata, - String fileType, Long contentLength, String contentType, byte[] contentMd5, String contentEncoding, - String cacheControl, String contentDisposition, LeaseStatusType leaseStatusType, + String fileType, Long contentLength, String contentType, String contentLanguage, byte[] contentMd5, + String contentEncoding, String cacheControl, String contentDisposition, LeaseStatusType leaseStatusType, LeaseStateType leaseStateType, LeaseDurationType leaseDurationType, OffsetDateTime copyCompletionTime, String copyStatusDescription, String copyId, String copyProgress, String copySource, CopyStatusType copyStatus, Boolean isServerEncrypted, FileSmbProperties smbProperties, - FilePosixProperties posixProperties); + FilePosixProperties posixProperties, String fileName); } /** @@ -97,6 +99,7 @@ public static void setAccessor(ShareFilePropertiesAccessor accessor) { * @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 contentLanguage The content language specified for the file. * @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. @@ -127,14 +130,16 @@ public static void setAccessor(ShareFilePropertiesAccessor accessor) { * completely encrypted using the specified algorithm. Otherwise, the value is set to false. * @param smbProperties The SMB properties of the file. * @param posixProperties The NFS properties of the file. + * @param fileName The name of the file when returned by a file-ID request. * @return A new instance of {@link ShareFileProperties}. */ public static ShareFileProperties create(String eTag, OffsetDateTime lastModified, Map metadata, - String fileType, Long contentLength, String contentType, byte[] contentMd5, String contentEncoding, - String cacheControl, String contentDisposition, LeaseStatusType leaseStatusType, LeaseStateType leaseStateType, - LeaseDurationType leaseDurationType, OffsetDateTime copyCompletionTime, String copyStatusDescription, - String copyId, String copyProgress, String copySource, CopyStatusType copyStatus, Boolean isServerEncrypted, - FileSmbProperties smbProperties, FilePosixProperties posixProperties) { + String fileType, Long contentLength, String contentType, String contentLanguage, byte[] contentMd5, + String contentEncoding, String cacheControl, String contentDisposition, LeaseStatusType leaseStatusType, + LeaseStateType leaseStateType, LeaseDurationType leaseDurationType, OffsetDateTime copyCompletionTime, + String copyStatusDescription, String copyId, String copyProgress, String copySource, CopyStatusType copyStatus, + Boolean isServerEncrypted, FileSmbProperties smbProperties, FilePosixProperties posixProperties, + String fileName) { // This looks odd but is necessary, it is possible to engage the access helper before anywhere else in the // application accesses ShareFileProperties which triggers the accessor to be configured. So, if the accessor // is null this effectively pokes the class to set up the accessor. @@ -144,9 +149,9 @@ public static ShareFileProperties create(String eTag, OffsetDateTime lastModifie } assert accessor != null; - return accessor.create(eTag, lastModified, metadata, fileType, contentLength, contentType, contentMd5, - contentEncoding, cacheControl, contentDisposition, leaseStatusType, leaseStateType, leaseDurationType, - copyCompletionTime, copyStatusDescription, copyId, copyProgress, copySource, copyStatus, isServerEncrypted, - smbProperties, posixProperties); + return accessor.create(eTag, lastModified, metadata, fileType, contentLength, contentType, contentLanguage, + contentMd5, contentEncoding, cacheControl, contentDisposition, leaseStatusType, leaseStateType, + leaseDurationType, copyCompletionTime, copyStatusDescription, copyId, copyProgress, copySource, copyStatus, + isServerEncrypted, smbProperties, posixProperties, fileName); } } diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/DirectoriesGetPropertiesHeaders.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/DirectoriesGetPropertiesHeaders.java index 6ac4d9f918b4c..84ed7b328e2f5 100644 --- a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/DirectoriesGetPropertiesHeaders.java +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/DirectoriesGetPropertiesHeaders.java @@ -97,6 +97,12 @@ public final class DirectoriesGetPropertiesHeaders { @Generated private String xMsFileId; + /* + * The x-ms-file-name property. + */ + @Generated + private String xMsFileName; + /* * The x-ms-file-parent-id property. */ @@ -145,6 +151,8 @@ public final class DirectoriesGetPropertiesHeaders { private static final HttpHeaderName X_MS_FILE_ID = HttpHeaderName.fromString("x-ms-file-id"); + private static final HttpHeaderName X_MS_FILE_NAME = HttpHeaderName.fromString("x-ms-file-name"); + private static final HttpHeaderName X_MS_FILE_PARENT_ID = HttpHeaderName.fromString("x-ms-file-parent-id"); private static final HttpHeaderName X_MS_MODE = HttpHeaderName.fromString("x-ms-mode"); @@ -204,6 +212,7 @@ public DirectoriesGetPropertiesHeaders(HttpHeaders rawHeaders) { } this.xMsFilePermissionKey = rawHeaders.getValue(X_MS_FILE_PERMISSION_KEY); this.xMsFileId = rawHeaders.getValue(X_MS_FILE_ID); + this.xMsFileName = rawHeaders.getValue(X_MS_FILE_NAME); this.xMsFileParentId = rawHeaders.getValue(X_MS_FILE_PARENT_ID); this.xMsMode = rawHeaders.getValue(X_MS_MODE); this.xMsOwner = rawHeaders.getValue(X_MS_OWNER); @@ -525,6 +534,28 @@ public DirectoriesGetPropertiesHeaders setXMsFileId(String xMsFileId) { return this; } + /** + * Get the xMsFileName property: The x-ms-file-name property. + * + * @return the xMsFileName value. + */ + @Generated + public String getXMsFileName() { + return this.xMsFileName; + } + + /** + * Set the xMsFileName property: The x-ms-file-name property. + * + * @param xMsFileName the xMsFileName value to set. + * @return the DirectoriesGetPropertiesHeaders object itself. + */ + @Generated + public DirectoriesGetPropertiesHeaders setXMsFileName(String xMsFileName) { + this.xMsFileName = xMsFileName; + return this; + } + /** * Get the xMsFileParentId property: The x-ms-file-parent-id property. * diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/FilesGetHardLinksHeaders.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/FilesGetHardLinksHeaders.java new file mode 100644 index 0000000000000..dd9919d5906a7 --- /dev/null +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/FilesGetHardLinksHeaders.java @@ -0,0 +1,1311 @@ +// 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.share.implementation.models; + +import com.azure.core.annotation.Fluent; +import com.azure.core.annotation.Generated; +import com.azure.core.http.HttpHeaderName; +import com.azure.core.http.HttpHeaders; +import com.azure.core.util.CoreUtils; +import com.azure.core.util.DateTimeRfc1123; +import com.azure.storage.file.share.models.CopyStatusType; +import com.azure.storage.file.share.models.LeaseDurationType; +import com.azure.storage.file.share.models.LeaseStateType; +import com.azure.storage.file.share.models.LeaseStatusType; +import com.azure.storage.file.share.models.NfsFileType; +import java.time.OffsetDateTime; +import java.util.Base64; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * The FilesGetHardLinksHeaders model. + */ +@Fluent +public final class FilesGetHardLinksHeaders { + /* + * The Last-Modified property. + */ + @Generated + private DateTimeRfc1123 lastModified; + + /* + * The x-ms-meta- property. + */ + @Generated + private Map xMsMeta; + + /* + * The x-ms-type property. + */ + @Generated + private String xMsType = "File"; + + /* + * The ETag property. + */ + @Generated + private String eTag; + + /* + * The x-ms-content-type property. + */ + @Generated + private String xMsContentType; + + /* + * The x-ms-content-length property. + */ + @Generated + private Long xMsContentLength; + + /* + * The x-ms-content-md5 property. + */ + @Generated + private byte[] xMsContentMd5; + + /* + * The x-ms-content-encoding property. + */ + @Generated + private String xMsContentEncoding; + + /* + * The x-ms-content-language property. + */ + @Generated + private String xMsContentLanguage; + + /* + * The x-ms-cache-control property. + */ + @Generated + private String xMsCacheControl; + + /* + * The x-ms-content-disposition property. + */ + @Generated + private String xMsContentDisposition; + + /* + * The x-ms-copy-completion-time property. + */ + @Generated + private DateTimeRfc1123 xMsCopyCompletionTime; + + /* + * The x-ms-copy-status-description property. + */ + @Generated + private String xMsCopyStatusDescription; + + /* + * The x-ms-copy-id property. + */ + @Generated + private String xMsCopyId; + + /* + * The x-ms-copy-progress property. + */ + @Generated + private String xMsCopyProgress; + + /* + * The x-ms-copy-source property. + */ + @Generated + private String xMsCopySource; + + /* + * The x-ms-copy-status property. + */ + @Generated + private CopyStatusType xMsCopyStatus; + + /* + * The x-ms-server-encrypted property. + */ + @Generated + private Boolean xMsServerEncrypted; + + /* + * The x-ms-file-attributes property. + */ + @Generated + private String xMsFileAttributes; + + /* + * The x-ms-file-creation-time property. + */ + @Generated + private OffsetDateTime xMsFileCreationTime; + + /* + * The x-ms-file-last-write-time property. + */ + @Generated + private OffsetDateTime xMsFileLastWriteTime; + + /* + * The x-ms-file-change-time property. + */ + @Generated + private OffsetDateTime xMsFileChangeTime; + + /* + * The x-ms-file-permission-key property. + */ + @Generated + private String xMsFilePermissionKey; + + /* + * The x-ms-file-id property. + */ + @Generated + private String xMsFileId; + + /* + * The x-ms-file-name property. + */ + @Generated + private String xMsFileName; + + /* + * The x-ms-file-parent-id property. + */ + @Generated + private String xMsFileParentId; + + /* + * The x-ms-lease-duration property. + */ + @Generated + private LeaseDurationType xMsLeaseDuration; + + /* + * The x-ms-lease-state property. + */ + @Generated + private LeaseStateType xMsLeaseState; + + /* + * The x-ms-lease-status property. + */ + @Generated + private LeaseStatusType xMsLeaseStatus; + + /* + * The x-ms-mode property. + */ + @Generated + private String xMsMode; + + /* + * The x-ms-owner property. + */ + @Generated + private String xMsOwner; + + /* + * The x-ms-group property. + */ + @Generated + private String xMsGroup; + + /* + * The x-ms-link-count property. + */ + @Generated + private Integer xMsLinkCount; + + /* + * The x-ms-file-file-type property. + */ + @Generated + private NfsFileType xMsFileFileType; + + /* + * The x-ms-request-id property. + */ + @Generated + private String xMsRequestId; + + /* + * The x-ms-version property. + */ + @Generated + private String xMsVersion; + + /* + * The Date property. + */ + @Generated + private DateTimeRfc1123 date; + + /* + * The x-ms-client-request-id property. + */ + @Generated + private String xMsClientRequestId; + + private static final HttpHeaderName X_MS_TYPE = HttpHeaderName.fromString("x-ms-type"); + + private static final HttpHeaderName X_MS_CONTENT_TYPE = HttpHeaderName.fromString("x-ms-content-type"); + + private static final HttpHeaderName X_MS_CONTENT_LENGTH = HttpHeaderName.fromString("x-ms-content-length"); + + private static final HttpHeaderName X_MS_CONTENT_MD5 = HttpHeaderName.fromString("x-ms-content-md5"); + + private static final HttpHeaderName X_MS_CONTENT_ENCODING = HttpHeaderName.fromString("x-ms-content-encoding"); + + private static final HttpHeaderName X_MS_CONTENT_LANGUAGE = HttpHeaderName.fromString("x-ms-content-language"); + + private static final HttpHeaderName X_MS_CACHE_CONTROL = HttpHeaderName.fromString("x-ms-cache-control"); + + private static final HttpHeaderName X_MS_CONTENT_DISPOSITION + = HttpHeaderName.fromString("x-ms-content-disposition"); + + private static final HttpHeaderName X_MS_COPY_COMPLETION_TIME + = HttpHeaderName.fromString("x-ms-copy-completion-time"); + + private static final HttpHeaderName X_MS_COPY_STATUS_DESCRIPTION + = HttpHeaderName.fromString("x-ms-copy-status-description"); + + private static final HttpHeaderName X_MS_COPY_ID = HttpHeaderName.fromString("x-ms-copy-id"); + + private static final HttpHeaderName X_MS_COPY_PROGRESS = HttpHeaderName.fromString("x-ms-copy-progress"); + + private static final HttpHeaderName X_MS_COPY_SOURCE = HttpHeaderName.fromString("x-ms-copy-source"); + + private static final HttpHeaderName X_MS_COPY_STATUS = HttpHeaderName.fromString("x-ms-copy-status"); + + private static final HttpHeaderName X_MS_SERVER_ENCRYPTED = HttpHeaderName.fromString("x-ms-server-encrypted"); + + private static final HttpHeaderName X_MS_FILE_ATTRIBUTES = HttpHeaderName.fromString("x-ms-file-attributes"); + + private static final HttpHeaderName X_MS_FILE_CREATION_TIME = HttpHeaderName.fromString("x-ms-file-creation-time"); + + private static final HttpHeaderName X_MS_FILE_LAST_WRITE_TIME + = HttpHeaderName.fromString("x-ms-file-last-write-time"); + + private static final HttpHeaderName X_MS_FILE_CHANGE_TIME = HttpHeaderName.fromString("x-ms-file-change-time"); + + private static final HttpHeaderName X_MS_FILE_PERMISSION_KEY + = HttpHeaderName.fromString("x-ms-file-permission-key"); + + private static final HttpHeaderName X_MS_FILE_ID = HttpHeaderName.fromString("x-ms-file-id"); + + private static final HttpHeaderName X_MS_FILE_NAME = HttpHeaderName.fromString("x-ms-file-name"); + + private static final HttpHeaderName X_MS_FILE_PARENT_ID = HttpHeaderName.fromString("x-ms-file-parent-id"); + + private static final HttpHeaderName X_MS_LEASE_DURATION = HttpHeaderName.fromString("x-ms-lease-duration"); + + private static final HttpHeaderName X_MS_LEASE_STATE = HttpHeaderName.fromString("x-ms-lease-state"); + + private static final HttpHeaderName X_MS_LEASE_STATUS = HttpHeaderName.fromString("x-ms-lease-status"); + + private static final HttpHeaderName X_MS_MODE = HttpHeaderName.fromString("x-ms-mode"); + + private static final HttpHeaderName X_MS_OWNER = HttpHeaderName.fromString("x-ms-owner"); + + private static final HttpHeaderName X_MS_GROUP = HttpHeaderName.fromString("x-ms-group"); + + private static final HttpHeaderName X_MS_LINK_COUNT = HttpHeaderName.fromString("x-ms-link-count"); + + private static final HttpHeaderName X_MS_FILE_FILE_TYPE = HttpHeaderName.fromString("x-ms-file-file-type"); + + private static final HttpHeaderName X_MS_VERSION = HttpHeaderName.fromString("x-ms-version"); + + // HttpHeaders containing the raw property values. + /** + * Creates an instance of FilesGetHardLinksHeaders class. + * + * @param rawHeaders The raw HttpHeaders that will be used to create the property values. + */ + public FilesGetHardLinksHeaders(HttpHeaders rawHeaders) { + String lastModified = rawHeaders.getValue(HttpHeaderName.LAST_MODIFIED); + if (lastModified != null) { + this.lastModified = new DateTimeRfc1123(lastModified); + } else { + this.lastModified = null; + } + this.xMsType = rawHeaders.getValue(X_MS_TYPE); + this.eTag = rawHeaders.getValue(HttpHeaderName.ETAG); + this.xMsContentType = rawHeaders.getValue(X_MS_CONTENT_TYPE); + String xMsContentLength = rawHeaders.getValue(X_MS_CONTENT_LENGTH); + if (xMsContentLength != null) { + this.xMsContentLength = Long.parseLong(xMsContentLength); + } else { + this.xMsContentLength = null; + } + String xMsContentMd5 = rawHeaders.getValue(X_MS_CONTENT_MD5); + if (xMsContentMd5 != null) { + this.xMsContentMd5 = Base64.getDecoder().decode(xMsContentMd5); + } else { + this.xMsContentMd5 = null; + } + this.xMsContentEncoding = rawHeaders.getValue(X_MS_CONTENT_ENCODING); + this.xMsContentLanguage = rawHeaders.getValue(X_MS_CONTENT_LANGUAGE); + this.xMsCacheControl = rawHeaders.getValue(X_MS_CACHE_CONTROL); + this.xMsContentDisposition = rawHeaders.getValue(X_MS_CONTENT_DISPOSITION); + String xMsCopyCompletionTime = rawHeaders.getValue(X_MS_COPY_COMPLETION_TIME); + if (xMsCopyCompletionTime != null) { + this.xMsCopyCompletionTime = new DateTimeRfc1123(xMsCopyCompletionTime); + } else { + this.xMsCopyCompletionTime = null; + } + this.xMsCopyStatusDescription = rawHeaders.getValue(X_MS_COPY_STATUS_DESCRIPTION); + this.xMsCopyId = rawHeaders.getValue(X_MS_COPY_ID); + this.xMsCopyProgress = rawHeaders.getValue(X_MS_COPY_PROGRESS); + this.xMsCopySource = rawHeaders.getValue(X_MS_COPY_SOURCE); + String xMsCopyStatus = rawHeaders.getValue(X_MS_COPY_STATUS); + if (xMsCopyStatus != null) { + this.xMsCopyStatus = CopyStatusType.fromString(xMsCopyStatus); + } else { + this.xMsCopyStatus = null; + } + String xMsServerEncrypted = rawHeaders.getValue(X_MS_SERVER_ENCRYPTED); + if (xMsServerEncrypted != null) { + this.xMsServerEncrypted = Boolean.parseBoolean(xMsServerEncrypted); + } else { + this.xMsServerEncrypted = null; + } + this.xMsFileAttributes = rawHeaders.getValue(X_MS_FILE_ATTRIBUTES); + String xMsFileCreationTime = rawHeaders.getValue(X_MS_FILE_CREATION_TIME); + if (xMsFileCreationTime != null) { + this.xMsFileCreationTime = OffsetDateTime.parse(xMsFileCreationTime); + } else { + this.xMsFileCreationTime = null; + } + String xMsFileLastWriteTime = rawHeaders.getValue(X_MS_FILE_LAST_WRITE_TIME); + if (xMsFileLastWriteTime != null) { + this.xMsFileLastWriteTime = OffsetDateTime.parse(xMsFileLastWriteTime); + } else { + this.xMsFileLastWriteTime = null; + } + String xMsFileChangeTime = rawHeaders.getValue(X_MS_FILE_CHANGE_TIME); + if (xMsFileChangeTime != null) { + this.xMsFileChangeTime = OffsetDateTime.parse(xMsFileChangeTime); + } else { + this.xMsFileChangeTime = null; + } + this.xMsFilePermissionKey = rawHeaders.getValue(X_MS_FILE_PERMISSION_KEY); + this.xMsFileId = rawHeaders.getValue(X_MS_FILE_ID); + this.xMsFileName = rawHeaders.getValue(X_MS_FILE_NAME); + this.xMsFileParentId = rawHeaders.getValue(X_MS_FILE_PARENT_ID); + String xMsLeaseDuration = rawHeaders.getValue(X_MS_LEASE_DURATION); + if (xMsLeaseDuration != null) { + this.xMsLeaseDuration = LeaseDurationType.fromString(xMsLeaseDuration); + } else { + this.xMsLeaseDuration = null; + } + String xMsLeaseState = rawHeaders.getValue(X_MS_LEASE_STATE); + if (xMsLeaseState != null) { + this.xMsLeaseState = LeaseStateType.fromString(xMsLeaseState); + } else { + this.xMsLeaseState = null; + } + String xMsLeaseStatus = rawHeaders.getValue(X_MS_LEASE_STATUS); + if (xMsLeaseStatus != null) { + this.xMsLeaseStatus = LeaseStatusType.fromString(xMsLeaseStatus); + } else { + this.xMsLeaseStatus = null; + } + this.xMsMode = rawHeaders.getValue(X_MS_MODE); + this.xMsOwner = rawHeaders.getValue(X_MS_OWNER); + this.xMsGroup = rawHeaders.getValue(X_MS_GROUP); + String xMsLinkCount = rawHeaders.getValue(X_MS_LINK_COUNT); + if (xMsLinkCount != null) { + this.xMsLinkCount = Integer.parseInt(xMsLinkCount); + } else { + this.xMsLinkCount = null; + } + String xMsFileFileType = rawHeaders.getValue(X_MS_FILE_FILE_TYPE); + if (xMsFileFileType != null) { + this.xMsFileFileType = NfsFileType.fromString(xMsFileFileType); + } else { + this.xMsFileFileType = null; + } + this.xMsRequestId = rawHeaders.getValue(HttpHeaderName.X_MS_REQUEST_ID); + this.xMsVersion = rawHeaders.getValue(X_MS_VERSION); + String date = rawHeaders.getValue(HttpHeaderName.DATE); + if (date != null) { + this.date = new DateTimeRfc1123(date); + } else { + this.date = null; + } + this.xMsClientRequestId = rawHeaders.getValue(HttpHeaderName.X_MS_CLIENT_REQUEST_ID); + Map xMsMetaHeaderCollection = new LinkedHashMap<>(); + + rawHeaders.stream().forEach(header -> { + String headerName = header.getName(); + if (headerName.startsWith("x-ms-meta-")) { + xMsMetaHeaderCollection.put(headerName.substring(10), header.getValue()); + } + }); + this.xMsMeta = xMsMetaHeaderCollection; + } + + /** + * Get the lastModified property: The Last-Modified property. + * + * @return the lastModified value. + */ + @Generated + public OffsetDateTime getLastModified() { + if (this.lastModified == null) { + return null; + } + return this.lastModified.getDateTime(); + } + + /** + * Set the lastModified property: The Last-Modified property. + * + * @param lastModified the lastModified value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setLastModified(OffsetDateTime lastModified) { + if (lastModified == null) { + this.lastModified = null; + } else { + this.lastModified = new DateTimeRfc1123(lastModified); + } + return this; + } + + /** + * Get the xMsMeta property: The x-ms-meta- property. + * + * @return the xMsMeta value. + */ + @Generated + public Map getXMsMeta() { + return this.xMsMeta; + } + + /** + * Set the xMsMeta property: The x-ms-meta- property. + * + * @param xMsMeta the xMsMeta value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsMeta(Map xMsMeta) { + this.xMsMeta = xMsMeta; + return this; + } + + /** + * Get the xMsType property: The x-ms-type property. + * + * @return the xMsType value. + */ + @Generated + public String getXMsType() { + return this.xMsType; + } + + /** + * Set the xMsType property: The x-ms-type property. + * + * @param xMsType the xMsType value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsType(String xMsType) { + this.xMsType = xMsType; + return this; + } + + /** + * Get the eTag property: The ETag property. + * + * @return the eTag value. + */ + @Generated + public String getETag() { + return this.eTag; + } + + /** + * Set the eTag property: The ETag property. + * + * @param eTag the eTag value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setETag(String eTag) { + this.eTag = eTag; + return this; + } + + /** + * Get the xMsContentType property: The x-ms-content-type property. + * + * @return the xMsContentType value. + */ + @Generated + public String getXMsContentType() { + return this.xMsContentType; + } + + /** + * Set the xMsContentType property: The x-ms-content-type property. + * + * @param xMsContentType the xMsContentType value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsContentType(String xMsContentType) { + this.xMsContentType = xMsContentType; + return this; + } + + /** + * Get the xMsContentLength property: The x-ms-content-length property. + * + * @return the xMsContentLength value. + */ + @Generated + public Long getXMsContentLength() { + return this.xMsContentLength; + } + + /** + * Set the xMsContentLength property: The x-ms-content-length property. + * + * @param xMsContentLength the xMsContentLength value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsContentLength(Long xMsContentLength) { + this.xMsContentLength = xMsContentLength; + return this; + } + + /** + * Get the xMsContentMd5 property: The x-ms-content-md5 property. + * + * @return the xMsContentMd5 value. + */ + @Generated + public byte[] getXMsContentMd5() { + return CoreUtils.clone(this.xMsContentMd5); + } + + /** + * Set the xMsContentMd5 property: The x-ms-content-md5 property. + * + * @param xMsContentMd5 the xMsContentMd5 value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsContentMd5(byte[] xMsContentMd5) { + this.xMsContentMd5 = CoreUtils.clone(xMsContentMd5); + return this; + } + + /** + * Get the xMsContentEncoding property: The x-ms-content-encoding property. + * + * @return the xMsContentEncoding value. + */ + @Generated + public String getXMsContentEncoding() { + return this.xMsContentEncoding; + } + + /** + * Set the xMsContentEncoding property: The x-ms-content-encoding property. + * + * @param xMsContentEncoding the xMsContentEncoding value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsContentEncoding(String xMsContentEncoding) { + this.xMsContentEncoding = xMsContentEncoding; + return this; + } + + /** + * Get the xMsContentLanguage property: The x-ms-content-language property. + * + * @return the xMsContentLanguage value. + */ + @Generated + public String getXMsContentLanguage() { + return this.xMsContentLanguage; + } + + /** + * Set the xMsContentLanguage property: The x-ms-content-language property. + * + * @param xMsContentLanguage the xMsContentLanguage value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsContentLanguage(String xMsContentLanguage) { + this.xMsContentLanguage = xMsContentLanguage; + return this; + } + + /** + * Get the xMsCacheControl property: The x-ms-cache-control property. + * + * @return the xMsCacheControl value. + */ + @Generated + public String getXMsCacheControl() { + return this.xMsCacheControl; + } + + /** + * Set the xMsCacheControl property: The x-ms-cache-control property. + * + * @param xMsCacheControl the xMsCacheControl value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsCacheControl(String xMsCacheControl) { + this.xMsCacheControl = xMsCacheControl; + return this; + } + + /** + * Get the xMsContentDisposition property: The x-ms-content-disposition property. + * + * @return the xMsContentDisposition value. + */ + @Generated + public String getXMsContentDisposition() { + return this.xMsContentDisposition; + } + + /** + * Set the xMsContentDisposition property: The x-ms-content-disposition property. + * + * @param xMsContentDisposition the xMsContentDisposition value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsContentDisposition(String xMsContentDisposition) { + this.xMsContentDisposition = xMsContentDisposition; + return this; + } + + /** + * Get the xMsCopyCompletionTime property: The x-ms-copy-completion-time property. + * + * @return the xMsCopyCompletionTime value. + */ + @Generated + public OffsetDateTime getXMsCopyCompletionTime() { + if (this.xMsCopyCompletionTime == null) { + return null; + } + return this.xMsCopyCompletionTime.getDateTime(); + } + + /** + * Set the xMsCopyCompletionTime property: The x-ms-copy-completion-time property. + * + * @param xMsCopyCompletionTime the xMsCopyCompletionTime value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsCopyCompletionTime(OffsetDateTime xMsCopyCompletionTime) { + if (xMsCopyCompletionTime == null) { + this.xMsCopyCompletionTime = null; + } else { + this.xMsCopyCompletionTime = new DateTimeRfc1123(xMsCopyCompletionTime); + } + return this; + } + + /** + * Get the xMsCopyStatusDescription property: The x-ms-copy-status-description property. + * + * @return the xMsCopyStatusDescription value. + */ + @Generated + public String getXMsCopyStatusDescription() { + return this.xMsCopyStatusDescription; + } + + /** + * Set the xMsCopyStatusDescription property: The x-ms-copy-status-description property. + * + * @param xMsCopyStatusDescription the xMsCopyStatusDescription value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsCopyStatusDescription(String xMsCopyStatusDescription) { + this.xMsCopyStatusDescription = xMsCopyStatusDescription; + return this; + } + + /** + * Get the xMsCopyId property: The x-ms-copy-id property. + * + * @return the xMsCopyId value. + */ + @Generated + public String getXMsCopyId() { + return this.xMsCopyId; + } + + /** + * Set the xMsCopyId property: The x-ms-copy-id property. + * + * @param xMsCopyId the xMsCopyId value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsCopyId(String xMsCopyId) { + this.xMsCopyId = xMsCopyId; + return this; + } + + /** + * Get the xMsCopyProgress property: The x-ms-copy-progress property. + * + * @return the xMsCopyProgress value. + */ + @Generated + public String getXMsCopyProgress() { + return this.xMsCopyProgress; + } + + /** + * Set the xMsCopyProgress property: The x-ms-copy-progress property. + * + * @param xMsCopyProgress the xMsCopyProgress value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsCopyProgress(String xMsCopyProgress) { + this.xMsCopyProgress = xMsCopyProgress; + return this; + } + + /** + * Get the xMsCopySource property: The x-ms-copy-source property. + * + * @return the xMsCopySource value. + */ + @Generated + public String getXMsCopySource() { + return this.xMsCopySource; + } + + /** + * Set the xMsCopySource property: The x-ms-copy-source property. + * + * @param xMsCopySource the xMsCopySource value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsCopySource(String xMsCopySource) { + this.xMsCopySource = xMsCopySource; + return this; + } + + /** + * Get the xMsCopyStatus property: The x-ms-copy-status property. + * + * @return the xMsCopyStatus value. + */ + @Generated + public CopyStatusType getXMsCopyStatus() { + return this.xMsCopyStatus; + } + + /** + * Set the xMsCopyStatus property: The x-ms-copy-status property. + * + * @param xMsCopyStatus the xMsCopyStatus value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsCopyStatus(CopyStatusType xMsCopyStatus) { + this.xMsCopyStatus = xMsCopyStatus; + return this; + } + + /** + * Get the xMsServerEncrypted property: The x-ms-server-encrypted property. + * + * @return the xMsServerEncrypted value. + */ + @Generated + public Boolean isXMsServerEncrypted() { + return this.xMsServerEncrypted; + } + + /** + * Set the xMsServerEncrypted property: The x-ms-server-encrypted property. + * + * @param xMsServerEncrypted the xMsServerEncrypted value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsServerEncrypted(Boolean xMsServerEncrypted) { + this.xMsServerEncrypted = xMsServerEncrypted; + return this; + } + + /** + * Get the xMsFileAttributes property: The x-ms-file-attributes property. + * + * @return the xMsFileAttributes value. + */ + @Generated + public String getXMsFileAttributes() { + return this.xMsFileAttributes; + } + + /** + * Set the xMsFileAttributes property: The x-ms-file-attributes property. + * + * @param xMsFileAttributes the xMsFileAttributes value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsFileAttributes(String xMsFileAttributes) { + this.xMsFileAttributes = xMsFileAttributes; + return this; + } + + /** + * Get the xMsFileCreationTime property: The x-ms-file-creation-time property. + * + * @return the xMsFileCreationTime value. + */ + @Generated + public OffsetDateTime getXMsFileCreationTime() { + return this.xMsFileCreationTime; + } + + /** + * Set the xMsFileCreationTime property: The x-ms-file-creation-time property. + * + * @param xMsFileCreationTime the xMsFileCreationTime value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsFileCreationTime(OffsetDateTime xMsFileCreationTime) { + this.xMsFileCreationTime = xMsFileCreationTime; + return this; + } + + /** + * Get the xMsFileLastWriteTime property: The x-ms-file-last-write-time property. + * + * @return the xMsFileLastWriteTime value. + */ + @Generated + public OffsetDateTime getXMsFileLastWriteTime() { + return this.xMsFileLastWriteTime; + } + + /** + * Set the xMsFileLastWriteTime property: The x-ms-file-last-write-time property. + * + * @param xMsFileLastWriteTime the xMsFileLastWriteTime value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsFileLastWriteTime(OffsetDateTime xMsFileLastWriteTime) { + this.xMsFileLastWriteTime = xMsFileLastWriteTime; + return this; + } + + /** + * Get the xMsFileChangeTime property: The x-ms-file-change-time property. + * + * @return the xMsFileChangeTime value. + */ + @Generated + public OffsetDateTime getXMsFileChangeTime() { + return this.xMsFileChangeTime; + } + + /** + * Set the xMsFileChangeTime property: The x-ms-file-change-time property. + * + * @param xMsFileChangeTime the xMsFileChangeTime value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsFileChangeTime(OffsetDateTime xMsFileChangeTime) { + this.xMsFileChangeTime = xMsFileChangeTime; + return this; + } + + /** + * Get the xMsFilePermissionKey property: The x-ms-file-permission-key property. + * + * @return the xMsFilePermissionKey value. + */ + @Generated + public String getXMsFilePermissionKey() { + return this.xMsFilePermissionKey; + } + + /** + * Set the xMsFilePermissionKey property: The x-ms-file-permission-key property. + * + * @param xMsFilePermissionKey the xMsFilePermissionKey value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsFilePermissionKey(String xMsFilePermissionKey) { + this.xMsFilePermissionKey = xMsFilePermissionKey; + return this; + } + + /** + * Get the xMsFileId property: The x-ms-file-id property. + * + * @return the xMsFileId value. + */ + @Generated + public String getXMsFileId() { + return this.xMsFileId; + } + + /** + * Set the xMsFileId property: The x-ms-file-id property. + * + * @param xMsFileId the xMsFileId value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsFileId(String xMsFileId) { + this.xMsFileId = xMsFileId; + return this; + } + + /** + * Get the xMsFileName property: The x-ms-file-name property. + * + * @return the xMsFileName value. + */ + @Generated + public String getXMsFileName() { + return this.xMsFileName; + } + + /** + * Set the xMsFileName property: The x-ms-file-name property. + * + * @param xMsFileName the xMsFileName value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsFileName(String xMsFileName) { + this.xMsFileName = xMsFileName; + return this; + } + + /** + * Get the xMsFileParentId property: The x-ms-file-parent-id property. + * + * @return the xMsFileParentId value. + */ + @Generated + public String getXMsFileParentId() { + return this.xMsFileParentId; + } + + /** + * Set the xMsFileParentId property: The x-ms-file-parent-id property. + * + * @param xMsFileParentId the xMsFileParentId value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsFileParentId(String xMsFileParentId) { + this.xMsFileParentId = xMsFileParentId; + return this; + } + + /** + * Get the xMsLeaseDuration property: The x-ms-lease-duration property. + * + * @return the xMsLeaseDuration value. + */ + @Generated + public LeaseDurationType getXMsLeaseDuration() { + return this.xMsLeaseDuration; + } + + /** + * Set the xMsLeaseDuration property: The x-ms-lease-duration property. + * + * @param xMsLeaseDuration the xMsLeaseDuration value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsLeaseDuration(LeaseDurationType xMsLeaseDuration) { + this.xMsLeaseDuration = xMsLeaseDuration; + return this; + } + + /** + * Get the xMsLeaseState property: The x-ms-lease-state property. + * + * @return the xMsLeaseState value. + */ + @Generated + public LeaseStateType getXMsLeaseState() { + return this.xMsLeaseState; + } + + /** + * Set the xMsLeaseState property: The x-ms-lease-state property. + * + * @param xMsLeaseState the xMsLeaseState value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsLeaseState(LeaseStateType xMsLeaseState) { + this.xMsLeaseState = xMsLeaseState; + return this; + } + + /** + * Get the xMsLeaseStatus property: The x-ms-lease-status property. + * + * @return the xMsLeaseStatus value. + */ + @Generated + public LeaseStatusType getXMsLeaseStatus() { + return this.xMsLeaseStatus; + } + + /** + * Set the xMsLeaseStatus property: The x-ms-lease-status property. + * + * @param xMsLeaseStatus the xMsLeaseStatus value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsLeaseStatus(LeaseStatusType xMsLeaseStatus) { + this.xMsLeaseStatus = xMsLeaseStatus; + return this; + } + + /** + * Get the xMsMode property: The x-ms-mode property. + * + * @return the xMsMode value. + */ + @Generated + public String getXMsMode() { + return this.xMsMode; + } + + /** + * Set the xMsMode property: The x-ms-mode property. + * + * @param xMsMode the xMsMode value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsMode(String xMsMode) { + this.xMsMode = xMsMode; + return this; + } + + /** + * Get the xMsOwner property: The x-ms-owner property. + * + * @return the xMsOwner value. + */ + @Generated + public String getXMsOwner() { + return this.xMsOwner; + } + + /** + * Set the xMsOwner property: The x-ms-owner property. + * + * @param xMsOwner the xMsOwner value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsOwner(String xMsOwner) { + this.xMsOwner = xMsOwner; + return this; + } + + /** + * Get the xMsGroup property: The x-ms-group property. + * + * @return the xMsGroup value. + */ + @Generated + public String getXMsGroup() { + return this.xMsGroup; + } + + /** + * Set the xMsGroup property: The x-ms-group property. + * + * @param xMsGroup the xMsGroup value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsGroup(String xMsGroup) { + this.xMsGroup = xMsGroup; + return this; + } + + /** + * Get the xMsLinkCount property: The x-ms-link-count property. + * + * @return the xMsLinkCount value. + */ + @Generated + public Integer getXMsLinkCount() { + return this.xMsLinkCount; + } + + /** + * Set the xMsLinkCount property: The x-ms-link-count property. + * + * @param xMsLinkCount the xMsLinkCount value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsLinkCount(Integer xMsLinkCount) { + this.xMsLinkCount = xMsLinkCount; + return this; + } + + /** + * Get the xMsFileFileType property: The x-ms-file-file-type property. + * + * @return the xMsFileFileType value. + */ + @Generated + public NfsFileType getXMsFileFileType() { + return this.xMsFileFileType; + } + + /** + * Set the xMsFileFileType property: The x-ms-file-file-type property. + * + * @param xMsFileFileType the xMsFileFileType value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsFileFileType(NfsFileType xMsFileFileType) { + this.xMsFileFileType = xMsFileFileType; + return this; + } + + /** + * Get the xMsRequestId property: The x-ms-request-id property. + * + * @return the xMsRequestId value. + */ + @Generated + public String getXMsRequestId() { + return this.xMsRequestId; + } + + /** + * Set the xMsRequestId property: The x-ms-request-id property. + * + * @param xMsRequestId the xMsRequestId value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsRequestId(String xMsRequestId) { + this.xMsRequestId = xMsRequestId; + return this; + } + + /** + * Get the xMsVersion property: The x-ms-version property. + * + * @return the xMsVersion value. + */ + @Generated + public String getXMsVersion() { + return this.xMsVersion; + } + + /** + * Set the xMsVersion property: The x-ms-version property. + * + * @param xMsVersion the xMsVersion value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsVersion(String xMsVersion) { + this.xMsVersion = xMsVersion; + return this; + } + + /** + * Get the date property: The Date property. + * + * @return the date value. + */ + @Generated + public OffsetDateTime getDate() { + if (this.date == null) { + return null; + } + return this.date.getDateTime(); + } + + /** + * Set the date property: The Date property. + * + * @param date the date value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setDate(OffsetDateTime date) { + if (date == null) { + this.date = null; + } else { + this.date = new DateTimeRfc1123(date); + } + return this; + } + + /** + * Get the xMsClientRequestId property: The x-ms-client-request-id property. + * + * @return the xMsClientRequestId value. + */ + @Generated + public String getXMsClientRequestId() { + return this.xMsClientRequestId; + } + + /** + * Set the xMsClientRequestId property: The x-ms-client-request-id property. + * + * @param xMsClientRequestId the xMsClientRequestId value to set. + * @return the FilesGetHardLinksHeaders object itself. + */ + @Generated + public FilesGetHardLinksHeaders setXMsClientRequestId(String xMsClientRequestId) { + this.xMsClientRequestId = xMsClientRequestId; + return this; + } +} diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/FilesGetPropertiesHeaders.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/FilesGetPropertiesHeaders.java index 857221a951aa2..e0c298598356c 100644 --- a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/FilesGetPropertiesHeaders.java +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/FilesGetPropertiesHeaders.java @@ -187,6 +187,12 @@ public final class FilesGetPropertiesHeaders { @Generated private String xMsFileId; + /* + * The x-ms-file-name property. + */ + @Generated + private String xMsFileName; + /* * The x-ms-file-parent-id property. */ @@ -275,6 +281,8 @@ public final class FilesGetPropertiesHeaders { private static final HttpHeaderName X_MS_FILE_ID = HttpHeaderName.fromString("x-ms-file-id"); + private static final HttpHeaderName X_MS_FILE_NAME = HttpHeaderName.fromString("x-ms-file-name"); + private static final HttpHeaderName X_MS_FILE_PARENT_ID = HttpHeaderName.fromString("x-ms-file-parent-id"); private static final HttpHeaderName X_MS_LEASE_DURATION = HttpHeaderName.fromString("x-ms-lease-duration"); @@ -376,6 +384,7 @@ public FilesGetPropertiesHeaders(HttpHeaders rawHeaders) { } this.xMsFilePermissionKey = rawHeaders.getValue(X_MS_FILE_PERMISSION_KEY); this.xMsFileId = rawHeaders.getValue(X_MS_FILE_ID); + this.xMsFileName = rawHeaders.getValue(X_MS_FILE_NAME); this.xMsFileParentId = rawHeaders.getValue(X_MS_FILE_PARENT_ID); String xMsLeaseDuration = rawHeaders.getValue(X_MS_LEASE_DURATION); if (xMsLeaseDuration != null) { @@ -1036,6 +1045,28 @@ public FilesGetPropertiesHeaders setXMsFileId(String xMsFileId) { return this; } + /** + * Get the xMsFileName property: The x-ms-file-name property. + * + * @return the xMsFileName value. + */ + @Generated + public String getXMsFileName() { + return this.xMsFileName; + } + + /** + * Set the xMsFileName property: The x-ms-file-name property. + * + * @param xMsFileName the xMsFileName value to set. + * @return the FilesGetPropertiesHeaders object itself. + */ + @Generated + public FilesGetPropertiesHeaders setXMsFileName(String xMsFileName) { + this.xMsFileName = xMsFileName; + return this; + } + /** * Get the xMsFileParentId property: The x-ms-file-parent-id property. * diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/HardLink.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/HardLink.java new file mode 100644 index 0000000000000..e73351509a837 --- /dev/null +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/HardLink.java @@ -0,0 +1,144 @@ +// 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.share.implementation.models; + +import com.azure.core.annotation.Fluent; +import com.azure.core.annotation.Generated; +import com.azure.xml.XmlReader; +import com.azure.xml.XmlSerializable; +import com.azure.xml.XmlToken; +import com.azure.xml.XmlWriter; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLStreamException; + +/** + * A hard link to a file. + */ +@Fluent +public final class HardLink implements XmlSerializable { + /* + * The name of the hard link. + */ + @Generated + private StringEncoded fileName; + + /* + * The fileId of the parent directory of the hard link. + */ + @Generated + private String parentId; + + /** + * Creates an instance of HardLink class. + */ + @Generated + public HardLink() { + } + + /** + * Get the fileName property: The name of the hard link. + * + * @return the fileName value. + */ + @Generated + public StringEncoded getFileName() { + return this.fileName; + } + + /** + * Set the fileName property: The name of the hard link. + * + * @param fileName the fileName value to set. + * @return the HardLink object itself. + */ + @Generated + public HardLink setFileName(StringEncoded fileName) { + this.fileName = fileName; + return this; + } + + /** + * Get the parentId property: The fileId of the parent directory of the hard link. + * + * @return the parentId value. + */ + @Generated + public String getParentId() { + return this.parentId; + } + + /** + * Set the parentId property: The fileId of the parent directory of the hard link. + * + * @param parentId the parentId value to set. + * @return the HardLink object itself. + */ + @Generated + public HardLink setParentId(String parentId) { + this.parentId = parentId; + return this; + } + + @Generated + @Override + public XmlWriter toXml(XmlWriter xmlWriter) throws XMLStreamException { + return toXml(xmlWriter, null); + } + + @Generated + @Override + public XmlWriter toXml(XmlWriter xmlWriter, String rootElementName) throws XMLStreamException { + rootElementName = rootElementName == null || rootElementName.isEmpty() ? "HardLink" : rootElementName; + xmlWriter.writeStartElement(rootElementName); + xmlWriter.writeXml(this.fileName, "FileName"); + xmlWriter.writeStringElement("ParentId", this.parentId); + return xmlWriter.writeEndElement(); + } + + /** + * Reads an instance of HardLink from the XmlReader. + * + * @param xmlReader The XmlReader being read. + * @return An instance of HardLink if the XmlReader was pointing to an instance of it, or null if it was pointing to + * XML null. + * @throws XMLStreamException If an error occurs while reading the HardLink. + */ + @Generated + public static HardLink fromXml(XmlReader xmlReader) throws XMLStreamException { + return fromXml(xmlReader, null); + } + + /** + * Reads an instance of HardLink from the XmlReader. + * + * @param xmlReader The XmlReader being read. + * @param rootElementName Optional root element name to override the default defined by the model. Used to support + * cases where the model can deserialize from different root element names. + * @return An instance of HardLink if the XmlReader was pointing to an instance of it, or null if it was pointing to + * XML null. + * @throws XMLStreamException If an error occurs while reading the HardLink. + */ + @Generated + public static HardLink fromXml(XmlReader xmlReader, String rootElementName) throws XMLStreamException { + String finalRootElementName + = rootElementName == null || rootElementName.isEmpty() ? "HardLink" : rootElementName; + return xmlReader.readObject(finalRootElementName, reader -> { + HardLink deserializedHardLink = new HardLink(); + while (reader.nextElement() != XmlToken.END_ELEMENT) { + QName elementName = reader.getElementName(); + + if ("FileName".equals(elementName.getLocalPart())) { + deserializedHardLink.fileName = StringEncoded.fromXml(reader, "FileName"); + } else if ("ParentId".equals(elementName.getLocalPart())) { + deserializedHardLink.parentId = reader.getStringElement(); + } else { + reader.skipElement(); + } + } + + return deserializedHardLink; + }); + } +} diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/HardLinkList.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/HardLinkList.java new file mode 100644 index 0000000000000..4dd75df71bb02 --- /dev/null +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/HardLinkList.java @@ -0,0 +1,119 @@ +// 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.share.implementation.models; + +import com.azure.core.annotation.Fluent; +import com.azure.core.annotation.Generated; +import com.azure.xml.XmlReader; +import com.azure.xml.XmlSerializable; +import com.azure.xml.XmlToken; +import com.azure.xml.XmlWriter; +import java.util.ArrayList; +import java.util.List; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLStreamException; + +/** + * The list of hard links for a file. + */ +@Fluent +public final class HardLinkList implements XmlSerializable { + /* + * The hard links. + */ + @Generated + private List hardLinks = new ArrayList<>(); + + /** + * Creates an instance of HardLinkList class. + */ + @Generated + public HardLinkList() { + } + + /** + * Get the hardLinks property: The hard links. + * + * @return the hardLinks value. + */ + @Generated + public List getHardLinks() { + return this.hardLinks; + } + + /** + * Set the hardLinks property: The hard links. + * + * @param hardLinks the hardLinks value to set. + * @return the HardLinkList object itself. + */ + @Generated + public HardLinkList setHardLinks(List hardLinks) { + this.hardLinks = hardLinks; + return this; + } + + @Generated + @Override + public XmlWriter toXml(XmlWriter xmlWriter) throws XMLStreamException { + return toXml(xmlWriter, null); + } + + @Generated + @Override + public XmlWriter toXml(XmlWriter xmlWriter, String rootElementName) throws XMLStreamException { + rootElementName = rootElementName == null || rootElementName.isEmpty() ? "HardLinks" : rootElementName; + xmlWriter.writeStartElement(rootElementName); + if (this.hardLinks != null) { + for (HardLink element : this.hardLinks) { + xmlWriter.writeXml(element, "HardLink"); + } + } + return xmlWriter.writeEndElement(); + } + + /** + * Reads an instance of HardLinkList from the XmlReader. + * + * @param xmlReader The XmlReader being read. + * @return An instance of HardLinkList if the XmlReader was pointing to an instance of it, or null if it was + * pointing to XML null. + * @throws XMLStreamException If an error occurs while reading the HardLinkList. + */ + @Generated + public static HardLinkList fromXml(XmlReader xmlReader) throws XMLStreamException { + return fromXml(xmlReader, null); + } + + /** + * Reads an instance of HardLinkList from the XmlReader. + * + * @param xmlReader The XmlReader being read. + * @param rootElementName Optional root element name to override the default defined by the model. Used to support + * cases where the model can deserialize from different root element names. + * @return An instance of HardLinkList if the XmlReader was pointing to an instance of it, or null if it was + * pointing to XML null. + * @throws XMLStreamException If an error occurs while reading the HardLinkList. + */ + @Generated + public static HardLinkList fromXml(XmlReader xmlReader, String rootElementName) throws XMLStreamException { + String finalRootElementName + = rootElementName == null || rootElementName.isEmpty() ? "HardLinks" : rootElementName; + return xmlReader.readObject(finalRootElementName, reader -> { + HardLinkList deserializedHardLinkList = new HardLinkList(); + while (reader.nextElement() != XmlToken.END_ELEMENT) { + QName elementName = reader.getElementName(); + + if ("HardLink".equals(elementName.getLocalPart())) { + deserializedHardLinkList.hardLinks.add(HardLink.fromXml(reader, "HardLink")); + } else { + reader.skipElement(); + } + } + + return deserializedHardLinkList; + }); + } +} diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/util/ModelHelper.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/util/ModelHelper.java index c3ba69c26fe5d..ccfc938689d33 100644 --- a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/util/ModelHelper.java +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/util/ModelHelper.java @@ -35,8 +35,11 @@ import com.azure.storage.file.share.implementation.models.FilesCreateHeaders; import com.azure.storage.file.share.implementation.models.FilesCreateSymbolicLinkHeaders; import com.azure.storage.file.share.implementation.models.FilesDownloadHeaders; +import com.azure.storage.file.share.implementation.models.FilesGetHardLinksHeaders; import com.azure.storage.file.share.implementation.models.FilesGetPropertiesHeaders; import com.azure.storage.file.share.implementation.models.FilesGetSymbolicLinkHeaders; +import com.azure.storage.file.share.implementation.models.HardLink; +import com.azure.storage.file.share.implementation.models.HardLinkList; import com.azure.storage.file.share.implementation.models.FilesSetHttpHeadersHeaders; import com.azure.storage.file.share.implementation.models.FilesSetMetadataHeaders; import com.azure.storage.file.share.implementation.models.FilesUploadRangeFromURLHeaders; @@ -69,6 +72,8 @@ import com.azure.storage.file.share.models.ShareFileInfo; import com.azure.storage.file.share.models.ShareFileItem; import com.azure.storage.file.share.models.ShareFileItemProperties; +import com.azure.storage.file.share.models.ShareFileLink; +import com.azure.storage.file.share.models.ShareFileLinks; import com.azure.storage.file.share.models.ShareFileMetadataInfo; import com.azure.storage.file.share.models.ShareFileProperties; import com.azure.storage.file.share.models.ShareFileRange; @@ -421,9 +426,10 @@ public static Response createFileInfoResponse(ResponseBase(response, shareFileProperties); } @@ -584,16 +590,16 @@ public static Response mapGetStatisticsResponse(Response(response, shareDirectoryInfo); } - public static Response - mapShareDirectoryPropertiesResponse(ResponseBase response) { - Map metadata = response.getDeserializedHeaders().getXMsMeta(); - String eTag = response.getDeserializedHeaders().getETag(); - OffsetDateTime offsetDateTime = response.getDeserializedHeaders().getLastModified(); - boolean isServerEncrypted = response.getDeserializedHeaders().isXMsServerEncrypted(); + public static Response mapShareDirectoryPropertiesResponse(Response response) { + DirectoriesGetPropertiesHeaders headers = new DirectoriesGetPropertiesHeaders(response.getHeaders()); + Map metadata = headers.getXMsMeta(); + String eTag = headers.getETag(); + OffsetDateTime offsetDateTime = headers.getLastModified(); + boolean isServerEncrypted = headers.isXMsServerEncrypted(); FileSmbProperties smbProperties = FileSmbPropertiesHelper.create(response.getHeaders()); FilePosixProperties posixProperties = FilePosixPropertiesHelper.create(response.getHeaders()); ShareDirectoryProperties shareDirectoryProperties = ShareDirectoryPropertiesHelper.create(metadata, eTag, - offsetDateTime, isServerEncrypted, smbProperties, posixProperties); + offsetDateTime, isServerEncrypted, smbProperties, posixProperties, headers.getXMsFileName()); return new SimpleResponse<>(response, shareDirectoryProperties); } @@ -765,4 +771,26 @@ public static List toShareFileRangeItems(ShareFileRangeList } return ranges; } + + public static Response + getFileLinksResponse(ResponseBase response) { + FilesGetHardLinksHeaders headers = response.getDeserializedHeaders(); + FileSmbProperties smbProperties = FileSmbPropertiesHelper.create(response.getHeaders()); + FilePosixProperties posixProperties = FilePosixPropertiesHelper.create(response.getHeaders()); + ShareFileProperties properties = ShareFilePropertiesHelper.create(headers.getETag(), headers.getLastModified(), + headers.getXMsMeta(), headers.getXMsType(), headers.getXMsContentLength(), headers.getXMsContentType(), + headers.getXMsContentLanguage(), headers.getXMsContentMd5(), headers.getXMsContentEncoding(), + headers.getXMsCacheControl(), headers.getXMsContentDisposition(), headers.getXMsLeaseStatus(), + headers.getXMsLeaseState(), headers.getXMsLeaseDuration(), headers.getXMsCopyCompletionTime(), + headers.getXMsCopyStatusDescription(), headers.getXMsCopyId(), headers.getXMsCopyProgress(), + headers.getXMsCopySource(), headers.getXMsCopyStatus(), headers.isXMsServerEncrypted(), smbProperties, + posixProperties, headers.getXMsFileName()); + List links = new ArrayList<>(); + for (HardLink hardLink : response.getValue().getHardLinks()) { + StringEncoded encodedFileName = hardLink.getFileName(); + links.add(new ShareFileLink(hardLink.getParentId(), + encodedFileName == null ? null : decodeName(encodedFileName))); + } + return new SimpleResponse<>(response, new ShareFileLinks(properties, links)); + } } diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/models/ShareDirectoryProperties.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/models/ShareDirectoryProperties.java index b70915b269370..bbae2cb5e3169 100644 --- a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/models/ShareDirectoryProperties.java +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/models/ShareDirectoryProperties.java @@ -21,6 +21,7 @@ public final class ShareDirectoryProperties { private final boolean isServerEncrypted; private final FileSmbProperties smbProperties; private final FilePosixProperties posixProperties; + private final String fileName; /** * Creates an instance of properties information about a specific Directory. @@ -40,17 +41,20 @@ public ShareDirectoryProperties(Map metadata, String eTag, Offse this.isServerEncrypted = isServerEncrypted; this.smbProperties = smbProperties; this.posixProperties = null; + this.fileName = null; } //Internal constructor to support FilePosixProperties class. private ShareDirectoryProperties(Map metadata, String eTag, OffsetDateTime lastModified, - boolean isServerEncrypted, FileSmbProperties smbProperties, FilePosixProperties posixProperties) { + boolean isServerEncrypted, FileSmbProperties smbProperties, FilePosixProperties posixProperties, + String fileName) { this.metadata = metadata; this.eTag = eTag; this.lastModified = lastModified; this.isServerEncrypted = isServerEncrypted; this.smbProperties = smbProperties; this.posixProperties = posixProperties; + this.fileName = fileName; } static { @@ -113,4 +117,13 @@ public FileSmbProperties getSmbProperties() { public FilePosixProperties getPosixProperties() { return posixProperties; } + + /** + * Gets the directory name returned by the service, or {@code null} when it was not returned. + * + * @return The directory name. + */ + public String getFileName() { + return fileName; + } } diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/models/ShareFileLink.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/models/ShareFileLink.java new file mode 100644 index 0000000000000..ade041f892a12 --- /dev/null +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/models/ShareFileLink.java @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.file.share.models; + +import com.azure.core.annotation.Immutable; + +/** + * Describes one path that links to a file. + */ +@Immutable +public final class ShareFileLink { + private final String parentId; + private final String name; + + /** + * Creates a file link. + * + * @param parentId The file ID of the parent directory. + * @param name The name of the file link. + */ + public ShareFileLink(String parentId, String name) { + this.parentId = parentId; + this.name = name; + } + + /** + * Gets the file ID of the parent directory. + * + * @return The parent directory ID. + */ + public String getParentId() { + return parentId; + } + + /** + * Gets the name of the file link. + * + * @return The file link name. + */ + public String getName() { + return name; + } +} diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/models/ShareFileLinks.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/models/ShareFileLinks.java new file mode 100644 index 0000000000000..6810198e76076 --- /dev/null +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/models/ShareFileLinks.java @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.file.share.models; + +import com.azure.core.annotation.Immutable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * Contains a file's properties and the paths that link to the file. + */ +@Immutable +public final class ShareFileLinks { + private final ShareFileProperties properties; + private final List links; + + /** + * Creates a file links result. + * + * @param properties The file's properties. + * @param links The paths that link to the file. + */ + public ShareFileLinks(ShareFileProperties properties, List links) { + this.properties = Objects.requireNonNull(properties, "'properties' cannot be null."); + this.links + = Collections.unmodifiableList(new ArrayList<>(Objects.requireNonNull(links, "'links' cannot be null."))); + } + + /** + * Gets the file's properties. + * + * @return The file's properties. + */ + public ShareFileProperties getProperties() { + return properties; + } + + /** + * Gets the paths that link to the file. + * + * @return The file links. + */ + public List getLinks() { + return links; + } +} diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/models/ShareFileProperties.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/models/ShareFileProperties.java index 740d480e88b36..321d815a429be 100644 --- a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/models/ShareFileProperties.java +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/models/ShareFileProperties.java @@ -22,6 +22,7 @@ public final class ShareFileProperties { private final String fileType; private final Long contentLength; private final String contentType; + private final String contentLanguage; private final byte[] contentMd5; private final String contentEncoding; private final String cacheControl; @@ -38,6 +39,7 @@ public final class ShareFileProperties { private final Boolean isServerEncrypted; private final FileSmbProperties smbProperties; private final FilePosixProperties posixProperties; + private final String fileName; /** * Creates an instance of property information about a specific File. @@ -138,6 +140,7 @@ public ShareFileProperties(String eTag, OffsetDateTime lastModified, Map metadata, String fileType, - Long contentLength, String contentType, byte[] contentMd5, String contentEncoding, String cacheControl, - String contentDisposition, LeaseStatusType leaseStatusType, LeaseStateType leaseStateType, + Long contentLength, String contentType, String contentLanguage, byte[] contentMd5, String contentEncoding, + String cacheControl, String contentDisposition, LeaseStatusType leaseStatusType, LeaseStateType leaseStateType, LeaseDurationType leaseDurationType, OffsetDateTime copyCompletionTime, String copyStatusDescription, String copyId, String copyProgress, String copySource, CopyStatusType copyStatus, Boolean isServerEncrypted, - FileSmbProperties smbProperties, FilePosixProperties posixProperties) { + FileSmbProperties smbProperties, FilePosixProperties posixProperties, String fileName) { this.eTag = eTag; this.lastModified = lastModified; this.metadata = metadata; this.fileType = fileType; this.contentLength = contentLength; this.contentType = contentType; + this.contentLanguage = contentLanguage; this.contentMd5 = CoreUtils.clone(contentMd5); this.contentEncoding = contentEncoding; this.cacheControl = cacheControl; @@ -185,6 +190,7 @@ private ShareFileProperties(String eTag, OffsetDateTime lastModified, MapFile-ID-addressed clients are not supported for lease operations. A client created via + * {@link ShareFileClient#getFileId()} or {@link ShareFileClientBuilder#fileId(String)} must be rejected before any + * request is sent.

+ * * @param fileClient ShareFileClient used to configure the builder. * @return the updated ShareLeaseClientBuilder object * @throws NullPointerException If {@code fileClient} is {@code null}. + * @throws IllegalStateException If {@code fileClient} is file-ID-addressed. */ public ShareLeaseClientBuilder fileClient(ShareFileClient fileClient) { Objects.requireNonNull(fileClient); + ShareErrors.validatePathOperation(fileClient.getFileId(), "fileClient"); this.pipeline = fileClient.getHttpPipeline(); this.url = fileClient.getAccountUrl(); this.shareName = fileClient.getShareName(); @@ -136,12 +144,18 @@ public ShareLeaseClientBuilder fileClient(ShareFileClient fileClient) { * Configures the builder based on the passed {@link ShareFileAsyncClient}. This will set the {@link HttpPipeline} * and {@link URL} that are used to interact with the service. * + *

File-ID-addressed clients are not supported for lease operations. A client created via + * {@link ShareFileAsyncClient#getFileId()} or {@link ShareFileClientBuilder#fileId(String)} must be rejected before + * any request is sent.

+ * * @param fileAsyncClient ShareFileAsyncClient used to configure the builder. * @return the updated ShareLeaseClientBuilder object * @throws NullPointerException If {@code fileAsyncClient} is {@code null}. + * @throws IllegalStateException If {@code fileAsyncClient} is file-ID-addressed. */ public ShareLeaseClientBuilder fileAsyncClient(ShareFileAsyncClient fileAsyncClient) { Objects.requireNonNull(fileAsyncClient); + ShareErrors.validatePathOperation(fileAsyncClient.getFileId(), "fileAsyncClient"); this.pipeline = fileAsyncClient.getHttpPipeline(); this.url = fileAsyncClient.getAccountUrl(); this.shareName = fileAsyncClient.getShareName(); diff --git a/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/BuilderHelperTests.java b/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/BuilderHelperTests.java index 607cd78fd05d3..3625365082c10 100644 --- a/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/BuilderHelperTests.java +++ b/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/BuilderHelperTests.java @@ -9,6 +9,7 @@ import com.azure.core.http.HttpHeaders; import com.azure.core.http.HttpMethod; import com.azure.core.http.HttpPipeline; +import com.azure.core.http.HttpPipelineBuilder; import com.azure.core.http.HttpRequest; import com.azure.core.http.HttpResponse; import com.azure.core.http.policy.FixedDelayOptions; @@ -44,6 +45,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; public class BuilderHelperTests { private static final ClientLogger LOGGER = new ClientLogger(BuilderHelperTests.class); @@ -56,6 +58,126 @@ public class BuilderHelperTests { private static final RetryOptions CORE_RETRY_OPTIONS = new RetryOptions(new FixedDelayOptions(1, Duration.ofSeconds(2))); + @ParameterizedTest + @MethodSource("fileIdUrls") + void syncFileIdUrls(String fileId, String snapshot, String expectedUrl) { + ShareClient shareClient = new ShareServiceClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .buildClient() + .getShareClient(FileIdTestHelper.SHARE_NAME) + .getSnapshotClient(snapshot); + ShareFileClient fileClient = shareClient.getFileClientByFileId(fileId); + ShareDirectoryClient directoryClient = shareClient.getDirectoryClientByFileId(fileId); + + assertEquals(fileId, fileClient.getFileId()); + assertEquals(expectedUrl, fileClient.getFileUrl()); + assertEquals(fileId, directoryClient.getFileId()); + assertEquals(expectedUrl, directoryClient.getDirectoryUrl()); + + ShareFileClient rebuiltClient + = new ShareFileClientBuilder().endpoint(fileClient.getFileUrl()).buildFileClient(); + assertEquals(fileId, rebuiltClient.getFileId()); + assertEquals(expectedUrl, rebuiltClient.getFileUrl()); + } + + @ParameterizedTest + @MethodSource("fileIdUrls") + void asyncFileIdUrls(String fileId, String snapshot, String expectedUrl) { + ShareAsyncClient shareClient = new ShareServiceClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .buildAsyncClient() + .getShareAsyncClient(FileIdTestHelper.SHARE_NAME) + .getSnapshotClient(snapshot); + ShareFileAsyncClient fileClient = shareClient.getFileClientByFileId(fileId); + ShareDirectoryAsyncClient directoryClient = shareClient.getDirectoryClientByFileId(fileId); + + assertEquals(fileId, fileClient.getFileId()); + assertEquals(expectedUrl, fileClient.getFileUrl()); + assertEquals(fileId, directoryClient.getFileId()); + assertEquals(expectedUrl, directoryClient.getDirectoryUrl()); + + ShareFileAsyncClient rebuiltClient + = new ShareFileClientBuilder().endpoint(fileClient.getFileUrl()).buildFileAsyncClient(); + assertEquals(fileId, rebuiltClient.getFileId()); + assertEquals(expectedUrl, rebuiltClient.getFileUrl()); + } + + @ParameterizedTest + @MethodSource("fileIdEndpointSetters") + void syncFileIdEndpointSetters(boolean connectionString, String endpointSuffix, String expectedFileId, + String expectedPath) { + ShareFileClient fileClient + = fileIdEndpointBuilder(connectionString, endpointSuffix, expectedFileId, expectedPath).buildFileClient(); + + assertEquals(expectedFileId, fileClient.getFileId()); + assertEquals(expectedPath, fileClient.getFilePath()); + fileClient.getProperties(); + if (!expectedFileId.isEmpty()) { + assertThrows(IllegalStateException.class, fileClient::exists); + } + } + + @ParameterizedTest + @MethodSource("fileIdEndpointSetters") + void asyncFileIdEndpointSetters(boolean connectionString, String endpointSuffix, String expectedFileId, + String expectedPath) { + ShareFileAsyncClient fileClient + = fileIdEndpointBuilder(connectionString, endpointSuffix, expectedFileId, expectedPath) + .buildFileAsyncClient(); + + assertEquals(expectedFileId, fileClient.getFileId()); + assertEquals(expectedPath, fileClient.getFilePath()); + StepVerifier.create(fileClient.getProperties()).expectNextCount(1).verifyComplete(); + if (!expectedFileId.isEmpty()) { + StepVerifier.create(fileClient.exists()).verifyError(IllegalStateException.class); + } + } + + private static ShareFileClientBuilder fileIdEndpointBuilder(boolean connectionString, String endpointSuffix, + String expectedFileId, String expectedPath) { + HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(request -> { + assertEquals(HttpMethod.HEAD, request.getHttpMethod()); + assertEquals( + "/" + FileIdTestHelper.SHARE_NAME + + (expectedFileId.isEmpty() ? "/" + expectedPath.replace("/", "%2F") : ""), + request.getUrl().getPath()); + if (expectedFileId.isEmpty()) { + assertNull(request.getUrl().getQuery()); + } else { + assertTrue(request.getUrl().getQuery().contains("fileid=" + expectedFileId)); + } + return Mono.just(new MockHttpResponse(request, 200, FileIdTestHelper.fileHeaders("file.txt"))); + }).build(); + ShareFileClientBuilder builder = new ShareFileClientBuilder().fileId(FileIdTestHelper.FILE_ID); + String endpoint = FileIdTestHelper.ENDPOINT + endpointSuffix; + if (connectionString) { + builder.connectionString("FileEndpoint=" + endpoint + ";SharedAccessSignature=sv=2027-03-07&sig=test"); + } else { + builder.endpoint(endpoint); + } + return builder.shareName(FileIdTestHelper.SHARE_NAME).pipeline(pipeline); + } + + private static Stream fileIdEndpointSetters() { + return Stream.of(false, true) + .flatMap(connectionString -> Stream.of(Arguments.of(connectionString, "", FileIdTestHelper.FILE_ID, ""), + Arguments.of(connectionString, "/", FileIdTestHelper.FILE_ID, ""), + Arguments.of(connectionString, "/share", "", ""), Arguments.of(connectionString, "/share/", "", ""), + Arguments.of(connectionString, "/share/directory/file.txt", "", "directory/file.txt"), + Arguments.of(connectionString, "?fileid=replacement-id", "replacement-id", ""), Arguments + .of(connectionString, "/share/directory/file.txt?fileid=replacement-id", "replacement-id", ""))); + } + + // These are not realistic file IDs, but are here for defensive testing + private static Stream fileIdUrls() { + return Stream + .of(new String[] { FileIdTestHelper.FILE_ID, FileIdTestHelper.FILE_ID }, new String[] { "a&b", "a%26b" }, + new String[] { "a%26b", "a%2526b" }, new String[] { "a +/=?#%", "a%20%2B%2F%3D%3F%23%25" }, + new String[] { "caf\u00e9", "caf%C3%A9" }) + .flatMap(pair -> Stream.of(null, "2026-09-29T12:34:56.1234567Z") + .map(snapshot -> Arguments.of(pair[0], snapshot, + FileIdTestHelper.ENDPOINT + "/" + FileIdTestHelper.SHARE_NAME + + (snapshot == null ? "?" : "?sharesnapshot=" + snapshot + "&") + "fileid=" + pair[1]))); + } + private static HttpRequest request(String url) { return new HttpRequest(HttpMethod.HEAD, url) .setHeaders(new HttpHeaders().set(HttpHeaderName.CONTENT_LENGTH, "0")) diff --git a/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/DirectoryApiTests.java b/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/DirectoryApiTests.java index 6f3ff54d53ee1..b268fd71bae4a 100644 --- a/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/DirectoryApiTests.java +++ b/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/DirectoryApiTests.java @@ -5,8 +5,14 @@ import com.azure.core.http.HttpHeader; import com.azure.core.http.HttpHeaderName; +import com.azure.core.http.HttpHeaders; +import com.azure.core.http.HttpMethod; +import com.azure.core.http.HttpPipeline; +import com.azure.core.http.HttpPipelineBuilder; import com.azure.core.http.rest.PagedResponse; import com.azure.core.http.rest.Response; +import com.azure.core.test.annotation.DoNotRecord; +import com.azure.core.test.http.MockHttpResponse; import com.azure.core.util.Context; import com.azure.core.util.HttpClientOptions; import com.azure.storage.blob.BlobServiceVersion; @@ -40,12 +46,16 @@ import com.azure.storage.file.share.options.ShareListFilesAndDirectoriesOptions; import com.azure.storage.file.share.sas.ShareFileSasPermission; import com.azure.storage.file.share.sas.ShareServiceSasSignatureValues; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.NullAndEmptySource; import org.junit.jupiter.params.provider.ValueSource; import java.io.File; @@ -61,6 +71,7 @@ import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; +import reactor.core.publisher.Mono; import static com.azure.storage.common.implementation.Constants.HeaderConstants.ERROR_CODE_HEADER_NAME; import static org.junit.jupiter.api.Assertions.assertArrayEquals; @@ -84,7 +95,11 @@ public class DirectoryApiTests extends FileShareTestBase { = "O:S-1-5-21-2127521184-1604012920-1887927527-21560751G:S-1-5-21-2127521184-1604012920-1887927527-513D:AI(A;;FA;;;SY)(A;;FA;;;BA)(A;;0x1200a9;;;S-1-5-21-397955417-626881126-188441444-3053964)S:NO_ACCESS_CONTROL"; @BeforeEach - public void setup() { + public void setup(TestInfo testInfo) { + if (testInfo.getTags().contains("file-id-mock")) { + return; + } + shareName = generateShareName(); directoryPath = generatePathName(); shareClient = shareBuilderHelper(shareName).buildClient(); @@ -105,6 +120,183 @@ public void getDirectoryUrl() { assertEquals(expectURL, directoryURL); } + @RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2027-03-07") + @ParameterizedTest + @ValueSource(booleans = { false, true }) + public void directoryPropertiesByFileId(boolean snapshot) { + primaryDirectoryClient.createWithResponse(new ShareDirectoryCreateOptions().setMetadata(testMetadata), null, + null); + ShareDirectoryProperties expected = primaryDirectoryClient.getProperties(); + String fileId = expected.getSmbProperties().getFileId(); + assertNotNull(fileId); + ShareClient source = shareClient; + if (snapshot) { + source = shareClient.getSnapshotClient(shareClient.createSnapshot().getSnapshot()); + primaryDirectoryClient.setMetadata(Collections.singletonMap("changed", "after-snapshot")); + } + + ShareDirectoryClient directoryClient = source.getDirectoryClientByFileId(fileId); + Response response = directoryClient.getPropertiesWithResponse(null, Context.NONE); + ShareDirectoryProperties actual = response.getValue(); + assertEquals(200, response.getStatusCode()); + assertEquals(fileId, directoryClient.getFileId()); + assertEquals(directoryPath, actual.getFileName()); + assertEquals(expected.getMetadata(), actual.getMetadata()); + assertEquals(expected.getETag(), actual.getETag()); + assertEquals(expected.getLastModified(), actual.getLastModified()); + assertEquals(expected.isServerEncrypted(), actual.isServerEncrypted()); + assertEquals(fileId, actual.getSmbProperties().getFileId()); + assertEquals(expected.getSmbProperties().getParentId(), actual.getSmbProperties().getParentId()); + assertEquals(expected.getSmbProperties().getFileCreationTime(), + actual.getSmbProperties().getFileCreationTime()); + assertEquals(expected.getSmbProperties().getFileLastWriteTime(), + actual.getSmbProperties().getFileLastWriteTime()); + } + + @LiveOnly + @RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2027-03-07") + @Test + public void directoryPropertiesByFileIdLive() { + primaryDirectoryClient.create(); + ShareDirectoryProperties expected = primaryDirectoryClient.getProperties(); + String fileId = expected.getSmbProperties().getFileId(); + + ShareDirectoryProperties actual = shareClient.getDirectoryClientByFileId(fileId).getProperties(); + + assertEquals(directoryPath, actual.getFileName()); + assertEquals(fileId, actual.getSmbProperties().getFileId()); + assertEquals(expected.getSmbProperties().getParentId(), actual.getSmbProperties().getParentId()); + } + + @RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2027-03-07") + @Test + public void directoryPropertiesByFileIdMapServiceErrors() { + ShareStorageException exception = assertThrows(ShareStorageException.class, + () -> shareClient.getDirectoryClientByFileId("invalid-file-id").getProperties()); + assertEquals(400, exception.getStatusCode()); + assertEquals(ShareErrorCode.INVALID_QUERY_PARAMETER_VALUE, exception.getErrorCode()); + } + + @DoNotRecord + @Tag("file-id-mock") + @ParameterizedTest + @ValueSource(booleans = { false, true }) + public void directoryClientByFileIdUrl(boolean snapshot) { + ShareClient source = new ShareServiceClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .serviceVersion(ShareServiceVersion.V2027_03_07) + .buildClient() + .getShareClient(FileIdTestHelper.SHARE_NAME); + if (snapshot) { + source = source.getSnapshotClient("snapshot"); + } + ShareDirectoryClient directoryClient = source.getDirectoryClientByFileId(FileIdTestHelper.FILE_ID); + assertEquals(FileIdTestHelper.FILE_ID, directoryClient.getFileId()); + assertEquals("", directoryClient.getDirectoryPath()); + assertEquals(ShareServiceVersion.V2027_03_07, directoryClient.getServiceVersion()); + assertEquals( + FileIdTestHelper.ENDPOINT + "/" + FileIdTestHelper.SHARE_NAME + + (snapshot ? "?sharesnapshot=snapshot&" : "?") + "fileid=" + FileIdTestHelper.FILE_ID, + directoryClient.getDirectoryUrl()); + } + + @DoNotRecord + @Tag("file-id-mock") + @Test + public void rootDirectoryAllowsMetadataUpdates() { + HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(request -> { + assertEquals(HttpMethod.PUT, request.getHttpMethod()); + assertEquals("/" + FileIdTestHelper.SHARE_NAME + "/", request.getUrl().getPath()); + assertFalse(request.getUrl().getQuery().contains("fileid=")); + return Mono.just(new MockHttpResponse(request, 200, FileIdTestHelper.fileHeaders("directory") + .set(HttpHeaderName.fromString("x-ms-request-server-encrypted"), "true"))); + }).build(); + ShareDirectoryClient directoryClient = new ShareServiceClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .pipeline(pipeline) + .buildClient() + .getShareClient(FileIdTestHelper.SHARE_NAME) + .getRootDirectoryClient(); + + directoryClient.setMetadata(Collections.singletonMap("key", "value")); + } + + @DoNotRecord + @Tag("file-id-mock") + @ParameterizedTest + @NullAndEmptySource + @ValueSource(strings = { " ", "\t", "\n" }) + public void directoryClientByFileIdRejectsBlankIds(String fileId) { + HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(request -> { + throw new AssertionError("Invalid file IDs must be rejected before sending a request."); + }).build(); + ShareClient source = new ShareServiceClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .pipeline(pipeline) + .buildClient() + .getShareClient(FileIdTestHelper.SHARE_NAME); + assertThrows(IllegalArgumentException.class, () -> source.getDirectoryClientByFileId(fileId)); + } + + @DoNotRecord + @Tag("file-id-mock") + @Test + public void directoryPropertiesWithoutFileName() { + HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(request -> { + HttpHeaders headers = FileIdTestHelper.fileHeaders("directory"); + headers.remove(HttpHeaderName.fromString("x-ms-file-name")); + return Mono.just(new MockHttpResponse(request, 200, headers)); + }).build(); + ShareDirectoryClient directoryClient = new ShareFileClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .shareName(FileIdTestHelper.SHARE_NAME) + .resourcePath("directory") + .pipeline(pipeline) + .buildDirectoryClient(); + assertEquals("", directoryClient.getFileId()); + ShareDirectoryProperties properties = directoryClient.getProperties(); + assertNull(properties.getFileName()); + FileIdTestHelper.assertSmbProperties(properties.getSmbProperties()); + assertNull(new ShareDirectoryProperties(Collections.emptyMap(), properties.getETag(), + properties.getLastModified(), false, properties.getSmbProperties()).getFileName()); + } + + @DoNotRecord + @Tag("file-id-mock") + @ParameterizedTest + @ValueSource(strings = { "", "parent/directory" }) + public void directoryPathClientSnapshotUrls(String path) { + ShareClient testShareClient = new ShareServiceClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .serviceVersion(ShareServiceVersion.V2027_03_07) + .buildClient() + .getShareClient(FileIdTestHelper.SHARE_NAME); + ShareDirectoryClient directoryClient = testShareClient.getSnapshotClient("snapshot").getDirectoryClient(path); + + Assertions.assertEquals(path, directoryClient.getDirectoryPath()); + Assertions.assertEquals( + FileIdTestHelper.ENDPOINT + "/" + FileIdTestHelper.SHARE_NAME + "/" + path + "?sharesnapshot=snapshot", + directoryClient.getDirectoryUrl()); + assertEquals("", directoryClient.getFileId()); + Assertions.assertEquals(FileIdTestHelper.ENDPOINT + "/" + FileIdTestHelper.SHARE_NAME + "/" + + (path.isEmpty() ? "" : path + "/") + "child?sharesnapshot=snapshot", + directoryClient.getSubdirectoryClient("child").getDirectoryUrl()); + } + + @DoNotRecord + @Tag("file-id-mock") + @ParameterizedTest + @ValueSource(strings = { "", "parent/directory" }) + public void directoryClientBuilderClearsFileIdForPath(String path) { + ShareFileClientBuilder builder = new ShareFileClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .shareName(FileIdTestHelper.SHARE_NAME) + .fileId(FileIdTestHelper.FILE_ID); + ShareDirectoryClient directoryClient = builder.resourcePath(path).buildDirectoryClient(); + Assertions.assertEquals(path, directoryClient.getDirectoryPath()); + assertEquals("", directoryClient.getFileId()); + String expectedUrl = FileIdTestHelper.ENDPOINT + "/" + FileIdTestHelper.SHARE_NAME + "/" + path; + Assertions.assertEquals(expectedUrl, directoryClient.getDirectoryUrl()); + Assertions.assertEquals(expectedUrl + (path.isEmpty() ? "" : "/") + "file.txt", + directoryClient.getFileClient("file.txt").getFileUrl()); + Assertions.assertEquals(expectedUrl, + builder.fileId(FileIdTestHelper.FILE_ID).endpoint(expectedUrl).buildDirectoryClient().getDirectoryUrl()); + } + @Test public void getShareSnapshotUrl() { String accountName diff --git a/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/DirectoryAsyncApiTests.java b/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/DirectoryAsyncApiTests.java index e4329f218fca0..5f519b80cada5 100644 --- a/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/DirectoryAsyncApiTests.java +++ b/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/DirectoryAsyncApiTests.java @@ -4,7 +4,14 @@ package com.azure.storage.file.share; import com.azure.core.http.HttpHeader; +import com.azure.core.http.HttpHeaderName; +import com.azure.core.http.HttpHeaders; +import com.azure.core.http.HttpMethod; +import com.azure.core.http.HttpPipeline; +import com.azure.core.http.HttpPipelineBuilder; import com.azure.core.http.rest.Response; +import com.azure.core.test.annotation.DoNotRecord; +import com.azure.core.test.http.MockHttpResponse; import com.azure.storage.blob.BlobServiceVersion; import com.azure.storage.common.StorageSharedKeyCredential; import com.azure.storage.common.implementation.Constants; @@ -30,10 +37,14 @@ import com.azure.storage.file.share.options.ShareFileRenameOptions; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @@ -73,7 +84,11 @@ public class DirectoryAsyncApiTests extends FileShareTestBase { = "O:S-1-5-21-2127521184-1604012920-1887927527-21560751G:S-1-5-21-2127521184-1604012920-1887927527-513D:AI(A;;FA;;;SY)(A;;FA;;;BA)(A;;0x1200a9;;;S-1-5-21-397955417-626881126-188441444-3053964)S:NO_ACCESS_CONTROL"; @BeforeEach - public void setup() { + public void setup(TestInfo testInfo) { + if (testInfo.getTags().contains("file-id-mock")) { + return; + } + shareName = generateShareName(); directoryPath = generatePathName(); shareClient = shareBuilderHelper(shareName).buildClient(); @@ -94,6 +109,196 @@ public void getDirectoryURL() { assertEquals(expectURL, directoryURL); } + @RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2027-03-07") + @ParameterizedTest + @ValueSource(booleans = { false, true }) + public void asyncDirectoryPropertiesByFileId(boolean snapshot) { + primaryDirectoryAsyncClient.createWithResponse(new ShareDirectoryCreateOptions().setMetadata(testMetadata)) + .block(); + ShareDirectoryProperties expected = primaryDirectoryAsyncClient.getProperties().block(); + String fileId = expected.getSmbProperties().getFileId(); + assertNotNull(fileId); + ShareAsyncClient source = shareBuilderHelper(shareName).buildAsyncClient(); + if (snapshot) { + source = source.getSnapshotClient(source.createSnapshot().block().getSnapshot()); + primaryDirectoryAsyncClient.setMetadata(Collections.singletonMap("changed", "after-snapshot")).block(); + } + + ShareDirectoryAsyncClient directoryClient = source.getDirectoryClientByFileId(fileId); + assertEquals(fileId, directoryClient.getFileId()); + StepVerifier.create(directoryClient.getPropertiesWithResponse()).assertNext(response -> { + ShareDirectoryProperties actual = response.getValue(); + assertEquals(200, response.getStatusCode()); + assertEquals(directoryPath, actual.getFileName()); + assertEquals(expected.getMetadata(), actual.getMetadata()); + assertEquals(expected.getETag(), actual.getETag()); + assertEquals(expected.getLastModified(), actual.getLastModified()); + assertEquals(expected.isServerEncrypted(), actual.isServerEncrypted()); + assertEquals(fileId, actual.getSmbProperties().getFileId()); + assertEquals(expected.getSmbProperties().getParentId(), actual.getSmbProperties().getParentId()); + assertEquals(expected.getSmbProperties().getFileCreationTime(), + actual.getSmbProperties().getFileCreationTime()); + assertEquals(expected.getSmbProperties().getFileLastWriteTime(), + actual.getSmbProperties().getFileLastWriteTime()); + }).verifyComplete(); + } + + @LiveOnly + @RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2027-03-07") + @Test + public void asyncDirectoryPropertiesByFileIdLive() { + StepVerifier.create(primaryDirectoryAsyncClient.create() + .then(primaryDirectoryAsyncClient.getProperties()) + .flatMap(expected -> shareBuilderHelper(shareName).buildAsyncClient() + .getDirectoryClientByFileId(expected.getSmbProperties().getFileId()) + .getProperties() + .doOnNext(actual -> { + assertEquals(directoryPath, actual.getFileName()); + assertEquals(expected.getSmbProperties().getFileId(), actual.getSmbProperties().getFileId()); + assertEquals(expected.getSmbProperties().getParentId(), actual.getSmbProperties().getParentId()); + }))) + .expectNextCount(1) + .verifyComplete(); + } + + @RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2027-03-07") + @Test + public void asyncDirectoryPropertiesByFileIdMapServiceErrors() { + ShareAsyncClient source = shareBuilderHelper(shareName).buildAsyncClient(); + StepVerifier.create(source.getDirectoryClientByFileId("invalid-file-id").getProperties()) + .verifyErrorSatisfies(error -> { + ShareStorageException exception = assertInstanceOf(ShareStorageException.class, error); + assertEquals(400, exception.getStatusCode()); + assertEquals(ShareErrorCode.INVALID_QUERY_PARAMETER_VALUE, exception.getErrorCode()); + }); + } + + @DoNotRecord + @Tag("file-id-mock") + @ParameterizedTest + @ValueSource(booleans = { false, true }) + public void asyncDirectoryClientByFileIdUrl(boolean snapshot) { + ShareAsyncClient source = new ShareServiceClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .serviceVersion(ShareServiceVersion.V2027_03_07) + .buildAsyncClient() + .getShareAsyncClient(FileIdTestHelper.SHARE_NAME); + if (snapshot) { + source = source.getSnapshotClient("snapshot"); + } + ShareDirectoryAsyncClient original = source.getDirectoryClientByFileId(FileIdTestHelper.FILE_ID); + ShareDirectoryAsyncClient directoryClient = new ShareDirectoryAsyncClient(original); + assertEquals(original.getDirectoryUrl(), directoryClient.getDirectoryUrl()); + assertEquals(FileIdTestHelper.FILE_ID, directoryClient.getFileId()); + assertEquals("", directoryClient.getDirectoryPath()); + assertEquals(ShareServiceVersion.V2027_03_07, directoryClient.getServiceVersion()); + assertEquals( + FileIdTestHelper.ENDPOINT + "/" + FileIdTestHelper.SHARE_NAME + + (snapshot ? "?sharesnapshot=snapshot&" : "?") + "fileid=" + FileIdTestHelper.FILE_ID, + directoryClient.getDirectoryUrl()); + } + + @DoNotRecord + @Tag("file-id-mock") + @Test + public void asyncRootDirectoryAllowsMetadataUpdates() { + HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(request -> { + assertEquals(HttpMethod.PUT, request.getHttpMethod()); + assertEquals("/" + FileIdTestHelper.SHARE_NAME + "/", request.getUrl().getPath()); + assertFalse(request.getUrl().getQuery().contains("fileid=")); + return Mono.just(new MockHttpResponse(request, 200, FileIdTestHelper.fileHeaders("directory") + .set(HttpHeaderName.fromString("x-ms-request-server-encrypted"), "true"))); + }).build(); + ShareDirectoryAsyncClient directoryClient = new ShareServiceClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .pipeline(pipeline) + .buildAsyncClient() + .getShareAsyncClient(FileIdTestHelper.SHARE_NAME) + .getRootDirectoryClient(); + + StepVerifier.create(directoryClient.setMetadata(Collections.singletonMap("key", "value"))) + .expectNextCount(1) + .verifyComplete(); + } + + @DoNotRecord + @Tag("file-id-mock") + @ParameterizedTest + @NullAndEmptySource + @ValueSource(strings = { " ", "\t", "\n" }) + public void asyncDirectoryClientByFileIdRejectsBlankIds(String fileId) { + HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(request -> { + throw new AssertionError("Invalid file IDs must be rejected before sending a request."); + }).build(); + ShareAsyncClient source = new ShareServiceClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .pipeline(pipeline) + .buildAsyncClient() + .getShareAsyncClient(FileIdTestHelper.SHARE_NAME); + Assertions.assertThrows(IllegalArgumentException.class, () -> source.getDirectoryClientByFileId(fileId)); + } + + @DoNotRecord + @Tag("file-id-mock") + @Test + public void asyncDirectoryPropertiesWithoutFileName() { + HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(request -> { + HttpHeaders headers = FileIdTestHelper.fileHeaders("directory"); + headers.remove(HttpHeaderName.fromString("x-ms-file-name")); + return Mono.just(new MockHttpResponse(request, 200, headers)); + }).build(); + ShareDirectoryAsyncClient directoryClient = new ShareFileClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .shareName(FileIdTestHelper.SHARE_NAME) + .resourcePath("directory") + .pipeline(pipeline) + .buildDirectoryAsyncClient(); + assertEquals("", directoryClient.getFileId()); + StepVerifier.create(directoryClient.getProperties()).assertNext(properties -> { + assertNull(properties.getFileName()); + FileIdTestHelper.assertSmbProperties(properties.getSmbProperties()); + }).verifyComplete(); + } + + @DoNotRecord + @Tag("file-id-mock") + @ParameterizedTest + @ValueSource(strings = { "", "parent/directory" }) + public void asyncDirectoryPathClientSnapshotUrls(String path) { + ShareAsyncClient testShareClient = new ShareServiceClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .serviceVersion(ShareServiceVersion.V2027_03_07) + .buildAsyncClient() + .getShareAsyncClient(FileIdTestHelper.SHARE_NAME); + ShareDirectoryAsyncClient directoryClient + = testShareClient.getSnapshotClient("snapshot").getDirectoryClient(path); + Assertions.assertEquals(path, directoryClient.getDirectoryPath()); + Assertions.assertEquals( + FileIdTestHelper.ENDPOINT + "/" + FileIdTestHelper.SHARE_NAME + "/" + path + "?sharesnapshot=snapshot", + directoryClient.getDirectoryUrl()); + assertEquals("", directoryClient.getFileId()); + Assertions.assertEquals(FileIdTestHelper.ENDPOINT + "/" + FileIdTestHelper.SHARE_NAME + "/" + + (path.isEmpty() ? "" : path + "/") + "child?sharesnapshot=snapshot", + directoryClient.getSubdirectoryClient("child").getDirectoryUrl()); + } + + @DoNotRecord + @Tag("file-id-mock") + @ParameterizedTest + @ValueSource(strings = { "", "parent/directory" }) + public void asyncDirectoryClientBuilderClearsFileIdForPath(String path) { + ShareFileClientBuilder builder = new ShareFileClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .shareName(FileIdTestHelper.SHARE_NAME) + .fileId(FileIdTestHelper.FILE_ID); + ShareDirectoryAsyncClient directoryClient = builder.resourcePath(path).buildDirectoryAsyncClient(); + Assertions.assertEquals(path, directoryClient.getDirectoryPath()); + assertEquals("", directoryClient.getFileId()); + String expectedUrl = FileIdTestHelper.ENDPOINT + "/" + FileIdTestHelper.SHARE_NAME + "/" + path; + Assertions.assertEquals(expectedUrl, directoryClient.getDirectoryUrl()); + Assertions.assertEquals(expectedUrl + (path.isEmpty() ? "" : "/") + "file.txt", + directoryClient.getFileClient("file.txt").getFileUrl()); + Assertions.assertEquals(expectedUrl, + builder.fileId(FileIdTestHelper.FILE_ID) + .endpoint(expectedUrl) + .buildDirectoryAsyncClient() + .getDirectoryUrl()); + } + @Test public void getSubDirectoryClient() { ShareDirectoryAsyncClient subDirectoryClient diff --git a/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileApiTests.java b/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileApiTests.java index f2d1e78db613c..11e85476ea030 100644 --- a/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileApiTests.java +++ b/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileApiTests.java @@ -6,8 +6,14 @@ import com.azure.core.exception.UnexpectedLengthException; import com.azure.core.http.HttpHeader; import com.azure.core.http.HttpHeaderName; +import com.azure.core.http.HttpHeaders; +import com.azure.core.http.HttpMethod; +import com.azure.core.http.HttpPipeline; +import com.azure.core.http.HttpPipelineBuilder; import com.azure.core.http.rest.PagedResponse; import com.azure.core.http.rest.Response; +import com.azure.core.test.annotation.DoNotRecord; +import com.azure.core.test.http.MockHttpResponse; import com.azure.core.test.TestMode; import com.azure.core.util.BinaryData; import com.azure.core.util.Context; @@ -51,6 +57,7 @@ import com.azure.storage.file.share.models.ShareFileHandleAccessRights; import com.azure.storage.file.share.models.ShareFileHttpHeaders; import com.azure.storage.file.share.models.ShareFileInfo; +import com.azure.storage.file.share.models.ShareFileLinks; import com.azure.storage.file.share.models.ShareFileItem; import com.azure.storage.file.share.models.ShareFileMetadataInfo; import com.azure.storage.file.share.models.ShareFilePermission; @@ -85,7 +92,9 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -121,6 +130,8 @@ import java.util.Scanner; import java.util.stream.Collectors; import java.util.stream.Stream; +import java.util.concurrent.atomic.AtomicReference; +import reactor.core.publisher.Mono; import static com.azure.storage.common.implementation.Constants.HeaderConstants.ERROR_CODE_HEADER_NAME; import static org.junit.jupiter.api.Assertions.assertArrayEquals; @@ -137,10 +148,13 @@ // It is still necessary to test deprecated APIs. @SuppressWarnings("deprecation") class FileApiTests extends FileShareTestBase { + private static final String FILE_ID_TEST_CONTENT = "file-id properties"; + private ShareFileClient primaryFileClient; private ShareClient shareClient; private String shareName; private String filePath; + private String fileId; private static Map testMetadata; private static ShareFileHttpHeaders httpHeaders; private FileSmbProperties smbProperties; @@ -148,7 +162,11 @@ class FileApiTests extends FileShareTestBase { = "O:S-1-5-21-2127521184-1604012920-1887927527-21560751G:S-1-5-21-2127521184-1604012920-1887927527-513D:AI(A;;FA;;;SY)(A;;FA;;;BA)(A;;0x1200a9;;;S-1-5-21-397955417-626881126-188441444-3053964)S:NO_ACCESS_CONTROL"; @BeforeEach - public void setup() { + public void setup(TestInfo testInfo) { + if (testInfo.getTags().contains("file-id-mock")) { + return; + } + shareName = generateShareName(); filePath = generatePathName(); shareClient = shareBuilderHelper(shareName).buildClient(); @@ -157,6 +175,14 @@ public void setup() { testMetadata = Collections.singletonMap("testmetadata", "value"); httpHeaders = new ShareFileHttpHeaders().setContentLanguage("en").setContentType("application/octet-stream"); smbProperties = new FileSmbProperties().setNtfsFileAttributes(EnumSet.of(NtfsFileAttributes.NORMAL)); + + if (testInfo.getTags().contains("file-id-integration")) { + byte[] content = FILE_ID_TEST_CONTENT.getBytes(StandardCharsets.UTF_8); + primaryFileClient.create(content.length); + primaryFileClient.upload(new ByteArrayInputStream(content), content.length); + fileId = primaryFileClient.getProperties().getSmbProperties().getFileId(); + assertNotNull(fileId); + } } @Test @@ -170,6 +196,162 @@ public void getFileURL() { assertEquals(expectURL, fileURL); } + @Tag("file-id-integration") + @RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2027-03-07") + @Test + public void filePropertiesById() { + ShareFileClient factoryClient = shareClient.getFileClientByFileId(fileId); + ShareFileClient builderClient = instrument(new ShareFileClientBuilder()) + .connectionString(ENVIRONMENT.getPrimaryAccount().getConnectionString()) + .shareName(shareName) + .fileId(fileId) + .buildFileClient(); + + for (ShareFileClient fileIdClient : Arrays.asList(factoryClient, builderClient)) { + ShareFileProperties properties = fileIdClient.getProperties(); + assertEquals(filePath, properties.getFileName()); + assertEquals(FILE_ID_TEST_CONTENT.length(), properties.getContentLength()); + assertEquals(fileId, properties.getSmbProperties().getFileId()); + } + } + + @Tag("file-id-integration") + @RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2027-03-07") + @Test + public void fileLinksById() { + ShareFileLinks links = shareClient.getFileClientByFileId(fileId).getFileLinks(); + + assertEquals(FILE_ID_TEST_CONTENT.length(), links.getProperties().getContentLength()); + assertEquals(fileId, links.getProperties().getSmbProperties().getFileId()); + assertEquals(1, links.getLinks().size()); + assertEquals(filePath, links.getLinks().get(0).getName()); + assertEquals(links.getProperties().getSmbProperties().getParentId(), links.getLinks().get(0).getParentId()); + } + + @Tag("file-id-integration") + @LiveOnly + @RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2027-03-07") + @Test + public void filePropertiesByIdLive() { + ShareFileProperties properties = shareClient.getFileClientByFileId(fileId).getProperties(); + + assertEquals(filePath, properties.getFileName()); + assertEquals(FILE_ID_TEST_CONTENT.length(), properties.getContentLength()); + assertEquals(fileId, properties.getSmbProperties().getFileId()); + } + + @Tag("file-id-integration") + @LiveOnly + @RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2027-03-07") + @Test + public void fileLinksByIdLive() { + ShareFileLinks links = shareClient.getFileClientByFileId(fileId).getFileLinks(); + + assertEquals(fileId, links.getProperties().getSmbProperties().getFileId()); + assertEquals(1, links.getLinks().size()); + assertEquals(filePath, links.getLinks().get(0).getName()); + } + + @DoNotRecord + @Tag("file-id-mock") + @Test + public void fileLinksMapPropertiesAndDecodeLinkNames() { + AtomicReference requestUrl = new AtomicReference<>(); + HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(request -> { + Assertions.assertEquals(HttpMethod.GET, request.getHttpMethod()); + requestUrl.set(request.getUrl().toString()); + String body = "file%20name.txt" + + "parent-id" + ""; + HttpHeaders headers + = FileIdTestHelper.fileHeaders("file.txt").set(HttpHeaderName.CONTENT_TYPE, "application/xml"); + return Mono.just(new MockHttpResponse(request, 200, headers, body.getBytes(StandardCharsets.UTF_8))); + }).build(); + + ShareClient testShareClient = new ShareServiceClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .pipeline(pipeline) + .serviceVersion(ShareServiceVersion.V2027_03_07) + .buildClient() + .getShareClient(FileIdTestHelper.SHARE_NAME); + ShareFileLinks links = testShareClient.getFileClientByFileId(FileIdTestHelper.FILE_ID).getFileLinks(); + + Assertions.assertEquals("file.txt", links.getProperties().getFileName()); + Assertions.assertEquals("en", links.getProperties().getContentLanguage()); + Assertions.assertEquals("gzip", links.getProperties().getContentEncoding()); + FileIdTestHelper.assertSmbProperties(links.getProperties().getSmbProperties()); + Assertions.assertEquals(1, links.getLinks().size()); + Assertions.assertEquals("file name.txt", links.getLinks().get(0).getName()); + Assertions.assertEquals("parent-id", links.getLinks().get(0).getParentId()); + Assertions.assertTrue(requestUrl.get().contains("comp=hardlinks")); + Assertions.assertTrue(requestUrl.get().contains("fileid=" + FileIdTestHelper.FILE_ID)); + } + + @RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2027-03-07") + @Test + public void propertiesByIdMapServiceErrors() { + ShareStorageException exception = assertThrows(ShareStorageException.class, + () -> shareClient.getFileClientByFileId("invalid-file-id").getProperties()); + assertEquals(400, exception.getStatusCode()); + assertEquals(ShareErrorCode.INVALID_QUERY_PARAMETER_VALUE, exception.getErrorCode()); + } + + @DoNotRecord + @Tag("file-id-mock") + @Test + public void fileClientFileIdValidation() { + AtomicReference requestSent = new AtomicReference<>(false); + HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(request -> { + requestSent.set(true); + return Mono.just(new MockHttpResponse(request, 200)); + }).build(); + + ShareClient testShareClient = new ShareServiceClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .pipeline(pipeline) + .serviceVersion(ShareServiceVersion.V2027_03_07) + .buildClient() + .getShareClient(FileIdTestHelper.SHARE_NAME); + IllegalStateException linksException = Assertions.assertThrows(IllegalStateException.class, + () -> testShareClient.getFileClient("path/file").getFileLinks()); + Assertions.assertEquals("getFileLinks requires a file-ID-addressed client.", linksException.getMessage()); + Assertions.assertThrows(IllegalArgumentException.class, () -> testShareClient.getFileClientByFileId(" ")); + Assertions.assertThrows(IllegalArgumentException.class, () -> testShareClient.getFileClientByFileId(null)); + Assertions.assertFalse(requestSent.get()); + } + + @DoNotRecord + @Tag("file-id-mock") + @Test + public void fileClientBuilderParsesFileIdFromEndpointQuery() { + ShareFileClient fileClient = new ShareFileClientBuilder() + .endpoint( + FileIdTestHelper.ENDPOINT + "/" + FileIdTestHelper.SHARE_NAME + "?fileid=" + FileIdTestHelper.FILE_ID) + .buildFileClient(); + + Assertions.assertEquals(FileIdTestHelper.FILE_ID, fileClient.getFileId()); + Assertions.assertEquals("", fileClient.getFilePath()); + Assertions.assertEquals( + FileIdTestHelper.ENDPOINT + "/" + FileIdTestHelper.SHARE_NAME + "?fileid=" + FileIdTestHelper.FILE_ID, + fileClient.getFileUrl()); + + ShareFileClient configuredFileIdClient = new ShareFileClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .shareName(FileIdTestHelper.SHARE_NAME) + .fileId(FileIdTestHelper.FILE_ID) + .buildFileClient(); + Assertions.assertEquals(FileIdTestHelper.FILE_ID, configuredFileIdClient.getFileId()); + Assertions.assertEquals("", configuredFileIdClient.getFilePath()); + + ShareFileClient pathClient = new ShareFileClientBuilder() + .endpoint(FileIdTestHelper.ENDPOINT + "/" + FileIdTestHelper.SHARE_NAME + "/file.txt") + .buildFileClient(); + Assertions.assertEquals("", pathClient.getFileId()); + Assertions.assertEquals("file.txt", pathClient.getFilePath()); + Assertions.assertThrows(IllegalArgumentException.class, () -> new ShareFileClientBuilder().fileId(" ")); + Assertions.assertThrows(IllegalArgumentException.class, () -> new ShareFileClientBuilder().fileId(null)); + Assertions.assertThrows(IllegalArgumentException.class, () -> new ShareFileClientBuilder() + .endpoint(FileIdTestHelper.ENDPOINT + "/" + FileIdTestHelper.SHARE_NAME + "?fileid=")); + Assertions.assertThrows(IllegalArgumentException.class, () -> new ShareFileClientBuilder() + .endpoint(FileIdTestHelper.ENDPOINT + "/" + FileIdTestHelper.SHARE_NAME + "?fileid=%20")); + } + @Test public void getShareSnapshotURL() { String accountName diff --git a/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileAsyncApiTests.java b/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileAsyncApiTests.java index 99d9120e0dd92..11c6b93f8648e 100644 --- a/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileAsyncApiTests.java +++ b/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileAsyncApiTests.java @@ -6,8 +6,14 @@ import com.azure.core.exception.UnexpectedLengthException; import com.azure.core.http.HttpHeader; import com.azure.core.http.HttpHeaderName; +import com.azure.core.http.HttpHeaders; +import com.azure.core.http.HttpMethod; +import com.azure.core.http.HttpPipeline; +import com.azure.core.http.HttpPipelineBuilder; import com.azure.core.http.rest.Response; +import com.azure.core.test.annotation.DoNotRecord; import com.azure.core.test.TestMode; +import com.azure.core.test.http.MockHttpResponse; import com.azure.core.util.BinaryData; import com.azure.core.util.CoreUtils; import com.azure.core.util.FluxUtil; @@ -38,6 +44,7 @@ import com.azure.storage.file.share.models.ShareFileDownloadHeaders; import com.azure.storage.file.share.models.ShareFileHttpHeaders; import com.azure.storage.file.share.models.ShareFileInfo; +import com.azure.storage.file.share.models.ShareFileLinks; import com.azure.storage.file.share.models.ShareFilePermission; import com.azure.storage.file.share.models.ShareFileProperties; import com.azure.storage.file.share.models.ShareFileRange; @@ -65,7 +72,9 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -102,6 +111,7 @@ import java.util.Map; import java.util.Objects; import java.util.Scanner; +import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -117,10 +127,13 @@ @SuppressWarnings("deprecation") public class FileAsyncApiTests extends FileShareTestBase { + private static final String FILE_ID_TEST_CONTENT = "file-id properties"; + private ShareFileAsyncClient primaryFileAsyncClient; private ShareAsyncClient shareAsyncClient; private String shareName; private String filePath; + private String fileId; private static Map testMetadata; private static ShareFileHttpHeaders httpHeaders; private FileSmbProperties smbProperties; @@ -128,7 +141,11 @@ public class FileAsyncApiTests extends FileShareTestBase { = "O:S-1-5-21-2127521184-1604012920-1887927527-21560751G:S-1-5-21-2127521184-1604012920-1887927527-513D:AI(A;;FA;;;SY)(A;;FA;;;BA)(A;;0x1200a9;;;S-1-5-21-397955417-626881126-188441444-3053964)S:NO_ACCESS_CONTROL"; @BeforeEach - public void setup() { + public void setup(TestInfo testInfo) { + if (testInfo.getTags().contains("file-id-mock")) { + return; + } + shareName = generateShareName(); filePath = generatePathName(); shareAsyncClient = shareBuilderHelper(shareName).buildAsyncClient(); @@ -137,6 +154,15 @@ public void setup() { testMetadata = Collections.singletonMap("testmetadata", "value"); httpHeaders = new ShareFileHttpHeaders().setContentLanguage("en").setContentType("application/octet-stream"); smbProperties = new FileSmbProperties().setNtfsFileAttributes(EnumSet.of(NtfsFileAttributes.NORMAL)); + + if (testInfo.getTags().contains("file-id-integration")) { + byte[] content = FILE_ID_TEST_CONTENT.getBytes(StandardCharsets.UTF_8); + primaryFileAsyncClient.create(content.length).block(); + primaryFileAsyncClient.upload(Flux.just(ByteBuffer.wrap(content)), content.length).block(); + ShareFileProperties properties = Objects.requireNonNull(primaryFileAsyncClient.getProperties().block()); + fileId = Objects.requireNonNull(properties.getSmbProperties()).getFileId(); + Assertions.assertNotNull(fileId); + } } @Test @@ -149,6 +175,125 @@ public void getFileURL() { assertEquals(expectURL, fileURL); } + @Tag("file-id-integration") + @RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2027-03-07") + @Test + public void asyncFilePropertiesById() { + ShareFileAsyncClient factoryClient = shareAsyncClient.getFileClientByFileId(fileId); + ShareFileAsyncClient builderClient = instrument(new ShareFileClientBuilder()) + .connectionString(ENVIRONMENT.getPrimaryAccount().getConnectionString()) + .shareName(shareName) + .fileId(fileId) + .buildFileAsyncClient(); + + for (ShareFileAsyncClient fileIdClient : Arrays.asList(factoryClient, builderClient)) { + ShareFileProperties properties = fileIdClient.getProperties().block(); + Assertions.assertEquals(filePath, properties.getFileName()); + Assertions.assertEquals(FILE_ID_TEST_CONTENT.length(), properties.getContentLength()); + Assertions.assertEquals(fileId, properties.getSmbProperties().getFileId()); + } + } + + @Tag("file-id-integration") + @RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2027-03-07") + @Test + public void asyncFileLinksById() { + ShareFileLinks links = shareAsyncClient.getFileClientByFileId(fileId).getFileLinks().block(); + + assertEquals(FILE_ID_TEST_CONTENT.length(), links.getProperties().getContentLength()); + assertEquals(fileId, links.getProperties().getSmbProperties().getFileId()); + assertEquals(1, links.getLinks().size()); + assertEquals(filePath, links.getLinks().get(0).getName()); + assertEquals(links.getProperties().getSmbProperties().getParentId(), links.getLinks().get(0).getParentId()); + } + + @Tag("file-id-integration") + @LiveOnly + @RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2027-03-07") + @Test + public void asyncFilePropertiesByIdLive() { + StepVerifier.create(shareAsyncClient.getFileClientByFileId(fileId).getProperties()).assertNext(properties -> { + assertEquals(filePath, properties.getFileName()); + assertEquals(FILE_ID_TEST_CONTENT.length(), properties.getContentLength()); + assertEquals(fileId, properties.getSmbProperties().getFileId()); + }).verifyComplete(); + } + + @Tag("file-id-integration") + @LiveOnly + @RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2027-03-07") + @Test + public void asyncFileLinksByIdLive() { + StepVerifier.create(shareAsyncClient.getFileClientByFileId(fileId).getFileLinks()).assertNext(links -> { + assertEquals(fileId, links.getProperties().getSmbProperties().getFileId()); + assertEquals(1, links.getLinks().size()); + assertEquals(filePath, links.getLinks().get(0).getName()); + }).verifyComplete(); + } + + @DoNotRecord + @Tag("file-id-mock") + @Test + public void asyncFileLinksMapPropertiesAndDecodeLinkNames() { + AtomicReference requestUrl = new AtomicReference<>(); + HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(request -> { + Assertions.assertEquals(HttpMethod.GET, request.getHttpMethod()); + requestUrl.set(request.getUrl().toString()); + String body = "file%20name.txt" + + "parent-id" + ""; + HttpHeaders headers + = FileIdTestHelper.fileHeaders("file.txt").set(HttpHeaderName.CONTENT_TYPE, "application/xml"); + return Mono.just(new MockHttpResponse(request, 200, headers, body.getBytes(StandardCharsets.UTF_8))); + }).build(); + + ShareAsyncClient testShareClient = new ShareServiceClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .pipeline(pipeline) + .serviceVersion(ShareServiceVersion.V2027_03_07) + .buildAsyncClient() + .getShareAsyncClient(FileIdTestHelper.SHARE_NAME); + ShareFileLinks links = testShareClient.getFileClientByFileId(FileIdTestHelper.FILE_ID).getFileLinks().block(); + + Assertions.assertEquals("file.txt", links.getProperties().getFileName()); + Assertions.assertEquals("en", links.getProperties().getContentLanguage()); + Assertions.assertEquals("gzip", links.getProperties().getContentEncoding()); + FileIdTestHelper.assertSmbProperties(links.getProperties().getSmbProperties()); + Assertions.assertEquals(1, links.getLinks().size()); + Assertions.assertEquals("file name.txt", links.getLinks().get(0).getName()); + Assertions.assertEquals("parent-id", links.getLinks().get(0).getParentId()); + Assertions.assertTrue(requestUrl.get().contains("comp=hardlinks")); + Assertions.assertTrue(requestUrl.get().contains("fileid=" + FileIdTestHelper.FILE_ID)); + } + + @RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2027-03-07") + @Test + public void asyncPropertiesByIdMapServiceErrors() { + ShareStorageException exception = Assertions.assertThrows(ShareStorageException.class, + () -> shareAsyncClient.getFileClientByFileId("invalid-file-id").getProperties().block()); + assertEquals(400, exception.getStatusCode()); + assertEquals(ShareErrorCode.INVALID_QUERY_PARAMETER_VALUE, exception.getErrorCode()); + } + + @DoNotRecord + @Tag("file-id-mock") + @Test + public void asyncFileLinksRequireFileId() { + AtomicReference requestSent = new AtomicReference<>(false); + HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(request -> { + requestSent.set(true); + return Mono.just(new MockHttpResponse(request, 200)); + }).build(); + + ShareAsyncClient testShareClient = new ShareServiceClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .pipeline(pipeline) + .serviceVersion(ShareServiceVersion.V2027_03_07) + .buildAsyncClient() + .getShareAsyncClient(FileIdTestHelper.SHARE_NAME); + IllegalStateException linksException = Assertions.assertThrows(IllegalStateException.class, + () -> testShareClient.getFileClient("path/file").getFileLinks().block()); + Assertions.assertEquals("getFileLinks requires a file-ID-addressed client.", linksException.getMessage()); + Assertions.assertFalse(requestSent.get()); + } + @Test public void createFile() { StepVerifier.create(primaryFileAsyncClient.createWithResponse(1024, null, null, null, null)) diff --git a/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileIdTestHelper.java b/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileIdTestHelper.java new file mode 100644 index 0000000000000..b9dc78e682994 --- /dev/null +++ b/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileIdTestHelper.java @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.file.share; + +import com.azure.core.http.HttpHeaderName; +import com.azure.core.http.HttpHeaders; + +import java.time.OffsetDateTime; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +final class FileIdTestHelper { + static final String ENDPOINT = "https://account.file.core.windows.net"; + static final String SHARE_NAME = "share"; + static final String FILE_ID = "12384898975283830"; + private static final String FILE_TIME = "2026-09-29T12:34:56.1234567Z"; + + private FileIdTestHelper() { + } + + static HttpHeaders fileHeaders(String fileName) { + return new HttpHeaders().set(HttpHeaderName.ETAG, "\"0x8DEAF1479E1C087\"") + .set(HttpHeaderName.LAST_MODIFIED, "Wed, 21 Oct 2015 07:28:00 GMT") + .set(HttpHeaderName.CONTENT_TYPE, "application/octet-stream") + .set(HttpHeaderName.CONTENT_LENGTH, "1024") + .set(HttpHeaderName.CONTENT_LANGUAGE, "en") + .set(HttpHeaderName.CONTENT_ENCODING, "gzip") + .set(HttpHeaderName.fromString("x-ms-meta-key"), "value") + .set(HttpHeaderName.fromString("x-ms-file-creation-time"), FILE_TIME) + .set(HttpHeaderName.fromString("x-ms-file-last-write-time"), FILE_TIME) + .set(HttpHeaderName.fromString("x-ms-file-change-time"), FILE_TIME) + .set(HttpHeaderName.fromString("x-ms-file-id"), FILE_ID) + .set(HttpHeaderName.fromString("x-ms-file-parent-id"), "parent-id") + .set(HttpHeaderName.fromString("x-ms-file-name"), fileName) + .set(HttpHeaderName.fromString("x-ms-type"), "file") + .set(HttpHeaderName.fromString("x-ms-server-encrypted"), "false") + .set(HttpHeaderName.fromString("x-ms-content-type"), "application/octet-stream") + .set(HttpHeaderName.fromString("x-ms-content-length"), "1024") + .set(HttpHeaderName.fromString("x-ms-content-language"), "en") + .set(HttpHeaderName.fromString("x-ms-content-encoding"), "gzip"); + } + + static void assertSmbProperties(FileSmbProperties properties) { + assertEquals(FILE_ID, properties.getFileId()); + assertEquals("parent-id", properties.getParentId()); + assertEquals(OffsetDateTime.parse(FILE_TIME), properties.getFileCreationTime()); + assertEquals(OffsetDateTime.parse(FILE_TIME), properties.getFileLastWriteTime()); + assertEquals(OffsetDateTime.parse(FILE_TIME), properties.getFileChangeTime()); + } +} diff --git a/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileIdUnsupportedOperationsTests.java b/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileIdUnsupportedOperationsTests.java new file mode 100644 index 0000000000000..8341cc51b0c5c --- /dev/null +++ b/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileIdUnsupportedOperationsTests.java @@ -0,0 +1,511 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.file.share; + +import com.azure.core.http.HttpPipeline; +import com.azure.core.http.HttpPipelineBuilder; +import com.azure.core.test.annotation.DoNotRecord; +import com.azure.core.test.http.MockHttpResponse; +import com.azure.core.util.Context; +import com.azure.storage.common.ParallelTransferOptions; +import com.azure.storage.file.share.models.ShareFileRange; +import com.azure.storage.file.share.models.ShareFileUploadOptions; +import com.azure.storage.file.share.models.ShareFileUploadRangeOptions; +import com.azure.storage.file.share.options.ShareFileCopyOptions; +import com.azure.storage.file.share.options.ShareFileCreateHardLinkOptions; +import com.azure.storage.file.share.options.ShareFileCreateOptions; +import com.azure.storage.file.share.specialized.ShareLeaseClientBuilder; +import com.azure.storage.file.share.options.ShareFileCreateSymbolicLinkOptions; +import com.azure.storage.file.share.options.ShareFileDownloadOptions; +import com.azure.storage.file.share.options.ShareFileListRangesDiffOptions; +import com.azure.storage.file.share.options.ShareFileListRangesOptions; +import com.azure.storage.file.share.options.ShareFileRenameOptions; +import com.azure.storage.file.share.options.ShareFileSetPropertiesOptions; +import com.azure.storage.file.share.options.ShareFileUploadRangeFromUrlOptions; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collections; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; + +// Temporary file-ID restrictions; remove the corresponding assertions as operations become supported. +@SuppressWarnings("deprecation") +class FileIdUnsupportedOperationsTests { + @DoNotRecord + @Test + public void fileIdClientsRejectPathOperations() { + AtomicReference requestSent = new AtomicReference<>(false); + HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(request -> { + requestSent.set(true); + return Mono.just(new MockHttpResponse(request, 200)); + }).build(); + + ShareClient testShareClient = new ShareServiceClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .pipeline(pipeline) + .serviceVersion(ShareServiceVersion.V2027_03_07) + .buildClient() + .getShareClient(FileIdTestHelper.SHARE_NAME); + ShareFileClient fileClient = testShareClient.getFileClientByFileId(FileIdTestHelper.FILE_ID); + + Assertions.assertThrows(IllegalStateException.class, fileClient::exists); + IllegalStateException createException + = Assertions.assertThrows(IllegalStateException.class, () -> fileClient.create(1024)); + Assertions.assertEquals("create is not supported for a file-ID-addressed client.", + createException.getMessage()); + Assertions.assertThrows(IllegalStateException.class, fileClient::delete); + Assertions.assertThrows(IllegalStateException.class, () -> fileClient.setMetadata(null)); + Assertions.assertThrows(IllegalStateException.class, () -> fileClient.rename("destination")); + Assertions.assertThrows(IllegalStateException.class, fileClient::getFileOutputStream); + Assertions.assertThrows(IllegalStateException.class, () -> fileClient.generateSas(null)); + assertThrows(IllegalStateException.class, () -> fileClient.existsWithResponse(null, Context.NONE)); + assertThrows(IllegalStateException.class, + () -> fileClient.createWithResponse(new ShareFileCreateOptions(1024), null, Context.NONE)); + assertThrows(IllegalStateException.class, () -> fileClient.deleteWithResponse(null, null, Context.NONE)); + assertThrows(IllegalStateException.class, fileClient::deleteIfExists); + assertThrows(IllegalStateException.class, + () -> fileClient.deleteIfExistsWithResponse(null, null, Context.NONE)); + String sourceUrl = FileIdTestHelper.ENDPOINT + "/" + FileIdTestHelper.SHARE_NAME + "/source"; + assertThrows(IllegalStateException.class, + () -> fileClient.beginCopy(sourceUrl, Collections.emptyMap(), null).poll()); + assertThrows(IllegalStateException.class, + () -> fileClient.beginCopy(sourceUrl, new ShareFileCopyOptions(), null).poll()); + assertThrows(IllegalStateException.class, () -> fileClient.abortCopy("copy-id")); + assertThrows(IllegalStateException.class, + () -> fileClient.abortCopyWithResponse("copy-id", null, null, Context.NONE)); + assertThrows(IllegalStateException.class, () -> fileClient.setProperties(1024, null, null, null)); + assertThrows(IllegalStateException.class, + () -> fileClient.setPropertiesWithResponse(new ShareFileSetPropertiesOptions(1024), null, Context.NONE)); + assertThrows(IllegalStateException.class, + () -> fileClient.setMetadataWithResponse(null, null, null, Context.NONE)); + assertThrows(IllegalStateException.class, () -> fileClient.uploadRangeFromUrl(1, 0, 0, sourceUrl)); + assertThrows(IllegalStateException.class, () -> fileClient + .uploadRangeFromUrlWithResponse(new ShareFileUploadRangeFromUrlOptions(1, sourceUrl), null, Context.NONE)); + assertThrows(IllegalStateException.class, () -> fileClient.clearRange(1)); + assertThrows(IllegalStateException.class, + () -> fileClient.clearRangeWithResponse(1, 0, null, null, Context.NONE)); + assertThrows(IllegalStateException.class, () -> fileClient.listRanges().iterator().hasNext()); + assertThrows(IllegalStateException.class, + () -> fileClient.listRanges(new ShareFileRange(0, 0L), null, null, Context.NONE).iterator().hasNext()); + assertThrows(IllegalStateException.class, () -> fileClient.listRangesDiff("snapshot")); + assertThrows(IllegalStateException.class, () -> fileClient + .listRangesDiffWithResponse(new ShareFileListRangesDiffOptions("snapshot"), null, Context.NONE)); + assertThrows(IllegalStateException.class, () -> fileClient.listAllRanges().iterator().hasNext()); + assertThrows(IllegalStateException.class, + () -> fileClient.listAllRanges(new ShareFileListRangesOptions(), null, Context.NONE).iterator().hasNext()); + assertThrows(IllegalStateException.class, () -> fileClient.listAllRangesDiff("snapshot").iterator().hasNext()); + assertThrows(IllegalStateException.class, + () -> fileClient.listAllRangesDiff(new ShareFileListRangesDiffOptions("snapshot"), null, Context.NONE) + .iterator() + .hasNext()); + assertThrows(IllegalStateException.class, () -> fileClient.listHandles().iterator().hasNext()); + assertThrows(IllegalStateException.class, + () -> fileClient.listHandles(1, null, Context.NONE).iterator().hasNext()); + assertThrows(IllegalStateException.class, () -> fileClient.forceCloseHandle("handle")); + assertThrows(IllegalStateException.class, + () -> fileClient.forceCloseHandleWithResponse("handle", null, Context.NONE)); + assertThrows(IllegalStateException.class, () -> fileClient.forceCloseAllHandles(null, Context.NONE)); + assertThrows(IllegalStateException.class, + () -> fileClient.renameWithResponse(new ShareFileRenameOptions("destination"), null, Context.NONE)); + assertThrows(IllegalStateException.class, () -> fileClient.createHardLink("target")); + assertThrows(IllegalStateException.class, () -> fileClient + .createHardLinkWithResponse(new ShareFileCreateHardLinkOptions("target"), null, Context.NONE)); + assertThrows(IllegalStateException.class, () -> fileClient.createSymbolicLink("target")); + assertThrows(IllegalStateException.class, () -> fileClient + .createSymbolicLinkWithResponse(new ShareFileCreateSymbolicLinkOptions("target"), null, Context.NONE)); + assertThrows(IllegalStateException.class, fileClient::getSymbolicLink); + assertThrows(IllegalStateException.class, () -> fileClient.getSymbolicLinkWithResponse(null, Context.NONE)); + assertThrows(IllegalStateException.class, () -> fileClient.generateSas(null, null, Context.NONE)); + assertThrows(IllegalStateException.class, () -> fileClient.generateUserDelegationSas(null, null)); + assertThrows(IllegalStateException.class, + () -> fileClient.generateUserDelegationSas(null, null, null, Context.NONE)); + assertThrows(IllegalStateException.class, fileClient::openInputStream); + assertThrows(IllegalStateException.class, () -> fileClient.openInputStream(new ShareFileRange(0, 0L))); + assertThrows(IllegalStateException.class, () -> fileClient.getFileOutputStream(0)); + assertThrows(IllegalStateException.class, () -> fileClient.getFileSeekableByteChannelRead(null)); + assertThrows(IllegalStateException.class, () -> fileClient.getFileSeekableByteChannelWrite(null)); + Assertions.assertFalse(requestSent.get()); + } + + @DoNotRecord + @Test + public void fileIdLeaseBuildersRejectPathOperations() { + AtomicReference requestSent = new AtomicReference<>(false); + HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(request -> { + requestSent.set(true); + return Mono.just(new MockHttpResponse(request, 200)); + }).build(); + + ShareClient testShareClient = new ShareServiceClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .pipeline(pipeline) + .serviceVersion(ShareServiceVersion.V2027_03_07) + .buildClient() + .getShareClient(FileIdTestHelper.SHARE_NAME); + ShareFileClient fileClient = testShareClient.getFileClientByFileId(FileIdTestHelper.FILE_ID); + + IllegalStateException buildException + = assertThrows(IllegalStateException.class, () -> new ShareLeaseClientBuilder().fileClient(fileClient)); + assertEquals("fileClient is not supported for a file-ID-addressed client.", buildException.getMessage()); + assertFalse(requestSent.get()); + } + + @DoNotRecord + @Test + public void asyncFileIdLeaseBuildersRejectPathOperations() { + AtomicReference requestSent = new AtomicReference<>(false); + HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(request -> { + requestSent.set(true); + return Mono.just(new MockHttpResponse(request, 200)); + }).build(); + + ShareAsyncClient testShareClient = new ShareServiceClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .pipeline(pipeline) + .serviceVersion(ShareServiceVersion.V2027_03_07) + .buildAsyncClient() + .getShareAsyncClient(FileIdTestHelper.SHARE_NAME); + ShareFileAsyncClient fileClient = testShareClient.getFileClientByFileId(FileIdTestHelper.FILE_ID); + + IllegalStateException buildException = assertThrows(IllegalStateException.class, + () -> new ShareLeaseClientBuilder().fileAsyncClient(fileClient)); + assertEquals("fileAsyncClient is not supported for a file-ID-addressed client.", buildException.getMessage()); + assertFalse(requestSent.get()); + } + + @DoNotRecord + @Test + public void asyncFileIdClientsRejectPathOperations() { + AtomicReference requestSent = new AtomicReference<>(false); + HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(request -> { + requestSent.set(true); + return Mono.just(new MockHttpResponse(request, 200)); + }).build(); + + ShareAsyncClient testShareClient = new ShareServiceClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .pipeline(pipeline) + .serviceVersion(ShareServiceVersion.V2027_03_07) + .buildAsyncClient() + .getShareAsyncClient(FileIdTestHelper.SHARE_NAME); + ShareFileAsyncClient fileClient = testShareClient.getFileClientByFileId(FileIdTestHelper.FILE_ID); + + IllegalStateException createException + = Assertions.assertThrows(IllegalStateException.class, () -> fileClient.create(1024).block()); + Assertions.assertEquals("create is not supported for a file-ID-addressed client.", + createException.getMessage()); + Assertions.assertThrows(IllegalStateException.class, () -> fileClient.delete().block()); + Assertions.assertThrows(IllegalStateException.class, () -> fileClient.exists().block()); + Assertions.assertThrows(IllegalStateException.class, () -> fileClient.download().blockFirst()); + Assertions.assertThrows(IllegalStateException.class, () -> fileClient.listRanges().blockFirst()); + Assertions.assertThrows(IllegalStateException.class, () -> fileClient.setMetadata(null).block()); + Assertions.assertThrows(IllegalStateException.class, () -> fileClient.rename("destination").block()); + Assertions.assertThrows(IllegalStateException.class, () -> fileClient.forceCloseAllHandles().block()); + StepVerifier.create(fileClient.existsWithResponse()).verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.createWithResponse(new ShareFileCreateOptions(1024))) + .verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.deleteWithResponse(null)).verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.deleteIfExists()).verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.deleteIfExistsWithResponse(null)).verifyError(IllegalStateException.class); + String sourceUrl = FileIdTestHelper.ENDPOINT + "/" + FileIdTestHelper.SHARE_NAME + "/source"; + Assertions.assertThrows(IllegalStateException.class, + () -> fileClient.beginCopy(sourceUrl, Collections.emptyMap(), null)); + Assertions.assertThrows(IllegalStateException.class, + () -> fileClient.beginCopy(sourceUrl, new ShareFileCopyOptions(), null)); + StepVerifier.create(fileClient.abortCopy("copy-id")).verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.abortCopyWithResponse("copy-id", null)).verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.setProperties(1024, null, null, null)).verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.setPropertiesWithResponse(new ShareFileSetPropertiesOptions(1024))) + .verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.setMetadataWithResponse(null, null)).verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.uploadRangeFromUrl(1, 0, 0, sourceUrl)).verifyError(IllegalStateException.class); + StepVerifier + .create(fileClient.uploadRangeFromUrlWithResponse(new ShareFileUploadRangeFromUrlOptions(1, sourceUrl))) + .verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.clearRange(1)).verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.clearRangeWithResponse(1, 0, null)).verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.listRanges(new ShareFileRange(0, 0L), null)) + .verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.listRangesDiff("snapshot")).verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.listRangesDiffWithResponse(new ShareFileListRangesDiffOptions("snapshot"))) + .verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.listAllRanges()).verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.listAllRanges(new ShareFileListRangesOptions())) + .verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.listAllRangesDiff("snapshot")).verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.listAllRangesDiff(new ShareFileListRangesDiffOptions("snapshot"))) + .verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.listHandles()).verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.listHandles(1)).verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.forceCloseHandle("handle")).verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.forceCloseHandleWithResponse("handle")).verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.renameWithResponse(new ShareFileRenameOptions("destination"))) + .verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.createHardLink("target")).verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.createHardLinkWithResponse(new ShareFileCreateHardLinkOptions("target"))) + .verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.createSymbolicLink("target")).verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.createSymbolicLinkWithResponse(new ShareFileCreateSymbolicLinkOptions("target"))) + .verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.getSymbolicLink()).verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.getSymbolicLinkWithResponse()).verifyError(IllegalStateException.class); + Assertions.assertThrows(IllegalStateException.class, () -> fileClient.generateSas(null)); + Assertions.assertThrows(IllegalStateException.class, () -> fileClient.generateSas(null, null, Context.NONE)); + Assertions.assertThrows(IllegalStateException.class, () -> fileClient.generateUserDelegationSas(null, null)); + Assertions.assertThrows(IllegalStateException.class, + () -> fileClient.generateUserDelegationSas(null, null, null, Context.NONE)); + Assertions.assertFalse(requestSent.get()); + } + + @DoNotRecord + @ParameterizedTest + @ValueSource(ints = { -1, 0, 3 }) + public void fileIdTransfersRejectBeforeAccessingData(int fileSize, @TempDir Path directory) throws IOException { + HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(request -> { + throw new AssertionError("File-ID transfers must not send a request."); + }).build(); + ShareFileClient fileClient = new ShareFileClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .shareName(FileIdTestHelper.SHARE_NAME) + .fileId(FileIdTestHelper.FILE_ID) + .pipeline(pipeline) + .buildFileClient(); + InputStream input = new InputStream() { + @Override + public int read() { + throw new AssertionError("Rejected uploads must not read input."); + } + }; + assertThrows(IllegalStateException.class, () -> fileClient.upload(input, 1)); + assertThrows(IllegalStateException.class, () -> fileClient.upload(input, 1, new ParallelTransferOptions())); + assertThrows(IllegalStateException.class, + () -> fileClient.uploadWithResponse(input, 1, 0L, null, null, Context.NONE)); + assertThrows(IllegalStateException.class, + () -> fileClient.uploadWithResponse(new ShareFileUploadOptions(input, 1), null, Context.NONE)); + assertThrows(IllegalStateException.class, () -> fileClient.uploadRange(input, 1)); + assertThrows(IllegalStateException.class, + () -> fileClient.uploadRangeWithResponse(new ShareFileUploadRangeOptions(input, 1), null, Context.NONE)); + ByteArrayOutputStream output = new ByteArrayOutputStream(); + assertThrows(IllegalStateException.class, () -> fileClient.download(output)); + assertThrows(IllegalStateException.class, + () -> fileClient.downloadWithResponse(output, new ShareFileDownloadOptions(), null, Context.NONE)); + assertEquals(0, output.size()); + + Path path = directory.resolve("transfer"); + byte[] content = new byte[Math.max(fileSize, 0)]; + Arrays.fill(content, (byte) 42); + if (fileSize >= 0) { + Files.write(path, content); + } + assertThrows(IllegalStateException.class, () -> fileClient.downloadToFile(path.toString())); + assertThrows(IllegalStateException.class, + () -> fileClient.downloadToFileWithResponse(path.toString(), null, null, null, Context.NONE)); + assertThrows(IllegalStateException.class, () -> fileClient.uploadFromFile(path.toString())); + assertThrows(IllegalStateException.class, () -> fileClient.uploadFromFile(path.toString(), null)); + if (fileSize < 0) { + assertFalse(Files.exists(path)); + } else { + assertArrayEquals(content, Files.readAllBytes(path)); + } + } + + @DoNotRecord + @ParameterizedTest + @ValueSource(ints = { -1, 0, 3 }) + public void asyncFileIdTransfersRejectBeforeAccessingData(int fileSize, @TempDir Path directory) + throws IOException { + HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(request -> { + throw new AssertionError("File-ID transfers must not send a request."); + }).build(); + ShareFileAsyncClient fileClient = new ShareFileClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .shareName(FileIdTestHelper.SHARE_NAME) + .fileId(FileIdTestHelper.FILE_ID) + .pipeline(pipeline) + .buildFileAsyncClient(); + Flux input = Flux.defer(() -> { + throw new AssertionError("Rejected uploads must not subscribe to input."); + }); + StepVerifier.create(fileClient.upload(input, 1)).verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.upload(input, new ParallelTransferOptions())) + .verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.uploadWithResponse(input, 1, 0L, null)).verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.uploadWithResponse(new ShareFileUploadOptions(input))) + .verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.uploadRange(input, 1)).verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.uploadRangeWithResponse(new ShareFileUploadRangeOptions(input, 1))) + .verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.downloadWithResponse(new ShareFileDownloadOptions())) + .verifyError(IllegalStateException.class); + + Path path = directory.resolve("transfer"); + byte[] content = new byte[Math.max(fileSize, 0)]; + Arrays.fill(content, (byte) 42); + if (fileSize >= 0) { + Files.write(path, content); + } + StepVerifier.create(fileClient.downloadToFile(path.toString())).verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.downloadToFileWithResponse(path.toString(), null, null)) + .verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.uploadFromFile(path.toString())).verifyError(IllegalStateException.class); + StepVerifier.create(fileClient.uploadFromFile(path.toString(), null)).verifyError(IllegalStateException.class); + if (fileSize < 0) { + assertFalse(Files.exists(path)); + } else { + assertArrayEquals(content, Files.readAllBytes(path)); + } + } + + @DoNotRecord + @Test + public void directoryFileIdClientsRejectPathOperations() { + HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(request -> { + throw new AssertionError("Path operations must be rejected before sending a request."); + }).build(); + ShareDirectoryClient directoryClient = new ShareServiceClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .pipeline(pipeline) + .buildClient() + .getShareClient(FileIdTestHelper.SHARE_NAME) + .getDirectoryClientByFileId(FileIdTestHelper.FILE_ID); + + IllegalStateException existsException = assertThrows(IllegalStateException.class, directoryClient::exists); + assertEquals("exists is not supported for a file-ID-addressed client.", existsException.getMessage()); + assertThrows(IllegalStateException.class, () -> directoryClient.existsWithResponse(null, Context.NONE)); + IllegalStateException exception + = assertThrows(IllegalStateException.class, () -> directoryClient.getFileClient("child")); + assertEquals("getFileClient is not supported for a file-ID-addressed client.", exception.getMessage()); + assertThrows(IllegalStateException.class, () -> directoryClient.getSubdirectoryClient("child")); + assertThrows(IllegalStateException.class, directoryClient::create); + assertThrows(IllegalStateException.class, directoryClient::createIfNotExists); + assertThrows(IllegalStateException.class, directoryClient::delete); + assertThrows(IllegalStateException.class, directoryClient::deleteIfExists); + assertThrows(IllegalStateException.class, () -> directoryClient.setProperties(null, null)); + assertThrows(IllegalStateException.class, () -> directoryClient.setMetadata(null)); + assertThrows(IllegalStateException.class, () -> directoryClient.listFilesAndDirectories().iterator().hasNext()); + assertThrows(IllegalStateException.class, + () -> directoryClient.listHandles(null, true, null, Context.NONE).iterator().hasNext()); + assertThrows(IllegalStateException.class, () -> directoryClient.forceCloseHandle("handle")); + assertThrows(IllegalStateException.class, () -> directoryClient.forceCloseAllHandles(true, null, Context.NONE)); + assertThrows(IllegalStateException.class, () -> directoryClient.rename("destination")); + assertThrows(IllegalStateException.class, () -> directoryClient.generateSas(null)); + assertThrows(IllegalStateException.class, () -> directoryClient.createSubdirectory("child")); + assertThrows(IllegalStateException.class, () -> directoryClient.createSubdirectoryIfNotExists("child")); + assertThrows(IllegalStateException.class, () -> directoryClient.deleteSubdirectory("child")); + assertThrows(IllegalStateException.class, () -> directoryClient.deleteSubdirectoryIfExists("child")); + assertThrows(IllegalStateException.class, () -> directoryClient.createFile("child", 1024)); + assertThrows(IllegalStateException.class, () -> directoryClient.deleteFile("child")); + assertThrows(IllegalStateException.class, () -> directoryClient.deleteFileIfExists("child")); + } + + @DoNotRecord + @Test + public void asyncDirectoryFileIdClientsRejectPathOperations() { + HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(request -> { + throw new AssertionError("Path operations must be rejected before sending a request."); + }).build(); + ShareDirectoryAsyncClient directoryClient = new ShareServiceClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .pipeline(pipeline) + .buildAsyncClient() + .getShareAsyncClient(FileIdTestHelper.SHARE_NAME) + .getDirectoryClientByFileId(FileIdTestHelper.FILE_ID); + + StepVerifier.create(directoryClient.exists()).verifyErrorSatisfies(error -> { + assertInstanceOf(IllegalStateException.class, error); + assertEquals("exists is not supported for a file-ID-addressed client.", error.getMessage()); + }); + StepVerifier.create(directoryClient.existsWithResponse()).verifyError(IllegalStateException.class); + IllegalStateException exception + = Assertions.assertThrows(IllegalStateException.class, () -> directoryClient.getFileClient("child")); + assertEquals("getFileClient is not supported for a file-ID-addressed client.", exception.getMessage()); + Assertions.assertThrows(IllegalStateException.class, () -> directoryClient.getSubdirectoryClient("child")); + StepVerifier.create(directoryClient.create()).verifyError(IllegalStateException.class); + StepVerifier.create(directoryClient.createIfNotExists()).verifyError(IllegalStateException.class); + StepVerifier.create(directoryClient.delete()).verifyError(IllegalStateException.class); + StepVerifier.create(directoryClient.deleteIfExists()).verifyError(IllegalStateException.class); + StepVerifier.create(directoryClient.setProperties(null, null)).verifyError(IllegalStateException.class); + StepVerifier.create(directoryClient.setMetadata(null)).verifyError(IllegalStateException.class); + StepVerifier.create(directoryClient.listFilesAndDirectories()).verifyError(IllegalStateException.class); + StepVerifier.create(directoryClient.listHandles(null, true)).verifyError(IllegalStateException.class); + StepVerifier.create(directoryClient.forceCloseHandle("handle")).verifyError(IllegalStateException.class); + StepVerifier.create(directoryClient.forceCloseAllHandles(true)).verifyError(IllegalStateException.class); + StepVerifier.create(directoryClient.rename("destination")).verifyError(IllegalStateException.class); + Assertions.assertThrows(IllegalStateException.class, () -> directoryClient.generateSas(null)); + StepVerifier.create(directoryClient.createSubdirectory("child")).verifyError(IllegalStateException.class); + StepVerifier.create(directoryClient.createSubdirectoryIfNotExists("child")) + .verifyError(IllegalStateException.class); + StepVerifier.create(directoryClient.deleteSubdirectory("child")).verifyError(IllegalStateException.class); + StepVerifier.create(directoryClient.deleteSubdirectoryIfExists("child")) + .verifyError(IllegalStateException.class); + StepVerifier.create(directoryClient.createFile("child", 1024)).verifyError(IllegalStateException.class); + StepVerifier.create(directoryClient.deleteFile("child")).verifyError(IllegalStateException.class); + StepVerifier.create(directoryClient.deleteFileIfExists("child")).verifyError(IllegalStateException.class); + } + + @DoNotRecord + @ParameterizedTest + @ValueSource(booleans = { false, true }) + public void directoryClientBuilderRejectsFileId(boolean fromEndpoint) { + AtomicReference requestSent = new AtomicReference<>(false); + HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(request -> { + requestSent.set(true); + return Mono.just(new MockHttpResponse(request, 200)); + }).build(); + + ShareFileClientBuilder builder = new ShareFileClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .shareName(FileIdTestHelper.SHARE_NAME) + .pipeline(pipeline); + if (fromEndpoint) { + builder.endpoint(FileIdTestHelper.ENDPOINT + "/" + FileIdTestHelper.SHARE_NAME + "/directory?fileid=" + + FileIdTestHelper.FILE_ID); + } else { + builder.resourcePath("directory").fileId(FileIdTestHelper.FILE_ID); + } + IllegalStateException exception + = Assertions.assertThrows(IllegalStateException.class, builder::buildDirectoryClient); + Assertions.assertEquals("buildDirectoryClient is not supported for a file-ID-addressed client.", + exception.getMessage()); + Assertions.assertEquals(FileIdTestHelper.FILE_ID, builder.buildFileClient().getFileId()); + Assertions.assertFalse(requestSent.get()); + } + + @DoNotRecord + @ParameterizedTest + @ValueSource(booleans = { false, true }) + public void asyncDirectoryClientBuilderRejectsFileId(boolean fromEndpoint) { + AtomicReference requestSent = new AtomicReference<>(false); + HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(request -> { + requestSent.set(true); + return Mono.just(new MockHttpResponse(request, 200)); + }).build(); + + ShareFileClientBuilder builder = new ShareFileClientBuilder().endpoint(FileIdTestHelper.ENDPOINT) + .shareName(FileIdTestHelper.SHARE_NAME) + .pipeline(pipeline); + if (fromEndpoint) { + builder.endpoint(FileIdTestHelper.ENDPOINT + "/" + FileIdTestHelper.SHARE_NAME + "/directory?fileid=" + + FileIdTestHelper.FILE_ID); + } else { + builder.resourcePath("directory").fileId(FileIdTestHelper.FILE_ID); + } + IllegalStateException exception + = Assertions.assertThrows(IllegalStateException.class, builder::buildDirectoryAsyncClient); + Assertions.assertEquals("buildDirectoryAsyncClient is not supported for a file-ID-addressed client.", + exception.getMessage()); + Assertions.assertEquals(FileIdTestHelper.FILE_ID, builder.buildFileAsyncClient().getFileId()); + Assertions.assertFalse(requestSent.get()); + } +} diff --git a/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileShareTestBase.java b/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileShareTestBase.java index b9ee8dfb621ef..d2e3bb701d34d 100644 --- a/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileShareTestBase.java +++ b/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileShareTestBase.java @@ -92,9 +92,10 @@ public void beforeTest() { super.beforeTest(); prefix = StorageCommonTestUtils.getCrc32(testContextManager.getTestPlaybackRecordingName()); - if (getTestMode() != TestMode.LIVE) { - interceptorManager - .addSanitizers(Arrays.asList(new TestProxySanitizer("sig=(.*)", "REDACTED", TestProxySanitizerType.URL), + if (!testContextManager.doNotRecordTest()) { + if (getTestMode() != TestMode.LIVE) { + interceptorManager.addSanitizers(Arrays.asList( + new TestProxySanitizer("sig=(.*)", "REDACTED", TestProxySanitizerType.URL), new TestProxySanitizer("x-ms-file-rename-source", ".*", "REDACTED", TestProxySanitizerType.HEADER), new TestProxySanitizer("x-ms-copy-source", "sig=(.*)", "REDACTED", TestProxySanitizerType.HEADER), new TestProxySanitizer("x-ms-copy-source-authorization", ".*", "REDACTED", @@ -103,12 +104,11 @@ public void beforeTest() { TestProxySanitizerType.HEADER), new TestProxySanitizer("x-ms-link-text", "((?<=http://|https://)([^/?]+)|sig=(.*))", "REDACTED", TestProxySanitizerType.HEADER))); - } + } - // Ignore changes to the order of query parameters and wholly ignore the 'sv' (service version) query parameter - // in SAS tokens. - interceptorManager - .addMatchers(Collections.singletonList(new CustomMatcher().setComparingBodies(false) + // Ignore changes to the order of query parameters and wholly ignore the 'sv' (service version) query + // parameter in SAS tokens. + interceptorManager.addMatchers(Collections.singletonList(new CustomMatcher().setComparingBodies(false) .setHeadersKeyOnlyMatch(Arrays.asList("x-ms-lease-id", "x-ms-proposed-lease-id", "If-Modified-Since", "If-Unmodified-Since", "x-ms-expiry-time", "x-ms-source-if-modified-since", "x-ms-copy-source", "x-ms-file-rename-source", "x-ms-source-if-unmodified-since", "x-ms-source-lease-id", @@ -117,13 +117,14 @@ public void beforeTest() { .setIgnoredQueryParameters(Arrays.asList("sv")) .setExcludedHeaders(Collections.singletonList("x-ms-meta-testmetadata")))); - ShareServiceClientBuilder builder = getServiceClientBuilder(ENVIRONMENT.getPrimaryAccount()); - primaryFileServiceClient = builder.buildClient(); - primaryFileServiceAsyncClient = builder.buildAsyncClient(); + ShareServiceClientBuilder builder = getServiceClientBuilder(ENVIRONMENT.getPrimaryAccount()); + primaryFileServiceClient = builder.buildClient(); + primaryFileServiceAsyncClient = builder.buildAsyncClient(); - builder = getServiceClientBuilder(ENVIRONMENT.getPremiumFileAccount()); - premiumFileServiceClient = builder.buildClient(); - premiumFileServiceAsyncClient = builder.buildAsyncClient(); + builder = getServiceClientBuilder(ENVIRONMENT.getPremiumFileAccount()); + premiumFileServiceClient = builder.buildClient(); + premiumFileServiceAsyncClient = builder.buildAsyncClient(); + } } /** @@ -132,7 +133,7 @@ public void beforeTest() { @Override protected void afterTest() { super.afterTest(); - if (getTestMode() == TestMode.PLAYBACK) { + if (getTestMode() == TestMode.PLAYBACK || testContextManager.doNotRecordTest()) { return; } diff --git a/sdk/storage/azure-storage-file-share/swagger/README.md b/sdk/storage/azure-storage-file-share/swagger/README.md index aad699d839edc..11280b25bd6c7 100644 --- a/sdk/storage/azure-storage-file-share/swagger/README.md +++ b/sdk/storage/azure-storage-file-share/swagger/README.md @@ -16,7 +16,7 @@ autorest ### Code generation settings ``` yaml use: '@autorest/java@4.1.63' -input-file: https://raw.githubusercontent.com/browndav-msft/azure-rest-api-specs/b6915c1d71519f3e720def065c4710bd96385d25/specification/storage/data-plane/Microsoft.FileStorage/stable/2027-03-07/file.json +input-file: https://raw.githubusercontent.com/browndav-msft/azure-rest-api-specs/672abf88f9686ec82cb07259f782a1b59d891ec5/specification/storage/data-plane/Microsoft.FileStorage/stable/2027-03-07/file.json java: true output-folder: ../ namespace: com.azure.storage.file.share @@ -102,6 +102,30 @@ directive: op.get.responses["200"].headers["x-ms-file-change-time"].format = "date-time"; ``` +### /{shareName} +``` yaml +directive: +- from: swagger-document + where: $["x-ms-paths"] + transform: > + const op = $["/{shareName}"]; + op.head.responses["200"].headers["x-ms-file-creation-time"].format = "date-time"; + op.head.responses["200"].headers["x-ms-file-last-write-time"].format = "date-time"; + op.head.responses["200"].headers["x-ms-file-change-time"].format = "date-time"; +``` + +### /{shareName}?comp=hardlinks +``` yaml +directive: +- from: swagger-document + where: $["x-ms-paths"] + transform: > + const op = $["/{shareName}?comp=hardlinks"]; + op.get.responses["200"].headers["x-ms-file-creation-time"].format = "date-time"; + op.get.responses["200"].headers["x-ms-file-last-write-time"].format = "date-time"; + op.get.responses["200"].headers["x-ms-file-change-time"].format = "date-time"; +``` + ### /{shareName}/{directory}?restype=directory&comp=rename ``` yaml directive: diff --git a/sdk/storage/azure-storage-file-share/swagger/src/main/java/ShareStorageCustomization.java b/sdk/storage/azure-storage-file-share/swagger/src/main/java/ShareStorageCustomization.java index 74f8922fbf89e..29155707f9d22 100644 --- a/sdk/storage/azure-storage-file-share/swagger/src/main/java/ShareStorageCustomization.java +++ b/sdk/storage/azure-storage-file-share/swagger/src/main/java/ShareStorageCustomization.java @@ -2,6 +2,7 @@ // Licensed under the MIT License. import com.azure.autorest.customization.Customization; +import com.azure.autorest.customization.Editor; import com.azure.autorest.customization.LibraryCustomization; import com.azure.autorest.customization.PackageCustomization; import com.github.javaparser.ParseProblemException; @@ -33,6 +34,37 @@ public void customize(LibraryCustomization customization, Logger logger) { + "required RBAC permission."))); updateImplToMapInternalException(customization.getPackage("com.azure.storage.file.share.implementation")); + reusePropertyHeaders(customization.getRawEditor(), "Directories", logger); + reusePropertyHeaders(customization.getRawEditor(), "Files", logger); + } + + private static void reusePropertyHeaders(Editor editor, String operationGroup, Logger logger) { + String implementationPath = "src/main/java/com/azure/storage/file/share/implementation/"; + String headersName = operationGroup + "GetPropertiesHeaders"; + String fileIdHeadersName = operationGroup + "GetPropertiesByFileIdHeaders"; + String headersPath = implementationPath + "models/" + headersName + ".java"; + String fileIdHeadersPath = implementationPath + "models/" + fileIdHeadersName + ".java"; + String clientPath = implementationPath + operationGroup + "Impl.java"; + String headers = editor.getFileContent(headersPath); + String fileIdHeaders = editor.getFileContent(fileIdHeadersPath); + String client = editor.getFileContent(clientPath); + if (headers == null || fileIdHeaders == null || client == null) { + throw new IllegalStateException("Missing generated sources for " + operationGroup + " property headers."); + } + if (!client.contains(fileIdHeadersName)) { + throw new IllegalStateException(clientPath + " no longer references " + fileIdHeadersName + "."); + } + + // Compare the entire model, including parsing, before discarding the operation-specific copy. + if (!headers.replace("\r\n", "\n") + .equals(fileIdHeaders.replace(fileIdHeadersName, headersName).replace("\r\n", "\n"))) { + logger.warn("{} differs from {}; preserving both generated response header models.", + fileIdHeadersName, headersName); + return; + } + + editor.replaceFile(clientPath, client.replace(fileIdHeadersName, headersName)); + editor.removeFile(fileIdHeadersPath); } /**