SetPathAccessControlListRecursive and ForceCloseAllHandles (#2173)

* SetPathAccessControlListRecursive

* ForceCloseAllHandles

* CL
This commit is contained in:
JinmingHu 2021-04-29 15:11:09 +08:00 committed by GitHub
parent df2ffc0577
commit 2aaceb0d3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 641 additions and 7 deletions

View File

@ -2,6 +2,10 @@
## 12.0.0-beta.11 (Unreleased)
### New Features
- Added `DataLakePathClient::SetAccessControlListRecursive()`, `UpdateAccessControlListRecursive()` and `RemoveAccessControlListRecursive()`.
### Breaking Changes
- Renamed `HasMorePages()` in paged response to `HasPage()`.

View File

@ -538,6 +538,42 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
PathAccessConditions AccessConditions;
};
/**
* @brief Optional parameters for DirectoryClient::SetAccessControlListRecursive.
*/
struct SetPathAccessControlListRecursiveOptions
{
/**
* @brief When performing setAccessControlRecursive on a directory, the number of paths that
* are processed with each invocation is limited. If the number of paths to be processed
* exceeds this limit, a continuation token is returned in this response header. When a
* continuation token is returned in the response, it must be specified in a subsequent
* invocation of the setAccessControlRecursive operation to continue the
* setAccessControlRecursive operation on the directory.
*/
Azure::Nullable<std::string> ContinuationToken;
/**
* @brief It specifies the maximum number of files or directories on which the acl change will
* be applied. If omitted or greater than 2,000, the request will process up to 2,000
* items.
*/
Azure::Nullable<int32_t> PageSizeHint;
/**
* @brief Optional. If set to false, the operation will terminate quickly on encountering user
* errors (4XX). If true, the operation will ignore user errors and proceed with the operation
* on other sub-entities of the directory. Continuation token will only be returned when
* ContinueOnFailure is true in case of user errors. If not set the default value is false for
* this.
*/
Azure::Nullable<bool> ContinueOnFailure;
};
using UpdatePathAccessControlListRecursiveOptions = SetPathAccessControlListRecursiveOptions;
using RemovePathAccessControlListRecursiveOptions = SetPathAccessControlListRecursiveOptions;
using CreateFileOptions = CreatePathOptions;
using CreateDirectoryOptions = CreatePathOptions;

View File

@ -220,6 +220,72 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
const SetPathMetadataOptions& options = SetPathMetadataOptions(),
const Azure::Core::Context& context = Azure::Core::Context()) const;
/**
* @brief Sets POSIX access control rights on files and directories under given directory
* recursively.
* @param acls Sets POSIX access control rights on files and directories. Each access control
* entry (ACE) consists of a scope, a type, a user or group identifier, and permissions.
* @param options Optional parameters to set an access control recursively to the resource the
* directory points to.
* @param context Context for cancelling long running operations.
* @return SetPathAccessControlListRecursivePagedResponse containing summary stats of the
* operation.
* @remark This request is sent to dfs endpoint.
*/
SetPathAccessControlListRecursivePagedResponse SetAccessControlListRecursive(
const std::vector<Models::Acl>& acls,
const SetPathAccessControlListRecursiveOptions& options
= SetPathAccessControlListRecursiveOptions(),
const Azure::Core::Context& context = Azure::Core::Context()) const
{
return SetAccessControlListRecursiveInternal(
_detail::PathSetAccessControlRecursiveMode::Set, acls, options, context);
}
/**
* @brief Updates POSIX access control rights on files and directories under given directory
* recursively.
* @param acls Updates POSIX access control rights on files and directories. Each access control
* entry (ACE) consists of a scope, a type, a user or group identifier, and permissions.
* @param options Optional parameters to set an access control recursively to the resource the
* directory points to.
* @param context Context for cancelling long running operations.
* @return UpdatePathAccessControlListRecursivePagedResponse containing summary stats of the
* operation.
* @remark This request is sent to dfs endpoint.
*/
UpdatePathAccessControlListRecursivePagedResponse UpdateAccessControlListRecursive(
const std::vector<Models::Acl>& acls,
const UpdatePathAccessControlListRecursiveOptions& options
= UpdatePathAccessControlListRecursiveOptions(),
const Azure::Core::Context& context = Azure::Core::Context()) const
{
return SetAccessControlListRecursiveInternal(
_detail::PathSetAccessControlRecursiveMode::Modify, acls, options, context);
}
/**
* @brief Removes POSIX access control rights on files and directories under given directory
* recursively.
* @param acls Removes POSIX access control rights on files and directories. Each access control
* entry (ACE) consists of a scope, a type, a user or group identifier, and permissions.
* @param options Optional parameters to set an access control recursively to the resource the
* directory points to.
* @param context Context for cancelling long running operations.
* @return RemovePathAccessControlListRecursivePagedResponse containing summary stats of the
* operation.
* @remark This request is sent to dfs endpoint.
*/
RemovePathAccessControlListRecursivePagedResponse RemoveAccessControlListRecursive(
const std::vector<Models::Acl>& acls,
const RemovePathAccessControlListRecursiveOptions& options
= RemovePathAccessControlListRecursiveOptions(),
const Azure::Core::Context& context = Azure::Core::Context()) const
{
return SetAccessControlListRecursiveInternal(
_detail::PathSetAccessControlRecursiveMode::Remove, acls, options, context);
}
protected:
Azure::Core::Url m_pathUrl;
Blobs::BlobClient m_blobClient;
@ -235,6 +301,12 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
}
private:
SetPathAccessControlListRecursivePagedResponse SetAccessControlListRecursiveInternal(
_detail::PathSetAccessControlRecursiveMode mode,
const std::vector<Models::Acl>& acls,
const SetPathAccessControlListRecursiveOptions& options,
const Azure::Core::Context& context) const;
friend class DataLakeFileSystemClient;
friend class DataLakeLeaseClient;
};

