From 84e4a819479428901d9cea1b917c9c6164cb2ee3 Mon Sep 17 00:00:00 2001 From: George Arama <50641385+gearama@users.noreply.github.com> Date: Fri, 15 Mar 2024 14:30:45 -0700 Subject: [PATCH] Api view comments (#5408) * mroe quotes * dssf * listTables -> queryTables * move create/delete to the tableservice * one more batch * small refactor, added default data types * oops * clangs * table sas builder * sas test * cspell * add more test * cspell * hjhjh * clang * rename table-> tables * clang * partition and row key * clang why? * constructors, update tests * IT WORKSSSSS * PR comments * wqe * ewr * enum operator * clang * dsaada * sada * assets.json * clang * Update sdk/tables/azure-data-tables/inc/azure/data/tables/enum_operators.hpp Co-authored-by: Anton Kolesnyk <41349689+antkmsft@users.noreply.github.com> * Update sdk/core/azure-core/test/ut/string_test.cpp Co-authored-by: Anton Kolesnyk <41349689+antkmsft@users.noreply.github.com> * the = enums * assert and remove empty * compact string tests * clangs * complement * azureSasCredentials * PR cpmments * some extra * clang 11 --------- Co-authored-by: Anton Kolesnyk <41349689+antkmsft@users.noreply.github.com> --- .../inc/azure/core/internal/strings.hpp | 28 +++ .../inc/azure/core/test/pipeline_test.hpp | 26 +-- sdk/core/azure-core/test/ut/string_test.cpp | 109 +++++++++++ sdk/tables/assets.json | 2 +- sdk/tables/azure-data-tables/CMakeLists.txt | 7 +- .../inc/azure/data/tables.hpp | 4 + .../azure/data/tables/account_sas_builder.hpp | 43 +---- .../credentials/azure_sas_credential.hpp | 47 +++++ .../credentials/shared_key_credential.hpp | 6 +- .../inc/azure/data/tables/enum_operators.hpp | 77 ++++++++ .../data/tables/internal/serializers.hpp | 7 + .../inc/azure/data/tables/models.hpp | 105 ++++++----- .../inc/azure/data/tables/tables_clients.hpp | 95 +++++++--- .../azure/data/tables/tables_sas_builder.hpp | 173 ++++++++++++++++++ .../samples/tables_entity_operations.cpp | 8 +- .../samples/tables_getting_started.cpp | 10 +- .../samples/tables_service_operations.cpp | 6 +- sdk/tables/azure-data-tables/src/models.cpp | 15 ++ .../azure-data-tables/src/serializers.cpp | 61 +++--- .../azure-data-tables/src/tables_clients.cpp | 117 ++++++------ .../src/tables_sas_builder.cpp | 155 ++++++++++++++++ .../azure-data-tables/test/ut/CMakeLists.txt | 3 + .../test/ut/enum_operators_test.cpp | 147 +++++++++++++++ .../test/ut/enum_operators_test.hpp | 21 +++ .../azure-data-tables/test/ut/sas_test.cpp | 133 ++++++++++++++ .../azure-data-tables/test/ut/sas_test.hpp | 30 +++ .../test/ut/table_client_test.cpp | 78 ++++---- .../test/ut/table_client_test.hpp | 2 +- 28 files changed, 1233 insertions(+), 282 deletions(-) create mode 100644 sdk/tables/azure-data-tables/inc/azure/data/tables/credentials/azure_sas_credential.hpp create mode 100644 sdk/tables/azure-data-tables/inc/azure/data/tables/enum_operators.hpp create mode 100644 sdk/tables/azure-data-tables/inc/azure/data/tables/tables_sas_builder.hpp create mode 100644 sdk/tables/azure-data-tables/src/models.cpp create mode 100644 sdk/tables/azure-data-tables/src/tables_sas_builder.cpp create mode 100644 sdk/tables/azure-data-tables/test/ut/enum_operators_test.cpp create mode 100644 sdk/tables/azure-data-tables/test/ut/enum_operators_test.hpp create mode 100644 sdk/tables/azure-data-tables/test/ut/sas_test.cpp create mode 100644 sdk/tables/azure-data-tables/test/ut/sas_test.hpp diff --git a/sdk/core/azure-core/inc/azure/core/internal/strings.hpp b/sdk/core/azure-core/inc/azure/core/internal/strings.hpp index be8c5c1c2..984b74982 100644 --- a/sdk/core/azure-core/inc/azure/core/internal/strings.hpp +++ b/sdk/core/azure-core/inc/azure/core/internal/strings.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace Azure { namespace Core { namespace _internal { @@ -91,6 +92,33 @@ namespace Azure { namespace Core { namespace _internal { std::transform(src.begin(), src.end(), src.begin(), [](auto c) { return ToUpper(c); }); return src; } + + static std::vector Split( + const std::string& s, + char separator, + bool removeEmptyEntries = false) + { + std::vector result; + + const auto len = s.size(); + size_t start = 0; + while (start < len) + { + auto end = s.find(separator, start); + if (end == std::string::npos) + { + end = len; + } + if (!removeEmptyEntries || start < end) + { + result.push_back(s.substr(start, end - start)); + } + + start = end + 1; + } + + return result; + } }; }}} // namespace Azure::Core::_internal diff --git a/sdk/core/azure-core/test/perf/inc/azure/core/test/pipeline_test.hpp b/sdk/core/azure-core/test/perf/inc/azure/core/test/pipeline_test.hpp index 885389eff..34c27665d 100644 --- a/sdk/core/azure-core/test/perf/inc/azure/core/test/pipeline_test.hpp +++ b/sdk/core/azure-core/test/perf/inc/azure/core/test/pipeline_test.hpp @@ -49,28 +49,6 @@ namespace Azure { namespace Core { namespace Test { class PipelineTest : public Azure::Perf::PerfTest { std::unique_ptr m_pipeline; - static std::vector SplitString(const std::string& s, char separator) - { - std::vector result; - - const auto len = s.size(); - size_t start = 0; - while (start < len) - { - auto end = s.find(separator, start); - if (end == std::string::npos) - { - end = len; - } - - result.push_back(s.substr(start, end - start)); - - start = end + 1; - } - - return result; - } - public: /** * @brief Construct a new PipelineTest test. @@ -96,8 +74,8 @@ namespace Azure { namespace Core { namespace Test { std::vector> policies2; auto const total = m_options.GetMandatoryOption("Count"); - auto const policyNames - = SplitString(m_options.GetOptionOrDefault("Policies", "TestPolicy"), ','); + auto const policyNames = Azure::Core::_internal::StringExtensions::Split( + m_options.GetOptionOrDefault("Policies", "TestPolicy"), ','); // we want a total number of policies added to the pipeline // thus for loop total / number , depends on rounding but close enough // since in each loop we add the whole set of desired policies diff --git a/sdk/core/azure-core/test/ut/string_test.cpp b/sdk/core/azure-core/test/ut/string_test.cpp index 9b3b5ee1d..5e3ad4e55 100644 --- a/sdk/core/azure-core/test/ut/string_test.cpp +++ b/sdk/core/azure-core/test/ut/string_test.cpp @@ -131,3 +131,112 @@ TEST(String, toUpper) EXPECT_NE(StringExtensions::ToUpper("a"), "aA"); EXPECT_NE(StringExtensions::ToUpper("abc"), "abcd"); } + +TEST(String, SplitEmpty) +{ + using Azure::Core::_internal::StringExtensions; + { + std::vector result = StringExtensions::Split("", ','); + EXPECT_TRUE(result.empty()); + } + { + std::vector result = StringExtensions::Split("", ',', true); + EXPECT_TRUE(result.empty()); + } +} + +TEST(String, Split3) +{ + using Azure::Core::_internal::StringExtensions; + { + std::vector result = StringExtensions::Split("1,2,3", ','); + ASSERT_EQ(result.size(), size_t{3}); + EXPECT_EQ(result[0], "1"); + EXPECT_EQ(result[1], "2"); + EXPECT_EQ(result[2], "3"); + } + { + std::vector result = StringExtensions::Split("1,2,3", ',', true); + ASSERT_EQ(result.size(), size_t{3}); + EXPECT_EQ(result[0], "1"); + EXPECT_EQ(result[1], "2"); + EXPECT_EQ(result[2], "3"); + } +} + +TEST(String, SplitBegin) +{ + using Azure::Core::_internal::StringExtensions; + { + std::vector result = StringExtensions::Split(",1,2,3", ','); + ASSERT_EQ(result.size(), size_t{4}); + EXPECT_EQ(result[0], ""); + EXPECT_EQ(result[1], "1"); + EXPECT_EQ(result[2], "2"); + EXPECT_EQ(result[3], "3"); + } + { + std::vector result = StringExtensions::Split(",1,2,3", ',', true); + ASSERT_EQ(result.size(), size_t{3}); + EXPECT_EQ(result[0], "1"); + EXPECT_EQ(result[1], "2"); + EXPECT_EQ(result[2], "3"); + } +} + +TEST(String, SplitEnd) +{ + using Azure::Core::_internal::StringExtensions; + { + std::vector result = StringExtensions::Split("1,2,3,", ','); + ASSERT_EQ(result.size(), size_t{3}); + EXPECT_EQ(result[0], "1"); + EXPECT_EQ(result[1], "2"); + EXPECT_EQ(result[2], "3"); + } + { + std::vector result = StringExtensions::Split("1,2,3,", ',', true); + ASSERT_EQ(result.size(), size_t{3}); + EXPECT_EQ(result[0], "1"); + EXPECT_EQ(result[1], "2"); + EXPECT_EQ(result[2], "3"); + } +} + +TEST(String, SplitPartEmpty) +{ + using Azure::Core::_internal::StringExtensions; + { + std::vector result = StringExtensions::Split("1,,2,,3", ','); + ASSERT_EQ(result.size(), size_t{5}); + EXPECT_EQ(result[0], "1"); + EXPECT_EQ(result[1], ""); + EXPECT_EQ(result[2], "2"); + EXPECT_EQ(result[3], ""); + EXPECT_EQ(result[4], "3"); + } + { + std::vector result = StringExtensions::Split("1,,2,,3", ',', true); + ASSERT_EQ(result.size(), size_t{3}); + EXPECT_EQ(result[0], "1"); + EXPECT_EQ(result[1], "2"); + EXPECT_EQ(result[2], "3"); + } +} + +TEST(String, SplitSeparator) +{ + using Azure::Core::_internal::StringExtensions; + { + std::vector result = StringExtensions::Split(",,,,", ','); + ASSERT_EQ(result.size(), size_t{4}); + EXPECT_EQ(result[0], ""); + EXPECT_EQ(result[1], ""); + EXPECT_EQ(result[2], ""); + EXPECT_EQ(result[3], ""); + } + { + std::vector result = StringExtensions::Split(",,,,", ',', true); + ASSERT_EQ(result.size(), size_t{0}); + } +} diff --git a/sdk/tables/assets.json b/sdk/tables/assets.json index 6649c236a..587fc753d 100644 --- a/sdk/tables/assets.json +++ b/sdk/tables/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "cpp", "TagPrefix": "cpp/tables", - "Tag": "cpp/tables_a490279ed7" + "Tag": "cpp/tables_ca30219607" } diff --git a/sdk/tables/azure-data-tables/CMakeLists.txt b/sdk/tables/azure-data-tables/CMakeLists.txt index 74296f322..b6aaec865 100644 --- a/sdk/tables/azure-data-tables/CMakeLists.txt +++ b/sdk/tables/azure-data-tables/CMakeLists.txt @@ -42,7 +42,10 @@ endif() set( AZURE_DATA_TABLES_HEADER inc/azure/data/tables/account_sas_builder.hpp + inc/azure/data/tables/credentials/azure_sas_credential.hpp + inc/azure/data/tables/credentials/shared_key_credential.hpp inc/azure/data/tables/dll_import_export.hpp + inc/azure/data/tables/enum_operators.hpp inc/azure/data/tables/internal/cryptography/hmacsha256.hpp inc/azure/data/tables/internal/cryptography/url_encode.hpp inc/azure/data/tables/internal/policies/service_version_policy.hpp @@ -57,8 +60,8 @@ set( inc/azure/data/tables/rtti.hpp inc/azure/data/tables.hpp inc/azure/data/tables/tables_clients.hpp + inc/azure/data/tables/tables_sas_builder.hpp inc/azure/data/tables/transactions.hpp - inc/azure/data/tables/credentials/shared_key_credential.hpp ) set( @@ -66,6 +69,7 @@ set( src/account_sas_builder.cpp src/credentials/shared_key_credential.cpp src/cryptography/hmacsha256.cpp + src/models.cpp src/policies/shared_key_lite_policy.cpp src/policies/shared_key_policy.cpp src/policies/switch_to_secondary_policy.cpp @@ -74,6 +78,7 @@ set( src/private/package_version.hpp src/serializers.cpp src/tables_clients.cpp + src/tables_sas_builder.cpp src/transactions.cpp src/xml_wrapper.cpp ) diff --git a/sdk/tables/azure-data-tables/inc/azure/data/tables.hpp b/sdk/tables/azure-data-tables/inc/azure/data/tables.hpp index a16113868..859efca68 100644 --- a/sdk/tables/azure-data-tables/inc/azure/data/tables.hpp +++ b/sdk/tables/azure-data-tables/inc/azure/data/tables.hpp @@ -8,9 +8,13 @@ #pragma once +#include "azure/data/tables/account_sas_builder.hpp" +#include "azure/data/tables/credentials/azure_sas_credential.hpp" #include "azure/data/tables/credentials/shared_key_credential.hpp" #include "azure/data/tables/dll_import_export.hpp" +#include "azure/data/tables/enum_operators.hpp" #include "azure/data/tables/models.hpp" #include "azure/data/tables/rtti.hpp" #include "azure/data/tables/tables_clients.hpp" +#include "azure/data/tables/tables_sas_builder.hpp" #include "azure/data/tables/transactions.hpp" diff --git a/sdk/tables/azure-data-tables/inc/azure/data/tables/account_sas_builder.hpp b/sdk/tables/azure-data-tables/inc/azure/data/tables/account_sas_builder.hpp index f03badc38..638a5a826 100644 --- a/sdk/tables/azure-data-tables/inc/azure/data/tables/account_sas_builder.hpp +++ b/sdk/tables/azure-data-tables/inc/azure/data/tables/account_sas_builder.hpp @@ -4,12 +4,12 @@ #pragma once #include "azure/data/tables/credentials/shared_key_credential.hpp" +#include "azure/data/tables/enum_operators.hpp" #include #include #include -#include namespace Azure { namespace Data { namespace Tables { namespace Sas { @@ -68,18 +68,6 @@ namespace Azure { namespace Data { namespace Tables { namespace Sas { All = ~0, }; - inline AccountSasResource operator|(AccountSasResource lhs, AccountSasResource rhs) - { - using type = std::underlying_type_t; - return static_cast(static_cast(lhs) | static_cast(rhs)); - } - - inline AccountSasResource operator&(AccountSasResource lhs, AccountSasResource rhs) - { - using type = std::underlying_type_t; - return static_cast(static_cast(lhs) & static_cast(rhs)); - } - /** * @brief Specifies the services accessible from an account level shared access signature. */ @@ -114,18 +102,6 @@ namespace Azure { namespace Data { namespace Tables { namespace Sas { All = ~0, }; - inline AccountSasServices operator|(AccountSasServices lhs, AccountSasServices rhs) - { - using type = std::underlying_type_t; - return static_cast(static_cast(lhs) | static_cast(rhs)); - } - - inline AccountSasServices operator&(AccountSasServices lhs, AccountSasServices rhs) - { - using type = std::underlying_type_t; - return static_cast(static_cast(lhs) & static_cast(rhs)); - } - /** * @brief The list of permissions that can be set for an account's access policy. */ @@ -202,24 +178,12 @@ namespace Azure { namespace Data { namespace Tables { namespace Sas { All = ~0, }; - inline AccountSasPermissions operator|(AccountSasPermissions lhs, AccountSasPermissions rhs) - { - using type = std::underlying_type_t; - return static_cast(static_cast(lhs) | static_cast(rhs)); - } - - inline AccountSasPermissions operator&(AccountSasPermissions lhs, AccountSasPermissions rhs) - { - using type = std::underlying_type_t; - return static_cast(static_cast(lhs) & static_cast(rhs)); - } - /** * @brief AccountSasBuilder is used to generate an account level Shared Access Signature * (SAS) for Azure Storage services. */ - struct AccountSasBuilder final - { + class AccountSasBuilder final { + public: /** * @brief The optional signed protocol field specifies the protocol permitted for a * request made with the SAS. @@ -293,5 +257,4 @@ namespace Azure { namespace Data { namespace Tables { namespace Sas { private: std::string Permissions; }; - }}}} // namespace Azure::Data::Tables::Sas diff --git a/sdk/tables/azure-data-tables/inc/azure/data/tables/credentials/azure_sas_credential.hpp b/sdk/tables/azure-data-tables/inc/azure/data/tables/credentials/azure_sas_credential.hpp new file mode 100644 index 000000000..9312538aa --- /dev/null +++ b/sdk/tables/azure-data-tables/inc/azure/data/tables/credentials/azure_sas_credential.hpp @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +#include + +#include +#include +#include + +namespace Azure { namespace Data { namespace Tables { namespace Credentials { + /** + * @brief Azure Shared Access Signature (SAS) credential. + */ + class AzureSasCredential final { + private: + std::string m_signature; + mutable std::mutex m_mutex; + + public: + /** + * @brief Initializes a new instance of the AzureSasCredential. + * + * @param signature The signature for the SAS token. + */ + explicit AzureSasCredential(std::string signature) : m_signature(std::move(signature)) {} + + /** + * @brief Get the signature for the SAS token. + */ + std::string GetSignature() const + { + std::lock_guard guard(m_mutex); + return m_signature; + } + + /** + * @brief Update the signature for the SAS token. + */ + void Update(std::string signature) + { + std::lock_guard guard(m_mutex); + m_signature = std::move(signature); + } + }; +}}}} // namespace Azure::Data::Tables::Credentials diff --git a/sdk/tables/azure-data-tables/inc/azure/data/tables/credentials/shared_key_credential.hpp b/sdk/tables/azure-data-tables/inc/azure/data/tables/credentials/shared_key_credential.hpp index aae33b096..5af53131b 100644 --- a/sdk/tables/azure-data-tables/inc/azure/data/tables/credentials/shared_key_credential.hpp +++ b/sdk/tables/azure-data-tables/inc/azure/data/tables/credentials/shared_key_credential.hpp @@ -14,7 +14,8 @@ namespace Azure { namespace Data { namespace Tables { namespace _detail { namesp }}}}} // namespace Azure::Data::Tables::_detail::Policies namespace Azure { namespace Data { namespace Tables { namespace Sas { - struct AccountSasBuilder; + class AccountSasBuilder; + class TablesSasBuilder; }}}} // namespace Azure::Data::Tables::Sas namespace Azure { namespace Data { namespace Tables { namespace Credentials { @@ -57,7 +58,8 @@ namespace Azure { namespace Data { namespace Tables { namespace Credentials { private: friend class Azure::Data::Tables::_detail::Policies::SharedKeyPolicy; friend class Azure::Data::Tables::_detail::Policies::SharedKeyLitePolicy; - friend struct Azure::Data::Tables::Sas::AccountSasBuilder; + friend class Azure::Data::Tables::Sas::AccountSasBuilder; + friend class Azure::Data::Tables::Sas::TablesSasBuilder; std::string GetAccountKey() const { diff --git a/sdk/tables/azure-data-tables/inc/azure/data/tables/enum_operators.hpp b/sdk/tables/azure-data-tables/inc/azure/data/tables/enum_operators.hpp new file mode 100644 index 000000000..55a6105f0 --- /dev/null +++ b/sdk/tables/azure-data-tables/inc/azure/data/tables/enum_operators.hpp @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +#include + +namespace Azure { namespace Data { namespace Tables { + /** + * @brief Bitwise OR operator for enum class. + */ + template {}>> + constexpr E operator|(E lhs, E rhs) + { + using type = std::underlying_type_t; + return static_cast(static_cast(lhs) | static_cast(rhs)); + } + + /** + * @brief Bitwise OR EQUALS operator for enum class. + */ + template {}>> + constexpr E& operator|=(E& lhs, E rhs) + { + lhs = lhs | rhs; + return lhs; + } + + /** + * @brief Bitwise AND operator for enum class. + */ + template {}>> + constexpr E operator&(E lhs, E rhs) + { + using type = std::underlying_type_t; + return static_cast(static_cast(lhs) & static_cast(rhs)); + } + + /** + * @brief Bitwise AND EQUALS operator for enum class. + */ + template {}>> + constexpr E& operator&=(E& lhs, E rhs) + { + lhs = lhs & rhs; + return lhs; + } + + /** + * @brief Bitwise XOR operator for enum class. + */ + template {}>> + constexpr E operator^(E lhs, E rhs) + { + using type = std::underlying_type_t; + return static_cast(static_cast(lhs) ^ static_cast(rhs)); + } + + /** + * @brief Bitwise XOR EQUALS operator for enum class. + */ + template {}>> + constexpr E& operator^=(E& lhs, E rhs) + { + lhs = lhs ^ rhs; + return lhs; + } + + /** + * @brief Bitwise COMPLEMENT operator for enum class. + */ + template {}>> constexpr E operator~(E rhs) + { + using type = std::underlying_type_t; + return static_cast(~static_cast(rhs)); + } +}}} // namespace Azure::Data::Tables diff --git a/sdk/tables/azure-data-tables/inc/azure/data/tables/internal/serializers.hpp b/sdk/tables/azure-data-tables/inc/azure/data/tables/internal/serializers.hpp index 0998a1959..197229270 100644 --- a/sdk/tables/azure-data-tables/inc/azure/data/tables/internal/serializers.hpp +++ b/sdk/tables/azure-data-tables/inc/azure/data/tables/internal/serializers.hpp @@ -5,6 +5,8 @@ #include "azure/data/tables/internal/xml_wrapper.hpp" #include "azure/data/tables/models.hpp" +#include + #include #include #include @@ -68,5 +70,10 @@ namespace Azure { namespace Data { namespace Tables { namespace _detail { */ static Models::TableServiceProperties ServicePropertiesFromXml( std::vector responseData); + + /** + * @brief Deserialize a TableEntity from JSON. + */ + static Models::TableEntity DeserializeEntity(Azure::Core::Json::_internal::json json); }; }}}} // namespace Azure::Data::Tables::_detail diff --git a/sdk/tables/azure-data-tables/inc/azure/data/tables/models.hpp b/sdk/tables/azure-data-tables/inc/azure/data/tables/models.hpp index c077ec74e..4ab77dcd8 100644 --- a/sdk/tables/azure-data-tables/inc/azure/data/tables/models.hpp +++ b/sdk/tables/azure-data-tables/inc/azure/data/tables/models.hpp @@ -4,6 +4,7 @@ #pragma once #include "azure/data/tables/dll_import_export.hpp" +#include "azure/data/tables/enum_operators.hpp" #include #include @@ -18,11 +19,10 @@ #include namespace Azure { namespace Data { namespace Tables { - class TableServicesClient; + class TableServiceClient; class TableClient; namespace Models { - /** * @brief Table definition struct. */ @@ -54,41 +54,17 @@ namespace Azure { namespace Data { namespace Tables { * @brief Include this parameter to specify that the tables' metadata be returned as part of * the response body. */ - enum class ListTablesIncludeFlags + enum class QueryTablesIncludeFlags { None = 0, Metadata = 1, }; - inline ListTablesIncludeFlags operator|(ListTablesIncludeFlags lhs, ListTablesIncludeFlags rhs) - { - using type = std::underlying_type_t; - return static_cast(static_cast(lhs) | static_cast(rhs)); - } - inline ListTablesIncludeFlags& operator|=( - ListTablesIncludeFlags& lhs, - ListTablesIncludeFlags rhs) - { - lhs = lhs | rhs; - return lhs; - } - inline ListTablesIncludeFlags operator&(ListTablesIncludeFlags lhs, ListTablesIncludeFlags rhs) - { - using type = std::underlying_type_t; - return static_cast(static_cast(lhs) & static_cast(rhs)); - } - inline ListTablesIncludeFlags& operator&=( - ListTablesIncludeFlags& lhs, - ListTablesIncludeFlags rhs) - { - lhs = lhs & rhs; - return lhs; - } /** - * @brief List Tables options. + * @brief Query Tables options. * */ - struct ListTablesOptions final + struct QueryTablesOptions final { /** * @brief Specifies a string that filters the results to return only tables whose name @@ -108,22 +84,22 @@ namespace Azure { namespace Data { namespace Tables { /** * @brief Specifies the maximum number of tables to return. */ - Azure::Nullable PageSizeHint; + Azure::Nullable PageSizeHint; /** * @brief Specifies that the table's metadata be returned. */ - Models::ListTablesIncludeFlags Include = Models::ListTablesIncludeFlags::None; + Models::QueryTablesIncludeFlags Include = Models::QueryTablesIncludeFlags::None; }; /** - * @brief List tables paged response. + * @brief Query tables paged response. */ - class ListTablesPagedResponse final - : public Azure::Core::PagedResponse { + class QueryTablesPagedResponse final + : public Azure::Core::PagedResponse { - friend class Azure::Data::Tables::TableServicesClient; - friend class Azure::Core::PagedResponse; + friend class Azure::Data::Tables::TableServiceClient; + friend class Azure::Core::PagedResponse; public: /** @@ -145,9 +121,9 @@ namespace Azure { namespace Data { namespace Tables { /** * Table Service Client. */ - std::shared_ptr m_tableServiceClient; + std::shared_ptr m_tableServiceClient; /** Operation options */ - ListTablesOptions m_operationOptions; + QueryTablesOptions m_operationOptions; private: void OnNextPage(const Azure::Core::Context& context); @@ -166,7 +142,7 @@ namespace Azure { namespace Data { namespace Tables { * Indicates the number of days that metrics or logging or soft-deleted data should be * retained. All data older than this value will be deleted. */ - Nullable Days; + Nullable Days; }; /** @@ -253,7 +229,7 @@ namespace Azure { namespace Data { namespace Tables { /** * The maximum amount time that a browser should cache the preflight OPTIONS request. */ - int32_t MaxAgeInSeconds = int32_t(); + std::int32_t MaxAgeInSeconds = int32_t(); }; /** @@ -347,6 +323,43 @@ namespace Azure { namespace Data { namespace Tables { std::string m_value; }; + /** + * @brief Table Entity Data Type. + */ + class TableEntityDataType final + : public Azure::Core::_internal::ExtendableEnumeration { + public: + /** + * @brief Construct a new TableEntityDataType object + */ + TableEntityDataType() = default; + /** + * @brief Construct a new TableEntityDataType object + * + * @param tableEntityDataType entity data type string. + */ + explicit TableEntityDataType(std::string tableEntityDataType) + : ExtendableEnumeration(std::move(tableEntityDataType)) + { + } + /** Constant value of type TableEntityDataType:EdmBinary */ + AZ_DATA_TABLES_DLLEXPORT const static TableEntityDataType EdmBinary; + /** Constant value of type TableEntityDataType:EdmBinary */ + AZ_DATA_TABLES_DLLEXPORT const static TableEntityDataType EdmBoolean; + /** Constant value of type TableEntityDataType:EdmBinary */ + AZ_DATA_TABLES_DLLEXPORT const static TableEntityDataType EdmDateTime; + /** Constant value of type TableEntityDataType:EdmBinary */ + AZ_DATA_TABLES_DLLEXPORT const static TableEntityDataType EdmDouble; + /** Constant value of type TableEntityDataType:EdmBinary */ + AZ_DATA_TABLES_DLLEXPORT const static TableEntityDataType EdmGuid; + /** Constant value of type TableEntityDataType:EdmBinary */ + AZ_DATA_TABLES_DLLEXPORT const static TableEntityDataType EdmInt32; + /** Constant value of type TableEntityDataType:EdmBinary */ + AZ_DATA_TABLES_DLLEXPORT const static TableEntityDataType EdmInt64; + /** Constant value of type TableEntityDataType:EdmBinary */ + AZ_DATA_TABLES_DLLEXPORT const static TableEntityDataType EdmString; + }; + /** * @brief Geo-Replication information for the Secondary Storage Service. */ @@ -380,7 +393,7 @@ namespace Azure { namespace Data { namespace Tables { * @brief Delete result. * */ - struct DeleteResult final + struct DeleteTableResult final { }; @@ -450,6 +463,12 @@ namespace Azure { namespace Data { namespace Tables { * ETag */ Azure::Nullable ETag; + + /** + * @brief Table Entity data type. + * + */ + TableEntityDataType DataType; }; /** @@ -486,7 +505,7 @@ namespace Azure { namespace Data { namespace Tables { * * @param other Upsert Entity options. */ - CreateEntityOptions(UpsertEntityOptions const& other) { (void)other; } + explicit CreateEntityOptions(UpsertEntityOptions const& other) { (void)other; } }; /** @@ -668,7 +687,7 @@ namespace Azure { namespace Data { namespace Tables { private: std::shared_ptr m_tableClient; QueryEntitiesOptions m_operationOptions; - friend class Azure::Data::Tables::TableServicesClient; + friend class Azure::Data::Tables::TableServiceClient; friend class Azure::Core::PagedResponse; void OnNextPage(const Azure::Core::Context& context); diff --git a/sdk/tables/azure-data-tables/inc/azure/data/tables/tables_clients.hpp b/sdk/tables/azure-data-tables/inc/azure/data/tables/tables_clients.hpp index 0a31d0002..5329824e2 100644 --- a/sdk/tables/azure-data-tables/inc/azure/data/tables/tables_clients.hpp +++ b/sdk/tables/azure-data-tables/inc/azure/data/tables/tables_clients.hpp @@ -3,6 +3,7 @@ #pragma once +#include "azure/data/tables/credentials/azure_sas_credential.hpp" #include "azure/data/tables/credentials/shared_key_credential.hpp" #include "azure/data/tables/models.hpp" #include "azure/data/tables/transactions.hpp" @@ -178,6 +179,21 @@ namespace Azure { namespace Data { namespace Tables { std::string url, const TableClientOptions& options = {}); + /** + * @brief Initializes a new instance of tableClient. + * + * @param serviceUrl The service Url + * @param credential The SAS credential used to sign requests. + * @param tableName The name of the table. + * @param options Optional client options that define the transport pipeline policies for + * authentication, retries, etc., that are applied to every request. + */ + explicit TableClient( + const std::string& serviceUrl, + std::shared_ptr credential, + const std::string& tableName, + const TableClientOptions& options = {}); + /** * @brief Initializes a new instance of tableClient. * @@ -192,22 +208,6 @@ namespace Azure { namespace Data { namespace Tables { const std::string& tableName, const TableClientOptions& options = {}); - /** - * @brief Create the table indicated in the tableName field of the client. - * - * @param context for canceling long running operations. - * @return Create table result. - */ - Response Create(Core::Context const& context = {}); - - /** - * @brief Delete the table indicated in the tableName field of the client. - * - * @param context for canceling long running operations. - * @return Delete table result. - */ - Response Delete(Core::Context const& context = {}); - /** * @brief Get table access policy. * @@ -300,6 +300,7 @@ namespace Azure { namespace Data { namespace Tables { Models::QueryEntitiesPagedResponse QueryEntities( Models::QueryEntitiesOptions const& options = {}, Core::Context const& context = {}); + /** * @brief Creates a new transaction. * @@ -323,21 +324,20 @@ namespace Azure { namespace Data { namespace Tables { std::shared_ptr m_pipeline; Core::Url m_url; std::string m_tableName; - Models::TableEntity DeserializeEntity(Azure::Core::Json::_internal::json json); }; /** - * @brief Table Services Client + * @brief Table Service Client */ - class TableServicesClient final { + class TableServiceClient final { public: /** - * @brief Initializes a new instance of tableServicesClient. + * @brief Initializes a new instance of tableServiceClient. * * @param options Optional client options that define the transport pipeline policies for * authentication, retries, etc., that are applied to every request. */ - explicit TableServicesClient(const TableClientOptions& options = {}); + explicit TableServiceClient(const TableClientOptions& options = {}); /** * @brief Initializes a new instance of tableClient. @@ -347,7 +347,7 @@ namespace Azure { namespace Data { namespace Tables { * @param options Optional client options that define the transport pipeline policies for * authentication, retries, etc., that are applied to every request. */ - explicit TableServicesClient( + explicit TableServiceClient( const std::string& serviceUrl, const TableClientOptions& options = {}); @@ -360,7 +360,7 @@ namespace Azure { namespace Data { namespace Tables { * @param options Optional client options that define the transport pipeline policies for * authentication, retries, etc., that are applied to every request. */ - explicit TableServicesClient( + explicit TableServiceClient( const std::string& serviceUrl, std::shared_ptr credential, const TableClientOptions& options = {}); @@ -374,31 +374,68 @@ namespace Azure { namespace Data { namespace Tables { * @param options Optional client options that define the transport pipeline policies for * authentication, retries, etc., that are applied to every request. */ - explicit TableServicesClient( + explicit TableServiceClient( const std::string& serviceUrl, std::shared_ptr credential, const TableClientOptions& options = {}); + + /** + * @brief Initializes a new instance of tableClient. + * + * @param serviceUrl A url referencing the table that includes the name of the account and the + * name of the table. + * @param credential The SAS credential used to sign requests. + * @param options Optional client options that define the transport pipeline policies for + * authentication, retries, etc., that are applied to every request. + */ + explicit TableServiceClient( + const std::string& serviceUrl, + std::shared_ptr credential, + const TableClientOptions& options = {}); + /** * @brief Initializes a new instance of tableClient. * * @param connectionString the connection string used to initialize. * @param options Optional client options that define the transport pipeline policies for * authentication, retries, etc., that are applied to every request. - * @return TableServicesClient. + * @return TableServiceClient. */ - static TableServicesClient CreateFromConnectionString( + static TableServiceClient CreateFromConnectionString( const std::string& connectionString, const TableClientOptions& options = {}); /** - * @brief List tables. + * @brief Create the table indicated in the tableName field of the client. + * + * @param context for canceling long running operations. + * @param tableName The name of the table to be created. + * @return Create table result. + */ + Response CreateTable( + std::string const& tableName, + Core::Context const& context = {}); + + /** + * @brief Delete the table indicated in the tableName field of the client. + * + * @param context for canceling long running operations. + * @param tableName The name of the table to be deleted. + * @return Delete table result. + */ + Response DeleteTable( + std::string const& tableName, + Core::Context const& context = {}); + + /** + * @brief Query tables. * * @param options Optional parameters to execute this function. * @param context for canceling long running operations. * @return List tables paged response. */ - Models::ListTablesPagedResponse ListTables( - const Models::ListTablesOptions& options = {}, + Models::QueryTablesPagedResponse QueryTables( + const Models::QueryTablesOptions& options = {}, const Azure::Core::Context& context = {}) const; /** diff --git a/sdk/tables/azure-data-tables/inc/azure/data/tables/tables_sas_builder.hpp b/sdk/tables/azure-data-tables/inc/azure/data/tables/tables_sas_builder.hpp new file mode 100644 index 000000000..86b6cc6cb --- /dev/null +++ b/sdk/tables/azure-data-tables/inc/azure/data/tables/tables_sas_builder.hpp @@ -0,0 +1,173 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +#include "azure/data/tables/account_sas_builder.hpp" +#include "azure/data/tables/credentials/shared_key_credential.hpp" +#include "azure/data/tables/enum_operators.hpp" + +#include +#include + +#include +#include + +namespace Azure { namespace Data { namespace Tables { namespace Sas { + /** + * @brief Defines the protocols permitted for Storage requests made with a shared + * access signature. + */ + enum class TablesSasProtocol + { + /** + * @brief No protocol has been specified. If no value is specified, + * the service will default to HttpsAndHttp. + */ + None = 0, + + /** + * @brief Only requests issued over HTTPS or HTTP will be permitted. + */ + HttpsAndHttp = 1, + + /** + * @brief Only requests issued over HTTPS will be permitted. + */ + Https = 2 + }; + + /** + * @brief Contains the list of + * permissions that can be set for a table's access policy. + */ + enum class TablesSasPermissions + { + /** + * @brief Indicates that Read is permitted. + */ + Read = 1, + /** + * @brief Indicates that Add is permitted. + */ + Add = 2, + /** + * @brief Indicates that Delete is permitted. + */ + Delete = 4, + /** + * @brief Indicates that Update is permitted. + */ + Update = 8, + /** + * @brief Indicates that all permissions are set. + */ + All = ~0 + }; + + /** + * @brief TableSasBuilder is used to generate a Shared Access Signature (SAS) for an Azure + * Storage Tables. + */ + class TablesSasBuilder final { + public: + /** + * @brief The optional signed protocol field specifies the protocol permitted for a + * request made with the SAS. + */ + SasProtocol Protocol; + + /** + * @brief Optionally specify the time at which the shared access signature becomes + * valid. This timestamp will be truncated to second. + */ + Azure::Nullable StartsOn; + + /** + * @brief The time at which the shared access signature becomes invalid. This field must + * be omitted if it has been specified in an associated stored access policy. This timestamp + * will be truncated to second. + */ + Azure::DateTime ExpiresOn; + + /** + * @brief Specifies an IP address or a range of IP addresses from which to accept + * requests. If the IP address from which the request originates does not match the IP address + * or address range specified on the SAS token, the request is not authenticated. When + * specifying a range of IP addresses, note that the range is inclusive. + */ + Azure::Nullable IPRange; + + /** + * @brief An optional unique value up to 64 characters in length that correlates to an + * access policy specified for the table. + */ + std::string Identifier; + + /** + * @brief The name of the table being made accessible. + */ + std::string TableName; + + /** + * @brief The optional start of the partition key values range being made available. + */ + std::string PartitionKeyStart; + + /** + * @brief The optional end of the partition key values range being made available. + */ + std::string PartitionKeyEnd; + + /** + * @brief The optional start of the row key values range being made available. + */ + std::string RowKeyStart; + + /** + * @brief The optional end of the partition key values range being made available. + */ + std::string RowKeyEnd; + + /** + * @brief Sets the permissions for the table SAS. + * + * @param permissions The allowed permissions. + */ + void SetPermissions(TablesSasPermissions permissions); + + /** + * @brief Sets the permissions for the SAS using a raw permissions string. + * + * @param rawPermissions Raw permissions string for the SAS. + */ + void SetPermissions(std::string rawPermissions) { Permissions = std::move(rawPermissions); } + + /** + * @brief Uses the StorageSharedKeyCredential to sign this shared access signature, to produce + * the proper SAS query parameters for authentication requests. + * + * @param credential The storage account's shared key credential. + * @return The SAS query parameters used for authenticating requests. + */ + std::string GenerateSasToken( + const Azure::Data::Tables::Credentials::SharedKeyCredential& credential); + + /** + * @brief Gets the canonical path for the shared access signature. + * + * @param credential The storage account's shared key credential. + * @return Canonical path. + */ + std::string GetCanonicalName( + const Azure::Data::Tables::Credentials::SharedKeyCredential& credential) const + { + return Azure::Core::_internal::StringExtensions::ToLower( + "/table/" + credential.AccountName + "/" + TableName); + } + + private: + std::string Permissions; + }; + +}}}} // namespace Azure::Data::Tables::Sas diff --git a/sdk/tables/azure-data-tables/samples/tables_entity_operations.cpp b/sdk/tables/azure-data-tables/samples/tables_entity_operations.cpp index 879f3dd74..6a3165da8 100644 --- a/sdk/tables/azure-data-tables/samples/tables_entity_operations.cpp +++ b/sdk/tables/azure-data-tables/samples/tables_entity_operations.cpp @@ -29,14 +29,14 @@ const std::string TableName = "table"; int main() { - auto tableServiceClient = TableServicesClient::CreateFromConnectionString(GetConnectionString()); + auto tableServiceClient = TableServiceClient::CreateFromConnectionString(GetConnectionString()); auto tableClient = TableClient::CreateFromConnectionString(GetConnectionString(), TableName); // create new table - tableClient.Create(); + tableServiceClient.CreateTable(TableName); // list tables - auto tables = tableServiceClient.ListTables(); + auto tables = tableServiceClient.QueryTables(); for (auto table : tables.Tables) { std::cout << table.TableName << std::endl; @@ -67,6 +67,6 @@ int main() auto deleteResponse = tableClient.DeleteEntity(entity); // delete existing table - tableClient.Delete(); + tableServiceClient.DeleteTable(TableName); return 0; } diff --git a/sdk/tables/azure-data-tables/samples/tables_getting_started.cpp b/sdk/tables/azure-data-tables/samples/tables_getting_started.cpp index dca573259..dc03a11f7 100644 --- a/sdk/tables/azure-data-tables/samples/tables_getting_started.cpp +++ b/sdk/tables/azure-data-tables/samples/tables_getting_started.cpp @@ -29,14 +29,14 @@ const std::string TableName = "sample1"; int main() { - auto tableServiceClient = TableServicesClient::CreateFromConnectionString(GetConnectionString()); + auto tableServiceClient = TableServiceClient::CreateFromConnectionString(GetConnectionString()); auto tableClient = TableClient::CreateFromConnectionString(GetConnectionString(), TableName); // create new table - tableClient.Create(); + tableServiceClient.CreateTable(TableName); - // list tables - auto tables = tableServiceClient.ListTables(); + // query tables + auto tables = tableServiceClient.QueryTables(); // print table names for (auto table : tables.Tables) @@ -44,6 +44,6 @@ int main() std::cout << table.TableName << std::endl; } // delete existing table - tableClient.Delete(); + tableServiceClient.DeleteTable(TableName); return 0; } diff --git a/sdk/tables/azure-data-tables/samples/tables_service_operations.cpp b/sdk/tables/azure-data-tables/samples/tables_service_operations.cpp index 19b3f61d5..ca72dcad2 100644 --- a/sdk/tables/azure-data-tables/samples/tables_service_operations.cpp +++ b/sdk/tables/azure-data-tables/samples/tables_service_operations.cpp @@ -29,10 +29,10 @@ const std::string TableName = "sample1"; int main() { - auto tableServiceClient = TableServicesClient::CreateFromConnectionString(GetConnectionString()); + auto tableServiceClient = TableServiceClient::CreateFromConnectionString(GetConnectionString()); - // list tables - auto tables = tableServiceClient.ListTables(); + // query tables + auto tables = tableServiceClient.QueryTables(); // print table names for (auto table : tables.Tables) diff --git a/sdk/tables/azure-data-tables/src/models.cpp b/sdk/tables/azure-data-tables/src/models.cpp new file mode 100644 index 000000000..7feb49f5c --- /dev/null +++ b/sdk/tables/azure-data-tables/src/models.cpp @@ -0,0 +1,15 @@ +#include "azure/data/tables/models.hpp" +namespace Azure { namespace Data { namespace Tables { namespace Models { + const GeoReplicationStatus GeoReplicationStatus::Live("live"); + const GeoReplicationStatus GeoReplicationStatus::Bootstrap("bootstrap"); + const GeoReplicationStatus GeoReplicationStatus::Unavailable("unavailable"); + + const TableEntityDataType TableEntityDataType::EdmBinary("Edm.Binary"); + const TableEntityDataType TableEntityDataType::EdmBoolean("Edm.Boolean"); + const TableEntityDataType TableEntityDataType::EdmDateTime("Edm.DateTime"); + const TableEntityDataType TableEntityDataType::EdmDouble("Edm.Double"); + const TableEntityDataType TableEntityDataType::EdmGuid("Edm.Guid"); + const TableEntityDataType TableEntityDataType::EdmInt32("Edm.Int32"); + const TableEntityDataType TableEntityDataType::EdmInt64("Edm.Int64"); + const TableEntityDataType TableEntityDataType::EdmString("Edm.String"); +}}}} // namespace Azure::Data::Tables::Models diff --git a/sdk/tables/azure-data-tables/src/serializers.cpp b/sdk/tables/azure-data-tables/src/serializers.cpp index 533ec5027..a6e692f8a 100644 --- a/sdk/tables/azure-data-tables/src/serializers.cpp +++ b/sdk/tables/azure-data-tables/src/serializers.cpp @@ -20,7 +20,6 @@ namespace Azure { namespace Data { namespace Tables { namespace _detail { { jsonRoot[entry.first] = entry.second; } - jsonBody = jsonRoot.dump(); } return jsonBody; @@ -28,38 +27,12 @@ namespace Azure { namespace Data { namespace Tables { namespace _detail { std::string const Serializers::MergeEntity(Models::TableEntity const& tableEntity) { - std::string jsonBody; - { - auto jsonRoot = Core::Json::_internal::json::object(); - - jsonRoot["PartitionKey"] = tableEntity.PartitionKey; - jsonRoot["RowKey"] = tableEntity.RowKey; - for (auto entry : tableEntity.Properties) - { - jsonRoot[entry.first] = entry.second; - } - - jsonBody = jsonRoot.dump(); - } - return jsonBody; + return CreateEntity(tableEntity); } std::string const Serializers::UpdateEntity(Models::TableEntity const& tableEntity) { - std::string jsonBody; - { - auto jsonRoot = Core::Json::_internal::json::object(); - - jsonRoot["PartitionKey"] = tableEntity.PartitionKey; - jsonRoot["RowKey"] = tableEntity.RowKey; - for (auto entry : tableEntity.Properties) - { - jsonRoot[entry.first] = entry.second; - } - - jsonBody = jsonRoot.dump(); - } - return jsonBody; + return CreateEntity(tableEntity); } std::string const Serializers::SetAccessPolicy(Models::TableAccessPolicy const& tableAccessPolicy) @@ -539,4 +512,34 @@ namespace Azure { namespace Data { namespace Tables { namespace _detail { } return response; } + + Models::TableEntity Serializers::DeserializeEntity(Azure::Core::Json::_internal::json json) + { + Models::TableEntity tableEntity{}; + if (json.contains("PartitionKey")) + { + tableEntity.PartitionKey = json["PartitionKey"].get(); + } + if (json.contains("RowKey")) + { + tableEntity.RowKey = json["RowKey"].get(); + } + if (json.contains("odata.etag")) + { + tableEntity.ETag = json["odata.etag"].get(); + } + for (auto properties : json.get>()) + { + if (properties.first.find("odata.type") != std::string::npos) + { + tableEntity.DataType = Models::TableEntityDataType(properties.second); + } + if (properties.first != "odata.metadata" && properties.first != "PartitionKey" + && properties.first != "RowKey" && properties.first != "odata.etag") + { + tableEntity.Properties[properties.first] = properties.second; + } + } + return tableEntity; + } }}}} // namespace Azure::Data::Tables::_detail diff --git a/sdk/tables/azure-data-tables/src/tables_clients.cpp b/sdk/tables/azure-data-tables/src/tables_clients.cpp index 78693cd7b..16263070f 100644 --- a/sdk/tables/azure-data-tables/src/tables_clients.cpp +++ b/sdk/tables/azure-data-tables/src/tables_clients.cpp @@ -19,7 +19,7 @@ using namespace Azure::Data::Tables::_detail::Xml; using namespace Azure::Data::Tables::Credentials::_detail; using namespace Azure::Data::Tables::_detail; -TableServicesClient::TableServicesClient(const TableClientOptions& options) +TableServiceClient::TableServiceClient(const TableClientOptions& options) { TableClientOptions newOptions = options; std::vector> perRetryPolicies; @@ -37,7 +37,7 @@ TableServicesClient::TableServicesClient(const TableClientOptions& options) std::move(perOperationPolicies)); } -TableServicesClient::TableServicesClient( +TableServiceClient::TableServiceClient( const std::string& serviceUrl, const TableClientOptions& options) { @@ -57,11 +57,11 @@ TableServicesClient::TableServicesClient( std::move(perOperationPolicies)); } -TableServicesClient::TableServicesClient( +TableServiceClient::TableServiceClient( const std::string& serviceUrl, std::shared_ptr credential, const TableClientOptions& options) - : TableServicesClient(options) + : TableServiceClient(options) { TableClientOptions newOptions = options; @@ -96,7 +96,7 @@ TableServicesClient::TableServicesClient( std::move(perOperationPolicies)); } -TableServicesClient::TableServicesClient( +TableServiceClient::TableServiceClient( const std::string& serviceUrl, std::shared_ptr credential, const TableClientOptions& options) @@ -135,7 +135,15 @@ TableServicesClient::TableServicesClient( std::move(perOperationPolicies2)); } -TableServicesClient TableServicesClient::CreateFromConnectionString( +TableServiceClient::TableServiceClient( + const std::string& serviceUrl, + std::shared_ptr credential, + const TableClientOptions& options) + : TableServiceClient(std::string{serviceUrl + credential->GetSignature()}, options) +{ +} + +TableServiceClient TableServiceClient::CreateFromConnectionString( const std::string& connectionString, const TableClientOptions& options) { @@ -144,16 +152,16 @@ TableServicesClient TableServicesClient::CreateFromConnectionString( if (parsedConnectionString.KeyCredential) { - return TableServicesClient( + return TableServiceClient( tablesUrl.GetAbsoluteUrl(), std::move(parsedConnectionString.KeyCredential), options); } else { - return TableServicesClient(options); + return TableServiceClient(options); } } -Azure::Response TableServicesClient::PreflightCheck( +Azure::Response TableServiceClient::PreflightCheck( Models::PreflightCheckOptions const& options, Core::Context const& context) { @@ -174,7 +182,7 @@ Azure::Response TableServicesClient::PreflightChec return Response(std::move(response), std::move(rawResponse)); } -Azure::Response TableServicesClient::SetServiceProperties( +Azure::Response TableServiceClient::SetServiceProperties( Models::SetServicePropertiesOptions const& options, Core::Context const& context) { @@ -204,7 +212,7 @@ Azure::Response TableServicesClient::SetServ return Response(std::move(response), std::move(rawResponse)); } -Azure::Response TableServicesClient::GetServiceProperties( +Azure::Response TableServiceClient::GetServiceProperties( Core::Context const& context) { auto url = m_url; @@ -227,7 +235,7 @@ Azure::Response TableServicesClient::GetServiceP return Response(std::move(response), std::move(pRawResponse)); } -Azure::Response TableServicesClient::GetStatistics( +Azure::Response TableServiceClient::GetStatistics( const Core::Context& context) { auto url = m_url; @@ -407,6 +415,19 @@ TableClient::TableClient( std::move(perOperationPolicies2)); } +TableClient::TableClient( + const std::string& serviceUrl, + std::shared_ptr credential, + const std::string& tableName, + const TableClientOptions& options) + : TableClient( + std::string{ + Azure::Core::Url(serviceUrl).GetAbsoluteUrl() + "/" + credential->GetSignature()}, + tableName, + options) +{ +} + TableClient TableClient::CreateFromConnectionString( const std::string& connectionString, const std::string& tableName, @@ -429,12 +450,14 @@ TableClient TableClient::CreateFromConnectionString( } } -Azure::Response TableClient::Create(Core::Context const& context) +Azure::Response TableServiceClient::CreateTable( + std::string const& tableName, + Core::Context const& context) { auto url = m_url; url.AppendPath("Tables"); - std::string jsonBody = Serializers::Create(m_tableName); + std::string jsonBody = Serializers::Create(tableName); Core::IO::MemoryBodyStream requestBody( reinterpret_cast(jsonBody.data()), jsonBody.length()); @@ -472,14 +495,14 @@ Azure::Response TableClient::Create(Core::Context const& context) return Response(std::move(response), std::move(rawResponse)); } -void Models::ListTablesPagedResponse::OnNextPage(const Azure::Core::Context& context) +void Models::QueryTablesPagedResponse::OnNextPage(const Azure::Core::Context& context) { m_operationOptions.ContinuationToken = NextPageToken; - *this = m_tableServiceClient->ListTables(m_operationOptions, context); + *this = m_tableServiceClient->QueryTables(m_operationOptions, context); } -Models::ListTablesPagedResponse TableServicesClient::ListTables( - Models::ListTablesOptions const& options, +Models::QueryTablesPagedResponse TableServiceClient::QueryTables( + Models::QueryTablesOptions const& options, Azure::Core::Context const& context) const { auto url = m_url; @@ -498,7 +521,7 @@ Models::ListTablesPagedResponse TableServicesClient::ListTables( throw Core::RequestFailedException(rawResponse); } - Models::ListTablesPagedResponse response; + Models::QueryTablesPagedResponse response; { auto const& responseBody = rawResponse->GetBody(); std::string responseString = std::string(responseBody.begin(), responseBody.end()); @@ -521,7 +544,7 @@ Models::ListTablesPagedResponse TableServicesClient::ListTables( response.ServiceEndpoint = url.GetAbsoluteUrl(); response.Prefix = options.Prefix; - response.m_tableServiceClient = std::make_shared(*this); + response.m_tableServiceClient = std::make_shared(*this); response.m_operationOptions = options; response.CurrentPageToken = options.ContinuationToken.ValueOr(std::string()); response.RawResponse = std::move(response.RawResponse); @@ -585,10 +608,12 @@ Azure::Response TableClient::GetAccessPolicy( return Response(std::move(response), std::move(pRawResponse)); } -Azure::Response TableClient::Delete(Core::Context const& context) +Azure::Response TableServiceClient::DeleteTable( + std::string const& tableName, + Core::Context const& context) { auto url = m_url; - url.AppendPath("Tables('" + m_tableName + "')"); + url.AppendPath("Tables('" + tableName + "')"); Core::Http::Request request(Core::Http::HttpMethod::Delete, url); @@ -602,9 +627,9 @@ Azure::Response TableClient::Delete(Core::Context const& c throw Core::RequestFailedException(rawResponse); } - Models::DeleteResult response{}; + Models::DeleteTableResult response{}; - return Response(std::move(response), std::move(rawResponse)); + return Response(std::move(response), std::move(rawResponse)); } Azure::Response TableClient::CreateEntity( @@ -616,19 +641,7 @@ Azure::Response TableClient::CreateEntity( auto url = m_url; url.AppendPath(m_tableName); - std::string jsonBody; - { - auto jsonRoot = Core::Json::_internal::json::object(); - - jsonRoot["PartitionKey"] = tableEntity.PartitionKey; - jsonRoot["RowKey"] = tableEntity.RowKey; - for (auto entry : tableEntity.Properties) - { - jsonRoot[entry.first] = entry.second; - } - - jsonBody = jsonRoot.dump(); - } + std::string jsonBody = Serializers::CreateEntity(tableEntity); Core::IO::MemoryBodyStream requestBody( reinterpret_cast(jsonBody.data()), jsonBody.length()); @@ -865,45 +878,19 @@ Models::QueryEntitiesPagedResponse TableClient::QueryEntities( if (!jsonRoot.contains("value")) { - response.TableEntities.emplace_back(DeserializeEntity(jsonRoot)); + response.TableEntities.emplace_back(Serializers::DeserializeEntity(jsonRoot)); } else { for (auto value : jsonRoot["value"]) { - response.TableEntities.emplace_back(DeserializeEntity(value)); + response.TableEntities.emplace_back(Serializers::DeserializeEntity(value)); } } } return response; } -Models::TableEntity TableClient::DeserializeEntity(Azure::Core::Json::_internal::json json) -{ - Models::TableEntity tableEntity{}; - if (json.contains("PartitionKey")) - { - tableEntity.PartitionKey = json["PartitionKey"].get(); - } - if (json.contains("PartitionKey")) - { - tableEntity.RowKey = json["RowKey"].get(); - } - if (json.contains("PartitionKey")) - { - tableEntity.ETag = json["odata.etag"].get(); - } - for (auto properties : json.get>()) - { - if (properties.first != "odata.metadata" && properties.first != "PartitionKey" - && properties.first != "RowKey" && properties.first != "odata.etag") - { - tableEntity.Properties[properties.first] = properties.second; - } - } - return tableEntity; -} - Transaction TableClient::CreateTransaction(std::string const& partitionKey) { return Transaction(m_url.GetAbsoluteUrl(), m_tableName, partitionKey); diff --git a/sdk/tables/azure-data-tables/src/tables_sas_builder.cpp b/sdk/tables/azure-data-tables/src/tables_sas_builder.cpp new file mode 100644 index 000000000..c52eb51b3 --- /dev/null +++ b/sdk/tables/azure-data-tables/src/tables_sas_builder.cpp @@ -0,0 +1,155 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "azure/data/tables/tables_sas_builder.hpp" + +#include "azure/data/tables/internal/cryptography/hmacsha256.hpp" +#include "azure/data/tables/internal/cryptography/url_encode.hpp" + +#include +#include + +namespace Azure { namespace Data { namespace Tables { namespace Sas { + namespace { + constexpr static const char* SasVersion = "2019-07-07"; + } + + void TablesSasBuilder::SetPermissions(TablesSasPermissions permissions) + { + Permissions.clear(); + // The order matters + if ((permissions & TablesSasPermissions::Read) == TablesSasPermissions::Read) + { + Permissions += "r"; + } + if ((permissions & TablesSasPermissions::Add) == TablesSasPermissions::Add) + { + Permissions += "a"; + } + if ((permissions & TablesSasPermissions::Update) == TablesSasPermissions::Update) + { + Permissions += "u"; + } + if ((permissions & TablesSasPermissions::Delete) == TablesSasPermissions::Delete) + { + Permissions += "d"; + } + } + + std::string TablesSasBuilder::GenerateSasToken( + const Azure::Data::Tables::Credentials::SharedKeyCredential& credential) + { + std::string canonicalName + = Azure::Data::Tables::_detail::Cryptography::UrlUtils::UrlEncodeQueryParameter( + GetCanonicalName(credential)); + + std::string protocol = _detail::SasProtocolToString(Protocol); + + std::string startsOnStr = StartsOn.HasValue() + ? StartsOn.Value().ToString( + Azure::DateTime::DateFormat::Rfc3339, Azure::DateTime::TimeFractionFormat::Truncate) + : ""; + std::string expiresOnStr = ExpiresOn.ToString( + Azure::DateTime::DateFormat::Rfc3339, Azure::DateTime::TimeFractionFormat::Truncate); + // the order here matters + std::string stringToSign = Permissions + "\n" + startsOnStr + "\n" + expiresOnStr + "\n" + + canonicalName + "\n" + Identifier + "\n" + (IPRange.HasValue() ? IPRange.Value() : "") + + "\n" + protocol + "\n" + SasVersion + "\n" + PartitionKeyStart + "\n" + RowKeyStart + "\n" + + PartitionKeyEnd + "\n" + RowKeyEnd; + + std::string signature = Azure::Core::Convert::Base64Encode( + Azure::Data::Tables::_detail::Cryptography::HmacSha256::Compute( + std::vector(stringToSign.begin(), stringToSign.end()), + Azure::Core::Convert::Base64Decode(credential.GetAccountKey()))); + + Azure::Core::Url builder; + + builder.AppendQueryParameter( + "sv", + Azure::Data::Tables::_detail::Cryptography::UrlUtils::UrlEncodeQueryParameter(SasVersion)); + + builder.AppendQueryParameter( + "tn", + Azure::Data::Tables::_detail::Cryptography::UrlUtils::UrlEncodeQueryParameter(TableName)); + + builder.AppendQueryParameter( + "spr", + Azure::Data::Tables::_detail::Cryptography::UrlUtils::UrlEncodeQueryParameter(protocol)); + + if (!startsOnStr.empty()) + { + builder.AppendQueryParameter( + "st", + Azure::Data::Tables::_detail::Cryptography::UrlUtils::UrlEncodeQueryParameter( + startsOnStr)); + } + + if (!expiresOnStr.empty()) + { + builder.AppendQueryParameter( + "se", + Azure::Data::Tables::_detail::Cryptography::UrlUtils::UrlEncodeQueryParameter( + expiresOnStr)); + } + + if (IPRange.HasValue()) + { + builder.AppendQueryParameter( + "sip", + Azure::Data::Tables::_detail::Cryptography::UrlUtils::UrlEncodeQueryParameter( + IPRange.Value())); + } + + if (!Identifier.empty()) + { + builder.AppendQueryParameter( + "si", + Azure::Data::Tables::_detail::Cryptography::UrlUtils::UrlEncodeQueryParameter( + Identifier)); + } + + if (!Permissions.empty()) + { + builder.AppendQueryParameter( + "sp", + Azure::Data::Tables::_detail::Cryptography::UrlUtils::UrlEncodeQueryParameter( + Permissions)); + } + + builder.AppendQueryParameter( + "sig", + Azure::Data::Tables::_detail::Cryptography::UrlUtils::UrlEncodeQueryParameter(signature)); + + if (!PartitionKeyStart.empty()) + { + builder.AppendQueryParameter( + "spk", + Azure::Data::Tables::_detail::Cryptography::UrlUtils::UrlEncodeQueryParameter( + PartitionKeyStart)); + if (!PartitionKeyEnd.empty()) + { + builder.AppendQueryParameter( + "epk", + Azure::Data::Tables::_detail::Cryptography::UrlUtils::UrlEncodeQueryParameter( + PartitionKeyEnd)); + } + } + + if (!RowKeyStart.empty()) + { + builder.AppendQueryParameter( + "srk", + Azure::Data::Tables::_detail::Cryptography::UrlUtils::UrlEncodeQueryParameter( + RowKeyStart)); + if (!RowKeyEnd.empty()) + { + builder.AppendQueryParameter( + "erk", + Azure::Data::Tables::_detail::Cryptography::UrlUtils::UrlEncodeQueryParameter( + RowKeyEnd)); + } + } + + return builder.GetAbsoluteUrl(); + } +}}}} // namespace Azure::Data::Tables::Sas diff --git a/sdk/tables/azure-data-tables/test/ut/CMakeLists.txt b/sdk/tables/azure-data-tables/test/ut/CMakeLists.txt index 7d6a3db98..8c7e915f2 100644 --- a/sdk/tables/azure-data-tables/test/ut/CMakeLists.txt +++ b/sdk/tables/azure-data-tables/test/ut/CMakeLists.txt @@ -16,7 +16,10 @@ SetUpTestProxy("sdk/tables") add_executable ( azure-data-tables-test + enum_operators_test.hpp + enum_operators_test.cpp macro_guard.cpp + sas_test.cpp serializers_test.hpp serializers_test.cpp shared_key_lite_policy_test.cpp diff --git a/sdk/tables/azure-data-tables/test/ut/enum_operators_test.cpp b/sdk/tables/azure-data-tables/test/ut/enum_operators_test.cpp new file mode 100644 index 000000000..605027c8f --- /dev/null +++ b/sdk/tables/azure-data-tables/test/ut/enum_operators_test.cpp @@ -0,0 +1,147 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "enum_operators_test.hpp" + +using namespace Azure::Data::Tables::Sas; +using namespace Azure::Data::Tables; +namespace Azure { namespace Data { namespace Test { + TEST(EnumOperator, AndTest) + { + { + constexpr auto val = TestEnum::One & TestEnum::Two; + EXPECT_EQ(val, TestEnum::Zero); + } + { + auto val = TestEnum::One & TestEnum::One; + EXPECT_EQ(val, TestEnum::One); + } + { + auto val = TestEnum::Two & TestEnum::One; + EXPECT_EQ(val, TestEnum::Zero); + } + { + auto val = TestEnum::Two & TestEnum::Two; + EXPECT_EQ(val, TestEnum::Two); + } + { + auto val = TestEnum::One; + val &= TestEnum::Two; + EXPECT_EQ(val, TestEnum::Zero); + } + { + auto val = TestEnum::One; + val &= TestEnum::One; + EXPECT_EQ(val, TestEnum::One); + } + { + auto val = TestEnum::Two; + val &= TestEnum::One; + EXPECT_EQ(val, TestEnum::Zero); + } + { + auto val = TestEnum::Two; + val &= TestEnum::Two; + EXPECT_EQ(val, TestEnum::Two); + } + } + + TEST(EnumOperator, OrTest) + { + { + constexpr auto val = TestEnum::One | TestEnum::Two; + EXPECT_EQ(val, TestEnum::Three); + } + { + auto val = TestEnum::One | TestEnum::One; + EXPECT_EQ(val, TestEnum::One); + } + { + auto val = TestEnum::Two | TestEnum::One; + EXPECT_EQ(val, TestEnum::Three); + } + { + auto val = TestEnum::Two | TestEnum::Two; + EXPECT_EQ(val, TestEnum::Two); + } + { + auto val = TestEnum::One; + val |= TestEnum::Two; + EXPECT_EQ(val, TestEnum::Three); + } + { + auto val = TestEnum::One; + val |= TestEnum::One; + EXPECT_EQ(val, TestEnum::One); + } + { + auto val = TestEnum::Two; + val |= TestEnum::One; + EXPECT_EQ(val, TestEnum::Three); + } + { + auto val = TestEnum::Two; + val |= TestEnum::Two; + EXPECT_EQ(val, TestEnum::Two); + } + } + + TEST(EnumOperator, XorTest) + { + { + constexpr auto val = TestEnum::One ^ TestEnum::Two; + EXPECT_EQ(val, TestEnum::Three); + } + { + auto val = TestEnum::One ^ TestEnum::One; + EXPECT_EQ(val, TestEnum::Zero); + } + { + auto val = TestEnum::Two ^ TestEnum::One; + EXPECT_EQ(val, TestEnum::Three); + } + { + auto val = TestEnum::Two ^ TestEnum::Two; + EXPECT_EQ(val, TestEnum::Zero); + } + { + auto val = TestEnum::One; + val ^= TestEnum::Two; + EXPECT_EQ(val, TestEnum::Three); + } + { + auto val = TestEnum::One; + val ^= TestEnum::One; + EXPECT_EQ(val, TestEnum::Zero); + } + { + auto val = TestEnum::Two; + val ^= TestEnum::One; + EXPECT_EQ(val, TestEnum::Three); + } + { + auto val = TestEnum::Two; + val ^= TestEnum::Two; + EXPECT_EQ(val, TestEnum::Zero); + } + } + + TEST(EnumOperator, ComplementTest) + { + { + constexpr auto val = ~TestEnum::Zero; + EXPECT_EQ(val, TestEnum::All); + } + { + auto val = ~TestEnum::All; + EXPECT_EQ(val, TestEnum::Zero); + } + { + auto val = ~~TestEnum::Zero; + EXPECT_EQ(val, TestEnum::Zero); + } + { + auto val = ~~TestEnum::One; + EXPECT_EQ(val, TestEnum::One); + } + } +}}} // namespace Azure::Data::Test diff --git a/sdk/tables/azure-data-tables/test/ut/enum_operators_test.hpp b/sdk/tables/azure-data-tables/test/ut/enum_operators_test.hpp new file mode 100644 index 000000000..cda17c2e8 --- /dev/null +++ b/sdk/tables/azure-data-tables/test/ut/enum_operators_test.hpp @@ -0,0 +1,21 @@ +#pragma once +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// clang-format off +#include "azure/data/tables/account_sas_builder.hpp" +#include "azure/data/tables/tables_sas_builder.hpp" +#include "test/ut/test_base.hpp" + +namespace Azure { namespace Data { namespace Test { + enum class TestEnum + { + Zero = 0, + One = 1, + Two = 2, + Three = 3, + All = ~0 + }; + + class EnumOperator : public Azure::Core::Test::TestBase {}; +}}} // namespace Azure::Data::Test diff --git a/sdk/tables/azure-data-tables/test/ut/sas_test.cpp b/sdk/tables/azure-data-tables/test/ut/sas_test.cpp new file mode 100644 index 000000000..22a0b7a01 --- /dev/null +++ b/sdk/tables/azure-data-tables/test/ut/sas_test.cpp @@ -0,0 +1,133 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "sas_test.hpp" + +#include +#include +#include +#include + +using namespace Azure::Data::Tables::Sas; +// cspell: words rwdxylacupitf raud bqft +namespace Azure { namespace Data { namespace Test { + TEST(SasTest, TableSasBuilderTestAllSet) + { + TablesSasBuilder sasBuilder; + sasBuilder.SetPermissions(TablesSasPermissions::All); + sasBuilder.Protocol = SasProtocol::HttpsAndHttp; + sasBuilder.StartsOn + = Azure::DateTime::Parse("2020-08-18T00:00:00Z", Azure::DateTime::DateFormat::Rfc3339); + sasBuilder.ExpiresOn + = Azure::DateTime::Parse("2022-08-18T00:00:00Z", Azure::DateTime::DateFormat::Rfc3339); + sasBuilder.Identifier = "myIdentifier"; + sasBuilder.IPRange = "iprange"; + sasBuilder.TableName = "myTableName"; + sasBuilder.RowKeyEnd = "myRowKeyEnd"; + sasBuilder.RowKeyStart = "myRowKeyStart"; + sasBuilder.PartitionKeyStart = "myStartPartitionKey"; + sasBuilder.PartitionKeyEnd = "myEndPartitionKey"; + std::string key = "accountKey"; + Azure::Data::Tables::Credentials::SharedKeyCredential cred( + "accountName", + Azure::Core::Convert::Base64Encode(std::vector(key.begin(), key.end()))); + auto sasToken = sasBuilder.GenerateSasToken(cred); + auto sasParts = SasTest::ParseQueryParameters(sasToken); + EXPECT_EQ(sasParts.at("si"), "myIdentifier"); + EXPECT_EQ(sasParts.at("sp"), "raud"); + EXPECT_EQ(sasParts.at("st"), "2020-08-18T00:00:00Z"); + EXPECT_EQ(sasParts.at("se"), "2022-08-18T00:00:00Z"); + EXPECT_EQ(sasParts.at("sip"), "iprange"); + EXPECT_EQ(sasParts.at("spr"), "https,http"); + EXPECT_FALSE(sasParts.at("sig").empty()); + EXPECT_EQ(sasParts.at("srk"), "myRowKeyStart"); + EXPECT_EQ(sasParts.at("erk"), "myRowKeyEnd"); + EXPECT_EQ(sasParts.at("spk"), "myStartPartitionKey"); + EXPECT_EQ(sasParts.at("?epk"), "myEndPartitionKey"); + } + TEST(SasTest, TableSasBuilderTestSomeSet) + { + TablesSasBuilder sasBuilder; + + sasBuilder.Protocol = SasProtocol::HttpsAndHttp; + + sasBuilder.ExpiresOn + = Azure::DateTime::Parse("2022-03-11T11:13:52Z", Azure::DateTime::DateFormat::Rfc3339); + sasBuilder.SetPermissions(TablesSasPermissions::Add); + sasBuilder.TableName = "someTableName"; + + std::string key = "*"; + Azure::Data::Tables::Credentials::SharedKeyCredential cred( + "someaccount", + Azure::Core::Convert::Base64Encode(std::vector(key.begin(), key.end()))); + auto sasToken = sasBuilder.GenerateSasToken(cred); + auto sasParts = SasTest::ParseQueryParameters(sasToken); + EXPECT_EQ(sasParts.at("?se"), "2022-03-11T11:13:52Z"); + EXPECT_EQ(sasParts.at("sp"), "a"); + EXPECT_EQ(sasParts.at("spr"), "https,http"); + EXPECT_EQ(sasParts.at("tn"), "someTableName"); + } + + TEST(SasTest, TableSasBuilderTestMin) + { + TablesSasBuilder sasBuilder; + sasBuilder.ExpiresOn + = Azure::DateTime::Parse("2022-08-18T00:00:00Z", Azure::DateTime::DateFormat::Rfc3339); + std::string key = "accountKey"; + Azure::Data::Tables::Credentials::SharedKeyCredential cred( + "accountName", + Azure::Core::Convert::Base64Encode(std::vector(key.begin(), key.end()))); + auto sasToken = sasBuilder.GenerateSasToken(cred); + auto sasParts = SasTest::ParseQueryParameters(sasToken); + EXPECT_FALSE(sasParts.at("sig").empty()); + } + + TEST(SasTest, AccountSasBuilderTestAllSet) + { + AccountSasBuilder sasBuilder; + sasBuilder.SetPermissions(AccountSasPermissions::All); + sasBuilder.Protocol = SasProtocol::HttpsAndHttp; + sasBuilder.StartsOn + = Azure::DateTime::Parse("2020-08-18T00:00:00Z", Azure::DateTime::DateFormat::Rfc3339); + sasBuilder.ExpiresOn + = Azure::DateTime::Parse("2022-08-18T00:00:00Z", Azure::DateTime::DateFormat::Rfc3339); + sasBuilder.IPRange = "iprange"; + sasBuilder.EncryptionScope = "myScope"; + sasBuilder.ResourceTypes = AccountSasResource::All; + sasBuilder.Services = AccountSasServices::All; + + std::string key = "accountKey"; + Azure::Data::Tables::Credentials::SharedKeyCredential cred( + "accountName", + Azure::Core::Convert::Base64Encode(std::vector(key.begin(), key.end()))); + auto sasToken = sasBuilder.GenerateSasToken(cred); + auto sasParts = SasTest::ParseQueryParameters(sasToken); + + EXPECT_EQ(sasParts.at("?se"), "2022-08-18T00:00:00Z"); + EXPECT_EQ(sasParts.at("ses"), "myScope"); + EXPECT_FALSE(sasParts.at("sig").empty()); + EXPECT_EQ(sasParts.at("sip"), "iprange"); + EXPECT_EQ(sasParts.at("sp"), "rwdxylacupitf"); + EXPECT_EQ(sasParts.at("spr"), "https,http"); + EXPECT_EQ(sasParts.at("srt"), "sco"); + EXPECT_EQ(sasParts.at("ss"), "bqft"); + EXPECT_EQ(sasParts.at("st"), "2020-08-18T00:00:00Z"); + EXPECT_EQ(sasParts.at("sv"), "2023-08-03"); + } + + TEST(SasTest, AccountSasBuilderTestMin) + { + AccountSasBuilder sasBuilder; + sasBuilder.SetPermissions(AccountSasPermissions::All); + sasBuilder.ExpiresOn + = Azure::DateTime::Parse("2022-08-18T00:00:00Z", Azure::DateTime::DateFormat::Rfc3339); + + std::string key = "accountKey"; + Azure::Data::Tables::Credentials::SharedKeyCredential cred( + "accountName", + Azure::Core::Convert::Base64Encode(std::vector(key.begin(), key.end()))); + auto sasToken = sasBuilder.GenerateSasToken(cred); + auto sasParts = SasTest::ParseQueryParameters(sasToken); + + EXPECT_FALSE(sasParts.at("sig").empty()); + } +}}} // namespace Azure::Data::Test diff --git a/sdk/tables/azure-data-tables/test/ut/sas_test.hpp b/sdk/tables/azure-data-tables/test/ut/sas_test.hpp new file mode 100644 index 000000000..d502014d7 --- /dev/null +++ b/sdk/tables/azure-data-tables/test/ut/sas_test.hpp @@ -0,0 +1,30 @@ +#pragma once +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "azure/data/tables/account_sas_builder.hpp" +#include "azure/data/tables/tables_sas_builder.hpp" +#include "test/ut/test_base.hpp" + +namespace Azure { namespace Data { namespace Test { + + class SasTest : public Azure::Core::Test::TestBase { + public: + static std::map ParseQueryParameters(const std::string& query) + { + std::map result; + + auto parameters = Azure::Core::_internal::StringExtensions::Split(query, '&'); + for (const auto& p : parameters) + { + auto keyValue = Azure::Core::_internal::StringExtensions::Split(p, '='); + if (keyValue.size() == 2) + { + result[keyValue[0]] = keyValue[1]; + } + } + + return result; + } + }; +}}} // namespace Azure::Data::Test diff --git a/sdk/tables/azure-data-tables/test/ut/table_client_test.cpp b/sdk/tables/azure-data-tables/test/ut/table_client_test.cpp index 17697bc66..e22f5b891 100644 --- a/sdk/tables/azure-data-tables/test/ut/table_client_test.cpp +++ b/sdk/tables/azure-data-tables/test/ut/table_client_test.cpp @@ -4,12 +4,14 @@ #include "table_client_test.hpp" #include "azure/data/tables/account_sas_builder.hpp" +#include "azure/data/tables/tables_sas_builder.hpp" #include #include #include #include + using namespace Azure::Data; namespace Azure { namespace Data { namespace Test { std::shared_ptr m_credential; @@ -34,8 +36,8 @@ namespace Azure { namespace Data { namespace Test { switch (param) { case AuthType::ConnectionString: - m_tableServiceClient = std::make_shared( - Tables::TableServicesClient::CreateFromConnectionString( + m_tableServiceClient = std::make_shared( + Tables::TableServiceClient::CreateFromConnectionString( GetEnv("STANDARD_STORAGE_CONNECTION_STRING"), clientOptions)); m_tableClient = std::make_shared( Tables::TableClient::CreateFromConnectionString( @@ -43,8 +45,8 @@ namespace Azure { namespace Data { namespace Test { break; case AuthType::Key: m_credential = GetTestCredential(); - m_tableServiceClient = std::make_shared( - Azure::Data::Tables::TableServicesClient( + m_tableServiceClient = std::make_shared( + Azure::Data::Tables::TableServiceClient( "https://" + GetAccountName() + ".table.core.windows.net/", m_credential, clientOptions)); @@ -63,15 +65,23 @@ namespace Azure { namespace Data { namespace Test { sasBuilder.Services = Azure::Data::Tables::Sas::AccountSasServices::All; sasBuilder.Protocol = Azure::Data::Tables::Sas::SasProtocol::HttpsOnly; sasBuilder.SetPermissions(Azure::Data::Tables::Sas::AccountSasPermissions::All); - auto sasToken = sasBuilder.GenerateSasToken(*creds); - m_tableServiceClient - = std::make_shared(Tables::TableServicesClient( - "https://" + GetAccountName() + ".table.core.windows.net/" + sasToken, - clientOptions)); - m_tableClient = std::make_shared(Tables::TableClient( - "https://" + GetAccountName() + ".table.core.windows.net/" + sasToken, - m_tableName, - tableClientOptions)); + std::string serviceUrl = "https://" + GetAccountName() + ".table.core.windows.net/"; + auto sasCreds = std::make_shared( + sasBuilder.GenerateSasToken(*creds)); + m_tableServiceClient = std::make_shared( + Tables::TableServiceClient(serviceUrl, sasCreds, clientOptions)); + + Azure::Data::Tables::Sas::TablesSasBuilder tableSasBuilder; + tableSasBuilder.Protocol = Azure::Data::Tables::Sas::SasProtocol::HttpsOnly; + tableSasBuilder.StartsOn = std::chrono::system_clock::now() - std::chrono::minutes(5); + tableSasBuilder.ExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60); + tableSasBuilder.SetPermissions(Azure::Data::Tables::Sas::TablesSasPermissions::All); + tableSasBuilder.TableName = m_tableName; + auto tableSasCreds + = std::make_shared( + tableSasBuilder.GenerateSasToken(*creds)); + m_tableClient = std::make_shared( + Tables::TableClient(serviceUrl, tableSasCreds, m_tableName, tableClientOptions)); break; } } @@ -83,8 +93,7 @@ namespace Azure { namespace Data { namespace Test { { try { - - auto deleteResponse = m_tableClient->Delete(); + auto deleteResponse = m_tableServiceClient->DeleteTable(m_tableName); } catch (...) { @@ -115,7 +124,7 @@ namespace Azure { namespace Data { namespace Test { TEST_P(TablesClientTest, CreateTable) { - auto createResponse = m_tableClient->Create(); + auto createResponse = m_tableServiceClient->CreateTable(m_tableName); EXPECT_EQ(createResponse.Value.TableName, m_tableName); EXPECT_EQ(createResponse.Value.EditLink, "Tables('" + m_tableName + "')"); EXPECT_TRUE(createResponse.Value.Type.find(".Tables") != std::string::npos); @@ -129,7 +138,7 @@ namespace Azure { namespace Data { namespace Test { SkipTest(); return; } - auto createResponse = m_tableClient->Create(); + auto createResponse = m_tableServiceClient->CreateTable(m_tableName); auto getResponse = m_tableClient->GetAccessPolicy(); EXPECT_EQ(getResponse.Value.SignedIdentifiers.size(), 0); @@ -142,7 +151,7 @@ namespace Azure { namespace Data { namespace Test { SkipTest(); return; } - auto createResponse = m_tableClient->Create(); + auto createResponse = m_tableServiceClient->CreateTable(m_tableName); Azure::Data::Tables::Models::TableAccessPolicy newPolicy{}; Azure::Data::Tables::Models::SignedIdentifier newIdentifier{}; newIdentifier.Id = "testid"; @@ -179,12 +188,11 @@ namespace Azure { namespace Data { namespace Test { } else { + auto createResponse = m_tableServiceClient->CreateTable(m_tableName); - auto createResponse = m_tableClient->Create(); + Azure::Data::Tables::Models::QueryTablesOptions listOptions; - Azure::Data::Tables::Models::ListTablesOptions listOptions; - - auto listResponse = m_tableServiceClient->ListTables(listOptions); + auto listResponse = m_tableServiceClient->QueryTables(listOptions); for (auto table : listResponse.Tables) { @@ -201,9 +209,9 @@ namespace Azure { namespace Data { namespace Test { TEST_P(TablesClientTest, DeleteTable) { - auto createResponse = m_tableClient->Create(); + auto createResponse = m_tableServiceClient->CreateTable(m_tableName); - auto response = m_tableClient->Delete(); + auto response = m_tableServiceClient->DeleteTable(m_tableName); EXPECT_EQ(response.RawResponse->GetStatusCode(), Azure::Core::Http::HttpStatusCode::NoContent); } @@ -266,7 +274,7 @@ namespace Azure { namespace Data { namespace Test { entity.RowKey = "R1"; entity.Properties["Name"] = "Azure"; entity.Properties["Product"] = "Tables"; - auto createResponse = m_tableClient->Create(); + auto createResponse = m_tableServiceClient->CreateTable(m_tableName); auto response = m_tableClient->CreateEntity(entity); EXPECT_EQ(response.RawResponse->GetStatusCode(), Azure::Core::Http::HttpStatusCode::NoContent); @@ -287,7 +295,7 @@ namespace Azure { namespace Data { namespace Test { entity.RowKey = "R1"; entity.Properties["Name"] = "Azure"; entity.Properties["Product"] = "Tables"; - auto createResponse = m_tableClient->Create(); + auto createResponse = m_tableServiceClient->CreateTable(m_tableName); auto response = m_tableClient->CreateEntity(entity); EXPECT_EQ(response.RawResponse->GetStatusCode(), Azure::Core::Http::HttpStatusCode::NoContent); EXPECT_FALSE(response.Value.ETag.empty()); @@ -322,7 +330,7 @@ namespace Azure { namespace Data { namespace Test { entity.RowKey = "R1"; entity.Properties["Name"] = "Azure"; entity.Properties["Product"] = "Tables"; - auto createResponse = m_tableClient->Create(); + auto createResponse = m_tableServiceClient->CreateTable(m_tableName); auto response = m_tableClient->CreateEntity(entity); EXPECT_EQ(response.RawResponse->GetStatusCode(), Azure::Core::Http::HttpStatusCode::NoContent); EXPECT_FALSE(response.Value.ETag.empty()); @@ -357,7 +365,7 @@ namespace Azure { namespace Data { namespace Test { entity.RowKey = "R1"; entity.Properties["Name"] = "Azure"; entity.Properties["Product"] = "Tables"; - auto createResponse = m_tableClient->Create(); + auto createResponse = m_tableServiceClient->CreateTable(m_tableName); auto response = m_tableClient->CreateEntity(entity); EXPECT_EQ(response.RawResponse->GetStatusCode(), Azure::Core::Http::HttpStatusCode::NoContent); EXPECT_FALSE(response.Value.ETag.empty()); @@ -393,7 +401,7 @@ namespace Azure { namespace Data { namespace Test { entity.RowKey = "R1"; entity.Properties["Name"] = "Azure"; entity.Properties["Product"] = "Tables"; - auto createResponse = m_tableClient->Create(); + auto createResponse = m_tableServiceClient->CreateTable(m_tableName); auto response = m_tableClient->UpsertEntity(entity); EXPECT_EQ(response.RawResponse->GetStatusCode(), Azure::Core::Http::HttpStatusCode::NoContent); EXPECT_FALSE(response.Value.ETag.empty()); @@ -433,7 +441,7 @@ namespace Azure { namespace Data { namespace Test { entity.RowKey = "R1"; entity.Properties["Name"] = "Azure"; entity.Properties["Product"] = "Tables"; - auto createResponse = m_tableClient->Create(); + auto createResponse = m_tableServiceClient->CreateTable(m_tableName); auto response = m_tableClient->CreateEntity(entity); EXPECT_EQ(response.RawResponse->GetStatusCode(), Azure::Core::Http::HttpStatusCode::NoContent); EXPECT_FALSE(response.Value.ETag.empty()); @@ -473,7 +481,7 @@ namespace Azure { namespace Data { namespace Test { entity2.RowKey = "R1"; entity2.Properties["Name"] = "Azure"; entity2.Properties["Product"] = "Tables"; - auto createResponse = m_tableClient->Create(); + auto createResponse = m_tableServiceClient->CreateTable(m_tableName); auto transaction = m_tableClient->CreateTransaction("P1"); transaction.CreateEntity(entity); @@ -501,7 +509,7 @@ namespace Azure { namespace Data { namespace Test { entity2.RowKey = "R2"; entity2.Properties["Name"] = "Azure"; entity2.Properties["Product"] = "Tables"; - auto createResponse = m_tableClient->Create(); + auto createResponse = m_tableServiceClient->CreateTable(m_tableName); auto transaction = m_tableClient->CreateTransaction("P1"); transaction.CreateEntity(entity); @@ -529,7 +537,7 @@ namespace Azure { namespace Data { namespace Test { entity2.RowKey = "R2"; entity2.Properties["Name"] = "Azure"; entity2.Properties["Product"] = "Tables"; - auto createResponse = m_tableClient->Create(); + auto createResponse = m_tableServiceClient->CreateTable(m_tableName); auto transaction = m_tableClient->CreateTransaction("P1"); transaction.CreateEntity(entity); @@ -563,7 +571,7 @@ namespace Azure { namespace Data { namespace Test { entity2.RowKey = "R1"; entity2.Properties["Name"] = "Azure2"; entity2.Properties["Product"] = "Tables3"; - auto createResponse = m_tableClient->Create(); + auto createResponse = m_tableServiceClient->CreateTable(m_tableName); auto transaction = m_tableClient->CreateTransaction("P1"); transaction.CreateEntity(entity); @@ -596,7 +604,7 @@ namespace Azure { namespace Data { namespace Test { entity2.RowKey = "R1"; entity2.Properties["Name"] = "Azure2"; entity2.Properties["Product"] = "Tables3"; - auto createResponse = m_tableClient->Create(); + auto createResponse = m_tableServiceClient->CreateTable(m_tableName); auto transaction = m_tableClient->CreateTransaction("P1"); transaction.CreateEntity(entity); diff --git a/sdk/tables/azure-data-tables/test/ut/table_client_test.hpp b/sdk/tables/azure-data-tables/test/ut/table_client_test.hpp index 616115d83..0714a48da 100644 --- a/sdk/tables/azure-data-tables/test/ut/table_client_test.hpp +++ b/sdk/tables/azure-data-tables/test/ut/table_client_test.hpp @@ -55,7 +55,7 @@ namespace Azure { namespace Data { namespace Test { protected: std::string m_tableName; - std::shared_ptr m_tableServiceClient; + std::shared_ptr m_tableServiceClient; std::shared_ptr m_tableClient; }; }}} // namespace Azure::Data::Test