Added 2020-02-10 features. (#692)

* Added 2020-02-10 features.

* Resolved comments.
This commit is contained in:
Kan Tang 2020-10-08 20:05:24 -07:00 committed by GitHub
parent 453545afd6
commit 1b351de2d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 2249 additions and 416 deletions

View File

@ -137,6 +137,27 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
bool Recursive,
const DeleteDirectoryOptions& options = DeleteDirectoryOptions()) const;
/**
* @brief Sets POSIX access control rights on files and directories under given directory
* recursively.
* @param mode Mode PathSetAccessControlRecursiveMode::Set sets POSIX access control rights on
* files and directories, PathSetAccessControlRecursiveMode::Modify modifies one or more POSIX
* access control rights that pre-exist on files and directories,
* PathSetAccessControlRecursiveMode::Remove removes one or more POSIX access control rights
* that were present earlier on files and directories
* @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.
* @return Azure::Core::Response<SetDirectoryAccessControlRecursiveResult>
* @remark This request is sent to dfs endpoint.
*/
Azure::Core::Response<SetDirectoryAccessControlRecursiveResult> SetAccessControlRecursive(
PathSetAccessControlRecursiveMode mode,
std::vector<Acl> acls,
const SetDirectoryAccessControlRecursiveOptions& options
= SetDirectoryAccessControlRecursiveOptions()) const;
private:
explicit DirectoryClient(
Azure::Core::Http::Url dfsUri,

View File

@ -647,6 +647,43 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
PathAccessConditions AccessConditions;
};
/**
* @brief Optional parameters for PathClient::SetAccessControlRecursive
*/
struct SetDirectoryAccessControlRecursiveOptions
{
/**
* @brief Context for cancelling long running operations.
*/
Azure::Core::Context Context;
/**
* @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::Core::Nullable<std::string> Continuation;
/**
* @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::Core::Nullable<int32_t> MaxRecords;
/**
* @brief Optional. Valid for "SetAccessControlRecursive" operation. 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 forceFlag is true in case of user
* errors. If not set the default value is false for this.
*/
Azure::Core::Nullable<bool> ForceFlag;
};
using CreateFileOptions = CreatePathOptions;
using CreateDirectoryOptions = CreatePathOptions;

View File

@ -183,7 +183,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
Azure::Core::Nullable<std::string> Continuation;
};
using DirectorySetAccessControlRecursiveInfo = PathSetAccessControlRecursiveResult;
using SetDirectoryAccessControlRecursiveResult = PathSetAccessControlRecursiveResult;
using CreateDirectoryResult = CreatePathResult;
using DeleteDirectoryResult = PathDeleteResult;

View File