View File

@ -284,4 +284,29 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
friend class PagedResponse<ListPathsPagedResponse>;
};
class SetPathAccessControlListRecursivePagedResponse
: public Azure::Core::PagedResponse<SetPathAccessControlListRecursivePagedResponse> {
public:
int32_t NumberOfSuccessfulDirectories = 0;
int32_t NumberOfSuccessfulFiles = 0;
int32_t NumberOfFailures = 0;
std::vector<Models::AclFailedEntry> FailedEntries;
private:
void OnNextPage(const Azure::Core::Context& context);
std::shared_ptr<DataLakePathClient> m_dataLakePathClient;
SetPathAccessControlListRecursiveOptions m_operationOptions;
std::vector<Models::Acl> m_acls;
_detail::PathSetAccessControlRecursiveMode m_mode;
friend class DataLakePathClient;
friend class PagedResponse<SetPathAccessControlListRecursivePagedResponse>;
};
using UpdatePathAccessControlListRecursivePagedResponse
= SetPathAccessControlListRecursivePagedResponse;
using RemovePathAccessControlListRecursivePagedResponse
= SetPathAccessControlListRecursivePagedResponse;
}}}} // namespace Azure::Storage::Files::DataLake

View File

@ -426,4 +426,36 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
std::move(ret), std::move(result.RawResponse));
}
SetPathAccessControlListRecursivePagedResponse
DataLakePathClient::SetAccessControlListRecursiveInternal(
_detail::PathSetAccessControlRecursiveMode mode,
const std::vector<Models::Acl>& acls,
const SetPathAccessControlListRecursiveOptions& options,
const Azure::Core::Context& context) const
{
_detail::DataLakeRestClient::Path::SetAccessControlRecursiveOptions protocolLayerOptions;
protocolLayerOptions.Mode = mode;
protocolLayerOptions.ContinuationToken = options.ContinuationToken;
protocolLayerOptions.MaxRecords = options.PageSizeHint;
protocolLayerOptions.ForceFlag = options.ContinueOnFailure;
protocolLayerOptions.Acl = Models::Acl::SerializeAcls(acls);
auto response = _detail::DataLakeRestClient::Path::SetAccessControlRecursive(
m_pathUrl, *m_pipeline, context, protocolLayerOptions);
SetPathAccessControlListRecursivePagedResponse pagedResponse;
pagedResponse.NumberOfSuccessfulFiles = response.Value.NumberOfSuccessfulDirectories;
pagedResponse.NumberOfSuccessfulFiles = response.Value.NumberOfSuccessfulFiles;
pagedResponse.NumberOfFailures = response.Value.NumberOfFailures;
pagedResponse.FailedEntries = std::move(response.Value.FailedEntries);
pagedResponse.m_dataLakePathClient = std::make_shared<DataLakePathClient>(*this);
pagedResponse.m_operationOptions = options;
pagedResponse.m_acls = acls;
pagedResponse.m_mode = mode;
pagedResponse.CurrentPageToken = options.ContinuationToken.ValueOr(std::string());
pagedResponse.NextPageToken = response.Value.ContinuationToken;
pagedResponse.RawResponse = std::move(response.RawResponse);
return pagedResponse;
}
}}}} // namespace Azure::Storage::Files::DataLake

