GetBlobServiceProperties and SetBlobServiceProperties (#366)

This commit is contained in:
JinmingHu 2020-07-30 16:39:06 +08:00 committed by GitHub
parent cbec5c6af6
commit b9212922e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 1065 additions and 3 deletions

View File

@ -137,6 +137,28 @@ namespace Azure { namespace Storage { namespace Blobs {
Azure::Core::Context Context;
};
/**
* @brief Optional parameters for BlobServiceClient::SetProperties.
*/
struct SetBlobServicePropertiesOptions
{
/**
* @brief Context for cancelling long running operations.
*/
Azure::Core::Context Context;
};
/**
* @brief Optional parameters for BlobServiceClient::GetProperties.
*/
struct GetBlobServicePropertiesOptions
{
/**
* @brief Context for cancelling long running operations.
*/
Azure::Core::Context Context;
};
/**
* @brief Container client options used to initalize BlobContainerClient.
*/

View File

@ -124,6 +124,34 @@ namespace Azure { namespace Storage { namespace Blobs {
const std::string& expiresOn,
const GetUserDelegationKeyOptions& options = GetUserDelegationKeyOptions()) const;
/**
* @brief Sets properties for a storage accounts Blob service endpoint, including
* properties for Storage Analytics, CORS (Cross-Origin Resource Sharing) rules and soft delete
* settings. You can also use this operation to set the default request version for all incoming
* requests to the Blob service that do not have a version specified.
*
* @param
* properties The blob service properties.
* @param options Optional parameters to execute
* this function.
* @return A SetServicePropertiesInfo on successfully setting the
* properties.
*/
Azure::Core::Response<SetServicePropertiesInfo> SetProperties(
BlobServiceProperties properties,
const SetBlobServicePropertiesOptions& options = SetBlobServicePropertiesOptions()) const;
/**
* @brief Gets the properties of a storage accounts blob service, including properties
* for Storage Analytics and CORS (Cross-Origin Resource Sharing) rules.
*
* @param options Optional parameters to execute this function.
* @return A BlobServiceProperties
* describing the service properties.
*/
Azure::Core::Response<BlobServiceProperties> GetProperties(
const GetBlobServicePropertiesOptions& options = GetBlobServicePropertiesOptions()) const;
protected:
UriBuilder m_serviceUrl;
std::shared_ptr<Azure::Core::Http::HttpPipeline> m_pipeline;

View File

@ -247,6 +247,15 @@ namespace Azure { namespace Storage { namespace Blobs {
Azure::Core::Nullable<std::string> EncryptionKeySHA256;
}; // struct BlobContentInfo
struct BlobCorsRule
{
std::string AllowedOrigins;
std::string AllowedMethods;
std::string AllowedHeaders;
std::string ExposedHeaders;
int32_t MaxAgeInSeconds = 0;
}; // struct BlobCorsRule
struct BlobHttpHeaders
{
std::string ContentType;
@ -354,6 +363,12 @@ namespace Azure { namespace Storage { namespace Blobs {
std::string Name;
}; // struct BlobPrefix
struct BlobRetentionPolicy
{
bool Enabled = false;
Azure::Core::Nullable<int32_t> Days;
}; // struct BlobRetentionPolicy
struct BlobSnapshotInfo
{
std::string Snapshot;
@ -363,6 +378,14 @@ namespace Azure { namespace Storage { namespace Blobs {
Azure::Core::Nullable<std::string> EncryptionKeySHA256;
}; // struct BlobSnapshotInfo
struct BlobStaticWebsite
{
bool Enabled = false;
Azure::Core::Nullable<std::string> IndexDocument;
Azure::Core::Nullable<std::string> DefaultIndexDocumentPath;
Azure::Core::Nullable<std::string> ErrorDocument404Path;
}; // struct BlobStaticWebsite
enum class BlobType
{
Unknown,
@ -824,6 +847,10 @@ namespace Azure { namespace Storage { namespace Blobs {
{
}; // struct SetBlobAccessTierInfo
struct SetServicePropertiesInfo
{
}; // struct SetServicePropertiesInfo
struct UndeleteBlobInfo
{
}; // struct UndeleteBlobInfo
@ -839,6 +866,15 @@ namespace Azure { namespace Storage { namespace Blobs {
std::string Value;
}; // struct UserDelegationKey
struct BlobAnalyticsLogging
{
std::string Version;
bool Delete = false;
bool Read = false;
bool Write = false;
BlobRetentionPolicy RetentionPolicy;
}; // struct BlobAnalyticsLogging
struct BlobBlockListInfo
{
std::string ETag;
@ -925,6 +961,14 @@ namespace Azure { namespace Storage { namespace Blobs {
Azure::Core::Nullable<std::string> EncryptionKeySHA256;
}; // struct BlobItem
struct BlobMetrics
{
std::string Version;
bool Enabled = false;
BlobRetentionPolicy RetentionPolicy;
Azure::Core::Nullable<bool> IncludeApis;
}; // struct BlobMetrics
struct BlobProperties
{
std::string ETag;
@ -952,6 +996,17 @@ namespace Azure { namespace Storage { namespace Blobs {
Azure::Core::Nullable<std::string> CopyCompletionTime;
}; // struct BlobProperties
struct BlobServiceProperties
{
BlobAnalyticsLogging Logging;
BlobMetrics HourMetrics;
BlobMetrics MinuteMetrics;
std::vector<BlobCorsRule> Cors;
std::string DefaultServiceVersion;
BlobRetentionPolicy DeleteRetentionPolicy;
BlobStaticWebsite StaticWebsite;
}; // struct BlobServiceProperties
struct BlobsFlatSegment
{
std::string ServiceEndpoint;
@ -1065,6 +1120,7 @@ namespace Azure { namespace Storage { namespace Blobs {
XmlWriter writer;
GetUserDelegationKeyOptionsToXml(writer, options);
xml_body = writer.GetDocument();
writer.Write(XmlNode{XmlNodeType::End});
}
Azure::Core::Http::MemoryBodyStream xml_body_stream(
reinterpret_cast<const uint8_t*>(xml_body.data()), xml_body.length());
@ -1098,7 +1154,222 @@ namespace Azure { namespace Storage { namespace Blobs {
std::move(response), std::move(pHttpResponse));
}
struct GetPropertiesOptions
{
Azure::Core::Nullable<int32_t> Timeout;
}; // struct GetPropertiesOptions
static Azure::Core::Response<BlobServiceProperties> GetProperties(
Azure::Core::Context context,
Azure::Core::Http::HttpPipeline& pipeline,
const std::string& url,
const GetPropertiesOptions& options)
{
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, url);
request.AddQueryParameter("restype", "service");
request.AddQueryParameter("comp", "properties");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
}
auto pHttpResponse = pipeline.Send(context, request);
Azure::Core::Http::RawResponse& httpResponse = *pHttpResponse;
BlobServiceProperties response;
auto http_status_code
= static_cast<std::underlying_type<Azure::Core::Http::HttpStatusCode>::type>(
httpResponse.GetStatusCode());
if (!(http_status_code == 200))
{
throw StorageError::CreateFromResponse(context, std::move(pHttpResponse));
}
{
const auto& httpResponseBody = httpResponse.GetBody();
XmlReader reader(
reinterpret_cast<const char*>(httpResponseBody.data()), httpResponseBody.size());
response = BlobServicePropertiesFromXml(reader);
}
return Azure::Core::Response<BlobServiceProperties>(
std::move(response), std::move(pHttpResponse));
}
struct SetPropertiesOptions
{
Azure::Core::Nullable<int32_t> Timeout;
BlobServiceProperties Properties;
}; // struct SetPropertiesOptions
static Azure::Core::Response<SetServicePropertiesInfo> SetProperties(
Azure::Core::Context context,
Azure::Core::Http::HttpPipeline& pipeline,
const std::string& url,
const SetPropertiesOptions& options)
{
std::string xml_body;
{
XmlWriter writer;
SetPropertiesOptionsToXml(writer, options);
xml_body = writer.GetDocument();
writer.Write(XmlNode{XmlNodeType::End});
}
Azure::Core::Http::MemoryBodyStream xml_body_stream(
reinterpret_cast<const uint8_t*>(xml_body.data()), xml_body.length());
auto request
= Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Put, url, &xml_body_stream);
request.AddHeader("Content-Length", std::to_string(xml_body_stream.Length()));
request.AddQueryParameter("restype", "service");
request.AddQueryParameter("comp", "properties");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
}
auto pHttpResponse = pipeline.Send(context, request);
Azure::Core::Http::RawResponse& httpResponse = *pHttpResponse;
SetServicePropertiesInfo response;
auto http_status_code
= static_cast<std::underlying_type<Azure::Core::Http::HttpStatusCode>::type>(
httpResponse.GetStatusCode());
if (!(http_status_code == 202))
{
throw StorageError::CreateFromResponse(context, std::move(pHttpResponse));
}
return Azure::Core::Response<SetServicePropertiesInfo>(
std::move(response), std::move(pHttpResponse));
}
private:
static BlobServiceProperties BlobServicePropertiesFromXml(XmlReader& reader)
{
BlobServiceProperties ret;
enum class XmlTagName
{
k_StorageServiceProperties,
k_Logging,
k_HourMetrics,
k_MinuteMetrics,
k_Cors,
k_CorsRule,
k_DefaultServiceVersion,
k_DeleteRetentionPolicy,
k_StaticWebsite,
k_Unknown,
};
std::vector<XmlTagName> path;
while (true)
{
auto node = reader.Read();
if (node.Type == XmlNodeType::End)
{
break;
}
else if (node.Type == XmlNodeType::EndTag)
{
if (path.size() > 0)
{
path.pop_back();
}
else
{
break;
}
}
else if (node.Type == XmlNodeType::StartTag)
{
if (std::strcmp(node.Name, "StorageServiceProperties") == 0)
{
path.emplace_back(XmlTagName::k_StorageServiceProperties);
}
else if (std::strcmp(node.Name, "Logging") == 0)
{
path.emplace_back(XmlTagName::k_Logging);
}
else if (std::strcmp(node.Name, "HourMetrics") == 0)
{
path.emplace_back(XmlTagName::k_HourMetrics);
}
else if (std::strcmp(node.Name, "MinuteMetrics") == 0)
{
path.emplace_back(XmlTagName::k_MinuteMetrics);
}
else if (std::strcmp(node.Name, "Cors") == 0)
{
path.emplace_back(XmlTagName::k_Cors);
}
else if (std::strcmp(node.Name, "CorsRule") == 0)
{
path.emplace_back(XmlTagName::k_CorsRule);
}
else if (std::strcmp(node.Name, "DefaultServiceVersion") == 0)
{
path.emplace_back(XmlTagName::k_DefaultServiceVersion);
}
else if (std::strcmp(node.Name, "DeleteRetentionPolicy") == 0)
{
path.emplace_back(XmlTagName::k_DeleteRetentionPolicy);
}
else if (std::strcmp(node.Name, "StaticWebsite") == 0)
{
path.emplace_back(XmlTagName::k_StaticWebsite);
}
else
{
path.emplace_back(XmlTagName::k_Unknown);
}
if (path.size() == 2 && path[0] == XmlTagName::k_StorageServiceProperties
&& path[1] == XmlTagName::k_Logging)
{
ret.Logging = BlobAnalyticsLoggingFromXml(reader);
path.pop_back();
}
else if (
path.size() == 2 && path[0] == XmlTagName::k_StorageServiceProperties
&& path[1] == XmlTagName::k_HourMetrics)
{
ret.HourMetrics = BlobMetricsFromXml(reader);
path.pop_back();
}
else if (
path.size() == 2 && path[0] == XmlTagName::k_StorageServiceProperties
&& path[1] == XmlTagName::k_MinuteMetrics)
{
ret.MinuteMetrics = BlobMetricsFromXml(reader);
path.pop_back();
}
else if (
path.size() == 3 && path[0] == XmlTagName::k_StorageServiceProperties
&& path[1] == XmlTagName::k_Cors && path[2] == XmlTagName::k_CorsRule)
{
ret.Cors.emplace_back(BlobCorsRuleFromXml(reader));
path.pop_back();
}
else if (
path.size() == 2 && path[0] == XmlTagName::k_StorageServiceProperties
&& path[1] == XmlTagName::k_DeleteRetentionPolicy)
{
ret.DeleteRetentionPolicy = BlobRetentionPolicyFromXml(reader);
path.pop_back();
}
else if (
path.size() == 2 && path[0] == XmlTagName::k_StorageServiceProperties
&& path[1] == XmlTagName::k_StaticWebsite)
{
ret.StaticWebsite = BlobStaticWebsiteFromXml(reader);
path.pop_back();
}
}
else if (node.Type == XmlNodeType::Text)
{
if (path.size() == 2 && path[0] == XmlTagName::k_StorageServiceProperties
&& path[1] == XmlTagName::k_DefaultServiceVersion)
{
ret.DefaultServiceVersion = node.Value;
}
}
}
return ret;
}
static ListContainersSegment ListContainersSegmentFromXml(XmlReader& reader)
{
ListContainersSegment ret;
@ -1321,6 +1592,92 @@ namespace Azure { namespace Storage { namespace Blobs {
return ret;
}
static BlobAnalyticsLogging BlobAnalyticsLoggingFromXml(XmlReader& reader)
{
BlobAnalyticsLogging ret;
enum class XmlTagName
{
k_Version,
k_Delete,
k_Read,
k_Write,
k_RetentionPolicy,
k_Unknown,
};
std::vector<XmlTagName> path;
while (true)
{
auto node = reader.Read();
if (node.Type == XmlNodeType::End)
{
break;
}
else if (node.Type == XmlNodeType::EndTag)
{
if (path.size() > 0)
{
path.pop_back();
}
else
{
break;
}
}
else if (node.Type == XmlNodeType::StartTag)
{
if (std::strcmp(node.Name, "Version") == 0)
{
path.emplace_back(XmlTagName::k_Version);
}
else if (std::strcmp(node.Name, "Delete") == 0)
{
path.emplace_back(XmlTagName::k_Delete);
}
else if (std::strcmp(node.Name, "Read") == 0)
{
path.emplace_back(XmlTagName::k_Read);
}
else if (std::strcmp(node.Name, "Write") == 0)
{
path.emplace_back(XmlTagName::k_Write);
}
else if (std::strcmp(node.Name, "RetentionPolicy") == 0)
{
path.emplace_back(XmlTagName::k_RetentionPolicy);
}
else
{
path.emplace_back(XmlTagName::k_Unknown);
}
if (path.size() == 1 && path[0] == XmlTagName::k_RetentionPolicy)
{
ret.RetentionPolicy = BlobRetentionPolicyFromXml(reader);
path.pop_back();
}
}
else if (node.Type == XmlNodeType::Text)
{
if (path.size() == 1 && path[0] == XmlTagName::k_Version)
{
ret.Version = node.Value;
}
else if (path.size() == 1 && path[0] == XmlTagName::k_Delete)
{
ret.Delete = std::strcmp(node.Value, "true") == 0;
}
else if (path.size() == 1 && path[0] == XmlTagName::k_Read)
{
ret.Read = std::strcmp(node.Value, "true") == 0;
}
else if (path.size() == 1 && path[0] == XmlTagName::k_Write)
{
ret.Write = std::strcmp(node.Value, "true") == 0;
}
}
}
return ret;
}
static BlobContainerItem BlobContainerItemFromXml(XmlReader& reader)
{
BlobContainerItem ret;
@ -1473,6 +1830,302 @@ namespace Azure { namespace Storage { namespace Blobs {
return ret;
}
static BlobCorsRule BlobCorsRuleFromXml(XmlReader& reader)
{
BlobCorsRule ret;
enum class XmlTagName
{
k_AllowedOrigins,
k_AllowedMethods,
k_MaxAgeInSeconds,
k_ExposedHeaders,
k_AllowedHeaders,
k_Unknown,
};
std::vector<XmlTagName> path;
while (true)
{
auto node = reader.Read();
if (node.Type == XmlNodeType::End)
{
break;
}
else if (node.Type == XmlNodeType::EndTag)
{
if (path.size() > 0)
{
path.pop_back();
}
else
{
break;
}
}
else if (node.Type == XmlNodeType::StartTag)
{
if (std::strcmp(node.Name, "AllowedOrigins") == 0)
{
path.emplace_back(XmlTagName::k_AllowedOrigins);
}
else if (std::strcmp(node.Name, "AllowedMethods") == 0)
{
path.emplace_back(XmlTagName::k_AllowedMethods);
}
else if (std::strcmp(node.Name, "MaxAgeInSeconds") == 0)
{
path.emplace_back(XmlTagName::k_MaxAgeInSeconds);
}
else if (std::strcmp(node.Name, "ExposedHeaders") == 0)
{
path.emplace_back(XmlTagName::k_ExposedHeaders);
}
else if (std::strcmp(node.Name, "AllowedHeaders") == 0)
{
path.emplace_back(XmlTagName::k_AllowedHeaders);
}
else
{
path.emplace_back(XmlTagName::k_Unknown);
}
}
else if (node.Type == XmlNodeType::Text)
{
if (path.size() == 1 && path[0] == XmlTagName::k_AllowedOrigins)
{
ret.AllowedOrigins = node.Value;
}
else if (path.size() == 1 && path[0] == XmlTagName::k_AllowedMethods)
{
ret.AllowedMethods = node.Value;
}
else if (path.size() == 1 && path[0] == XmlTagName::k_MaxAgeInSeconds)
{
ret.MaxAgeInSeconds = std::stoi(node.Value);
}
else if (path.size() == 1 && path[0] == XmlTagName::k_ExposedHeaders)
{
ret.ExposedHeaders = node.Value;
}
else if (path.size() == 1 && path[0] == XmlTagName::k_AllowedHeaders)
{
ret.AllowedHeaders = node.Value;
}
}
}
return ret;
}
static BlobMetrics BlobMetricsFromXml(XmlReader& reader)
{
BlobMetrics ret;
enum class XmlTagName
{
k_Version,
k_Enabled,
k_IncludeAPIs,
k_RetentionPolicy,
k_Unknown,
};
std::vector<XmlTagName> path;
while (true)
{
auto node = reader.Read();
if (node.Type == XmlNodeType::End)
{
break;
}
else if (node.Type == XmlNodeType::EndTag)
{
if (path.size() > 0)
{
path.pop_back();
}
else
{
break;
}
}
else if (node.Type == XmlNodeType::StartTag)
{
if (std::strcmp(node.Name, "Version") == 0)
{
path.emplace_back(XmlTagName::k_Version);
}
else if (std::strcmp(node.Name, "Enabled") == 0)
{
path.emplace_back(XmlTagName::k_Enabled);
}
else if (std::strcmp(node.Name, "IncludeAPIs") == 0)
{
path.emplace_back(XmlTagName::k_IncludeAPIs);
}
else if (std::strcmp(node.Name, "RetentionPolicy") == 0)
{
path.emplace_back(XmlTagName::k_RetentionPolicy);
}
else
{
path.emplace_back(XmlTagName::k_Unknown);
}
if (path.size() == 1 && path[0] == XmlTagName::k_RetentionPolicy)
{
ret.RetentionPolicy = BlobRetentionPolicyFromXml(reader);
path.pop_back();
}
}
else if (node.Type == XmlNodeType::Text)
{
if (path.size() == 1 && path[0] == XmlTagName::k_Version)
{
ret.Version = node.Value;
}
else if (path.size() == 1 && path[0] == XmlTagName::k_Enabled)
{
ret.Enabled = std::strcmp(node.Value, "true") == 0;
}
else if (path.size() == 1 && path[0] == XmlTagName::k_IncludeAPIs)
{
ret.IncludeApis = std::strcmp(node.Value, "true") == 0;
}
}
}
return ret;
}
static BlobRetentionPolicy BlobRetentionPolicyFromXml(XmlReader& reader)
{
BlobRetentionPolicy ret;
enum class XmlTagName
{
k_Enabled,
k_Days,
k_Unknown,
};
std::vector<XmlTagName> path;
while (true)
{
auto node = reader.Read();
if (node.Type == XmlNodeType::End)
{
break;
}
else if (node.Type == XmlNodeType::EndTag)
{
if (path.size() > 0)
{
path.pop_back();
}
else
{
break;
}
}
else if (node.Type == XmlNodeType::StartTag)
{
if (std::strcmp(node.Name, "Enabled") == 0)
{
path.emplace_back(XmlTagName::k_Enabled);
}
else if (std::strcmp(node.Name, "Days") == 0)
{
path.emplace_back(XmlTagName::k_Days);
}
else
{
path.emplace_back(XmlTagName::k_Unknown);
}
}
else if (node.Type == XmlNodeType::Text)
{
if (path.size() == 1 && path[0] == XmlTagName::k_Enabled)
{
ret.Enabled = std::strcmp(node.Value, "true") == 0;
}
else if (path.size() == 1 && path[0] == XmlTagName::k_Days)
{
ret.Days = std::stoi(node.Value);
}
}
}
return ret;
}
static BlobStaticWebsite BlobStaticWebsiteFromXml(XmlReader& reader)
{
BlobStaticWebsite ret;
enum class XmlTagName
{
k_Enabled,
k_IndexDocument,
k_DefaultIndexDocumentPath,
k_ErrorDocument404Path,
k_Unknown,
};
std::vector<XmlTagName> path;
while (true)
{
auto node = reader.Read();
if (node.Type == XmlNodeType::End)
{
break;
}
else if (node.Type == XmlNodeType::EndTag)
{
if (path.size() > 0)
{
path.pop_back();
}
else
{
break;
}
}
else if (node.Type == XmlNodeType::StartTag)
{
if (std::strcmp(node.Name, "Enabled") == 0)
{
path.emplace_back(XmlTagName::k_Enabled);
}
else if (std::strcmp(node.Name, "IndexDocument") == 0)
{
path.emplace_back(XmlTagName::k_IndexDocument);
}
else if (std::strcmp(node.Name, "DefaultIndexDocumentPath") == 0)
{
path.emplace_back(XmlTagName::k_DefaultIndexDocumentPath);
}
else if (std::strcmp(node.Name, "ErrorDocument404Path") == 0)
{
path.emplace_back(XmlTagName::k_ErrorDocument404Path);
}
else
{
path.emplace_back(XmlTagName::k_Unknown);
}
}
else if (node.Type == XmlNodeType::Text)
{
if (path.size() == 1 && path[0] == XmlTagName::k_Enabled)
{
ret.Enabled = std::strcmp(node.Value, "true") == 0;
}
else if (path.size() == 1 && path[0] == XmlTagName::k_IndexDocument)
{
ret.IndexDocument = node.Value;
}
else if (path.size() == 1 && path[0] == XmlTagName::k_DefaultIndexDocumentPath)
{
ret.DefaultIndexDocumentPath = node.Value;
}
else if (path.size() == 1 && path[0] == XmlTagName::k_ErrorDocument404Path)
{
ret.ErrorDocument404Path = node.Value;
}
}
}
return ret;
}
static std::map<std::string, std::string> MetadataFromXml(XmlReader& reader)
{
std::map<std::string, std::string> ret;
@ -1519,7 +2172,146 @@ namespace Azure { namespace Storage { namespace Blobs {
writer.Write(XmlNode{XmlNodeType::Text, nullptr, options.ExpiresOn.data()});
writer.Write(XmlNode{XmlNodeType::EndTag});
writer.Write(XmlNode{XmlNodeType::EndTag});
writer.Write(XmlNode{XmlNodeType::End});
}
static void SetPropertiesOptionsToXml(XmlWriter& writer, const SetPropertiesOptions& options)
{
writer.Write(XmlNode{XmlNodeType::StartTag, "StorageServiceProperties"});
BlobServicePropertiesToXml(writer, options.Properties);
writer.Write(XmlNode{XmlNodeType::EndTag});
}
static void BlobServicePropertiesToXml(
XmlWriter& writer,
const BlobServiceProperties& options)
{
writer.Write(XmlNode{XmlNodeType::StartTag, "Logging"});
BlobAnalyticsLoggingToXml(writer, options.Logging);
writer.Write(XmlNode{XmlNodeType::EndTag});
writer.Write(XmlNode{XmlNodeType::StartTag, "HourMetrics"});
BlobMetricsToXml(writer, options.HourMetrics);
writer.Write(XmlNode{XmlNodeType::EndTag});
writer.Write(XmlNode{XmlNodeType::StartTag, "MinuteMetrics"});
BlobMetricsToXml(writer, options.MinuteMetrics);
writer.Write(XmlNode{XmlNodeType::EndTag});
writer.Write(XmlNode{XmlNodeType::StartTag, "Cors"});
for (const auto& i : options.Cors)
{
BlobCorsRuleToXml(writer, i);
}
writer.Write(XmlNode{XmlNodeType::EndTag});
writer.Write(XmlNode{XmlNodeType::StartTag, "DefaultServiceVersion"});
writer.Write(XmlNode{XmlNodeType::Text, nullptr, options.DefaultServiceVersion.data()});
writer.Write(XmlNode{XmlNodeType::EndTag});
writer.Write(XmlNode{XmlNodeType::StartTag, "DeleteRetentionPolicy"});
BlobRetentionPolicyToXml(writer, options.DeleteRetentionPolicy);
writer.Write(XmlNode{XmlNodeType::EndTag});
writer.Write(XmlNode{XmlNodeType::StartTag, "StaticWebsite"});
BlobStaticWebsiteToXml(writer, options.StaticWebsite);
writer.Write(XmlNode{XmlNodeType::EndTag});
}
static void BlobAnalyticsLoggingToXml(XmlWriter& writer, const BlobAnalyticsLogging& options)
{
writer.Write(XmlNode{XmlNodeType::StartTag, "Version"});
writer.Write(XmlNode{XmlNodeType::Text, nullptr, options.Version.data()});
writer.Write(XmlNode{XmlNodeType::EndTag});
writer.Write(XmlNode{XmlNodeType::StartTag, "Delete"});
writer.Write(XmlNode{XmlNodeType::Text, nullptr, options.Delete ? "true" : "false"});
writer.Write(XmlNode{XmlNodeType::EndTag});
writer.Write(XmlNode{XmlNodeType::StartTag, "Read"});
writer.Write(XmlNode{XmlNodeType::Text, nullptr, options.Read ? "true" : "false"});
writer.Write(XmlNode{XmlNodeType::EndTag});
writer.Write(XmlNode{XmlNodeType::StartTag, "Write"});
writer.Write(XmlNode{XmlNodeType::Text, nullptr, options.Write ? "true" : "false"});
writer.Write(XmlNode{XmlNodeType::EndTag});
writer.Write(XmlNode{XmlNodeType::StartTag, "RetentionPolicy"});
BlobRetentionPolicyToXml(writer, options.RetentionPolicy);
writer.Write(XmlNode{XmlNodeType::EndTag});
}
static void BlobCorsRuleToXml(XmlWriter& writer, const BlobCorsRule& options)
{
writer.Write(XmlNode{XmlNodeType::StartTag, "CorsRule"});
writer.Write(XmlNode{XmlNodeType::StartTag, "AllowedOrigins"});
writer.Write(XmlNode{XmlNodeType::Text, nullptr, options.AllowedOrigins.data()});
writer.Write(XmlNode{XmlNodeType::EndTag});
writer.Write(XmlNode{XmlNodeType::StartTag, "AllowedMethods"});
writer.Write(XmlNode{XmlNodeType::Text, nullptr, options.AllowedMethods.data()});
writer.Write(XmlNode{XmlNodeType::EndTag});
writer.Write(XmlNode{XmlNodeType::StartTag, "AllowedHeaders"});
writer.Write(XmlNode{XmlNodeType::Text, nullptr, options.AllowedHeaders.data()});
writer.Write(XmlNode{XmlNodeType::EndTag});
writer.Write(XmlNode{XmlNodeType::StartTag, "ExposedHeaders"});
writer.Write(XmlNode{XmlNodeType::Text, nullptr, options.ExposedHeaders.data()});
writer.Write(XmlNode{XmlNodeType::EndTag});
writer.Write(XmlNode{XmlNodeType::StartTag, "MaxAgeInSeconds"});
writer.Write(
XmlNode{XmlNodeType::Text, nullptr, std::to_string(options.MaxAgeInSeconds).data()});
writer.Write(XmlNode{XmlNodeType::EndTag});
writer.Write(XmlNode{XmlNodeType::EndTag});
}
static void BlobMetricsToXml(XmlWriter& writer, const BlobMetrics& options)
{
writer.Write(XmlNode{XmlNodeType::StartTag, "Version"});
writer.Write(XmlNode{XmlNodeType::Text, nullptr, options.Version.data()});
writer.Write(XmlNode{XmlNodeType::EndTag});
writer.Write(XmlNode{XmlNodeType::StartTag, "Enabled"});
writer.Write(XmlNode{XmlNodeType::Text, nullptr, options.Enabled ? "true" : "false"});
writer.Write(XmlNode{XmlNodeType::EndTag});
if (options.IncludeApis.HasValue())
{
writer.Write(XmlNode{XmlNodeType::StartTag, "IncludeAPIs"});
writer.Write(XmlNode{
XmlNodeType::Text, nullptr, options.IncludeApis.GetValue() ? "true" : "false"});
writer.Write(XmlNode{XmlNodeType::EndTag});
}
writer.Write(XmlNode{XmlNodeType::StartTag, "RetentionPolicy"});
BlobRetentionPolicyToXml(writer, options.RetentionPolicy);
writer.Write(XmlNode{XmlNodeType::EndTag});
}
static void BlobRetentionPolicyToXml(XmlWriter& writer, const BlobRetentionPolicy& options)
{
writer.Write(XmlNode{XmlNodeType::StartTag, "Enabled"});
writer.Write(XmlNode{XmlNodeType::Text, nullptr, options.Enabled ? "true" : "false"});
writer.Write(XmlNode{XmlNodeType::EndTag});
if (options.Days.HasValue())
{
writer.Write(XmlNode{XmlNodeType::StartTag, "Days"});
writer.Write(
XmlNode{XmlNodeType::Text, nullptr, std::to_string(options.Days.GetValue()).data()});
writer.Write(XmlNode{XmlNodeType::EndTag});
}
}
static void BlobStaticWebsiteToXml(XmlWriter& writer, const BlobStaticWebsite& options)
{
writer.Write(XmlNode{XmlNodeType::StartTag, "Enabled"});
writer.Write(XmlNode{XmlNodeType::Text, nullptr, options.Enabled ? "true" : "false"});
writer.Write(XmlNode{XmlNodeType::EndTag});
if (options.IndexDocument.HasValue())
{
writer.Write(XmlNode{XmlNodeType::StartTag, "IndexDocument"});
writer.Write(
XmlNode{XmlNodeType::Text, nullptr, options.IndexDocument.GetValue().data()});
writer.Write(XmlNode{XmlNodeType::EndTag});
}
if (options.DefaultIndexDocumentPath.HasValue())
{
writer.Write(XmlNode{XmlNodeType::StartTag, "DefaultIndexDocumentPath"});
writer.Write(XmlNode{
XmlNodeType::Text, nullptr, options.DefaultIndexDocumentPath.GetValue().data()});
writer.Write(XmlNode{XmlNodeType::EndTag});
}
if (options.ErrorDocument404Path.HasValue())
{
writer.Write(XmlNode{XmlNodeType::StartTag, "ErrorDocument404Path"});
writer.Write(
XmlNode{XmlNodeType::Text, nullptr, options.ErrorDocument404Path.GetValue().data()});
writer.Write(XmlNode{XmlNodeType::EndTag});
}
}
}; // class Service
@ -2582,6 +3374,7 @@ namespace Azure { namespace Storage { namespace Blobs {
{
throw StorageError::CreateFromResponse(context, std::move(pHttpResponse));
}
response.BodyStream = httpResponse.GetBodyStream();
response.ETag = httpResponse.GetHeaders().at("etag");
response.LastModified = httpResponse.GetHeaders().at("last-modified");
auto response_content_md5_iterator = httpResponse.GetHeaders().find("content-md5");
@ -2686,7 +3479,6 @@ namespace Azure { namespace Storage { namespace Blobs {
= std::stoll(response_committed_block_count_iterator->second);
}
response.BlobType = BlobTypeFromString(httpResponse.GetHeaders().at("x-ms-blob-type"));
response.BodyStream = httpResponse.GetBodyStream();
return Azure::Core::Response<BlobDownloadResponse>(
std::move(response), std::move(pHttpResponse));
}
@ -3881,6 +4673,7 @@ namespace Azure { namespace Storage { namespace Blobs {
XmlWriter writer;
CommitBlockListOptionsToXml(writer, options);
xml_body = writer.GetDocument();
writer.Write(XmlNode{XmlNodeType::End});
}
Azure::Core::Http::MemoryBodyStream xml_body_stream(
reinterpret_cast<const uint8_t*>(xml_body.data()), xml_body.length());
@ -4194,7 +4987,6 @@ namespace Azure { namespace Storage { namespace Blobs {
XmlNode{XmlNodeType::StartTag, BlockTypeToString(i.first).data(), i.second.data()});
}
writer.Write(XmlNode{XmlNodeType::EndTag});
writer.Write(XmlNode{XmlNodeType::End});
}
}; // class BlockBlob

View File

@ -141,4 +141,22 @@ namespace Azure { namespace Storage { namespace Blobs {
options.Context, *m_pipeline, m_serviceUrl.ToString(), protocolLayerOptions);
}
Azure::Core::Response<SetServicePropertiesInfo> BlobServiceClient::SetProperties(
BlobServiceProperties properties,
const SetBlobServicePropertiesOptions& options) const
{
BlobRestClient::Service::SetPropertiesOptions protocolLayerOptions;
protocolLayerOptions.Properties = std::move(properties);
return BlobRestClient::Service::SetProperties(
options.Context, *m_pipeline, m_serviceUrl.ToString(), protocolLayerOptions);
}
Azure::Core::Response<BlobServiceProperties> BlobServiceClient::GetProperties(
const GetBlobServicePropertiesOptions& options) const
{
BlobRestClient::Service::GetPropertiesOptions protocolLayerOptions;
return BlobRestClient::Service::GetProperties(
options.Context, *m_pipeline, m_serviceUrl.ToString(), protocolLayerOptions);
}
}}} // namespace Azure::Storage::Blobs

View File

@ -4,6 +4,74 @@
#include "blobs/blob.hpp"
#include "test_base.hpp"
#include <thread>
namespace Azure { namespace Storage { namespace Blobs {
bool operator==(
const Azure::Storage::Blobs::BlobRetentionPolicy& lhs,
const Azure::Storage::Blobs::BlobRetentionPolicy& rhs)
{
if (lhs.Enabled != rhs.Enabled)
{
return false;
}
if (lhs.Days.HasValue() != rhs.Days.HasValue())
{
return false;
}
if (lhs.Days.HasValue() && rhs.Days.HasValue() && lhs.Days.GetValue() != rhs.Days.GetValue())
{
return false;
}
return true;
}
bool operator==(
const Azure::Storage::Blobs::BlobCorsRule& lhs,
const Azure::Storage::Blobs::BlobCorsRule& rhs)
{
return lhs.AllowedHeaders == rhs.AllowedHeaders && lhs.AllowedMethods == rhs.AllowedMethods
&& lhs.AllowedOrigins == rhs.AllowedOrigins && lhs.ExposedHeaders == rhs.ExposedHeaders
&& lhs.MaxAgeInSeconds == rhs.MaxAgeInSeconds;
}
bool operator==(
const Azure::Storage::Blobs::BlobStaticWebsite& lhs,
const Azure::Storage::Blobs::BlobStaticWebsite& rhs)
{
if (lhs.Enabled != rhs.Enabled)
{
return false;
}
if (lhs.DefaultIndexDocumentPath.HasValue() != rhs.DefaultIndexDocumentPath.HasValue())
{
return false;
}
if (lhs.DefaultIndexDocumentPath.HasValue() && rhs.DefaultIndexDocumentPath.HasValue()
&& lhs.DefaultIndexDocumentPath.GetValue() != rhs.DefaultIndexDocumentPath.GetValue())
{
return false;
}
if (lhs.DefaultIndexDocumentPath.HasValue() != rhs.DefaultIndexDocumentPath.HasValue())
{
return false;
}
if (lhs.ErrorDocument404Path.HasValue() && rhs.ErrorDocument404Path.HasValue()
&& lhs.ErrorDocument404Path.GetValue() != rhs.ErrorDocument404Path.GetValue())
{
return false;
}
if (lhs.IndexDocument.HasValue() && rhs.IndexDocument.HasValue()
&& lhs.IndexDocument.GetValue() != rhs.IndexDocument.GetValue())
{
return false;
}
return true;
}
}}} // namespace Azure::Storage::Blobs
namespace Azure { namespace Storage { namespace Test {
class BlobServiceClientTest : public ::testing::Test {
@ -95,4 +163,138 @@ namespace Azure { namespace Storage { namespace Test {
}
}
TEST_F(BlobServiceClientTest, GetProperties)
{
auto ret = m_blobServiceClient.GetProperties();
auto properties = *ret;
auto logging = properties.Logging;
EXPECT_FALSE(logging.Version.empty());
if (logging.RetentionPolicy.Enabled)
{
EXPECT_TRUE(logging.RetentionPolicy.Days.HasValue());
}
auto hourMetrics = properties.HourMetrics;
if (hourMetrics.Enabled)
{
EXPECT_FALSE(hourMetrics.Version.empty());
if (hourMetrics.RetentionPolicy.Enabled)
{
EXPECT_TRUE(hourMetrics.RetentionPolicy.Days.HasValue());
}
}
auto minuteMetrics = properties.HourMetrics;
if (minuteMetrics.Enabled)
{
EXPECT_FALSE(minuteMetrics.Version.empty());
if (minuteMetrics.RetentionPolicy.Enabled)
{
EXPECT_TRUE(minuteMetrics.RetentionPolicy.Days.HasValue());
}
}
EXPECT_FALSE(properties.DefaultServiceVersion.empty());
auto deleteRetentionPolicy = properties.DeleteRetentionPolicy;
if (deleteRetentionPolicy.Enabled)
{
EXPECT_TRUE(deleteRetentionPolicy.Days.HasValue());
}
}
TEST_F(BlobServiceClientTest, DISABLED_SetProperties)
{
Blobs::BlobServiceProperties properties = *m_blobServiceClient.GetProperties();
auto originalProperties = properties;
properties.Logging.Delete = !properties.Logging.Delete;
properties.Logging.Read = !properties.Logging.Read;
properties.Logging.Write = !properties.Logging.Write;
properties.Logging.RetentionPolicy.Enabled = true;
properties.Logging.RetentionPolicy.Days = 3;
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 = 4;
properties.MinuteMetrics.IncludeApis = true;
properties.DefaultServiceVersion = Blobs::c_APIVersion;
properties.StaticWebsite.Enabled = true;
properties.StaticWebsite.IndexDocument = "index.html";
properties.StaticWebsite.ErrorDocument404Path = "404.html";
properties.StaticWebsite.DefaultIndexDocumentPath.Reset();
Blobs::BlobCorsRule 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);
properties.DeleteRetentionPolicy.Enabled = true;
properties.DeleteRetentionPolicy.Days = 5;
EXPECT_NO_THROW(m_blobServiceClient.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 = *m_blobServiceClient.GetProperties();
EXPECT_EQ(downloadedProperties.Logging.Version, properties.Logging.Version);
EXPECT_EQ(downloadedProperties.Logging.Delete, properties.Logging.Delete);
EXPECT_EQ(downloadedProperties.Logging.Read, properties.Logging.Read);
EXPECT_EQ(downloadedProperties.Logging.Write, properties.Logging.Write);
EXPECT_EQ(downloadedProperties.Logging.RetentionPolicy, properties.Logging.RetentionPolicy);
EXPECT_EQ(downloadedProperties.HourMetrics.Version, properties.HourMetrics.Version);
EXPECT_EQ(downloadedProperties.HourMetrics.Enabled, properties.HourMetrics.Enabled);
EXPECT_EQ(
downloadedProperties.HourMetrics.IncludeApis.HasValue(),
properties.HourMetrics.IncludeApis.HasValue());
if (downloadedProperties.HourMetrics.IncludeApis.HasValue()
== properties.HourMetrics.IncludeApis.HasValue())
{
EXPECT_EQ(
downloadedProperties.HourMetrics.IncludeApis.GetValue(),
properties.HourMetrics.IncludeApis.GetValue());
}
EXPECT_EQ(
downloadedProperties.HourMetrics.RetentionPolicy, properties.HourMetrics.RetentionPolicy);
EXPECT_EQ(downloadedProperties.MinuteMetrics.Version, properties.MinuteMetrics.Version);
EXPECT_EQ(downloadedProperties.MinuteMetrics.Enabled, properties.MinuteMetrics.Enabled);
EXPECT_EQ(
downloadedProperties.MinuteMetrics.IncludeApis.HasValue(),
properties.MinuteMetrics.IncludeApis.HasValue());
if (downloadedProperties.MinuteMetrics.IncludeApis.HasValue()
== properties.MinuteMetrics.IncludeApis.HasValue())
{
EXPECT_EQ(
downloadedProperties.MinuteMetrics.IncludeApis.GetValue(),
properties.MinuteMetrics.IncludeApis.GetValue());
}
EXPECT_EQ(
downloadedProperties.MinuteMetrics.RetentionPolicy,
properties.MinuteMetrics.RetentionPolicy);
EXPECT_EQ(downloadedProperties.DefaultServiceVersion, properties.DefaultServiceVersion);
EXPECT_EQ(downloadedProperties.Cors, properties.Cors);
EXPECT_EQ(downloadedProperties.StaticWebsite, properties.StaticWebsite);
EXPECT_EQ(downloadedProperties.DeleteRetentionPolicy, properties.DeleteRetentionPolicy);
m_blobServiceClient.SetProperties(originalProperties);
}
}}} // namespace Azure::Storage::Test