@ -23,7 +23,7 @@
namespace Azure { namespace Storage { namespace Files { namespace DataLake {
namespace Details {
constexpr static const char* c_DefaultServiceApiVersion = "2019-12-12";
constexpr static const char* c_DefaultServiceApiVersion = "2020-02-10";
constexpr static const char* c_PathDnsSuffixDefault = "dfs.core.windows.net";
constexpr static const char* c_QueryFileSystemResource = "resource";
constexpr static const char* c_QueryTimeout = "timeout";
@ -31,6 +31,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
constexpr static const char* c_QueryRecursiveRequired = "recursive";
constexpr static const char* c_QueryContinuation = "continuation";
constexpr static const char* c_QueryPathSetAccessControlRecursiveMode = "mode";
constexpr static const char* c_QueryForceFlag = "forceflag";
constexpr static const char* c_QueryDirectory = "directory";
constexpr static const char* c_QueryPrefix = "prefix";
constexpr static const char* c_QueryMaxResults = "maxresults";
@ -45,6 +46,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
constexpr static const char* c_QueryMaxRecords = "maxrecords";
constexpr static const char* c_QueryPathGetPropertiesAction = "action";
constexpr static const char* c_QueryAction = "action";
constexpr static const char* c_QueryComp = "comp";
constexpr static const char* c_HeaderApiVersionParameter = "x-ms-version";
constexpr static const char* c_HeaderClientRequestId = "x-ms-client-request-id";
constexpr static const char* c_HeaderIfMatch = "if-match";
@ -68,6 +70,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
constexpr static const char* c_HeaderContentType = "x-ms-content-type";
constexpr static const char* c_HeaderTransactionalContentMd5 = "content-md5";
constexpr static const char* c_HeaderContentMd5 = "x-ms-content-md5";
constexpr static const char* c_HeaderContentCrc64 = "x-ms-content-crc64";
constexpr static const char* c_HeaderUmask = "x-ms-umask";
constexpr static const char* c_HeaderPermissions = "x-ms-permissions";
constexpr static const char* c_HeaderRenameSource = "x-ms-rename-source";
@ -75,6 +78,8 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
constexpr static const char* c_HeaderGroup = "x-ms-group";
constexpr static const char* c_HeaderAcl = "x-ms-acl";
constexpr static const char* c_HeaderContentLength = "content-length";
constexpr static const char* c_HeaderPathExpiryOptions = "x-ms-expiry-option";
constexpr static const char* c_HeaderPathExpiryTime = "x-ms-expiry-time";
constexpr static const char* c_HeaderDate = "date";
constexpr static const char* c_HeaderXMsRequestId = "x-ms-request-id";
constexpr static const char* c_HeaderXMsClientRequestId = "x-ms-client-request-id";
@ -103,6 +108,9 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
constexpr static const char* c_HeaderXMsGroup = "x-ms-group";
constexpr static const char* c_HeaderXMsPermissions = "x-ms-permissions";
constexpr static const char* c_HeaderXMsAcl = "x-ms-acl";
constexpr static const char* c_HeaderXMsContentCrc64 = "x-ms-content-crc64";
constexpr static const char* c_HeaderXMsRequestServerEncrypted
= "x-ms-request-server-encrypted";
} // namespace Details
struct DataLakeHttpHeaders
{
@ -159,6 +167,54 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
+ " to PathSetAccessControlRecursiveMode");
}
// Required. Indicates mode of the expiry time
enum class PathExpiryOptions
{
NeverExpire,
RelativeToCreation,
RelativeToNow,
Absolute,
Unknown
};
inline std::string PathExpiryOptionsToString(const PathExpiryOptions& pathExpiryOptions)
{
switch (pathExpiryOptions)
{
case PathExpiryOptions::NeverExpire:
return "NeverExpire";
case PathExpiryOptions::RelativeToCreation:
return "RelativeToCreation";
case PathExpiryOptions::RelativeToNow:
return "RelativeToNow";
case PathExpiryOptions::Absolute:
return "Absolute";
default:
return std::string();
}
}
inline PathExpiryOptions PathExpiryOptionsFromString(const std::string& pathExpiryOptions)
{
if (pathExpiryOptions == "NeverExpire")
{
return PathExpiryOptions::NeverExpire;
}
if (pathExpiryOptions == "RelativeToCreation")
{
return PathExpiryOptions::RelativeToCreation;
}
if (pathExpiryOptions == "RelativeToNow")
{
return PathExpiryOptions::RelativeToNow;
}
if (pathExpiryOptions == "Absolute")
{
return PathExpiryOptions::Absolute;
}
throw std::runtime_error("Cannot convert " + pathExpiryOptions + " to PathExpiryOptions");
}
struct AclFailedEntry
{
std::string Name;
@ -185,9 +241,9 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
static SetAccessControlRecursiveResponse CreateFromJson(const nlohmann::json& node)
{
SetAccessControlRecursiveResponse result;
result.DirectoriesSuccessful = std::stoi(node["directoriesSuccessful"].get<std::string>());
result.FilesSuccessful = std::stoi(node["filesSuccessful"].get<std::string>());
result.FailureCount = std::stoi(node["failureCount"].get<std::string>());
result.DirectoriesSuccessful = node["directoriesSuccessful"].get<int32_t>();
result.FilesSuccessful = node["filesSuccessful"].get<int32_t>();
result.FailureCount = node["failureCount"].get<int32_t>();
for (const auto& element : node["failedEntries"])
{
result.FailedEntries.emplace_back(AclFailedEntry::CreateFromJson(element));
@ -481,7 +537,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
throw std::runtime_error("Cannot convert " + pathLeaseAction + " to PathLeaseAction");
}
// Lease state of the blob.
// Lease state of the resource.
enum class LeaseStateType
{
Available,
@ -536,7 +592,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
throw std::runtime_error("Cannot convert " + leaseStateType + " to LeaseStateType");
}
// The current lease status of the blob.
// The lease status of the resource.
enum class LeaseStatusType
{
Locked,
@ -652,6 +708,8 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
struct FileSystemListPathsResult
{
Azure::Core::Nullable<std::string> ETag;
Azure::Core::Nullable<std::string> LastModified;
Azure::Core::Nullable<std::string> Continuation;
std::vector<Path> Paths;
@ -674,8 +732,8 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
struct PathUpdateResult
{
std::string ETag;
std::string LastModified;
Azure::Core::Nullable<std::string> ETag;
Azure::Core::Nullable<std::string> LastModified;
Azure::Core::Nullable<std::string> AcceptRanges;
DataLakeHttpHeaders HttpHeaders;
int64_t ContentLength = int64_t();
@ -789,6 +847,16 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
struct PathAppendDataResult
{
Azure::Core::Nullable<std::string> ETag;
Azure::Core::Nullable<std::string> ContentMD5;
Azure::Core::Nullable<std::string> XMsContentCrc64;
bool IsServerEncrypted = bool();
};
struct PathSetExpiryResult
{
std::string ETag;
std::string LastModified;
};
class DataLakeRestClient {
@ -1290,6 +1358,15 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
? FileSystemListPathsResult()
: FileSystemListPathsResult::FileSystemListPathsResultFromPathList(
PathList::CreateFromJson(nlohmann::json::parse(bodyBuffer)));
if (response.GetHeaders().find(Details::c_HeaderETag) != response.GetHeaders().end())
{
result.ETag = response.GetHeaders().at(Details::c_HeaderETag);
}
if (response.GetHeaders().find(Details::c_HeaderLastModified)
!= response.GetHeaders().end())
{
result.LastModified = response.GetHeaders().at(Details::c_HeaderLastModified);
}
if (response.GetHeaders().find(Details::c_HeaderXMsContinuation)
!= response.GetHeaders().end())
{
@ -1361,7 +1438,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
// active and matches this ID.
Azure::Core::Nullable<std::string>
SourceLeaseId; // A lease ID for the source path. If specified, the source path must
// have an active lease and the leaase ID must match.
// have an active lease and the lease ID must match.
Azure::Core::Nullable<std::string>
Properties; // Optional. User-defined properties to be stored with the filesystem, in
// the format of a comma-separated list of name and value pairs "n1=v1,
@ -1576,6 +1653,13 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
// modifies one or more POSIX access control rights that pre-exist on files and
// directories, "remove" removes one or more POSIX access control rights that
// were present earlier on files and directories
Azure::Core::Nullable<bool>
ForceFlag; // Optional. Valid for "SetAccessControlRecursive" operation. 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 forceFlag is true in case of user errors. If not set
// the default value is false for this.
Azure::Core::Nullable<int64_t>
Position; // This parameter allows the caller to upload data in parallel and control the
// order in which it is appended to the file. It is required when uploading
@ -1701,6 +1785,11 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
request.GetUrl().AppendQuery(
Details::c_QueryPathSetAccessControlRecursiveMode,
PathSetAccessControlRecursiveModeToString(updateOptions.Mode));
if (updateOptions.ForceFlag.HasValue())
{
request.GetUrl().AppendQuery(
Details::c_QueryForceFlag, (updateOptions.ForceFlag.GetValue() ? "true" : "false"));
}
if (updateOptions.Position.HasValue())
{
request.GetUrl().AppendQuery(
@ -2324,6 +2413,13 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
// modifies one or more POSIX access control rights that pre-exist on files and
// directories, "remove" removes one or more POSIX access control rights that
// were present earlier on files and directories
Azure::Core::Nullable<bool>
ForceFlag; // Optional. Valid for "SetAccessControlRecursive" operation. 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 forceFlag is true in case of user errors. If not set
// the default value is false for this.
Azure::Core::Nullable<int32_t>
MaxRecords; // Optional. It specifies the maximum number of files or directories on
// which the acl change will be applied. If omitted or greater than 2,000,
@ -2365,6 +2461,12 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
request.GetUrl().AppendQuery(
Details::c_QueryPathSetAccessControlRecursiveMode,
PathSetAccessControlRecursiveModeToString(setAccessControlRecursiveOptions.Mode));
if (setAccessControlRecursiveOptions.ForceFlag.HasValue())
{
request.GetUrl().AppendQuery(
Details::c_QueryForceFlag,
(setAccessControlRecursiveOptions.ForceFlag.GetValue() ? "true" : "false"));
}
if (setAccessControlRecursiveOptions.MaxRecords.HasValue())
{
request.GetUrl().AppendQuery(
@ -2585,6 +2687,8 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
Azure::Core::Nullable<std::string>
TransactionalContentMd5; // Specify the transactional md5 for the body, to be validated
// by the service.
Azure::Core::Nullable<std::string> ContentCrc64; // Specify the transactional crc64 for the
// body, to be validated by the service.
Azure::Core::Nullable<std::string>
LeaseIdOptional; // If specified, the operation only succeeds if the resource's lease is
// active and matches this ID.
@ -2629,6 +2733,11 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
Details::c_HeaderTransactionalContentMd5,
appendDataOptions.TransactionalContentMd5.GetValue());
}
if (appendDataOptions.ContentCrc64.HasValue())
{
request.AddHeader(
Details::c_HeaderContentCrc64, appendDataOptions.ContentCrc64.GetValue());
}
if (appendDataOptions.LeaseIdOptional.HasValue())
{
request.AddHeader(
@ -2644,6 +2753,55 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
return AppendDataParseResult(context, pipeline.Send(context, request));
}
struct SetExpiryOptions
{
Azure::Core::Nullable<int32_t>
Timeout; // The timeout parameter is expressed in seconds. For more information, see <a
// href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting
// Timeouts for Blob Service Operations.</a>
std::string ApiVersionParameter
= Details::c_DefaultServiceApiVersion; // Specifies the version of the operation to use
// for this request.
Azure::Core::Nullable<std::string>
ClientRequestId; // 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.
PathExpiryOptions XMsExpiryOption; // Required. Indicates mode of the expiry time
Azure::Core::Nullable<std::string> PathExpiryTime; // The time to set the blob to expiry
};
static Azure::Core::Response<PathSetExpiryResult> SetExpiry(
const Azure::Core::Http::Url& url,
Azure::Core::Http::HttpPipeline& pipeline,
const Azure::Core::Context& context,
const SetExpiryOptions& setExpiryOptions)
{
Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Put, url);
request.AddHeader(Details::c_HeaderContentLength, "0");
request.GetUrl().AppendQuery(Details::c_QueryComp, "expiry");
if (setExpiryOptions.Timeout.HasValue())
{
request.GetUrl().AppendQuery(
Details::c_QueryTimeout, std::to_string(setExpiryOptions.Timeout.GetValue()));
}
request.AddHeader(
Details::c_HeaderApiVersionParameter, setExpiryOptions.ApiVersionParameter);
if (setExpiryOptions.ClientRequestId.HasValue())
{
request.AddHeader(
Details::c_HeaderClientRequestId, setExpiryOptions.ClientRequestId.GetValue());
}
request.AddHeader(
Details::c_HeaderPathExpiryOptions,
PathExpiryOptionsToString(setExpiryOptions.XMsExpiryOption));
if (setExpiryOptions.PathExpiryTime.HasValue())
{
request.AddHeader(
Details::c_HeaderPathExpiryTime, setExpiryOptions.PathExpiryTime.GetValue());
}
return SetExpiryParseResult(context, pipeline.Send(context, request));
}
private:
static Azure::Core::Response<PathCreateResult> CreateParseResult(
const Azure::Core::Context& context,
@ -2698,8 +2856,15 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
: PathUpdateResult::PathUpdateResultFromSetAccessControlRecursiveResponse(
SetAccessControlRecursiveResponse::CreateFromJson(
nlohmann::json::parse(bodyBuffer)));
result.ETag = response.GetHeaders().at(Details::c_HeaderETag);
result.LastModified = response.GetHeaders().at(Details::c_HeaderLastModified);
if (response.GetHeaders().find(Details::c_HeaderETag) != response.GetHeaders().end())
{
result.ETag = response.GetHeaders().at(Details::c_HeaderETag);
}
if (response.GetHeaders().find(Details::c_HeaderLastModified)
!= response.GetHeaders().end())
{
result.LastModified = response.GetHeaders().at(Details::c_HeaderLastModified);
}
if (response.GetHeaders().find(Details::c_HeaderAcceptRanges)
!= response.GetHeaders().end())
{
@ -3182,6 +3347,22 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
{
// Append data to file control response.
PathAppendDataResult result;
if (response.GetHeaders().find(Details::c_HeaderETag) != response.GetHeaders().end())
{
result.ETag = response.GetHeaders().at(Details::c_HeaderETag);
}
if (response.GetHeaders().find(Details::c_HeaderContentMD5)
!= response.GetHeaders().end())
{
result.ContentMD5 = response.GetHeaders().at(Details::c_HeaderContentMD5);
}
if (response.GetHeaders().find(Details::c_HeaderXMsContentCrc64)
!= response.GetHeaders().end())
{
result.XMsContentCrc64 = response.GetHeaders().at(Details::c_HeaderXMsContentCrc64);
}
result.IsServerEncrypted
= response.GetHeaders().at(Details::c_HeaderXMsRequestServerEncrypted) == "true";
return Azure::Core::Response<PathAppendDataResult>(
std::move(result), std::move(responsePtr));
}
@ -3191,6 +3372,27 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
throw Azure::Storage::StorageError::CreateFromResponse(std::move(responsePtr));
}
}
static Azure::Core::Response<PathSetExpiryResult> SetExpiryParseResult(
const Azure::Core::Context& context,
std::unique_ptr<Azure::Core::Http::RawResponse> responsePtr)
{
/* const */ auto& response = *responsePtr;
if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Ok)
{
// The blob expiry was set successfully.
PathSetExpiryResult result;
result.ETag = response.GetHeaders().at(Details::c_HeaderETag);
result.LastModified = response.GetHeaders().at(Details::c_HeaderLastModified);
return Azure::Core::Response<PathSetExpiryResult>(
std::move(result), std::move(responsePtr));
}
else
{
unused(context);
throw Azure::Storage::StorageError::CreateFromResponse(std::move(responsePtr));
}
}
};
}; // class DataLakeRestClient

View File

@ -196,4 +196,21 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
return DataLakeRestClient::Path::Delete(
m_dfsUri, *m_pipeline, options.Context, protocolLayerOptions);
}
Azure::Core::Response<SetDirectoryAccessControlRecursiveResult>
DirectoryClient::SetAccessControlRecursive(
PathSetAccessControlRecursiveMode mode,
std::vector<Acl> acls,
const SetDirectoryAccessControlRecursiveOptions& options) const
{
DataLakeRestClient::Path::SetAccessControlRecursiveOptions protocolLayerOptions;
protocolLayerOptions.Mode = mode;
protocolLayerOptions.Continuation = options.Continuation;
protocolLayerOptions.MaxRecords = options.MaxRecords;
protocolLayerOptions.ForceFlag = options.ForceFlag;
protocolLayerOptions.Acl = Acl::SerializeAcls(acls);
return DataLakeRestClient::Path::SetAccessControlRecursive(
m_dfsUri, *m_pipeline, options.Context, protocolLayerOptions);
}
}}}} // namespace Azure::Storage::Files::DataLake

View File

@ -105,8 +105,8 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
{
auto parsedConnectionString = Azure::Storage::Details::ParseConnectionString(connectionString);
auto fileUri = std::move(parsedConnectionString.DataLakeServiceUri);
fileUri.AppendPath(fileSystemName, true);
fileUri.AppendPath(filePath, true);
fileUri.AppendPath(fileSystemName);
fileUri.AppendPath(filePath);
if (parsedConnectionString.KeyCredential)
{

View File

@ -295,4 +295,42 @@ namespace Azure { namespace Storage { namespace Test {
}
}
TEST_F(DataLakeDirectoryClientTest, DirectorySetAccessControlRecursive)
{
// Setup directories.
auto rootDirectoryName = LowercaseRandomString();
auto directoryName1 = LowercaseRandomString();
auto directoryName2 = LowercaseRandomString();
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/Get Acls recursive works.
std::vector<Files::DataLake::Acl> acls = GetValidAcls();
EXPECT_NO_THROW(directoryClient1.SetAccessControl(acls));
EXPECT_NO_THROW(rootDirectoryClient.SetAccessControlRecursive(
Files::DataLake::PathSetAccessControlRecursiveMode::Modify, acls));
std::vector<Files::DataLake::Acl> resultAcls1;
std::vector<Files::DataLake::Acl> resultAcls2;
EXPECT_NO_THROW(resultAcls1 = directoryClient1.GetAccessControls()->Acls);
EXPECT_NO_THROW(resultAcls2 = directoryClient2.GetAccessControls()->Acls);
for (const auto& acl : resultAcls2)
{
auto iter = std::find_if(
resultAcls1.begin(), resultAcls1.end(), [&acl](const Files::DataLake::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);
}
}
}
}}} // namespace Azure::Storage::Test

View File

@ -218,6 +218,65 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
const ListFilesAndDirectoriesSegmentOptions& options
= ListFilesAndDirectoriesSegmentOptions()) const;
/**
* @brief Acquires a lease on the share.
*
* @param proposedLeaseId Proposed lease ID, in a GUID string format.
* @param duration Specifies the duration of the lease, in seconds, or
* Azure::Storage::c_InfiniteLeaseDuration for a lease that never expires. A non-infinite lease
* can be between 15 and 60 seconds. A lease duration cannot be changed using renew or change.
* @param options Optional parameters to execute this function.
* @return Azure::Core::Response<AcquireShareLeaseResult> describing the lease.
*/
Azure::Core::Response<AcquireShareLeaseResult> AcquireLease(
const std::string& proposedLeaseId,
int32_t duration,
const AcquireShareLeaseOptions& options = AcquireShareLeaseOptions()) const;
/**
* @brief Releases the share's previously-acquired lease.
*
* @param leaseId ID of the previously-acquired lease.
* @param options Optional parameters to execute this function.
* @return Azure::Core::Response<ReleaseShareLeaseResult> describing the updated lease status.
*/
Azure::Core::Response<ReleaseShareLeaseResult> ReleaseLease(
const std::string& leaseId,
const ReleaseShareLeaseOptions& options = ReleaseShareLeaseOptions()) const;
/**
* @brief Changes the lease of an active lease.
*
* @param leaseId ID of the previously-acquired lease.
* @param proposedLeaseId Proposed lease ID, in a GUID string format.
* @param options Optional parameters to execute this function.
* @return Azure::Core::Response<ChangeShareLeaseResult> describing the changed lease.
*/
Azure::Core::Response<ChangeShareLeaseResult> ChangeLease(
const std::string& leaseId,
const std::string& proposedLeaseId,
const ChangeShareLeaseOptions& options = ChangeShareLeaseOptions()) const;
/**
* @brief Breaks the previously-acquired lease.
*
* @param options Optional parameters to execute this function.
* @return Azure::Core::Response<BreakShareLeaseResult> describing the broken lease.
*/
Azure::Core::Response<BreakShareLeaseResult> BreakLease(
const BreakShareLeaseOptions& options = BreakShareLeaseOptions()) const;
/**
* @brief Renew the previously-acquired lease.
*
* @param leaseId ID of the previously-acquired lease.
* @param options Optional parameters to execute this function.
* @return Azure::Core::Response<BreakShareLeaseResult> describing the renewed lease.
*/
Azure::Core::Response<RenewShareLeaseResult> RenewLease(
const std::string& leaseId,
const RenewShareLeaseOptions& options = RenewShareLeaseOptions()) const;
private:
Azure::Core::Http::Url m_shareUri;
std::shared_ptr<Azure::Core::Http::HttpPipeline> m_pipeline;

View File

@ -209,6 +209,70 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
Azure::Core::Context Context;
};
/**
* @brief Optional parameters for ShareClient::AcquireLease.
*/
struct AcquireShareLeaseOptions
{
/**
* @brief Context for cancelling long running operations.
*/
Azure::Core::Context Context;
};
/**
* @brief Optional parameters for ShareClient::ChangeLease.
*/
struct ChangeShareLeaseOptions
{
/**
* @brief Context for cancelling long running operations.
*/
Azure::Core::Context Context;
};
/**
* @brief Optional parameters for ShareClient::ReleaseLease.
*/
struct ReleaseShareLeaseOptions
{
/**
* @brief Context for cancelling long running operations.
*/
Azure::Core::Context Context;
};
/**
* @brief Optional parameters for ShareClient::BreakLease.
*/
struct BreakShareLeaseOptions
{
/**
* @brief Context for cancelling long running operations.
*/
Azure::Core::Context Context;
/**
* @brief Proposed duration the lease should continue before it is broken, in seconds,
* between 0 and 60. This break period is only used if it is shorter than the time remaining on
* the lease. If longer, the time remaining on the lease is used. A new lease will not be
* available before the break period has expired, but the lease may be held for longer than the
* break period.
*/
Azure::Core::Nullable<int32_t> BreakPeriod;
};
/**
* @brief Optional parameters for ShareClient::BreakLease.
*/
struct RenewShareLeaseOptions
{
/**
* @brief Context for cancelling long running operations.
*/
Azure::Core::Context Context;
};
struct CreateDirectoryOptions
{
/**
@ -640,6 +704,12 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
*/
Azure::Core::Nullable<int64_t> Length;
/**
* @brief The previous snapshot parameter is an opaque DateTime value that, when present,
* specifies the previous snapshot.
*/
Azure::Core::Nullable<std::string> PrevShareSnapshot;
/**
* @brief The operation will only succeed if the access condition is met.
*/

View File

@ -27,6 +27,11 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
using CreateSharePermissionResult = ShareCreatePermissionResult;
using GetShareAccessPolicyResult = ShareGetAccessPolicyResult;
using GetSharePermissionResult = ShareGetPermissionResult;
using AcquireShareLeaseResult = ShareAcquireLeaseResult;
using RenewShareLeaseResult = ShareRenewLeaseResult;
using ReleaseShareLeaseResult = ShareReleaseLeaseResult;
using BreakShareLeaseResult = ShareBreakLeaseResult;
using ChangeShareLeaseResult = ShareChangeLeaseResult;
// DirectoryClient models:
using CreateDirectoryResult = DirectoryCreateResult;

View File

@ -274,4 +274,57 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
std::move(ret), result.ExtractRawResponse());
}
Azure::Core::Response<AcquireShareLeaseResult> ShareClient::AcquireLease(
const std::string& proposedLeaseId,
int32_t duration,
const AcquireShareLeaseOptions& options) const
{
ShareRestClient::Share::AcquireLeaseOptions protocolLayerOptions;
protocolLayerOptions.ProposedLeaseIdOptional = proposedLeaseId;
protocolLayerOptions.LeaseDuration = duration;
return ShareRestClient::Share::AcquireLease(
m_shareUri, *m_pipeline, options.Context, protocolLayerOptions);
}
Azure::Core::Response<ChangeShareLeaseResult> ShareClient::ChangeLease(
const std::string& leaseId,
const std::string& proposedLeaseId,
const ChangeShareLeaseOptions& options) const
{
ShareRestClient::Share::ChangeLeaseOptions protocolLayerOptions;
protocolLayerOptions.LeaseIdRequired = leaseId;
protocolLayerOptions.ProposedLeaseIdOptional = proposedLeaseId;
return ShareRestClient::Share::ChangeLease(
m_shareUri, *m_pipeline, options.Context, protocolLayerOptions);
}
Azure::Core::Response<ReleaseShareLeaseResult> ShareClient::ReleaseLease(
const std::string& leaseId,
const ReleaseShareLeaseOptions& options) const
{
ShareRestClient::Share::ReleaseLeaseOptions protocolLayerOptions;
protocolLayerOptions.LeaseIdRequired = leaseId;
return ShareRestClient::Share::ReleaseLease(
m_shareUri, *m_pipeline, options.Context, protocolLayerOptions);
}
Azure::Core::Response<BreakShareLeaseResult> ShareClient::BreakLease(
const BreakShareLeaseOptions& options) const
{
ShareRestClient::Share::BreakLeaseOptions protocolLayerOptions;
protocolLayerOptions.LeaseBreakPeriod = options.BreakPeriod;
return ShareRestClient::Share::BreakLease(
m_shareUri, *m_pipeline, options.Context, protocolLayerOptions);
}
Azure::Core::Response<RenewShareLeaseResult> ShareClient::RenewLease(
const std::string& leaseId,
const RenewShareLeaseOptions& options) const
{
ShareRestClient::Share::RenewLeaseOptions protocolLayerOptions;
protocolLayerOptions.LeaseIdRequired = leaseId;
return ShareRestClient::Share::RenewLease(
m_shareUri, *m_pipeline, options.Context, protocolLayerOptions);
}
}}}} // namespace Azure::Storage::Files::Shares