View File

@ -92,4 +92,27 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
*this = m_onNextPageFunc(NextPageToken.Value(), context);
}
void SetPathAccessControlListRecursivePagedResponse::OnNextPage(
const Azure::Core::Context& context)
{
m_operationOptions.ContinuationToken = NextPageToken;
if (m_mode == _detail::PathSetAccessControlRecursiveMode::Set)
{
*this = m_dataLakePathClient->SetAccessControlListRecursive(
m_acls, m_operationOptions, context);
}
else if (m_mode == _detail::PathSetAccessControlRecursiveMode::Modify)
{
*this = m_dataLakePathClient->UpdateAccessControlListRecursive(
m_acls, m_operationOptions, context);
}
else if (m_mode == _detail::PathSetAccessControlRecursiveMode::Remove)
{
*this = m_dataLakePathClient->RemoveAccessControlListRecursive(
m_acls, m_operationOptions, context);
}
// Execution is never expected to reach here
std::abort();
}
}}}} // namespace Azure::Storage::Files::DataLake

View File

@ -364,6 +364,274 @@ namespace Azure { namespace Storage { namespace Test {
}
}
TEST_F(DataLakeDirectoryClientTest, DirectoryAccessControlRecursive)
{
// Setup directories.
auto rootDirectoryName = RandomString();
auto directoryName1 = RandomString();
auto directoryName2 = RandomString();
auto rootDirectoryClient = m_fileSystemClient->GetDirectoryClient(rootDirectoryName);
rootDirectoryClient.Create();
auto directoryClient1
= m_fileSystemClient->GetDirectoryClient(rootDirectoryName + "/" + directoryName1);
directoryClient1.Create();
auto directoryClient2
= m_fileSystemClient->GetDirectoryClient(rootDirectoryName + "/" + directoryName2);
directoryClient2.Create();
{
// Set Acls recursive.
std::vector<Files::DataLake::Models::Acl> acls = GetValidAcls();
EXPECT_NO_THROW(rootDirectoryClient.SetAccessControlListRecursive(acls));
std::vector<Files::DataLake::Models::Acl> resultAcls1;
std::vector<Files::DataLake::Models::Acl> resultAcls2;
EXPECT_NO_THROW(resultAcls1 = directoryClient1.GetAccessControlList().Value.Acls);
EXPECT_NO_THROW(resultAcls2 = directoryClient2.GetAccessControlList().Value.Acls);
for (const auto& acl : resultAcls2)
{
auto iter = std::find_if(
resultAcls1.begin(),
resultAcls1.end(),
[&acl](const Files::DataLake::Models::Acl& targetAcl) {
return (targetAcl.Type == acl.Type) && (targetAcl.Id == acl.Id)
&& (targetAcl.Scope == acl.Scope);
});
EXPECT_TRUE(iter != resultAcls1.end());
EXPECT_EQ(iter->Permissions, acl.Permissions);
}
}
{
// Update Acls recursive.
std::vector<Files::DataLake::Models::Acl> originalAcls = GetValidAcls();
Files::DataLake::Models::Acl newAcl;
newAcl.Type = "group";
newAcl.Id = "";
newAcl.Permissions = "rw-";
std::vector<Files::DataLake::Models::Acl> acls;
acls.emplace_back(std::move(newAcl));
EXPECT_NO_THROW(rootDirectoryClient.UpdateAccessControlListRecursive(acls));
std::vector<Files::DataLake::Models::Acl> resultAcls1;
std::vector<Files::DataLake::Models::Acl> resultAcls2;
EXPECT_NO_THROW(resultAcls1 = directoryClient1.GetAccessControlList().Value.Acls);
EXPECT_NO_THROW(resultAcls2 = directoryClient2.GetAccessControlList().Value.Acls);
for (const auto& acl : resultAcls2)
{
auto iter = std::find_if(
resultAcls1.begin(),
resultAcls1.end(),
[&acl](const Files::DataLake::Models::Acl& targetAcl) {
return (targetAcl.Type == acl.Type) && (targetAcl.Id == acl.Id)
&& (targetAcl.Scope == acl.Scope);
});
EXPECT_TRUE(iter != resultAcls1.end());
EXPECT_EQ(iter->Permissions, acl.Permissions);
}
{
// verify group has changed
auto groupFinder = [](const Files::DataLake::Models::Acl& targetAcl) {
return targetAcl.Type == "group";
};
auto iter = std::find_if(resultAcls1.begin(), resultAcls1.end(), groupFinder);
EXPECT_TRUE(iter != resultAcls1.end());
EXPECT_EQ("rw-", iter->Permissions);
iter = std::find_if(resultAcls2.begin(), resultAcls2.end(), groupFinder);
EXPECT_TRUE(iter != resultAcls2.end());
EXPECT_EQ("rw-", iter->Permissions);
}
{
// verify other has not changed
{
auto otherFinder = [](const Files::DataLake::Models::Acl& targetAcl) {
return targetAcl.Type == "other";
};
auto iter = std::find_if(resultAcls1.begin(), resultAcls1.end(), otherFinder);
EXPECT_TRUE(iter != resultAcls1.end());
EXPECT_EQ(originalAcls[3].Permissions, iter->Permissions);
iter = std::find_if(resultAcls2.begin(), resultAcls2.end(), otherFinder);
EXPECT_TRUE(iter != resultAcls2.end());
EXPECT_EQ(originalAcls[3].Permissions, iter->Permissions);
}
{
auto userFinder = [](const Files::DataLake::Models::Acl& targetAcl) {
return targetAcl.Type == "user";
};
auto iter = std::find_if(resultAcls1.begin(), resultAcls1.end(), userFinder);
EXPECT_TRUE(iter != resultAcls1.end());
if (iter->Id == originalAcls[0].Id)
{
EXPECT_EQ(originalAcls[0].Permissions, iter->Permissions);
}
else
{
EXPECT_EQ(originalAcls[1].Permissions, iter->Permissions);
}
iter = std::find_if(resultAcls2.begin(), resultAcls2.end(), userFinder);
EXPECT_TRUE(iter != resultAcls2.end());
if (iter->Id == originalAcls[0].Id)
{
EXPECT_EQ(originalAcls[0].Permissions, iter->Permissions);
}
else
{
EXPECT_EQ(originalAcls[1].Permissions, iter->Permissions);
}
}
}
}
{
// Remove Acls recursive.
std::vector<Files::DataLake::Models::Acl> originalAcls = GetValidAcls();
Files::DataLake::Models::Acl removeAcl;
removeAcl.Type = "user";
removeAcl.Id = "72a3f86f-271f-439e-b031-25678907d381";
std::vector<Files::DataLake::Models::Acl> acls;
acls.emplace_back(std::move(removeAcl));
EXPECT_NO_THROW(rootDirectoryClient.RemoveAccessControlListRecursive(acls));
std::vector<Files::DataLake::Models::Acl> resultAcls1;
std::vector<Files::DataLake::Models::Acl> resultAcls2;
EXPECT_NO_THROW(resultAcls1 = directoryClient1.GetAccessControlList().Value.Acls);
EXPECT_NO_THROW(resultAcls2 = directoryClient2.GetAccessControlList().Value.Acls);
for (const auto& acl : resultAcls2)
{
auto iter = std::find_if(
resultAcls1.begin(),
resultAcls1.end(),
[&acl](const Files::DataLake::Models::Acl& targetAcl) {
return (targetAcl.Type == acl.Type) && (targetAcl.Id == acl.Id)
&& (targetAcl.Scope == acl.Scope);
});
EXPECT_TRUE(iter != resultAcls1.end());
EXPECT_EQ(iter->Permissions, acl.Permissions);
}
{
// verify group policy has been removed.
auto userFinder = [](const Files::DataLake::Models::Acl& targetAcl) {
return targetAcl.Type == "user" && targetAcl.Id == "72a3f86f-271f-439e-b031-25678907d381";
};
auto iter = std::find_if(resultAcls1.begin(), resultAcls1.end(), userFinder);
EXPECT_TRUE(iter == resultAcls1.end());
iter = std::find_if(resultAcls2.begin(), resultAcls2.end(), userFinder);
EXPECT_TRUE(iter == resultAcls2.end());
}
{
// verify other has not changed
{
auto otherFinder = [](const Files::DataLake::Models::Acl& targetAcl) {
return targetAcl.Type == "other";
};
auto iter = std::find_if(resultAcls1.begin(), resultAcls1.end(), otherFinder);
EXPECT_TRUE(iter != resultAcls1.end());
EXPECT_EQ(originalAcls[3].Permissions, iter->Permissions);
iter = std::find_if(resultAcls2.begin(), resultAcls2.end(), otherFinder);
EXPECT_TRUE(iter != resultAcls2.end());
EXPECT_EQ(originalAcls[3].Permissions, iter->Permissions);
}
{
auto userFinder = [](const Files::DataLake::Models::Acl& targetAcl) {
return targetAcl.Type == "user";
};
auto iter = std::find_if(resultAcls1.begin(), resultAcls1.end(), userFinder);
EXPECT_TRUE(iter != resultAcls1.end());
EXPECT_EQ(originalAcls[1].Id, iter->Id);
EXPECT_EQ(originalAcls[1].Permissions, iter->Permissions);
iter = std::find_if(resultAcls2.begin(), resultAcls2.end(), userFinder);
EXPECT_TRUE(iter != resultAcls2.end());
EXPECT_EQ(originalAcls[1].Id, iter->Id);
EXPECT_EQ(originalAcls[1].Permissions, iter->Permissions);
}
}
}
{
// Set Acls recursive, with new set of acls
std::vector<Files::DataLake::Models::Acl> acls;
{
Files::DataLake::Models::Acl newAcl;
newAcl.Type = "user";
newAcl.Permissions = "rw-";
acls.emplace_back(std::move(newAcl));
}
{
Files::DataLake::Models::Acl newAcl;
newAcl.Type = "group";
newAcl.Permissions = "rw-";
acls.emplace_back(std::move(newAcl));
}
{
Files::DataLake::Models::Acl newAcl;
newAcl.Type = "other";
newAcl.Permissions = "rw-";
acls.emplace_back(std::move(newAcl));
}
(rootDirectoryClient.SetAccessControlListRecursive(acls));
std::vector<Files::DataLake::Models::Acl> resultAcls1;
std::vector<Files::DataLake::Models::Acl> resultAcls2;
EXPECT_NO_THROW(resultAcls1 = directoryClient1.GetAccessControlList().Value.Acls);
EXPECT_NO_THROW(resultAcls2 = directoryClient2.GetAccessControlList().Value.Acls);
for (const auto& acl : resultAcls2)
{
auto iter = std::find_if(
resultAcls1.begin(),
resultAcls1.end(),
[&acl](const Files::DataLake::Models::Acl& targetAcl) {
return (targetAcl.Type == acl.Type) && (targetAcl.Id == acl.Id)
&& (targetAcl.Scope == acl.Scope);
});
EXPECT_TRUE(iter != resultAcls1.end());
EXPECT_EQ(iter->Permissions, acl.Permissions);
}
{
// verify group has changed
auto groupFinder = [](const Files::DataLake::Models::Acl& targetAcl) {
return targetAcl.Type == "group";
};
auto iter = std::find_if(resultAcls1.begin(), resultAcls1.end(), groupFinder);
EXPECT_TRUE(iter != resultAcls1.end());
EXPECT_EQ("rw-", iter->Permissions);
EXPECT_EQ("", iter->Id);
iter = std::find_if(resultAcls2.begin(), resultAcls2.end(), groupFinder);
EXPECT_EQ("rw-", iter->Permissions);
EXPECT_EQ("", iter->Id);
}
{
// verify other has changed
auto otherFinder = [](const Files::DataLake::Models::Acl& targetAcl) {
return targetAcl.Type == "other";
};
auto iter = std::find_if(resultAcls1.begin(), resultAcls1.end(), otherFinder);
EXPECT_TRUE(iter != resultAcls1.end());
EXPECT_EQ("rw-", iter->Permissions);
EXPECT_EQ("", iter->Id);
iter = std::find_if(resultAcls2.begin(), resultAcls2.end(), otherFinder);
EXPECT_EQ("rw-", iter->Permissions);
EXPECT_EQ("", iter->Id);
}
{
// verify user has only one entry
std::vector<Files::DataLake::Models::Acl> originalAcls = GetValidAcls();
auto userFinder = [&originalAcls](const Files::DataLake::Models::Acl& targetAcl) {
return targetAcl.Type == "user" && targetAcl.Id == originalAcls[0].Id;
};
auto iter = std::find_if(resultAcls1.begin(), resultAcls1.end(), userFinder);
EXPECT_TRUE(iter == resultAcls1.end());
iter = std::find_if(resultAcls2.begin(), resultAcls2.end(), userFinder);
EXPECT_TRUE(iter == resultAcls2.end());
}
{
// verify user has changed
auto userFinder = [](const Files::DataLake::Models::Acl& targetAcl) {
return targetAcl.Type == "user";
};
auto iter = std::find_if(resultAcls1.begin(), resultAcls1.end(), userFinder);
EXPECT_TRUE(iter != resultAcls1.end());
EXPECT_EQ("rw-", iter->Permissions);
EXPECT_EQ("", iter->Id);
iter = std::find_if(resultAcls2.begin(), resultAcls2.end(), userFinder);
EXPECT_EQ("rw-", iter->Permissions);
EXPECT_EQ("", iter->Id);
}
}
}
TEST_F(DataLakeDirectoryClientTest, ConstructorsWorks)
{
{

View File

@ -2,6 +2,10 @@
## 12.0.0-beta.11 (Unreleased)
### New Features
- Added `ShareDirectoryClient::ForceCloseAllHandles()` and `ShareFileClient::ForceCloseAllHandles()`.
### Breaking Changes
- Renamed `HasMorePages()` in paged response to `HasPage()`.

View File

@ -212,6 +212,19 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
const ForceCloseDirectoryHandleOptions& options = ForceCloseDirectoryHandleOptions(),
const Azure::Core::Context& context = Azure::Core::Context()) const;
/**
* @brief Closes all handles opened on a directory at the service. Optionally supports
* recursively closing handles on subresources.
* @param options Optional parameters to close all this directory's open handles.
* @param context Context for cancelling long running operations.
* @return ForceCloseAllDirectoryHandlesPagedResponse containing the information of the closed
* handles.
*/
ForceCloseAllDirectoryHandlesPagedResponse ForceCloseAllHandles(
const ForceCloseAllDirectoryHandlesOptions& options
= ForceCloseAllDirectoryHandlesOptions(),
const Azure::Core::Context& context = Azure::Core::Context()) const;
private:
Azure::Core::Url m_shareDirectoryUrl;
std::shared_ptr<Azure::Core::Http::_internal::HttpPipeline> m_pipeline;

View File

@ -333,6 +333,17 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
const ForceCloseFileHandleOptions& options = ForceCloseFileHandleOptions(),
const Azure::Core::Context& context = Azure::Core::Context()) const;
/**
* @brief Closes all handles opened on a file at the service.
* @param options Optional parameters to close all this file's open handles.
* @param context Context for cancelling long running operations.
* @return ForceCloseAllFileHandlesPagedResponse containing the information of the closed
* handles.
*/
ForceCloseAllFileHandlesPagedResponse ForceCloseAllHandles(
const ForceCloseAllFileHandlesOptions& options = ForceCloseAllFileHandlesOptions(),
const Azure::Core::Context& context = Azure::Core::Context()) const;
/**
* @brief Upload a range from the source URI to this file's specific range.
* @param destinationOffset Specifies the starting offset for the content to be written.

View File

@ -27,7 +27,8 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
};
/**
* @brief Optional parameters for #Azure::Storage::Files::Shares::ShareServiceClient::ListShares.
* @brief Optional parameters for
* #Azure::Storage::Files::Shares::ShareServiceClient::ListShares.
*/
struct ListSharesOptions
{
@ -40,8 +41,8 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
/**
* A string value that identifies the portion of the list to be returned with the next
* list operation. The operation returns a marker value within the response body if the list
* returned was not complete. The marker value may then be used in a subsequent call to request
* the next set of list items. The marker value is opaque to the client.
* returned was not complete. The marker value may then be used in a subsequent call to
* request the next set of list items. The marker value is opaque to the client.
*/
Azure::Nullable<std::string> ContinuationToken;
@ -259,8 +260,8 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
/**
* A string value that identifies the portion of the list to be returned with the next
* list operation. The operation returns a marker value within the response body if the list
* returned was not complete. The marker value may then be used in a subsequent call to request
* the next set of list items. The marker value is opaque to the client.
* returned was not complete. The marker value may then be used in a subsequent call to
* request the next set of list items. The marker value is opaque to the client.
*/
Azure::Nullable<std::string> ContinuationToken;
@ -281,8 +282,8 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
/**
* A string value that identifies the portion of the list to be returned with the next
* list operation. The operation returns a marker value within the response body if the list
* returned was not complete. The marker value may then be used in a subsequent call to request
* the next set of list items. The marker value is opaque to the client.
* returned was not complete. The marker value may then be used in a subsequent call to
* request the next set of list items. The marker value is opaque to the client.
*/
Azure::Nullable<std::string> ContinuationToken;
@ -308,6 +309,27 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
{
};
/**
* @brief Optional parameters for
* #Azure::Storage::Files::Shares::ShareDirectoryClient::ForceCloseAllHandles.
*/
struct ForceCloseAllDirectoryHandlesOptions
{
/**
* A string value that identifies the portion of the list to be returned with the next
* close operation. The operation returns a marker value within the response body if the force
* close was not complete. The marker value may then be used in a subsequent call to
* close the next handle. The marker value is opaque to the client.
*/
Azure::Nullable<std::string> ContinuationToken;
/**
* @brief Specifies operation should apply to the directory specified in the URI, its files, its
* subdirectories and their files.
*/
Azure::Nullable<bool> Recursive;
};
/**
* @brief Optional parameters for #Azure::Storage::Files::Shares::ShareFileClient::Create.
*/
@ -571,6 +593,21 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
{
};
/**
* @brief Optional parameters for
* #Azure::Storage::Files::Shares::ShareFileClient::ForceCloseAllHandles.
*/
struct ForceCloseAllFileHandlesOptions
{
/**
* A string value that identifies the portion of the list to be returned with the next
* close operation. The operation returns a marker value within the response body if the force
* close was not complete. The marker value may then be used in a subsequent call to
* close the next handle. The marker value is opaque to the client.
*/
Azure::Nullable<std::string> ContinuationToken;
};
/**
* @brief Optional parameters for #Azure::Storage::Files::Shares::ShareFileClient::DownloadTo.
*/

View File

@ -244,6 +244,22 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
friend class PagedResponse<ListFileHandlesPagedResponse>;
};
class ForceCloseAllFileHandlesPagedResponse
: public Azure::Core::PagedResponse<ForceCloseAllFileHandlesPagedResponse> {
public:
int32_t NumberOfHandlesClosed = 0;
int32_t NumberOfHandlesFailedToClose = 0;
private:
void OnNextPage(const Azure::Core::Context& context);
std::shared_ptr<ShareFileClient> m_shareFileClient;
ForceCloseAllFileHandlesOptions m_operationOptions;
friend class ShareFileClient;
friend class PagedResponse<ForceCloseAllFileHandlesPagedResponse>;
};
class ListDirectoryHandlesPagedResponse
: public Azure::Core::PagedResponse<ListDirectoryHandlesPagedResponse> {
public:
@ -259,4 +275,20 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
friend class PagedResponse<ListDirectoryHandlesPagedResponse>;
};
class ForceCloseAllDirectoryHandlesPagedResponse
: public Azure::Core::PagedResponse<ForceCloseAllDirectoryHandlesPagedResponse> {
public:
int32_t NumberOfHandlesClosed = 0;
int32_t NumberOfHandlesFailedToClose = 0;
private:
void OnNextPage(const Azure::Core::Context& context);
std::shared_ptr<ShareDirectoryClient> m_shareDirectoryClient;
ForceCloseAllDirectoryHandlesOptions m_operationOptions;
friend class ShareDirectoryClient;
friend class PagedResponse<ForceCloseAllDirectoryHandlesPagedResponse>;
};
}}}} // namespace Azure::Storage::Files::Shares

