diff --git a/sdk/storage/azure-storage-files-shares/CHANGELOG.md b/sdk/storage/azure-storage-files-shares/CHANGELOG.md index 78b320033..210be1e91 100644 --- a/sdk/storage/azure-storage-files-shares/CHANGELOG.md +++ b/sdk/storage/azure-storage-files-shares/CHANGELOG.md @@ -2,6 +2,11 @@ ## 12.0.0-beta.7 (Unreleased) +### New Features + +- Added support for `SetProperties` in share client. This API supports update share tier and adjusting share's quota. +- Added support to get share's tier status in `ListSharesSinglePage` and `GetProperties`. + ### Breaking Changes - Removed `GetDirectoryClient` and `GetFileClient` from `ShareClient`. `ShareDirectoryClient` and `ShareFileClient` now initializes with the name of the resource, not path, to indicate that no path parsing is done for the API @@ -18,6 +23,7 @@ - Removed `c_` for constants: `c_FileDefaultTimeValue`, `c_FileCopySourceTime`, `c_FileInheritPermission`, `FilePreserveSmbProperties` and `FileAllHandles`. - `Concurrency`, `ChunkSize` and `InitialChunkSize` were moved into `DownloadShareFileToOptions::TansferOptions`. - `Concurrency`, `ChunkSize` and `SingleUploadThreshold` were moved into `UploadShareFileFromOptions::TransferOptions`. +- Removed `SetQuota` related API, result and options. The functionality is moved into `SetProperties`. ### Other Changes and Improvements diff --git a/sdk/storage/azure-storage-files-shares/inc/azure/storage/files/shares/protocol/share_rest_client.hpp b/sdk/storage/azure-storage-files-shares/inc/azure/storage/files/shares/protocol/share_rest_client.hpp index 7c1861246..9135a9702 100644 --- a/sdk/storage/azure-storage-files-shares/inc/azure/storage/files/shares/protocol/share_rest_client.hpp +++ b/sdk/storage/azure-storage-files-shares/inc/azure/storage/files/shares/protocol/share_rest_client.hpp @@ -39,6 +39,24 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { Storage::ContentHash ContentHash; }; + // Specifies the access tier of the share. + class ShareAccessTier { + public: + ShareAccessTier() = default; + explicit ShareAccessTier(std::string value) : m_value(std::move(value)) {} + bool operator==(const ShareAccessTier& other) const { return m_value == other.m_value; } + bool operator!=(const ShareAccessTier& other) const { return !(*this == other); } + const std::string& Get() const { return m_value; } + + AZ_STORAGE_FILES_SHARES_DLLEXPORT const static ShareAccessTier TransactionOptimized; + AZ_STORAGE_FILES_SHARES_DLLEXPORT const static ShareAccessTier Hot; + AZ_STORAGE_FILES_SHARES_DLLEXPORT const static ShareAccessTier Cool; + AZ_STORAGE_FILES_SHARES_DLLEXPORT const static ShareAccessTier Premium; + + private: + std::string m_value; + }; // extensible enum ShareAccessTier + // Specifies the option to copy file security descriptor from source file or to set it using the // value which is defined by the header value of x-ms-file-permission or // x-ms-file-permission-key. @@ -227,6 +245,9 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { Azure::Core::Nullable NextAllowedQuotaDowngradeTime; Azure::Core::Nullable DeletedOn; int32_t RemainingRetentionDays = int32_t(); + Azure::Core::Nullable AccessTier; // The access tier of the share. + Azure::Core::Nullable AccessTierChangeTime; + Azure::Core::Nullable AccessTierTransitionState; LeaseStatusType LeaseStatus; LeaseStateType LeaseState; LeaseDurationType LeaseDuration; @@ -467,6 +488,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { constexpr static const char* QueryRestype = "restype"; constexpr static const char* QueryComp = "comp"; constexpr static const char* HeaderVersion = "x-ms-version"; + constexpr static const char* HeaderAccessTier = "x-ms-access-tier"; constexpr static const char* HeaderContentLength = "content-length"; constexpr static const char* HeaderContentHashMd5 = "content-md5"; constexpr static const char* HeaderCopyActionAbortConstant = "x-ms-copy-action"; @@ -519,6 +541,9 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { constexpr static const char* HeaderLeaseDuration = "x-ms-lease-duration"; constexpr static const char* HeaderLeaseState = "x-ms-lease-state"; constexpr static const char* HeaderLeaseStatus = "x-ms-lease-status"; + constexpr static const char* HeaderAccessTierChangeTime = "x-ms-access-tier-change-time"; + constexpr static const char* HeaderAccessTierTransitionState + = "x-ms-access-tier-transition-state"; constexpr static const char* HeaderLeaseTime = "x-ms-lease-time"; constexpr static const char* HeaderClientRequestId = "x-ms-client-request-id"; constexpr static const char* HeaderAction = "x-ms-lease-action"; @@ -634,6 +659,9 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { Azure::Core::Nullable LeaseDuration; Azure::Core::Nullable LeaseState; Azure::Core::Nullable LeaseStatus; + Azure::Core::Nullable AccessTier; + Azure::Core::Nullable AccessTierChangeTime; + Azure::Core::Nullable AccessTierTransitionState; }; struct ShareDeleteResult @@ -705,7 +733,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { std::string RequestId; }; - struct ShareSetQuotaResult + struct ShareSetPropertiesResult { Core::ETag ETag; Core::DateTime LastModified; @@ -2081,6 +2109,9 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { auto result = ShareProperties(); enum class XmlTagName { + AccessTier, + AccessTierChangeTime, + AccessTierTransitionState, DeletedTime, Etag, LastModified, @@ -2118,7 +2149,19 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { else if (node.Type == Storage::Details::XmlNodeType::StartTag) { - if (std::strcmp(node.Name, "DeletedTime") == 0) + if (std::strcmp(node.Name, "AccessTier") == 0) + { + path.emplace_back(XmlTagName::AccessTier); + } + else if (std::strcmp(node.Name, "AccessTierChangeTime") == 0) + { + path.emplace_back(XmlTagName::AccessTierChangeTime); + } + else if (std::strcmp(node.Name, "AccessTierTransitionState") == 0) + { + path.emplace_back(XmlTagName::AccessTierTransitionState); + } + else if (std::strcmp(node.Name, "DeletedTime") == 0) { path.emplace_back(XmlTagName::DeletedTime); } @@ -2189,7 +2232,20 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { } else if (node.Type == Storage::Details::XmlNodeType::Text) { - if (path.size() == 1 && path[0] == XmlTagName::DeletedTime) + if (path.size() == 1 && path[0] == XmlTagName::AccessTier) + { + result.AccessTier = ShareAccessTier(node.Value); + } + else if (path.size() == 1 && path[0] == XmlTagName::AccessTierChangeTime) + { + result.AccessTierChangeTime + = Core::DateTime::Parse(node.Value, Core::DateTime::DateFormat::Rfc1123); + } + else if (path.size() == 1 && path[0] == XmlTagName::AccessTierTransitionState) + { + result.AccessTierTransitionState = node.Value; + } + else if (path.size() == 1 && path[0] == XmlTagName::DeletedTime) { result.DeletedOn = Core::DateTime::Parse(node.Value, Core::DateTime::DateFormat::Rfc1123); @@ -2490,6 +2546,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { Azure::Core::Nullable Timeout; Storage::Metadata Metadata; Azure::Core::Nullable ShareQuota; + Azure::Core::Nullable XMsAccessTier; std::string ApiVersionParameter = Details::DefaultServiceApiVersion; }; @@ -2518,6 +2575,11 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { request.AddHeader( Details::HeaderQuota, std::to_string(createOptions.ShareQuota.GetValue())); } + if (createOptions.XMsAccessTier.HasValue()) + { + request.AddHeader( + Details::HeaderAccessTier, (createOptions.XMsAccessTier.GetValue().Get())); + } request.AddHeader(Details::HeaderVersion, createOptions.ApiVersionParameter); return CreateParseResult(context, pipeline.Send(context, request)); } @@ -2918,42 +2980,49 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { return GetPermissionParseResult(context, pipeline.Send(context, request)); } - struct SetQuotaOptions + struct SetPropertiesOptions { Azure::Core::Nullable Timeout; std::string ApiVersionParameter = Details::DefaultServiceApiVersion; Azure::Core::Nullable ShareQuota; + Azure::Core::Nullable XMsAccessTier; Azure::Core::Nullable LeaseIdOptional; }; - static Azure::Core::Response SetQuota( + static Azure::Core::Response SetProperties( const Azure::Core::Http::Url& url, Azure::Core::Http::HttpPipeline& pipeline, Azure::Core::Context context, - const SetQuotaOptions& setQuotaOptions) + const SetPropertiesOptions& setPropertiesOptions) { Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Put, url); request.AddHeader(Details::HeaderContentLength, "0"); request.GetUrl().AppendQueryParameter(Details::QueryRestype, "share"); request.GetUrl().AppendQueryParameter(Details::QueryComp, "properties"); - if (setQuotaOptions.Timeout.HasValue()) + if (setPropertiesOptions.Timeout.HasValue()) { request.GetUrl().AppendQueryParameter( Details::QueryTimeout, Storage::Details::UrlEncodeQueryParameter( - std::to_string(setQuotaOptions.Timeout.GetValue()))); + std::to_string(setPropertiesOptions.Timeout.GetValue()))); } - request.AddHeader(Details::HeaderVersion, setQuotaOptions.ApiVersionParameter); - if (setQuotaOptions.ShareQuota.HasValue()) + request.AddHeader(Details::HeaderVersion, setPropertiesOptions.ApiVersionParameter); + if (setPropertiesOptions.ShareQuota.HasValue()) { request.AddHeader( - Details::HeaderQuota, std::to_string(setQuotaOptions.ShareQuota.GetValue())); + Details::HeaderQuota, std::to_string(setPropertiesOptions.ShareQuota.GetValue())); } - if (setQuotaOptions.LeaseIdOptional.HasValue()) + if (setPropertiesOptions.XMsAccessTier.HasValue()) { - request.AddHeader(Details::HeaderLeaseId, setQuotaOptions.LeaseIdOptional.GetValue()); + request.AddHeader( + Details::HeaderAccessTier, (setPropertiesOptions.XMsAccessTier.GetValue().Get())); } - return SetQuotaParseResult(context, pipeline.Send(context, request)); + if (setPropertiesOptions.LeaseIdOptional.HasValue()) + { + request.AddHeader( + Details::HeaderLeaseId, setPropertiesOptions.LeaseIdOptional.GetValue()); + } + return SetPropertiesParseResult(context, pipeline.Send(context, request)); } struct SetMetadataOptions @@ -3232,6 +3301,23 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { result.LeaseStatus = LeaseStatusType(response.GetHeaders().at(Details::HeaderLeaseStatus)); } + if (response.GetHeaders().find("x-ms-access-tier") != response.GetHeaders().end()) + { + result.AccessTier = ShareAccessTier(response.GetHeaders().at("x-ms-access-tier")); + } + if (response.GetHeaders().find(Details::HeaderAccessTierChangeTime) + != response.GetHeaders().end()) + { + result.AccessTierChangeTime = Core::DateTime::Parse( + response.GetHeaders().at(Details::HeaderAccessTierChangeTime), + Core::DateTime::DateFormat::Rfc1123); + } + if (response.GetHeaders().find(Details::HeaderAccessTierTransitionState) + != response.GetHeaders().end()) + { + result.AccessTierTransitionState + = response.GetHeaders().at(Details::HeaderAccessTierTransitionState); + } return Azure::Core::Response( std::move(result), std::move(responsePtr)); } @@ -3502,7 +3588,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { return result; } - static Azure::Core::Response SetQuotaParseResult( + static Azure::Core::Response SetPropertiesParseResult( Azure::Core::Context context, std::unique_ptr responsePtr) { @@ -3510,13 +3596,13 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { if (response.GetStatusCode() == Azure::Core::Http::HttpStatusCode::Ok) { // Success - ShareSetQuotaResult result; + ShareSetPropertiesResult result; result.ETag = Core::ETag(response.GetHeaders().at(Details::HeaderETag)); result.LastModified = Core::DateTime::Parse( response.GetHeaders().at(Details::HeaderLastModified), Core::DateTime::DateFormat::Rfc1123); result.RequestId = response.GetHeaders().at(Details::HeaderRequestId); - return Azure::Core::Response( + return Azure::Core::Response( std::move(result), std::move(responsePtr)); } else diff --git a/sdk/storage/azure-storage-files-shares/inc/azure/storage/files/shares/share_client.hpp b/sdk/storage/azure-storage-files-shares/inc/azure/storage/files/shares/share_client.hpp index 2288855fb..12ac2ee72 100644 --- a/sdk/storage/azure-storage-files-shares/inc/azure/storage/files/shares/share_client.hpp +++ b/sdk/storage/azure-storage-files-shares/inc/azure/storage/files/shares/share_client.hpp @@ -126,6 +126,15 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { Azure::Core::Response CreateSnapshot( const CreateShareSnapshotOptions& options = CreateShareSnapshotOptions()) const; + /** + * @brief Sets the properties of the share. + * @param options Optional parameters to set the share properties. + * @return Azure::Core::Response containing the information + * including the version and modified time of a share. + */ + Azure::Core::Response SetProperties( + const SetSharePropertiesOptions& options = SetSharePropertiesOptions()) const; + /** * @brief Gets the properties of the share. * @param options Optional parameters to get the share properties. @@ -135,17 +144,6 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { Azure::Core::Response GetProperties( const GetSharePropertiesOptions& options = GetSharePropertiesOptions()) const; - /** - * @brief Sets the quota of the share. - * @param quota Specifies the maximum size of the share, in gigabytes. - * @param options Optional parameters to set the share quota. - * @return Azure::Core::Response containing the information - * including the version and modified time of a share. - */ - Azure::Core::Response SetQuota( - int32_t quotaInGiB, - const SetShareQuotaOptions& options = SetShareQuotaOptions()) const; - /** * @brief Sets the metadata to the share. * @param metadata A name-value pair to associate with a file storage 'Share' object.. diff --git a/sdk/storage/azure-storage-files-shares/inc/azure/storage/files/shares/share_options.hpp b/sdk/storage/azure-storage-files-shares/inc/azure/storage/files/shares/share_options.hpp index 022b3eaa4..c5ed18344 100644 --- a/sdk/storage/azure-storage-files-shares/inc/azure/storage/files/shares/share_options.hpp +++ b/sdk/storage/azure-storage-files-shares/inc/azure/storage/files/shares/share_options.hpp @@ -107,6 +107,12 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { */ Storage::Metadata Metadata; + /** + * @brief Specifies the access tier of the share. This is only valid for standard file account + * and the value can only be one of `Hot`, `Cool` or `TransactionOptimized` + */ + Azure::Core::Nullable AccessTier; + /** * @brief Specifies the maximum size of the share, in gigabytes. */ @@ -147,12 +153,23 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { Azure::Core::Context Context; }; - struct SetShareQuotaOptions + struct SetSharePropertiesOptions { /** * @brief Context for cancelling long running operations. */ Azure::Core::Context Context; + + /** + * @brief Specifies the access tier of the share. This is only valid for standard file account + * and the value can only be one of `Hot`, `Cool` or `TransactionOptimized` + */ + Azure::Core::Nullable AccessTier; + + /** + * @brief Specifies the maximum size of the share, in gigabytes. + */ + Azure::Core::Nullable ShareQuotaInGiB; }; struct SetShareMetadataOptions diff --git a/sdk/storage/azure-storage-files-shares/inc/azure/storage/files/shares/share_responses.hpp b/sdk/storage/azure-storage-files-shares/inc/azure/storage/files/shares/share_responses.hpp index 57575fa76..0ec2acab0 100644 --- a/sdk/storage/azure-storage-files-shares/inc/azure/storage/files/shares/share_responses.hpp +++ b/sdk/storage/azure-storage-files-shares/inc/azure/storage/files/shares/share_responses.hpp @@ -31,7 +31,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { names }; using CreateShareSnapshotResult = Details::ShareCreateSnapshotResult; using GetSharePropertiesResult = Details::ShareGetPropertiesResult; - using SetShareQuotaResult = Details::ShareSetQuotaResult; + using SetSharePropertiesResult = Details::ShareSetPropertiesResult; using SetShareMetadataResult = Details::ShareSetMetadataResult; using SetShareAccessPolicyResult = Details::ShareSetAccessPolicyResult; using GetShareStatisticsResult = Details::ShareGetStatisticsResult; diff --git a/sdk/storage/azure-storage-files-shares/src/share_client.cpp b/sdk/storage/azure-storage-files-shares/src/share_client.cpp index f4b57bea0..0947b4546 100644 --- a/sdk/storage/azure-storage-files-shares/src/share_client.cpp +++ b/sdk/storage/azure-storage-files-shares/src/share_client.cpp @@ -115,6 +115,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { auto protocolLayerOptions = Details::ShareRestClient::Share::CreateOptions(); protocolLayerOptions.Metadata = options.Metadata; protocolLayerOptions.ShareQuota = options.ShareQuotaInGiB; + protocolLayerOptions.XMsAccessTier = options.AccessTier; auto result = Details::ShareRestClient::Share::Create( m_shareUrl, *m_pipeline, options.Context, protocolLayerOptions); Models::CreateShareResult ret; @@ -202,13 +203,13 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { m_shareUrl, *m_pipeline, options.Context, protocolLayerOptions); } - Azure::Core::Response ShareClient::SetQuota( - int32_t quotaInGiB, - const SetShareQuotaOptions& options) const + Azure::Core::Response ShareClient::SetProperties( + const SetSharePropertiesOptions& options) const { - auto protocolLayerOptions = Details::ShareRestClient::Share::SetQuotaOptions(); - protocolLayerOptions.ShareQuota = quotaInGiB; - return Details::ShareRestClient::Share::SetQuota( + auto protocolLayerOptions = Details::ShareRestClient::Share::SetPropertiesOptions(); + protocolLayerOptions.ShareQuota = options.ShareQuotaInGiB; + protocolLayerOptions.XMsAccessTier = options.AccessTier; + return Details::ShareRestClient::Share::SetProperties( m_shareUrl, *m_pipeline, options.Context, protocolLayerOptions); } diff --git a/sdk/storage/azure-storage-files-shares/src/share_rest_client.cpp b/sdk/storage/azure-storage-files-shares/src/share_rest_client.cpp index 2b4bede8c..2eb3bc422 100644 --- a/sdk/storage/azure-storage-files-shares/src/share_rest_client.cpp +++ b/sdk/storage/azure-storage-files-shares/src/share_rest_client.cpp @@ -5,6 +5,11 @@ #include "azure/storage/files/shares/protocol/share_rest_client.hpp" namespace Azure { namespace Storage { namespace Files { namespace Shares { namespace Models { + const ShareAccessTier ShareAccessTier::TransactionOptimized("TransactionOptimized"); + const ShareAccessTier ShareAccessTier::Hot("Hot"); + const ShareAccessTier ShareAccessTier::Cool("Cool"); + const ShareAccessTier ShareAccessTier::Premium("Premium"); + const PermissionCopyModeType PermissionCopyModeType::Source("source"); const PermissionCopyModeType PermissionCopyModeType::Override("override"); diff --git a/sdk/storage/azure-storage-files-shares/test/share_client_test.cpp b/sdk/storage/azure-storage-files-shares/test/share_client_test.cpp index 2a9ea2b3e..9215a5ebd 100644 --- a/sdk/storage/azure-storage-files-shares/test/share_client_test.cpp +++ b/sdk/storage/azure-storage-files-shares/test/share_client_test.cpp @@ -145,7 +145,7 @@ namespace Azure { namespace Storage { namespace Test { } } - TEST_F(FileShareClientTest, ShareQuota) + TEST_F(FileShareClientTest, ShareProperties) { const int32_t quota32GB = 32; const int32_t quota64GB = 64; @@ -153,16 +153,19 @@ namespace Azure { namespace Storage { namespace Test { { // Set quota /Get properties works - EXPECT_NO_THROW(m_shareClient->SetQuota(quota32GB)); + Files::Shares::SetSharePropertiesOptions options; + options.ShareQuotaInGiB = quota32GB; + EXPECT_NO_THROW(m_shareClient->SetProperties(options)); auto result = m_shareClient->GetProperties(); EXPECT_EQ(quota32GB, result->Quota); - EXPECT_NO_THROW(m_shareClient->SetQuota(quota64GB)); + options.ShareQuotaInGiB = quota64GB; + EXPECT_NO_THROW(m_shareClient->SetProperties(options)); result = m_shareClient->GetProperties(); EXPECT_EQ(quota64GB, result->Quota); } { - // Create file system with quota works + // Create share with quota works auto client1 = Files::Shares::ShareClient::CreateFromConnectionString( AdlsGen2ConnectionString(), LowercaseRandomString()); auto client2 = Files::Shares::ShareClient::CreateFromConnectionString( @@ -182,7 +185,9 @@ namespace Azure { namespace Storage { namespace Test { { // Limit/negative cases: - EXPECT_NO_THROW(m_shareClient->SetQuota(quota5120GB)); + Files::Shares::SetSharePropertiesOptions options; + options.ShareQuotaInGiB = quota5120GB; + EXPECT_NO_THROW(m_shareClient->SetProperties(options)); auto result = m_shareClient->GetProperties()->Quota; EXPECT_EQ(quota5120GB, result); } @@ -361,4 +366,146 @@ namespace Azure { namespace Storage { namespace Test { EXPECT_EQ(fileUrl, m_shareClient->GetUrl() + "/" + Storage::Details::UrlEncodePath(fileName)); } } + + TEST_F(FileShareClientTest, ShareTierRelated) + { + // Create/Get properties works + std::unordered_map shareClients; + std::string prefix = LowercaseRandomString(5); + Files::Shares::Models::GetSharePropertiesResult properties; + { + auto shareName = prefix + LowercaseRandomString(5); + auto shareClient = Files::Shares::ShareClient::CreateFromConnectionString( + StandardStorageConnectionString(), shareName); + auto options = Files::Shares::CreateShareOptions(); + options.AccessTier = Files::Shares::Models::ShareAccessTier::TransactionOptimized; + EXPECT_NO_THROW(shareClient.Create(options)); + EXPECT_NO_THROW(properties = *shareClient.GetProperties()); + EXPECT_EQ( + Files::Shares::Models::ShareAccessTier::TransactionOptimized, + properties.AccessTier.GetValue()); + EXPECT_FALSE(properties.AccessTierTransitionState.HasValue()); + EXPECT_EQ(properties.LastModified, properties.AccessTierChangeTime.GetValue()); + shareClients.emplace(std::move(shareName), std::move(shareClient)); + } + { + auto shareName = prefix + LowercaseRandomString(5); + auto shareClient = Files::Shares::ShareClient::CreateFromConnectionString( + StandardStorageConnectionString(), shareName); + auto options = Files::Shares::CreateShareOptions(); + options.AccessTier = Files::Shares::Models::ShareAccessTier::Hot; + EXPECT_NO_THROW(shareClient.Create(options)); + EXPECT_NO_THROW(properties = *shareClient.GetProperties()); + EXPECT_EQ(Files::Shares::Models::ShareAccessTier::Hot, properties.AccessTier.GetValue()); + EXPECT_FALSE(properties.AccessTierTransitionState.HasValue()); + EXPECT_EQ(properties.LastModified, properties.AccessTierChangeTime.GetValue()); + shareClients.emplace(std::move(shareName), std::move(shareClient)); + } + { + auto shareName = prefix + LowercaseRandomString(5); + auto shareClient = Files::Shares::ShareClient::CreateFromConnectionString( + StandardStorageConnectionString(), shareName); + auto options = Files::Shares::CreateShareOptions(); + options.AccessTier = Files::Shares::Models::ShareAccessTier::Cool; + EXPECT_NO_THROW(shareClient.Create(options)); + EXPECT_NO_THROW(properties = *shareClient.GetProperties()); + EXPECT_EQ(Files::Shares::Models::ShareAccessTier::Cool, properties.AccessTier.GetValue()); + EXPECT_FALSE(properties.AccessTierTransitionState.HasValue()); + EXPECT_EQ(properties.LastModified, properties.AccessTierChangeTime.GetValue()); + shareClients.emplace(std::move(shareName), std::move(shareClient)); + } + + // Set properties works + { + auto shareClient = Files::Shares::ShareClient::CreateFromConnectionString( + StandardStorageConnectionString(), LowercaseRandomString(10)); + auto options = Files::Shares::CreateShareOptions(); + options.AccessTier = Files::Shares::Models::ShareAccessTier::Cool; + EXPECT_NO_THROW(shareClient.Create(options)); + EXPECT_EQ( + Files::Shares::Models::ShareAccessTier::Cool, + shareClient.GetProperties()->AccessTier.GetValue()); + + auto setPropertiesOptions = Files::Shares::SetSharePropertiesOptions(); + setPropertiesOptions.AccessTier = Files::Shares::Models::ShareAccessTier::Hot; + EXPECT_NO_THROW(shareClient.SetProperties(setPropertiesOptions)); + properties = *shareClient.GetProperties(); + if (properties.AccessTierTransitionState.HasValue()) + { + EXPECT_EQ(Files::Shares::Models::ShareAccessTier::Cool, properties.AccessTier.GetValue()); + } + else + { + EXPECT_EQ(Files::Shares::Models::ShareAccessTier::Hot, properties.AccessTier.GetValue()); + } + EXPECT_EQ(properties.LastModified, properties.AccessTierChangeTime.GetValue()); + } + + // List shares works. + Files::Shares::ListSharesSinglePageOptions listOptions; + listOptions.Prefix = prefix; + auto shareItems = Files::Shares::ShareServiceClient::CreateFromConnectionString( + StandardStorageConnectionString()) + .ListSharesSinglePage(listOptions) + ->Items; + EXPECT_EQ(3U, shareItems.size()); + for (const auto& shareItem : shareItems) + { + EXPECT_TRUE(shareClients.find(shareItem.Name) != shareClients.end()); + properties = *shareClients.at(shareItem.Name).GetProperties(); + EXPECT_EQ( + true, shareItem.Properties.AccessTier.HasValue() && properties.AccessTier.HasValue()); + EXPECT_EQ(shareItem.Properties.AccessTier.GetValue(), properties.AccessTier.GetValue()); + EXPECT_EQ( + true, + shareItem.Properties.AccessTierChangeTime.HasValue() + && properties.AccessTierChangeTime.HasValue()); + EXPECT_EQ( + shareItem.Properties.AccessTierChangeTime.GetValue(), + properties.AccessTierChangeTime.GetValue()); + EXPECT_EQ( + false, + shareItem.Properties.AccessTierTransitionState.HasValue() + || properties.AccessTierTransitionState.HasValue()); + } + } + + TEST_F(FileShareClientTest, PremiumShare) + { + auto shareName = LowercaseRandomString(10); + auto shareClient = Files::Shares::ShareClient::CreateFromConnectionString( + PremiumFileConnectionString(), shareName); + EXPECT_NO_THROW(shareClient.Create()); + Files::Shares::Models::GetSharePropertiesResult properties; + EXPECT_NO_THROW(properties = *shareClient.GetProperties()); + EXPECT_EQ(Files::Shares::Models::ShareAccessTier::Premium, properties.AccessTier.GetValue()); + EXPECT_FALSE(properties.AccessTierTransitionState.HasValue()); + EXPECT_FALSE(properties.AccessTierChangeTime.HasValue()); + + Files::Shares::ListSharesSinglePageOptions listOptions; + listOptions.Prefix = shareName; + auto shareItems = Files::Shares::ShareServiceClient::CreateFromConnectionString( + PremiumFileConnectionString()) + .ListSharesSinglePage(listOptions) + ->Items; + EXPECT_EQ(1U, shareItems.size()); + EXPECT_EQ( + Files::Shares::Models::ShareAccessTier::Premium, + shareItems[0].Properties.AccessTier.GetValue()); + EXPECT_FALSE(shareItems[0].Properties.AccessTierTransitionState.HasValue()); + EXPECT_FALSE(shareItems[0].Properties.AccessTierChangeTime.HasValue()); + + auto setPropertiesOptions = Files::Shares::SetSharePropertiesOptions(); + setPropertiesOptions.AccessTier = Files::Shares::Models::ShareAccessTier::Hot; + EXPECT_THROW(shareClient.SetProperties(setPropertiesOptions), StorageException); + setPropertiesOptions.AccessTier = Files::Shares::Models::ShareAccessTier::Cool; + EXPECT_THROW(shareClient.SetProperties(setPropertiesOptions), StorageException); + setPropertiesOptions.AccessTier = Files::Shares::Models::ShareAccessTier::TransactionOptimized; + EXPECT_THROW(shareClient.SetProperties(setPropertiesOptions), StorageException); + setPropertiesOptions.AccessTier = Files::Shares::Models::ShareAccessTier::Premium; + EXPECT_NO_THROW(shareClient.SetProperties(setPropertiesOptions)); + EXPECT_EQ(Files::Shares::Models::ShareAccessTier::Premium, properties.AccessTier.GetValue()); + EXPECT_FALSE(properties.AccessTierTransitionState.HasValue()); + EXPECT_FALSE(properties.AccessTierChangeTime.HasValue()); + } }}} // namespace Azure::Storage::Test diff --git a/sdk/storage/test-resources.json b/sdk/storage/test-resources.json index b4163f510..254c41b08 100644 --- a/sdk/storage/test-resources.json +++ b/sdk/storage/test-resources.json @@ -62,7 +62,8 @@ }, "authorizationApiVersion": "2018-01-01-preview", "blobDataContributorRoleId": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", - "blobDataOwnerRoleId": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/b7e6dc6d-f1e8-4753-8033-0f276bb0955b')]" + "blobDataOwnerRoleId": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/b7e6dc6d-f1e8-4753-8033-0f276bb0955b')]", + "premiumFileAccountName": "[concat(parameters('baseName'), 'pfile')]" }, "resources": [ { @@ -254,6 +255,40 @@ }, "accessTier": "Hot" } + }, + { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "[variables('storageApiVersion')]", + "name": "[variables('premiumFileAccountName')]", + "location": "[variables('location')]", + "sku": { + "name": "Premium_LRS", + "tier": "Premium" + }, + "kind": "FileStorage", + "properties": { + "networkAcls": { + "bypass": "AzureServices", + "virtualNetworkRules": [ + ], + "ipRules": [ + ], + "defaultAction": "Allow" + }, + "supportsHttpsTrafficOnly": true, + "encryption": { + "services": { + "file": { + "enabled": true + }, + "blob": { + "enabled": true + } + }, + "keySource": "Microsoft.Storage" + }, + "accessTier": "Hot" + } } ], "outputs": { @@ -281,6 +316,10 @@ "type": "string", "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('dataLakeAccountName'), ';AccountKey=', listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('dataLakeAccountName')), variables('storageApiVersion')).keys[0].value, ';EndpointSuffix=', parameters('storageEndpointSuffix'))]" }, + "PREMIUM_FILE_CONNECTION_STRING": { + "type": "string", + "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('premiumFileAccountName'), ';AccountKey=', listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('premiumFileAccountName')), variables('storageApiVersion')).keys[0].value, ';EndpointSuffix=', parameters('storageEndpointSuffix'))]" + }, "AAD_TENANT_ID": { "type": "string", "value": "[parameters('tenantId')]"