View File

@ -476,6 +476,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
}
}
protocolLayerOptions.PrevShareSnapshot = options.PrevShareSnapshot;
protocolLayerOptions.LeaseIdOptional = options.AccessConditions.LeaseId;
return ShareRestClient::File::GetRangeList(
m_shareFileUri, *m_pipeline, options.Context, protocolLayerOptions);

View File

@ -31,7 +31,12 @@ namespace Azure { namespace Storage { namespace Test {
m_shareClient->Create();
}
void FileShareClientTest::TearDownTestSuite() { m_shareClient->Delete(); }
void FileShareClientTest::TearDownTestSuite()
{
auto deleteOptions = Files::Shares::DeleteShareOptions();
deleteOptions.IncludeSnapshots = true;
m_shareClient->Delete(deleteOptions);
}
Files::Shares::FileShareHttpHeaders FileShareClientTest::GetInterestingHttpHeaders()
{
@ -182,4 +187,115 @@ namespace Azure { namespace Storage { namespace Test {
auto ret2 = m_shareClient->GetPermission(ret->FilePermissionKey);
EXPECT_EQ(expectedPermission, ret2->Permission);
}
TEST_F(FileShareClientTest, Lease)
{
std::string leaseId1 = CreateUniqueLeaseId();
int32_t leaseDuration = 20;
auto aLease = *m_shareClient->AcquireLease(leaseId1, leaseDuration);
EXPECT_FALSE(aLease.ETag.empty());
EXPECT_FALSE(aLease.LastModified.empty());
EXPECT_EQ(aLease.LeaseId, leaseId1);
aLease = *m_shareClient->AcquireLease(leaseId1, leaseDuration);
EXPECT_FALSE(aLease.ETag.empty());
EXPECT_FALSE(aLease.LastModified.empty());
EXPECT_EQ(aLease.LeaseId, leaseId1);
auto properties = *m_shareClient->GetProperties();
EXPECT_EQ(properties.LeaseState.GetValue(), Files::Shares::LeaseStateType::Leased);
EXPECT_EQ(properties.LeaseStatus.GetValue(), Files::Shares::LeaseStatusType::Locked);
EXPECT_EQ(Files::Shares::LeaseDurationType::Fixed, properties.LeaseDuration.GetValue());
auto rLease = *m_shareClient->RenewLease(leaseId1);
EXPECT_FALSE(rLease.ETag.empty());
EXPECT_FALSE(rLease.LastModified.empty());
EXPECT_EQ(rLease.LeaseId, leaseId1);
std::string leaseId2 = CreateUniqueLeaseId();
EXPECT_NE(leaseId1, leaseId2);
auto cLease = *m_shareClient->ChangeLease(leaseId1, leaseId2);
EXPECT_FALSE(cLease.ETag.empty());
EXPECT_FALSE(cLease.LastModified.empty());
EXPECT_EQ(cLease.LeaseId, leaseId2);
auto blobInfo = *m_shareClient->ReleaseLease(leaseId2);
EXPECT_FALSE(blobInfo.ETag.empty());
EXPECT_FALSE(blobInfo.LastModified.empty());
aLease = *m_shareClient->AcquireLease(CreateUniqueLeaseId(), c_InfiniteLeaseDuration);
properties = *m_shareClient->GetProperties();
EXPECT_EQ(Files::Shares::LeaseDurationType::Infinite, properties.LeaseDuration.GetValue());
auto brokenLease = *m_shareClient->BreakLease();
EXPECT_FALSE(brokenLease.ETag.empty());
EXPECT_FALSE(brokenLease.LastModified.empty());
EXPECT_EQ(brokenLease.LeaseTime, 0);
aLease = *m_shareClient->AcquireLease(CreateUniqueLeaseId(), leaseDuration);
brokenLease = *m_shareClient->BreakLease();
EXPECT_FALSE(brokenLease.ETag.empty());
EXPECT_FALSE(brokenLease.LastModified.empty());
EXPECT_NE(brokenLease.LeaseTime, 0);
Files::Shares::BreakShareLeaseOptions options;
options.BreakPeriod = 0;
m_shareClient->BreakLease(options);
}
TEST_F(FileShareClientTest, SnapshotLease)
{
std::string leaseId1 = CreateUniqueLeaseId();
int32_t leaseDuration = 20;
auto snapshotResult = m_shareClient->CreateSnapshot();
auto shareSnapshot = m_shareClient->WithSnapshot(snapshotResult->Snapshot);
auto aLease = *shareSnapshot.AcquireLease(leaseId1, leaseDuration);
EXPECT_FALSE(aLease.ETag.empty());
EXPECT_FALSE(aLease.LastModified.empty());
EXPECT_EQ(aLease.LeaseId, leaseId1);
aLease = *shareSnapshot.AcquireLease(leaseId1, leaseDuration);
EXPECT_FALSE(aLease.ETag.empty());
EXPECT_FALSE(aLease.LastModified.empty());
EXPECT_EQ(aLease.LeaseId, leaseId1);
auto properties = *shareSnapshot.GetProperties();
EXPECT_EQ(properties.LeaseState.GetValue(), Files::Shares::LeaseStateType::Leased);
EXPECT_EQ(properties.LeaseStatus.GetValue(), Files::Shares::LeaseStatusType::Locked);
EXPECT_EQ(Files::Shares::LeaseDurationType::Fixed, properties.LeaseDuration.GetValue());
auto rLease = *shareSnapshot.RenewLease(leaseId1);
EXPECT_FALSE(rLease.ETag.empty());
EXPECT_FALSE(rLease.LastModified.empty());
EXPECT_EQ(rLease.LeaseId, leaseId1);
std::string leaseId2 = CreateUniqueLeaseId();
EXPECT_NE(leaseId1, leaseId2);
auto cLease = *shareSnapshot.ChangeLease(leaseId1, leaseId2);
EXPECT_FALSE(cLease.ETag.empty());
EXPECT_FALSE(cLease.LastModified.empty());
EXPECT_EQ(cLease.LeaseId, leaseId2);
auto blobInfo = *shareSnapshot.ReleaseLease(leaseId2);
EXPECT_FALSE(blobInfo.ETag.empty());
EXPECT_FALSE(blobInfo.LastModified.empty());
aLease = *shareSnapshot.AcquireLease(CreateUniqueLeaseId(), c_InfiniteLeaseDuration);
properties = *shareSnapshot.GetProperties();
EXPECT_EQ(Files::Shares::LeaseDurationType::Infinite, properties.LeaseDuration.GetValue());
auto brokenLease = *shareSnapshot.BreakLease();
EXPECT_FALSE(brokenLease.ETag.empty());
EXPECT_FALSE(brokenLease.LastModified.empty());
EXPECT_EQ(brokenLease.LeaseTime, 0);
aLease = *shareSnapshot.AcquireLease(CreateUniqueLeaseId(), leaseDuration);
brokenLease = *shareSnapshot.BreakLease();
EXPECT_FALSE(brokenLease.ETag.empty());
EXPECT_FALSE(brokenLease.LastModified.empty());
EXPECT_NE(brokenLease.LeaseTime, 0);
Files::Shares::BreakShareLeaseOptions options;
options.BreakPeriod = 0;
shareSnapshot.BreakLease(options);
EXPECT_THROW(m_shareClient->Delete(), StorageError);
}
}}} // namespace Azure::Storage::Test

View File

@ -615,11 +615,51 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_NO_THROW(fileClient.ClearRange(512, 512));
Files::Shares::GetFileRangeListResult result;
EXPECT_NO_THROW(result = fileClient.GetRangeList().ExtractValue());
EXPECT_EQ(2U, result.RangeList.size());
result.RangeList[0].Start = 0;
result.RangeList[0].End = 511;
result.RangeList[1].Start = 1024;
result.RangeList[1].End = fileSize / 2;
EXPECT_EQ(2U, result.Ranges.size());
EXPECT_EQ(0, result.Ranges[0].Start);
EXPECT_EQ(511, result.Ranges[0].End);
EXPECT_EQ(1024, result.Ranges[1].Start);
EXPECT_EQ(static_cast<int32_t>(fileSize / 2) - 1, result.Ranges[1].End);
}
TEST_F(FileShareFileClientTest, DISABLED_PreviousRangeWithSnapshot)
{
size_t fileSize = 1 * 1024 * 1024;
auto fileContent = RandomBuffer(fileSize);
auto memBodyStream = Core::Http::MemoryBodyStream(fileContent);
auto halfContent
= std::vector<uint8_t>(fileContent.begin(), fileContent.begin() + fileSize / 2);
halfContent.resize(fileSize);
auto fileClient = m_shareClient->GetFileClient(LowercaseRandomString(10));
fileClient.Create(fileSize);
EXPECT_NO_THROW(fileClient.UploadRange(0, &memBodyStream));
EXPECT_NO_THROW(fileClient.ClearRange(fileSize / 2, fileSize / 2));
std::vector<uint8_t> downloadContent(static_cast<std::size_t>(fileSize), '\x00');
EXPECT_NO_THROW(
fileClient.DownloadTo(downloadContent.data(), static_cast<std::size_t>(fileSize)));
EXPECT_EQ(halfContent, downloadContent);
auto snapshot1 = m_shareClient->CreateSnapshot()->Snapshot;
EXPECT_NO_THROW(fileClient.ClearRange(500, 2048));
auto snapshot2 = m_shareClient->CreateSnapshot()->Snapshot;
Files::Shares::GetFileRangeListResult result;
Files::Shares::GetFileRangeListOptions options;
options.PrevShareSnapshot = snapshot1;
EXPECT_NO_THROW(result = fileClient.GetRangeList(options).ExtractValue());
EXPECT_EQ(2U, result.Ranges.size());
EXPECT_EQ(0, result.Ranges[0].Start);
EXPECT_EQ(511, result.Ranges[0].End);
EXPECT_EQ(1024, result.Ranges[1].Start);
EXPECT_EQ(static_cast<int32_t>(fileSize / 2) - 1, result.Ranges[1].End);
EXPECT_NO_THROW(fileClient.ClearRange(3096, 2048));
auto snapshot3 = m_shareClient->CreateSnapshot()->Snapshot;
options.PrevShareSnapshot = snapshot1;
EXPECT_NO_THROW(result = fileClient.GetRangeList(options).ExtractValue());
EXPECT_EQ(2U, result.Ranges.size());
EXPECT_EQ(0, result.Ranges[0].Start);
EXPECT_EQ(511, result.Ranges[0].End);
EXPECT_EQ(1024, result.Ranges[1].Start);
EXPECT_EQ(static_cast<int32_t>(fileSize / 2) - 1, result.Ranges[1].End);
}
}}} // namespace Azure::Storage::Test