View File

@ -361,4 +361,31 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
std::move(ret), std::move(result.RawResponse));
}
ForceCloseAllDirectoryHandlesPagedResponse ShareDirectoryClient::ForceCloseAllHandles(
const ForceCloseAllDirectoryHandlesOptions& options,
const Azure::Core::Context& context) const
{
auto protocolLayerOptions = _detail::ShareRestClient::Directory::ForceCloseHandlesOptions();
protocolLayerOptions.HandleId = FileAllHandles;
if (options.ContinuationToken.HasValue() && !options.ContinuationToken.Value().empty())
{
protocolLayerOptions.ContinuationToken = options.ContinuationToken;
}
protocolLayerOptions.Recursive = options.Recursive;
auto response = _detail::ShareRestClient::Directory::ForceCloseHandles(
m_shareDirectoryUrl, *m_pipeline, context, protocolLayerOptions);
ForceCloseAllDirectoryHandlesPagedResponse pagedResponse;
pagedResponse.NumberOfHandlesClosed = response.Value.NumberOfHandlesClosed;
pagedResponse.NumberOfHandlesFailedToClose = response.Value.NumberOfHandlesFailedToClose;
pagedResponse.m_shareDirectoryClient = std::make_shared<ShareDirectoryClient>(*this);
pagedResponse.m_operationOptions = options;
pagedResponse.CurrentPageToken = options.ContinuationToken.ValueOr(std::string());
pagedResponse.NextPageToken = response.Value.ContinuationToken.ValueOr(std::string());
pagedResponse.RawResponse = std::move(response.RawResponse);
return pagedResponse;
}
}}}} // namespace Azure::Storage::Files::Shares

