SetPathAccessControlListRecursive and ForceCloseAllHandles (#2173)
* SetPathAccessControlListRecursive * ForceCloseAllHandles * CL
This commit is contained in:
parent
df2ffc0577
commit
2aaceb0d3c
@ -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()`.
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
{
|
||||
{
|
||||
|
||||
@ -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()`.
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
*/
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user