View File

@ -186,8 +186,14 @@ namespace Azure { namespace Storage { namespace Test {
downloadedProperties.HourMetrics.RetentionPolicy.Enabled,
properties.HourMetrics.RetentionPolicy.Enabled);
EXPECT_EQ(
downloadedProperties.HourMetrics.RetentionPolicy.Days,
properties.HourMetrics.RetentionPolicy.Days);
downloadedProperties.HourMetrics.RetentionPolicy.Days.HasValue(),
properties.HourMetrics.RetentionPolicy.Days.HasValue());
if (properties.HourMetrics.RetentionPolicy.Days.HasValue())
{
EXPECT_EQ(
downloadedProperties.HourMetrics.RetentionPolicy.Days.GetValue(),
properties.HourMetrics.RetentionPolicy.Days.GetValue());
}
EXPECT_EQ(downloadedProperties.MinuteMetrics.Version, properties.MinuteMetrics.Version);
EXPECT_EQ(downloadedProperties.MinuteMetrics.Enabled, properties.MinuteMetrics.Enabled);
@ -196,8 +202,14 @@ namespace Azure { namespace Storage { namespace Test {
downloadedProperties.MinuteMetrics.RetentionPolicy.Enabled,
properties.MinuteMetrics.RetentionPolicy.Enabled);
EXPECT_EQ(
downloadedProperties.MinuteMetrics.RetentionPolicy.Days,
properties.MinuteMetrics.RetentionPolicy.Days);
downloadedProperties.MinuteMetrics.RetentionPolicy.Days.HasValue(),
properties.MinuteMetrics.RetentionPolicy.Days.HasValue());
if (properties.MinuteMetrics.RetentionPolicy.Days.HasValue())
{
EXPECT_EQ(
downloadedProperties.MinuteMetrics.RetentionPolicy.Days.GetValue(),
properties.MinuteMetrics.RetentionPolicy.Days.GetValue());
}
EXPECT_EQ(downloadedProperties.Cors.size(), properties.Cors.size());
for (const auto& cors : downloadedProperties.Cors)
@ -218,4 +230,99 @@ namespace Azure { namespace Storage { namespace Test {
m_fileShareServiceClient->SetProperties(originalProperties);
}
TEST_F(FileShareServiceClientTest, DISABLED_SetPremiumFileProperties)
{
auto premiumFileShareServiceClient = std::make_shared<Files::Shares::ServiceClient>(
Files::Shares::ServiceClient::CreateFromConnectionString(PremiumFileConnectionString()));
auto properties = *premiumFileShareServiceClient->GetProperties();
auto originalProperties = properties;
properties.HourMetrics.Enabled = true;
properties.HourMetrics.RetentionPolicy.Enabled = true;
properties.HourMetrics.RetentionPolicy.Days = 4;
properties.HourMetrics.IncludeAPIs = true;
properties.MinuteMetrics.Enabled = true;
properties.MinuteMetrics.RetentionPolicy.Enabled = true;
properties.MinuteMetrics.RetentionPolicy.Days = 3;
properties.MinuteMetrics.IncludeAPIs = true;
Files::Shares::CorsRule corsRule;
corsRule.AllowedOrigins = "http://www.example1.com";
corsRule.AllowedMethods = "GET,PUT";
corsRule.AllowedHeaders = "x-ms-header1,x-ms-header2";
corsRule.ExposedHeaders = "x-ms-header3";
corsRule.MaxAgeInSeconds = 10;
properties.Cors.emplace_back(corsRule);
corsRule.AllowedOrigins = "http://www.example2.com";
corsRule.AllowedMethods = "DELETE";
corsRule.AllowedHeaders = "x-ms-header1";
corsRule.ExposedHeaders = "x-ms-header2,x-ms-header3";
corsRule.MaxAgeInSeconds = 20;
properties.Cors.emplace_back(corsRule);
auto protocolSettings = Files::Shares::ShareProtocolSettings();
protocolSettings.Settings.Multichannel.Enabled = true;
properties.Protocol = protocolSettings;
EXPECT_NO_THROW(premiumFileShareServiceClient->SetProperties(properties));
// It takes some time before the new properties comes into effect.
using namespace std::chrono_literals;
std::this_thread::sleep_for(10s);
auto downloadedProperties = *premiumFileShareServiceClient->GetProperties();
EXPECT_EQ(downloadedProperties.HourMetrics.Version, properties.HourMetrics.Version);
EXPECT_EQ(downloadedProperties.HourMetrics.Enabled, properties.HourMetrics.Enabled);
EXPECT_EQ(downloadedProperties.HourMetrics.IncludeAPIs, properties.HourMetrics.IncludeAPIs);
EXPECT_EQ(
downloadedProperties.HourMetrics.RetentionPolicy.Enabled,
properties.HourMetrics.RetentionPolicy.Enabled);
EXPECT_EQ(
downloadedProperties.HourMetrics.RetentionPolicy.Days.HasValue(),
properties.HourMetrics.RetentionPolicy.Days.HasValue());
if (properties.HourMetrics.RetentionPolicy.Days.HasValue())
{
EXPECT_EQ(
downloadedProperties.HourMetrics.RetentionPolicy.Days.GetValue(),
properties.HourMetrics.RetentionPolicy.Days.GetValue());
}
EXPECT_EQ(downloadedProperties.MinuteMetrics.Version, properties.MinuteMetrics.Version);
EXPECT_EQ(downloadedProperties.MinuteMetrics.Enabled, properties.MinuteMetrics.Enabled);
EXPECT_EQ(downloadedProperties.MinuteMetrics.IncludeAPIs, properties.MinuteMetrics.IncludeAPIs);
EXPECT_EQ(
downloadedProperties.MinuteMetrics.RetentionPolicy.Enabled,
properties.MinuteMetrics.RetentionPolicy.Enabled);
EXPECT_EQ(
downloadedProperties.MinuteMetrics.RetentionPolicy.Days.HasValue(),
properties.MinuteMetrics.RetentionPolicy.Days.HasValue());
if (properties.MinuteMetrics.RetentionPolicy.Days.HasValue())
{
EXPECT_EQ(
downloadedProperties.MinuteMetrics.RetentionPolicy.Days.GetValue(),
properties.MinuteMetrics.RetentionPolicy.Days.GetValue());
}
EXPECT_EQ(downloadedProperties.Cors.size(), properties.Cors.size());
for (const auto& cors : downloadedProperties.Cors)
{
auto iter = std::find_if(
properties.Cors.begin(),
properties.Cors.end(),
[&cors](const Files::Shares::CorsRule& rule) {
return rule.AllowedOrigins == cors.AllowedOrigins;
});
EXPECT_EQ(iter->AllowedMethods, cors.AllowedMethods);
EXPECT_EQ(iter->AllowedHeaders, cors.AllowedHeaders);
EXPECT_EQ(iter->ExposedHeaders, cors.ExposedHeaders);
EXPECT_EQ(iter->MaxAgeInSeconds, cors.MaxAgeInSeconds);
EXPECT_NE(properties.Cors.end(), iter);
}
EXPECT_EQ(true, properties.Protocol.GetValue().Settings.Multichannel.Enabled);
premiumFileShareServiceClient->SetProperties(originalProperties);
}
}}} // namespace Azure::Storage::Test