View File

@ -621,6 +621,32 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
Models::ForceCloseFileHandleResult(), std::move(result.RawResponse));
}
ForceCloseAllFileHandlesPagedResponse ShareFileClient::ForceCloseAllHandles(
const ForceCloseAllFileHandlesOptions& options,
const Azure::Core::Context& context) const
{
auto protocolLayerOptions = _detail::ShareRestClient::File::ForceCloseHandlesOptions();
protocolLayerOptions.HandleId = FileAllHandles;
if (options.ContinuationToken.HasValue() && !options.ContinuationToken.Value().empty())
{
protocolLayerOptions.ContinuationToken = options.ContinuationToken;
}
auto response = _detail::ShareRestClient::File::ForceCloseHandles(
m_shareFileUrl, *m_pipeline, context, protocolLayerOptions);
ForceCloseAllFileHandlesPagedResponse pagedResponse;
pagedResponse.NumberOfHandlesClosed = response.Value.NumberOfHandlesClosed;
pagedResponse.NumberOfHandlesFailedToClose = response.Value.NumberOfHandlesFailedToClose;
pagedResponse.m_shareFileClient = std::make_shared<ShareFileClient>(*this);
pagedResponse.m_operationOptions = options;
pagedResponse.CurrentPageToken = options.ContinuationToken.ValueOr(std::string());
pagedResponse.NextPageToken = response.Value.ContinuationToken.ValueOr(std::string());
pagedResponse.RawResponse = std::move(response.RawResponse);
return pagedResponse;
}
Azure::Response<Models::DownloadFileToResult> ShareFileClient::DownloadTo(
uint8_t* buffer,
std::size_t bufferSize,

View File

@ -79,10 +79,22 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
*this = m_shareFileClient->ListHandles(m_operationOptions, context);
}
void ForceCloseAllFileHandlesPagedResponse::OnNextPage(const Azure::Core::Context& context)
{
m_operationOptions.ContinuationToken = NextPageToken;
*this = m_shareFileClient->ForceCloseAllHandles(m_operationOptions, context);
}
void ListDirectoryHandlesPagedResponse::OnNextPage(const Azure::Core::Context& context)
{
m_operationOptions.ContinuationToken = NextPageToken;
*this = m_shareDirectoryClient->ListHandles(m_operationOptions, context);
}
void ForceCloseAllDirectoryHandlesPagedResponse::OnNextPage(const Azure::Core::Context& context)
{
m_operationOptions.ContinuationToken = NextPageToken;
*this = m_shareDirectoryClient->ForceCloseAllHandles(m_operationOptions, context);
}
}}}} // namespace Azure::Storage::Files::Shares

View File

@ -440,5 +440,10 @@ namespace Azure { namespace Storage { namespace Test {
pageResult.MoveToNextPage())
{
}
EXPECT_NO_THROW(m_fileShareDirectoryClient->ForceCloseAllHandles());
for (auto pageResult = m_fileShareDirectoryClient->ForceCloseAllHandles(); pageResult.HasPage();
pageResult.MoveToNextPage())
{
}
}
}}} // namespace Azure::Storage::Test

View File

@ -268,6 +268,13 @@ namespace Azure { namespace Storage { namespace Test {
pageResult.MoveToNextPage())
{
}
EXPECT_NO_THROW(m_fileClient->ForceCloseAllHandles());
for (auto pageResult = m_fileClient->ForceCloseAllHandles(); pageResult.HasPage();
pageResult.MoveToNextPage())
{
}
}
TEST_F(FileShareFileClientTest, LeaseRelated)