diff --git a/sdk/tables/assets.json b/sdk/tables/assets.json index c9b0f1d86..791c88a21 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_69657814a3" + "Tag": "cpp/tables_55f4cc2c1e" } diff --git a/sdk/tables/azure-data-tables/CMakeLists.txt b/sdk/tables/azure-data-tables/CMakeLists.txt index b6aaec865..184112b71 100644 --- a/sdk/tables/azure-data-tables/CMakeLists.txt +++ b/sdk/tables/azure-data-tables/CMakeLists.txt @@ -43,15 +43,13 @@ 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/credentials/named_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 inc/azure/data/tables/internal/policies/shared_key_lite_policy.hpp - inc/azure/data/tables/internal/policies/shared_key_policy.hpp - inc/azure/data/tables/internal/policies/switch_to_secondary_policy.hpp inc/azure/data/tables/internal/policies/tenant_bearer_token_policy.hpp inc/azure/data/tables/internal/policies/timeout_policy.hpp inc/azure/data/tables/internal/serializers.hpp @@ -61,25 +59,21 @@ set( 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 ) set( AZURE_DATA_TABLES_SOURCE src/account_sas_builder.cpp - src/credentials/shared_key_credential.cpp + src/credentials/named_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 src/policies/tenant_bearer_token_policy.cpp src/policies/timeout_policy.cpp src/private/package_version.hpp src/serializers.cpp src/tables_clients.cpp src/tables_sas_builder.cpp - src/transactions.cpp src/xml_wrapper.cpp ) @@ -127,6 +121,7 @@ az_rtti_setup( create_code_coverage(tables azure-data-tables azure-data-tables-test "tests?/*;samples?/*") if(BUILD_TESTING) + add_compile_definitions(_azure_TABLES_TESTING_BUILD) add_subdirectory(test/ut) add_subdirectory(test/stress) endif() 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 859efca68..6d3680ce3 100644 --- a/sdk/tables/azure-data-tables/inc/azure/data/tables.hpp +++ b/sdk/tables/azure-data-tables/inc/azure/data/tables.hpp @@ -10,11 +10,10 @@ #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/credentials/named_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 638a5a826..2b8b1761b 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 @@ -3,7 +3,7 @@ #pragma once -#include "azure/data/tables/credentials/shared_key_credential.hpp" +#include "azure/data/tables/credentials/named_key_credential.hpp" #include "azure/data/tables/enum_operators.hpp" #include @@ -12,7 +12,8 @@ #include namespace Azure { namespace Data { namespace Tables { namespace Sas { - + constexpr const char* HttpsAndHttp = "https,http"; + constexpr const char* HttpsOnly = "https"; /** * @brief Defines the protocols permitted for Storage requests made with a shared access * signature. @@ -33,7 +34,7 @@ namespace Azure { namespace Data { namespace Tables { namespace Sas { namespace _detail { inline std::string SasProtocolToString(SasProtocol protocol) { - return protocol == SasProtocol::HttpsAndHttp ? "https,http" : "https"; + return protocol == SasProtocol::HttpsAndHttp ? HttpsAndHttp : HttpsOnly; } } // namespace _detail @@ -41,7 +42,7 @@ namespace Azure { namespace Data { namespace Tables { namespace Sas { * @brief Specifies the resource types accessible from an account level shared access * signature. */ - enum class AccountSasResource + enum class AccountSasResourceType { /** * @brief Indicates whether service-level APIs are accessible from this shared access @@ -73,28 +74,11 @@ namespace Azure { namespace Data { namespace Tables { namespace Sas { */ enum class AccountSasServices { - /** - * @brief Indicates whether Azure Blob Storage resources are accessible from the shared - * access signature. - */ - Blobs = 1, - - /** - * @brief Indicates whether Azure Queue Storage resources are accessible from the shared - * access signature. - */ - Queue = 2, - - /** - * @brief Indicates whether Azure File Storage resources are accessible from the shared - * access signature. - */ - Files = 4, /** * @brief Indicates whether Azure Table Storage resources are accessible from the shared * access signature. */ - Table = 8, + Table = 1, /** * @brief Indicates all services are accessible from the shared * access signature. @@ -123,54 +107,19 @@ namespace Azure { namespace Data { namespace Tables { namespace Sas { Delete = 4, /** - * @brief Indicates that deleting previous blob version is permitted. + * @brief Indicates that Add is permitted. */ - DeleteVersion = 8, + Add = 8, /** * @brief Indicates that List is permitted. */ List = 16, - /** - * @brief Indicates that Add is permitted. - */ - Add = 32, - - /** - * @brief Indicates that Create is permitted. - */ - Create = 64, - /** * @brief Indicates that Update is permitted. */ - Update = 128, - - /** - * @brief Indicates that Process is permitted. - */ - Process = 256, - - /** - * @brief Indicates that reading and writing tags is permitted. - */ - Tags = 512, - - /** - * @brief Indicates that filtering by tags is permitted. - */ - Filter = 1024, - - /** - * @brief Indicates that setting immutability policy is permitted. - */ - SetImmutabilityPolicy = 2048, - - /** - * @brief Indicates that permanent delete is permitted. - */ - PermanentDelete = 4096, + Update = 32, /** * @brief Indicates that all permissions are set. @@ -220,7 +169,7 @@ namespace Azure { namespace Data { namespace Tables { namespace Sas { * The resource types associated with the shared access signature. The user is * restricted to operations on the specified resources. */ - AccountSasResource ResourceTypes; + AccountSasResourceType ResourceTypes; /** * @brief Optional encryption scope to use when sending requests authorized with this SAS url. @@ -243,16 +192,14 @@ namespace Azure { namespace Data { namespace Tables { namespace Sas { void SetPermissions(std::string rawPermissions) { Permissions = std::move(rawPermissions); } /** - * @brief Uses the StorageSharedKeyCredential to sign this shared access signature, to produce + * @brief Uses the NamedKeyCredential 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. + * @param credential The named key credential. + * @return The SAS query parameters used for authenticating requests. */ std::string GenerateSasToken( - const Azure::Data::Tables::Credentials::SharedKeyCredential& credential); + const Azure::Data::Tables::Credentials::NamedKeyCredential& credential); private: std::string Permissions; 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/named_key_credential.hpp similarity index 83% rename from sdk/tables/azure-data-tables/inc/azure/data/tables/credentials/shared_key_credential.hpp rename to sdk/tables/azure-data-tables/inc/azure/data/tables/credentials/named_key_credential.hpp index 5af53131b..32012c159 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/named_key_credential.hpp @@ -9,7 +9,6 @@ #include #include namespace Azure { namespace Data { namespace Tables { namespace _detail { namespace Policies { - class SharedKeyPolicy; class SharedKeyLitePolicy; }}}}} // namespace Azure::Data::Tables::_detail::Policies @@ -21,19 +20,19 @@ namespace Azure { namespace Data { namespace Tables { namespace Sas { namespace Azure { namespace Data { namespace Tables { namespace Credentials { /** - * @brief A SharedKeyCredential is a credential backed by an account's name and + * @brief A NamedKeyCredential is a credential backed by an account's name and * one of its access keys. */ - class SharedKeyCredential final { + class NamedKeyCredential final { public: /** - * @brief Initializes a new instance of the SharedKeyCredential. + * @brief Initializes a new instance of the NamedKeyCredential. * * @param accountName Name of the account. * @param accountKey Access key of the * account. */ - explicit SharedKeyCredential(std::string accountName, std::string accountKey) + explicit NamedKeyCredential(std::string accountName, std::string accountKey) : AccountName{std::move(accountName)}, m_accountKey{std::move(accountKey)} { } @@ -56,7 +55,6 @@ namespace Azure { namespace Data { namespace Tables { namespace Credentials { const std::string AccountName; private: - friend class Azure::Data::Tables::_detail::Policies::SharedKeyPolicy; friend class Azure::Data::Tables::_detail::Policies::SharedKeyLitePolicy; friend class Azure::Data::Tables::Sas::AccountSasBuilder; friend class Azure::Data::Tables::Sas::TablesSasBuilder; @@ -77,7 +75,7 @@ namespace Azure { namespace Data { namespace Tables { namespace Credentials { std::string AccountName; std::string AccountKey; Azure::Core::Url TableServiceUrl; - std::shared_ptr KeyCredential; + std::shared_ptr KeyCredential; }; ConnectionStringParts ParseConnectionString(const std::string& connectionString); 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 index 55a6105f0..462a3ec44 100644 --- 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 @@ -1,6 +1,14 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +/** + * @file enum_operators.hpp + * @brief Defines bitwise operators for enums. + * @details This file defines bitwise operators for enum classes. This allows the use of the + * operators |, |=, &, &=, ^, ^=, and ~ with enum classes. This is useful for flags enums. + * Example: enum class MyEnum { A = 1, B = 2, C = 4 }; MyEnum e = MyEnum::A | MyEnum::B; + * Example: enum class MyEnum { A = 1, B = 2, C = 4 }; MyEnum e = MyEnum::A; e &= MyEnum::B; + */ #pragma once #include diff --git a/sdk/tables/azure-data-tables/inc/azure/data/tables/internal/policies/shared_key_lite_policy.hpp b/sdk/tables/azure-data-tables/inc/azure/data/tables/internal/policies/shared_key_lite_policy.hpp index 777fdb9ac..094b4b78f 100644 --- a/sdk/tables/azure-data-tables/inc/azure/data/tables/internal/policies/shared_key_lite_policy.hpp +++ b/sdk/tables/azure-data-tables/inc/azure/data/tables/internal/policies/shared_key_lite_policy.hpp @@ -3,7 +3,7 @@ #pragma once -#include "azure/data/tables/credentials/shared_key_credential.hpp" +#include "azure/data/tables/credentials/named_key_credential.hpp" #include @@ -13,7 +13,7 @@ namespace Azure { namespace Data { namespace Tables { namespace _detail { namespace Policies { class SharedKeyLitePolicy final : public Core::Http::Policies::HttpPolicy { public: - explicit SharedKeyLitePolicy(std::shared_ptr credential) + explicit SharedKeyLitePolicy(std::shared_ptr credential) : m_credential{std::move(credential)} { } @@ -37,7 +37,7 @@ namespace Azure { namespace Data { namespace Tables { namespace _detail { namesp } std::string GetSignature(const Core::Http::Request& request) const; - std::shared_ptr m_credential; + std::shared_ptr m_credential; }; }}}}} // namespace Azure::Data::Tables::_detail::Policies diff --git a/sdk/tables/azure-data-tables/inc/azure/data/tables/internal/policies/shared_key_policy.hpp b/sdk/tables/azure-data-tables/inc/azure/data/tables/internal/policies/shared_key_policy.hpp deleted file mode 100644 index d7fc76267..000000000 --- a/sdk/tables/azure-data-tables/inc/azure/data/tables/internal/policies/shared_key_policy.hpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "azure/data/tables/credentials/shared_key_credential.hpp" - -#include - -#include -#include - -namespace Azure { namespace Data { namespace Tables { namespace _detail { namespace Policies { - - class SharedKeyPolicy final : public Core::Http::Policies::HttpPolicy { - public: - explicit SharedKeyPolicy(std::shared_ptr credential) - : m_credential{std::move(credential)} - { - } - - ~SharedKeyPolicy() override {} - - std::unique_ptr Clone() const override - { - return std::make_unique(m_credential); - } - - std::unique_ptr Send( - Core::Http::Request& request, - Core::Http::Policies::NextHttpPolicy nextPolicy, - Core::Context const& context) const override - { - request.SetHeader( - "Authorization", "SharedKey " + m_credential->AccountName + ":" + GetSignature(request)); - return nextPolicy.Send(request, context); - } - - private: - std::string GetSignature(const Core::Http::Request& request) const; - - std::shared_ptr m_credential; - }; - -}}}}} // namespace Azure::Data::Tables::_detail::Policies diff --git a/sdk/tables/azure-data-tables/inc/azure/data/tables/internal/policies/switch_to_secondary_policy.hpp b/sdk/tables/azure-data-tables/inc/azure/data/tables/internal/policies/switch_to_secondary_policy.hpp deleted file mode 100644 index 93aa7b145..000000000 --- a/sdk/tables/azure-data-tables/inc/azure/data/tables/internal/policies/switch_to_secondary_policy.hpp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "azure/data/tables/dll_import_export.hpp" - -#include - -#include -#include - -namespace Azure { namespace Data { namespace Tables { namespace _detail { namespace Policies { - - AZ_DATA_TABLES_DLLEXPORT extern const Azure::Core::Context::Key SecondaryHostReplicaStatusKey; - - inline Azure::Core::Context WithReplicaStatus(const Azure::Core::Context& context) - { - return context.WithValue(SecondaryHostReplicaStatusKey, std::make_shared(true)); - } - - class SwitchToSecondaryPolicy final : public Azure::Core::Http::Policies::HttpPolicy { - public: - explicit SwitchToSecondaryPolicy(std::string primaryHost, std::string secondaryHost) - : m_primaryHost{std::move(primaryHost)}, m_secondaryHost{std::move(secondaryHost)} - { - } - - std::unique_ptr Clone() const override - { - return std::make_unique(*this); - } - - std::unique_ptr Send( - Azure::Core::Http::Request& request, - Azure::Core::Http::Policies::NextHttpPolicy nextPolicy, - const Azure::Core::Context& context) const override; - - private: - std::string m_primaryHost; - std::string m_secondaryHost; - }; - -}}}}} // namespace Azure::Data::Tables::_detail::Policies 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 4ab77dcd8..9f990cc9b 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 @@ -142,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 DataRetentionInDays; }; /** @@ -344,19 +344,19 @@ namespace Azure { namespace Data { namespace Tables { } /** Constant value of type TableEntityDataType:EdmBinary */ AZ_DATA_TABLES_DLLEXPORT const static TableEntityDataType EdmBinary; - /** Constant value of type TableEntityDataType:EdmBinary */ + /** Constant value of type TableEntityDataType:EdmBoolean */ AZ_DATA_TABLES_DLLEXPORT const static TableEntityDataType EdmBoolean; - /** Constant value of type TableEntityDataType:EdmBinary */ + /** Constant value of type TableEntityDataType:EdmDateTime */ AZ_DATA_TABLES_DLLEXPORT const static TableEntityDataType EdmDateTime; - /** Constant value of type TableEntityDataType:EdmBinary */ + /** Constant value of type TableEntityDataType:EdmDouble */ AZ_DATA_TABLES_DLLEXPORT const static TableEntityDataType EdmDouble; - /** Constant value of type TableEntityDataType:EdmBinary */ + /** Constant value of type TableEntityDataType:EdmGuid */ AZ_DATA_TABLES_DLLEXPORT const static TableEntityDataType EdmGuid; - /** Constant value of type TableEntityDataType:EdmBinary */ + /** Constant value of type TableEntityDataType:EdmInt32 */ AZ_DATA_TABLES_DLLEXPORT const static TableEntityDataType EdmInt32; - /** Constant value of type TableEntityDataType:EdmBinary */ + /** Constant value of type TableEntityDataType:EdmInt64 */ AZ_DATA_TABLES_DLLEXPORT const static TableEntityDataType EdmInt64; - /** Constant value of type TableEntityDataType:EdmBinary */ + /** Constant value of type TableEntityDataType:EdmString */ AZ_DATA_TABLES_DLLEXPORT const static TableEntityDataType EdmString; }; @@ -441,34 +441,127 @@ namespace Azure { namespace Data { namespace Tables { { }; + /** + * @brief Table entity property. + * + */ + class TableEntityProperty final { + public: + /** Default constructor. */ + TableEntityProperty() = default; + + /** + * @brief Construct a new TableEntityProperty object. + * + * @param value Property value. + */ + TableEntityProperty(std::string const& value) : Value(std::move(value)) {} + /** + * @brief Construct a new TableEntityProperty object. + * @param value Property value. + * @param type Property type. + */ + TableEntityProperty(std::string const& value, TableEntityDataType type) + : Value(std::move(value)), Type(type) + { + } + /** + * Property value. + */ + std::string Value; + /** + * Property type. + */ + Azure::Nullable Type; + }; + /** * @brief Table Entity * */ - struct TableEntity final - { - /** - * Partition Key - */ - std::string PartitionKey; - /** - * Row Key - */ - std::string RowKey; + class TableEntity final { + constexpr static const char* PartitionKeyPropertyName = "PartitionKey"; + constexpr static const char* RowKeyPropertyName = "RowKey"; + constexpr static const char* ETagPropertyName = "odata.etag"; + constexpr static const char* TimestampPropertyName = "Timestamp"; + + public: /** * Properties */ - std::map Properties; - /** - * ETag - */ - Azure::Nullable ETag; + std::map Properties; /** - * @brief Table Entity data type. + * @brief Get partition key. * + * @return Partition key */ - TableEntityDataType DataType; + TableEntityProperty GetPartitionKey() const { return GetProperty(PartitionKeyPropertyName); } + + /** + * @brief Set Partition key. + * + * @param partitionKey Partition key. + */ + void SetPartitionKey(const std::string& partitionKey) + { + Properties[PartitionKeyPropertyName] = TableEntityProperty(partitionKey); + } + + /** + * @brief Get row key. + * + * @return Row key + */ + TableEntityProperty GetRowKey() const { return GetProperty(RowKeyPropertyName); } + /** + * @brief Set Row key. + * + * @param rowKey Row key. + */ + void SetRowKey(const std::string& rowKey) + { + Properties[RowKeyPropertyName] = TableEntityProperty(rowKey); + } + + /** + * @brief Get ETag. + * + * @return ETag + */ + TableEntityProperty GetETag() const { return GetProperty(ETagPropertyName); } + /** + * @brief Set ETag. + * + * @param eTag ETag. + */ + void SetETag(const std::string& eTag) + { + Properties[ETagPropertyName] = TableEntityProperty(eTag); + } + + /** + * @brief Get time stamp. + * + * @return timestamp + */ + TableEntityProperty GetTimestamp() const { return GetProperty(TimestampPropertyName); } + /** + * @brief Set time stamp. + * + * @param timestamp time stamp. + */ + void SetTimestamp(const std::string& timestamp) + { + Properties[TimestampPropertyName] = TableEntityProperty(timestamp); + } + + private: + TableEntityProperty GetProperty(std::string const& name) const + { + return Properties.find(name) == Properties.end() ? TableEntityProperty() + : Properties.at(name); + } }; /** @@ -494,25 +587,25 @@ namespace Azure { namespace Data { namespace Tables { UpsertKind UpsertType = UpsertKind::Update; }; /** - * @brief Create Entity options. + * @brief Add Entity options. * */ - struct CreateEntityOptions : public UpsertEntityOptions + struct AddEntityOptions : public UpsertEntityOptions { - CreateEntityOptions() = default; + AddEntityOptions() = default; /** * @brief Create Entity options constructor. * * @param other Upsert Entity options. */ - explicit CreateEntityOptions(UpsertEntityOptions const& other) { (void)other; } + explicit AddEntityOptions(UpsertEntityOptions const& other) { (void)other; } }; /** - * @brief Create Entity result. + * @brief Add Entity result. * */ - struct CreateEntityResult + struct AddEntityResult { /** * ETag @@ -594,9 +687,7 @@ namespace Azure { namespace Data { namespace Tables { * @brief Upsert Entity result. * */ - struct UpsertEntityResult final : public MergeEntityResult, - UpdateEntityResult, - CreateEntityResult + struct UpsertEntityResult final : public MergeEntityResult, UpdateEntityResult, AddEntityResult { /** * ETag @@ -628,10 +719,10 @@ namespace Azure { namespace Data { namespace Tables { /** * @brief Upsert Entity result constructor. * - * @param other Create Entity result. + * @param other Add Entity result. */ - UpsertEntityResult(CreateEntityResult const& other) - : CreateEntityResult(other), ETag(std::move(other.ETag)) + UpsertEntityResult(AddEntityResult const& other) + : AddEntityResult(other), ETag(std::move(other.ETag)) { } }; @@ -697,14 +788,14 @@ namespace Azure { namespace Data { namespace Tables { * @brief Transaction Action * */ - enum class TransactionAction + enum class TransactionActionType { - InsertEntity = 32, - DeleteEntity, - MergeEntity, - UpdateEntity, - InsertMergeEntity, - InsertReplaceEntity + Add, + UpdateMerge, + UpdateReplace, + Delete, + InsertMerge, + InsertReplace }; /** @@ -716,7 +807,7 @@ namespace Azure { namespace Data { namespace Tables { /** * Action. */ - TransactionAction Action; + TransactionActionType Action; /** * Entity. */ 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 05dee604b..150b3751a 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 @@ -4,9 +4,8 @@ #pragma once #include "azure/data/tables/credentials/azure_sas_credential.hpp" -#include "azure/data/tables/credentials/shared_key_credential.hpp" +#include "azure/data/tables/credentials/named_key_credential.hpp" #include "azure/data/tables/models.hpp" -#include "azure/data/tables/transactions.hpp" #include #include @@ -21,6 +20,21 @@ #include #include +#ifdef _azure_TABLES_TESTING_BUILD +namespace Azure { namespace Data { namespace Tables { namespace StressTest { + class TransactionStressTest; +}}}} // namespace Azure::Data::Tables::StressTest +namespace Azure { namespace Data { namespace Test { + class TransactionsBodyTest_TransactionCreate_Test; + class TransactionsBodyTest_TransactionBodyInsertMergeOp_Test; + class TransactionsBodyTest_TransactionBodyInsertReplaceOp_Test; + class TransactionsBodyTest_TransactionBodyDeleteOp_Test; + class TransactionsBodyTest_TransactionBodyUpdateMergeOp_Test; + class TransactionsBodyTest_TransactionBodyUpdateReplaceOp_Test; + class TransactionsBodyTest_TransactionBodyAddOp_Test; +}}} // namespace Azure::Data::Test +#endif + namespace Azure { namespace Data { namespace Tables { namespace _detail { @@ -32,6 +46,32 @@ namespace Azure { namespace Data { namespace Tables { * The package name of the SDK. */ constexpr static const char* TablesServicePackageName = "data-tables"; + // various strings used in the library + constexpr static const char* OriginHeader = "Origin"; + constexpr static const char* AccessControlRequestMethodHeader = "Access-Control-Request-Method"; + constexpr static const char* ResrouceTypeService = "service"; + constexpr static const char* ComponentProperties = "properties"; + constexpr static const char* ContentTypeXml = "application/xml"; + constexpr static const char* ContentTypeJson = "application/json"; + constexpr static const char* ResourceTypeHeader = "restype"; + constexpr static const char* CompHeader = "comp"; + constexpr static const char* ContentTypeHeader = "Content-Type"; + constexpr static const char* ContentLengthHeader = "Content-Length"; + constexpr static const char* AcceptHeader = "Accept"; + constexpr static const char* PreferHeader = "Prefer"; + constexpr static const char* PreferNoContent = "return-no-content"; + constexpr static const char* AcceptFullMeta = "application/json;odata=fullmetadata"; + constexpr static const char* IfMatch = "If-Match"; + constexpr static const char* PartitionKeyFragment = "(PartitionKey='"; + constexpr static const char* RowKeyFragment = "',RowKey='"; + constexpr static const char* ClosingFragment = "')"; + constexpr static const char* Value = "value"; + constexpr static const char* TableName = "TableName"; + constexpr static const char* ODataEditLink = "odata.editLink"; + constexpr static const char* ODataId = "odata.id"; + constexpr static const char* ODataType = "odata.type"; + constexpr static const char* ODataMeta = "odata.metadata"; + constexpr static const char* ODataError = "odata.error"; } // namespace _detail /** @@ -102,15 +142,6 @@ namespace Azure { namespace Data { namespace Tables { */ struct TableClientOptions final : Azure::Core::_internal::ClientOptions { - /** - * SecondaryHostForRetryReads specifies whether the retry policy should retry a read - * operation against another host. If SecondaryHostForRetryReads is "" (the default) then - * operations are not retried against another host. NOTE: Before setting this field, make sure - * you understand the issues around reading stale & potentially-inconsistent data at this - * webpage: https://docs.microsoft.com/azure/storage/common/geo-redundant-design. - */ - std::string SecondaryHostForRetryReads; - /** * API version used by this client. */ @@ -168,14 +199,14 @@ namespace Azure { namespace Data { namespace Tables { * @brief Initializes a new instance of tableClient. * * @param tableName The name of the table. - * @param credential The shared key credential used to sign requests. + * @param credential The named key credential used to sign requests. * @param url A url referencing the table that includes the name of the account and the name of * @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& tableName, - std::shared_ptr credential, + std::shared_ptr credential, std::string url, const TableClientOptions& options = {}); @@ -228,16 +259,16 @@ namespace Azure { namespace Data { namespace Tables { Core::Context const& context = {}); /** - * @brief Create table entity. + * @brief Add table entity. * * @param tableEntity The TableEntity to set. * @param options Optional parameters to execute this function. * @param context for canceling long running operations. - * @return Create entity result. + * @return Add entity result. */ - Response CreateEntity( + Response AddEntity( Models::TableEntity const& tableEntity, - Models::CreateEntityOptions const& options = {}, + Models::AddEntityOptions const& options = {}, Core::Context const& context = {}); /** @@ -314,26 +345,37 @@ namespace Azure { namespace Data { namespace Tables { const std::string& rowKey, Core::Context const& context = {}); - /** - * @brief Creates a new transaction. - * - * @param partitionKey The partition key of the transaction. - * @return New transaction. - */ - Transaction CreateTransaction(std::string const& partitionKey); - /** * @brief Submits a transaction. * - * @param transaction The transaction to submit. + * @param steps the transaction steps to execute. * @param context for canceling long running operations. * @return Submit transaction result. */ Response SubmitTransaction( - Transaction& transaction, + std::vector const& steps, Core::Context const& context = {}); private: +#ifdef _azure_TABLES_TESTING_BUILD + friend class Azure::Data::Tables::StressTest::TransactionStressTest; + friend class Azure::Data::Test::TransactionsBodyTest_TransactionCreate_Test; + friend class Azure::Data::Test::TransactionsBodyTest_TransactionBodyInsertMergeOp_Test; + friend class Azure::Data::Test::TransactionsBodyTest_TransactionBodyInsertReplaceOp_Test; + friend class Azure::Data::Test::TransactionsBodyTest_TransactionBodyDeleteOp_Test; + friend class Azure::Data::Test::TransactionsBodyTest_TransactionBodyUpdateMergeOp_Test; + friend class Azure::Data::Test::TransactionsBodyTest_TransactionBodyUpdateReplaceOp_Test; + friend class Azure::Data::Test::TransactionsBodyTest_TransactionBodyAddOp_Test; +#endif + + std::string PreparePayload( + std::string const& batchId, + std::string const& changesetId, + std::vector const& steps); + std::string PrepAddEntity(std::string const& changesetId, Models::TableEntity entity); + std::string PrepDeleteEntity(std::string const& changesetId, Models::TableEntity entity); + std::string PrepMergeEntity(std::string const& changesetId, Models::TableEntity entity); + std::string PrepUpdateEntity(std::string const& changesetId, Models::TableEntity entity); std::shared_ptr m_pipeline; Core::Url m_url; std::string m_tableName; @@ -383,13 +425,13 @@ namespace Azure { namespace Data { namespace Tables { * * @param serviceUrl A url referencing the table that includes the name of the account and the * name of the table. - * @param credential The shared key credential used to sign requests. + * @param credential The named key 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, + std::shared_ptr credential, const TableClientOptions& options = {}); /** @@ -488,9 +530,23 @@ namespace Azure { namespace Data { namespace Tables { Response PreflightCheck( Models::PreflightCheckOptions const& options, Core::Context const& context = {}); + /** + * @brief Get table client. + * + * @param tableName The name of the table. + * @param options Optional parameters for the table client. + * @return TableClient. + * @remark The TableClient can be used to perform operations on the table. This method will + * attempt to create a table client with the same credentials as the service client except the + * SAS token credential as the SAS token permissions varies from the service level permissions. + */ + TableClient GetTableClient(const std::string& tableName, TableClientOptions const& options = {}) + const; private: std::shared_ptr m_pipeline; + std::shared_ptr m_tokenCredential; + std::shared_ptr m_namedKeyCredential; Core::Url m_url; }; }}} // namespace Azure::Data::Tables 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 index 86b6cc6cb..156af16f4 100644 --- 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 @@ -4,7 +4,7 @@ #pragma once #include "azure/data/tables/account_sas_builder.hpp" -#include "azure/data/tables/credentials/shared_key_credential.hpp" +#include "azure/data/tables/credentials/named_key_credential.hpp" #include "azure/data/tables/enum_operators.hpp" #include @@ -14,28 +14,6 @@ #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 @@ -144,23 +122,23 @@ namespace Azure { namespace Data { namespace Tables { namespace Sas { void SetPermissions(std::string rawPermissions) { Permissions = std::move(rawPermissions); } /** - * @brief Uses the StorageSharedKeyCredential to sign this shared access signature, to produce + * @brief Uses the NamedKeyCredential 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. + * @param credential The named key credential. * @return The SAS query parameters used for authenticating requests. */ std::string GenerateSasToken( - const Azure::Data::Tables::Credentials::SharedKeyCredential& credential); + const Azure::Data::Tables::Credentials::NamedKeyCredential& credential); /** * @brief Gets the canonical path for the shared access signature. * - * @param credential The storage account's shared key credential. + * @param credential The named key credential. * @return Canonical path. */ std::string GetCanonicalName( - const Azure::Data::Tables::Credentials::SharedKeyCredential& credential) const + const Azure::Data::Tables::Credentials::NamedKeyCredential& credential) const { return Azure::Core::_internal::StringExtensions::ToLower( "/table/" + credential.AccountName + "/" + TableName); diff --git a/sdk/tables/azure-data-tables/inc/azure/data/tables/transactions.hpp b/sdk/tables/azure-data-tables/inc/azure/data/tables/transactions.hpp deleted file mode 100644 index 22b546c63..000000000 --- a/sdk/tables/azure-data-tables/inc/azure/data/tables/transactions.hpp +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -#pragma once - -#include "azure/data/tables/models.hpp" - -#include -#include - -namespace Azure { namespace Data { namespace Tables { - /** - * @brief Transaction Definition - * - */ - class Transaction final { - public: - /** - * @brief Construct a new Transaction object - * - * @param url The url of the service - * @param tableName the name of the table - * @param partitionKey the partition key - */ - Transaction( - std::string const& url, - std::string const& tableName, - std::string const& partitionKey) - : m_partitionKey{std::move(partitionKey)}, m_tableName{tableName} - { - Azure::Core::Url _url(url); - _url.SetQueryParameters({}); - m_url = _url.GetAbsoluteUrl(); - m_batchId = "batch_" + Azure::Core::Uuid::CreateUuid().ToString(); - m_changesetId = "changeset_" + Azure::Core::Uuid::CreateUuid().ToString(); - } - - /** - * @brief Get the Partition Key - * - * @return Partition key - */ - std::string const& GetPartitionKey() const { return m_partitionKey; } - - /** - * @brief Get the Batch Id - * - * @return Batch id - */ - std::string const& GetBatchId() const { return m_batchId; } - - /** - * @brief Get the Changeset Id - * - * @return Changeset id - */ - std::string const& GetChangesetId() const { return m_changesetId; } - - /** - * @brief Get the Steps - * - * @return vector of steps - */ - std::vector const& GetSteps() const { return m_steps; } - - /** - * @brief Add a Create Entity Step - * - * @param entity The entity to create - */ - void CreateEntity(Models::TableEntity const& entity); - - /** - * @brief Add a Delete Entity Step - * - * @param entity The entity to delete - */ - void DeleteEntity(Models::TableEntity const& entity); - - /** - * @brief Add a Merge Entity Step - * - * @param entity The entity to merge - */ - void MergeEntity(Models::TableEntity const& entity); - - /** - * @brief Add a Update Entity Step - * - * @param entity The entity to update - */ - void UpdateEntity(Models::TableEntity const& entity); - - /** - * @brief Add a Upsert Entity Step - * - * @param entity The entity to upsert - */ - void UpsertEntity(Models::TableEntity const& entity); - - /** - * @brief Add a Insert Or Replace Entity Step - * - * @param entity The entity to insert or replace - */ - void InsertReplaceEntity(Models::TableEntity const& entity); - - /** - * @brief Add a Insert Or Merge Entity Step - * - * @param entity The entity to insert or merge - */ - void InsertMergeEntity(Models::TableEntity const& entity); - - /** - * @brief Prepare the payload - * - * @return The payload - */ - std::string PreparePayload(); - - private: - std::string PrepCreateEntity(Models::TableEntity entity); - std::string PrepDeleteEntity(Models::TableEntity entity); - std::string PrepMergeEntity(Models::TableEntity entity); - std::string PrepUpdateEntity(Models::TableEntity entity); - std::string m_partitionKey; - std::string m_url; - std::string m_tableName; - std::vector m_steps; - std::string m_batchId; - std::string m_changesetId; - }; -}}} // namespace Azure::Data::Tables 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 6a3165da8..9a3b362c6 100644 --- a/sdk/tables/azure-data-tables/samples/tables_entity_operations.cpp +++ b/sdk/tables/azure-data-tables/samples/tables_entity_operations.cpp @@ -8,6 +8,10 @@ #include #include +using namespace Azure::Data::Tables; +using namespace Azure::Data::Tables::Models; +const std::string TableName = "table"; + std::string GetConnectionString() { const static std::string ConnectionString = ""; @@ -24,9 +28,6 @@ std::string GetConnectionString() throw std::runtime_error("Cannot find connection string."); } -using namespace Azure::Data::Tables; -const std::string TableName = "table"; - int main() { auto tableServiceClient = TableServiceClient::CreateFromConnectionString(GetConnectionString()); @@ -43,27 +44,27 @@ int main() } // init new entity Azure::Data::Tables::Models::TableEntity entity; - entity.PartitionKey = "P1"; - entity.RowKey = "R1"; - entity.Properties["Name"] = "Azure"; - entity.Properties["Product"] = "Tables"; + entity.SetPartitionKey("P1"); + entity.SetRowKey("R1"); + entity.Properties["Name"] = TableEntityProperty("Azure"); + entity.Properties["Product"] = TableEntityProperty("Tables"); // create new entity - auto response = tableClient.CreateEntity(entity); + auto response = tableClient.AddEntity(entity); // update entity std::cout << response.Value.ETag << std::endl; - entity.Properties["Product"] = "Tables2"; + entity.Properties["Product"] = TableEntityProperty("Tables2"); auto updateResponse = tableClient.UpdateEntity(entity); std::cout << updateResponse.Value.ETag << std::endl; // merge entity - entity.Properties["Product"] = "Tables3"; - entity.ETag = updateResponse.Value.ETag; + entity.Properties["Product"] = TableEntityProperty("Tables3"); + entity.SetETag(updateResponse.Value.ETag); auto updateResponse2 = tableClient.MergeEntity(entity); // delete entity std::cout << updateResponse2.Value.ETag << std::endl; - entity.ETag = updateResponse2.Value.ETag; + entity.SetETag(updateResponse2.Value.ETag); auto deleteResponse = tableClient.DeleteEntity(entity); // delete existing table diff --git a/sdk/tables/azure-data-tables/src/account_sas_builder.cpp b/sdk/tables/azure-data-tables/src/account_sas_builder.cpp index 365aa9b86..b1e95c882 100644 --- a/sdk/tables/azure-data-tables/src/account_sas_builder.cpp +++ b/sdk/tables/azure-data-tables/src/account_sas_builder.cpp @@ -29,16 +29,6 @@ namespace Azure { namespace Data { namespace Tables { namespace Sas { { Permissions += "d"; } - if ((permissions & AccountSasPermissions::DeleteVersion) - == AccountSasPermissions::DeleteVersion) - { - Permissions += "x"; - } - if ((permissions & AccountSasPermissions::PermanentDelete) - == AccountSasPermissions::PermanentDelete) - { - Permissions += "y"; - } if ((permissions & AccountSasPermissions::List) == AccountSasPermissions::List) { Permissions += "l"; @@ -47,66 +37,33 @@ namespace Azure { namespace Data { namespace Tables { namespace Sas { { Permissions += "a"; } - if ((permissions & AccountSasPermissions::Create) == AccountSasPermissions::Create) - { - Permissions += "c"; - } if ((permissions & AccountSasPermissions::Update) == AccountSasPermissions::Update) { Permissions += "u"; } - if ((permissions & AccountSasPermissions::Process) == AccountSasPermissions::Process) - { - Permissions += "p"; - } - if ((permissions & AccountSasPermissions::SetImmutabilityPolicy) - == AccountSasPermissions::SetImmutabilityPolicy) - { - Permissions += "i"; - } - if ((permissions & AccountSasPermissions::Tags) == AccountSasPermissions::Tags) - { - Permissions += "t"; - } - if ((permissions & AccountSasPermissions::Filter) == AccountSasPermissions::Filter) - { - Permissions += "f"; - } } std::string AccountSasBuilder::GenerateSasToken( - const Azure::Data::Tables::Credentials::SharedKeyCredential& credential) + const Azure::Data::Tables::Credentials::NamedKeyCredential& credential) { std::string protocol = _detail::SasProtocolToString(Protocol); std::string services; - if ((Services & AccountSasServices::Blobs) == AccountSasServices::Blobs) - { - services += "b"; - } - if ((Services & AccountSasServices::Queue) == AccountSasServices::Queue) - { - services += "q"; - } - if ((Services & AccountSasServices::Files) == AccountSasServices::Files) - { - services += "f"; - } if ((Services & AccountSasServices::Table) == AccountSasServices::Table) { services += "t"; } std::string resourceTypes; - if ((ResourceTypes & AccountSasResource::Service) == AccountSasResource::Service) + if ((ResourceTypes & AccountSasResourceType::Service) == AccountSasResourceType::Service) { resourceTypes += "s"; } - if ((ResourceTypes & AccountSasResource::Container) == AccountSasResource::Container) + if ((ResourceTypes & AccountSasResourceType::Container) == AccountSasResourceType::Container) { resourceTypes += "c"; } - if ((ResourceTypes & AccountSasResource::Object) == AccountSasResource::Object) + if ((ResourceTypes & AccountSasResourceType::Object) == AccountSasResourceType::Object) { resourceTypes += "o"; } diff --git a/sdk/tables/azure-data-tables/src/credentials/shared_key_credential.cpp b/sdk/tables/azure-data-tables/src/credentials/named_key_credential.cpp similarity index 95% rename from sdk/tables/azure-data-tables/src/credentials/shared_key_credential.cpp rename to sdk/tables/azure-data-tables/src/credentials/named_key_credential.cpp index 7304b35f3..bfd14ded9 100644 --- a/sdk/tables/azure-data-tables/src/credentials/shared_key_credential.cpp +++ b/sdk/tables/azure-data-tables/src/credentials/named_key_credential.cpp @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -#include "azure/data/tables/credentials/shared_key_credential.hpp" +#include "azure/data/tables/credentials/named_key_credential.hpp" #include @@ -71,7 +71,7 @@ namespace Azure { namespace Data { namespace Tables { namespace Credentials { na throw std::runtime_error("Cannot find account name in connection string."); } connectionStringParts.KeyCredential - = std::make_shared(accountName, accountKey); + = std::make_shared(accountName, accountKey); } std::string sas = getWithDefault(connectionStringMap, "SharedAccessSignature"); diff --git a/sdk/tables/azure-data-tables/src/policies/shared_key_policy.cpp b/sdk/tables/azure-data-tables/src/policies/shared_key_policy.cpp deleted file mode 100644 index 7ece1d164..000000000 --- a/sdk/tables/azure-data-tables/src/policies/shared_key_policy.cpp +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "azure/data/tables/internal/policies/shared_key_policy.hpp" - -#include "azure/data/tables/internal/cryptography/hmacsha256.hpp" - -#include -#include -#include -#include - -#include - -namespace Azure { namespace Data { namespace Tables { namespace _detail { namespace Policies { - - std::string SharedKeyPolicy::GetSignature(const Core::Http::Request& request) const - { - std::string string_to_sign; - string_to_sign += request.GetMethod().ToString() + "\n"; - - const auto& headers = request.GetHeaders(); - for (std::string headerName : - {"Content-Encoding", - "Content-Language", - "Content-Length", - "Content-MD5", - "Content-Type", - "Date", - "If-Modified-Since", - "If-Match", - "If-None-Match", - "If-Unmodified-Since", - "Range"}) - { - auto ite = headers.find(headerName); - if (ite != headers.end()) - { - if (headerName == "Content-Length" && ite->second == "0") - { - // do nothing - } - else - { - string_to_sign += ite->second; - } - } - string_to_sign += "\n"; - } - - // canonicalized headers - const std::string prefix = "x-ms-"; - std::vector> ordered_kv; - for (auto ite = headers.lower_bound(prefix); - ite != headers.end() && ite->first.substr(0, prefix.length()) == prefix; - ++ite) - { - std::string key = Azure::Core::_internal::StringExtensions::ToLower(ite->first); - ordered_kv.emplace_back(std::make_pair(std::move(key), ite->second)); - } - std::sort(ordered_kv.begin(), ordered_kv.end()); - for (const auto& p : ordered_kv) - { - string_to_sign += p.first + ":" + p.second + "\n"; - } - ordered_kv.clear(); - - // canonicalized resource - string_to_sign += "/" + m_credential->AccountName + "/" + request.GetUrl().GetPath() + "\n"; - for (const auto& query : request.GetUrl().GetQueryParameters()) - { - std::string key = Azure::Core::_internal::StringExtensions::ToLower(query.first); - ordered_kv.emplace_back( - std::make_pair(Azure::Core::Url::Decode(key), Azure::Core::Url::Decode(query.second))); - } - std::sort(ordered_kv.begin(), ordered_kv.end()); - for (const auto& p : ordered_kv) - { - string_to_sign += p.first + ":" + p.second + "\n"; - } - - // remove last linebreak - string_to_sign.pop_back(); - - return Azure::Core::Convert::Base64Encode( - Azure::Data::Tables::_detail::Cryptography::HmacSha256::Compute( - std::vector(string_to_sign.begin(), string_to_sign.end()), - Azure::Core::Convert::Base64Decode(m_credential->GetAccountKey()))); - } -}}}}} // namespace Azure::Data::Tables::_detail::Policies diff --git a/sdk/tables/azure-data-tables/src/policies/switch_to_secondary_policy.cpp b/sdk/tables/azure-data-tables/src/policies/switch_to_secondary_policy.cpp deleted file mode 100644 index e35eba745..000000000 --- a/sdk/tables/azure-data-tables/src/policies/switch_to_secondary_policy.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "azure/data/tables/internal/policies/switch_to_secondary_policy.hpp" - -namespace Azure { namespace Data { namespace Tables { namespace _detail { namespace Policies { - - Azure::Core::Context::Key const SecondaryHostReplicaStatusKey; - - std::unique_ptr SwitchToSecondaryPolicy::Send( - Azure::Core::Http::Request& request, - Azure::Core::Http::Policies::NextHttpPolicy nextPolicy, - const Azure::Core::Context& context) const - { - std::shared_ptr replicaStatus; - context.TryGetValue(SecondaryHostReplicaStatusKey, replicaStatus); - - bool considerSecondary = (request.GetMethod() == Azure::Core::Http::HttpMethod::Get - || request.GetMethod() == Azure::Core::Http::HttpMethod::Head) - && !m_secondaryHost.empty() && replicaStatus && *replicaStatus; - - if (considerSecondary - && Azure::Core::Http::Policies::_internal::RetryPolicy::GetRetryCount(context) > 0) - { - // switch host - if (request.GetUrl().GetHost() == m_primaryHost) - { - request.GetUrl().SetHost(m_secondaryHost); - } - else - { - request.GetUrl().SetHost(m_primaryHost); - } - } - - auto response = nextPolicy.Send(request, context); - - if (considerSecondary - && (response->GetStatusCode() == Azure::Core::Http::HttpStatusCode::NotFound - || response->GetStatusCode() == Core::Http::HttpStatusCode::PreconditionFailed) - && request.GetUrl().GetHost() == m_secondaryHost) - { - *replicaStatus = false; - // switch back - request.GetUrl().SetHost(m_primaryHost); - response = nextPolicy.Send(request, context); - } - - return response; - } - -}}}}} // namespace Azure::Data::Tables::_detail::Policies diff --git a/sdk/tables/azure-data-tables/src/serializers.cpp b/sdk/tables/azure-data-tables/src/serializers.cpp index a6e692f8a..f2b16ae87 100644 --- a/sdk/tables/azure-data-tables/src/serializers.cpp +++ b/sdk/tables/azure-data-tables/src/serializers.cpp @@ -7,6 +7,7 @@ using namespace Azure::Data::Tables::_detail::Xml; using namespace Azure::Data::Tables; +using namespace Azure::Data::Tables::Models; namespace Azure { namespace Data { namespace Tables { namespace _detail { std::string const Serializers::CreateEntity(Models::TableEntity const& tableEntity) { @@ -14,11 +15,15 @@ namespace Azure { namespace Data { namespace Tables { namespace _detail { { auto jsonRoot = Core::Json::_internal::json::object(); - jsonRoot["PartitionKey"] = tableEntity.PartitionKey; - jsonRoot["RowKey"] = tableEntity.RowKey; + jsonRoot["PartitionKey"] = tableEntity.GetPartitionKey().Value; + jsonRoot["RowKey"] = tableEntity.GetRowKey().Value; for (auto entry : tableEntity.Properties) { - jsonRoot[entry.first] = entry.second; + jsonRoot[entry.first] = entry.second.Value; + if (entry.second.Type.HasValue()) + { + jsonRoot[entry.first + "@odata.type"] = entry.second.Type.Value().ToString(); + } } jsonBody = jsonRoot.dump(); } @@ -115,13 +120,14 @@ namespace Azure { namespace Data { namespace Tables { namespace _detail { "Enabled", options.ServiceProperties.Logging.RetentionPolicyDefinition.IsEnabled ? "true" : "false"}); - if (options.ServiceProperties.Logging.RetentionPolicyDefinition.Days.HasValue()) + if (options.ServiceProperties.Logging.RetentionPolicyDefinition.DataRetentionInDays + .HasValue()) { writer.Write(XmlNode{ XmlNodeType::StartTag, "Days", - std::to_string( - options.ServiceProperties.Logging.RetentionPolicyDefinition.Days.Value())}); + std::to_string(options.ServiceProperties.Logging.RetentionPolicyDefinition + .DataRetentionInDays.Value())}); } writer.Write(XmlNode{XmlNodeType::EndTag}); writer.Write(XmlNode{XmlNodeType::EndTag}); @@ -145,13 +151,14 @@ namespace Azure { namespace Data { namespace Tables { namespace _detail { "Enabled", options.ServiceProperties.HourMetrics.RetentionPolicyDefinition.IsEnabled ? "true" : "false"}); - if (options.ServiceProperties.HourMetrics.RetentionPolicyDefinition.Days.HasValue()) + if (options.ServiceProperties.HourMetrics.RetentionPolicyDefinition.DataRetentionInDays + .HasValue()) { writer.Write(XmlNode{ XmlNodeType::StartTag, "Days", - std::to_string( - options.ServiceProperties.HourMetrics.RetentionPolicyDefinition.Days.Value())}); + std::to_string(options.ServiceProperties.HourMetrics.RetentionPolicyDefinition + .DataRetentionInDays.Value())}); } writer.Write(XmlNode{XmlNodeType::EndTag}); writer.Write(XmlNode{XmlNodeType::EndTag}); @@ -175,13 +182,14 @@ namespace Azure { namespace Data { namespace Tables { namespace _detail { "Enabled", options.ServiceProperties.MinuteMetrics.RetentionPolicyDefinition.IsEnabled ? "true" : "false"}); - if (options.ServiceProperties.MinuteMetrics.RetentionPolicyDefinition.Days.HasValue()) + if (options.ServiceProperties.MinuteMetrics.RetentionPolicyDefinition.DataRetentionInDays + .HasValue()) { writer.Write(XmlNode{ XmlNodeType::StartTag, "Days", - std::to_string( - options.ServiceProperties.MinuteMetrics.RetentionPolicyDefinition.Days.Value())}); + std::to_string(options.ServiceProperties.MinuteMetrics.RetentionPolicyDefinition + .DataRetentionInDays.Value())}); } writer.Write(XmlNode{XmlNodeType::EndTag}); writer.Write(XmlNode{XmlNodeType::EndTag}); @@ -392,7 +400,7 @@ namespace Azure { namespace Data { namespace Tables { namespace _detail { && xmlPath[1] == XmlTagEnum::kLogging && xmlPath[2] == XmlTagEnum::kRetentionPolicy && xmlPath[3] == XmlTagEnum::kDays) { - response.Logging.RetentionPolicyDefinition.Days = std::stoi(node.Value); + response.Logging.RetentionPolicyDefinition.DataRetentionInDays = std::stoi(node.Value); } else if ( xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kStorageServiceProperties @@ -425,7 +433,8 @@ namespace Azure { namespace Data { namespace Tables { namespace _detail { && xmlPath[1] == XmlTagEnum::kHourMetrics && xmlPath[2] == XmlTagEnum::kRetentionPolicy && xmlPath[3] == XmlTagEnum::kDays) { - response.HourMetrics.RetentionPolicyDefinition.Days = std::stoi(node.Value); + response.HourMetrics.RetentionPolicyDefinition.DataRetentionInDays + = std::stoi(node.Value); } else if ( xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kStorageServiceProperties @@ -458,7 +467,8 @@ namespace Azure { namespace Data { namespace Tables { namespace _detail { && xmlPath[1] == XmlTagEnum::kMinuteMetrics && xmlPath[2] == XmlTagEnum::kRetentionPolicy && xmlPath[3] == XmlTagEnum::kDays) { - response.MinuteMetrics.RetentionPolicyDefinition.Days = std::stoi(node.Value); + response.MinuteMetrics.RetentionPolicyDefinition.DataRetentionInDays + = std::stoi(node.Value); } else if ( xmlPath.size() == 4 && xmlPath[0] == XmlTagEnum::kStorageServiceProperties @@ -516,30 +526,30 @@ namespace Azure { namespace Data { namespace Tables { namespace _detail { Models::TableEntity Serializers::DeserializeEntity(Azure::Core::Json::_internal::json json) { Models::TableEntity tableEntity{}; - if (json.contains("PartitionKey")) + + auto properties = json.get>(); + std::vector erasable; + for (auto property : properties) { - 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) + auto value = property.second; + auto name = property.first; + std::string typeFieldName = name + "@odata.type"; + if (properties.find(typeFieldName) != properties.end()) { - tableEntity.DataType = Models::TableEntityDataType(properties.second); + auto type = properties[typeFieldName]; + tableEntity.Properties[name] = TableEntityProperty( + value, static_cast(type)); + erasable.push_back(typeFieldName); } - if (properties.first != "odata.metadata" && properties.first != "PartitionKey" - && properties.first != "RowKey" && properties.first != "odata.etag") + else { - tableEntity.Properties[properties.first] = properties.second; + tableEntity.Properties[name] = TableEntityProperty(value); } } + for (auto erase : erasable) + { + tableEntity.Properties.erase(erase); + } 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 9f8e8605f..d9251cd9d 100644 --- a/sdk/tables/azure-data-tables/src/tables_clients.cpp +++ b/sdk/tables/azure-data-tables/src/tables_clients.cpp @@ -5,14 +5,13 @@ #include "azure/data/tables/internal/policies/service_version_policy.hpp" #include "azure/data/tables/internal/policies/shared_key_lite_policy.hpp" -#include "azure/data/tables/internal/policies/shared_key_policy.hpp" -#include "azure/data/tables/internal/policies/switch_to_secondary_policy.hpp" #include "azure/data/tables/internal/policies/tenant_bearer_token_policy.hpp" #include "azure/data/tables/internal/policies/timeout_policy.hpp" #include "azure/data/tables/internal/serializers.hpp" #include #include + using namespace Azure::Data::Tables; using namespace Azure::Data::Tables::_detail::Policies; using namespace Azure::Data::Tables::_detail::Xml; @@ -24,8 +23,6 @@ TableServiceClient::TableServiceClient(const TableClientOptions& options) TableClientOptions newOptions = options; std::vector> perRetryPolicies; std::vector> perOperationPolicies; - perRetryPolicies.emplace_back(std::make_unique( - m_url.GetHost(), newOptions.SecondaryHostForRetryReads)); perRetryPolicies.emplace_back(std::make_unique()); perOperationPolicies.emplace_back( std::make_unique(newOptions.ApiVersion.ToString())); @@ -44,8 +41,6 @@ TableServiceClient::TableServiceClient( m_url = Azure::Core::Url(serviceUrl); std::vector> perRetryPolicies; std::vector> perOperationPolicies; - perRetryPolicies.emplace_back(std::make_unique( - m_url.GetHost(), options.SecondaryHostForRetryReads)); perRetryPolicies.emplace_back(std::make_unique()); perOperationPolicies.emplace_back( std::make_unique(options.ApiVersion.ToString())); @@ -64,18 +59,15 @@ TableServiceClient::TableServiceClient( : TableServiceClient(options) { + m_tokenCredential = credential; TableClientOptions newOptions = options; m_url = Azure::Core::Url(serviceUrl); std::vector> perRetryPolicies; std::vector> perOperationPolicies; - perRetryPolicies.emplace_back(std::make_unique( - m_url.GetHost(), options.SecondaryHostForRetryReads)); perRetryPolicies.emplace_back(std::make_unique()); perOperationPolicies.emplace_back( std::make_unique(options.ApiVersion.ToString())); - perRetryPolicies.emplace_back(std::make_unique( - m_url.GetHost(), newOptions.SecondaryHostForRetryReads)); perRetryPolicies.emplace_back(std::make_unique()); { Azure::Core::Credentials::TokenRequestContext tokenContext; @@ -98,14 +90,13 @@ TableServiceClient::TableServiceClient( TableServiceClient::TableServiceClient( const std::string& serviceUrl, - std::shared_ptr credential, + std::shared_ptr credential, const TableClientOptions& options) : m_url(Azure::Core::Url(serviceUrl)) { + m_namedKeyCredential = credential; std::vector> perRetryPolicies; std::vector> perOperationPolicies; - perRetryPolicies.emplace_back(std::make_unique( - m_url.GetHost(), options.SecondaryHostForRetryReads)); perRetryPolicies.emplace_back(std::make_unique()); perOperationPolicies.emplace_back( std::make_unique(options.ApiVersion.ToString())); @@ -122,8 +113,6 @@ TableServiceClient::TableServiceClient( std::vector> perRetryPolicies2; std::vector> perOperationPolicies2; - perRetryPolicies2.emplace_back(std::make_unique( - m_url.GetHost(), newOptions.SecondaryHostForRetryReads)); perRetryPolicies2.emplace_back(std::make_unique()); perOperationPolicies2.emplace_back( std::make_unique(newOptions.ApiVersion.ToString())); @@ -143,6 +132,25 @@ TableServiceClient::TableServiceClient( { } +TableClient TableServiceClient::GetTableClient( + const std::string& tableName, + TableClientOptions const& options) const +{ + if (m_namedKeyCredential != nullptr) + { + return TableClient(tableName, m_namedKeyCredential, m_url.GetAbsoluteUrl(), options); + } + if (m_tokenCredential != nullptr) + { + return TableClient(m_url.GetAbsoluteUrl(), tableName, m_tokenCredential, options); + } + if (!m_url.GetAbsoluteUrl().empty()) + { + return TableClient(m_url.GetAbsoluteUrl(), tableName, options); + } + throw std::runtime_error("TableServiceClient is not properly initialized."); +} + TableServiceClient TableServiceClient::CreateFromConnectionString( const std::string& connectionString, const TableClientOptions& options) @@ -168,8 +176,8 @@ Azure::Response TableServiceClient::PreflightCheck auto url = m_url; url.AppendPath(options.TableName); Core::Http::Request request(Core::Http::HttpMethod::Options, url); - request.SetHeader("Origin", options.Origin); - request.SetHeader("Access-Control-Request-Method", Core::Http::HttpMethod::Options.ToString()); + request.SetHeader(OriginHeader, options.Origin); + request.SetHeader(AccessControlRequestMethodHeader, Core::Http::HttpMethod::Options.ToString()); auto rawResponse = m_pipeline->Send(request, context); auto const httpStatusCode = rawResponse->GetStatusCode(); @@ -182,6 +190,7 @@ Azure::Response TableServiceClient::PreflightCheck return Response(std::move(response), std::move(rawResponse)); } + Azure::Response TableServiceClient::SetServiceProperties( Models::SetServicePropertiesOptions const& options, Core::Context const& context) @@ -189,15 +198,15 @@ Azure::Response TableServiceClient::SetServi std::string xmlBody = Serializers::SetServiceProperties(options); auto url = m_url; - url.AppendQueryParameter("restype", "service"); - url.AppendQueryParameter("comp", "properties"); + url.AppendQueryParameter(ResourceTypeHeader, ResrouceTypeService); + url.AppendQueryParameter(CompHeader, ComponentProperties); Core::IO::MemoryBodyStream requestBody( reinterpret_cast(xmlBody.data()), xmlBody.length()); Core::Http::Request request(Core::Http::HttpMethod::Put, url, &requestBody); - request.SetHeader("Content-Type", "application/xml"); - request.SetHeader("Content-Length", std::to_string(requestBody.Length())); + request.SetHeader(ContentTypeHeader, ContentTypeXml); + request.SetHeader(ContentLengthHeader, std::to_string(requestBody.Length())); auto rawResponse = m_pipeline->Send(request, context); auto const httpStatusCode = rawResponse->GetStatusCode(); @@ -216,8 +225,8 @@ Azure::Response TableServiceClient::GetServicePr Core::Context const& context) { auto url = m_url; - url.AppendQueryParameter("restype", "service"); - url.AppendQueryParameter("comp", "properties"); + url.AppendQueryParameter(ResourceTypeHeader, ResrouceTypeService); + url.AppendQueryParameter(CompHeader, ComponentProperties); Core::Http::Request request(Core::Http::HttpMethod::Get, url); @@ -243,8 +252,8 @@ Azure::Response TableServiceClient::GetStatistics( std::string accountName = host.substr(0, host.find('.')); accountName += "-secondary"; url.SetHost(accountName + "." + host.substr(host.find('.') + 1)); - url.AppendQueryParameter("restype", "service"); - url.AppendQueryParameter("comp", "stats"); + url.AppendQueryParameter(ResourceTypeHeader, ResrouceTypeService); + url.AppendQueryParameter(CompHeader, "stats"); Core::Http::Request request(Core::Http::HttpMethod::Get, url); auto pRawResponse = m_pipeline->Send(request, context); @@ -321,8 +330,6 @@ TableClient::TableClient( { std::vector> perRetryPolicies; std::vector> perOperationPolicies; - perRetryPolicies.emplace_back(std::make_unique( - m_url.GetHost(), options.SecondaryHostForRetryReads)); perRetryPolicies.emplace_back(std::make_unique()); perOperationPolicies.emplace_back( std::make_unique(options.ApiVersion.ToString())); @@ -346,14 +353,10 @@ TableClient::TableClient( m_url = Azure::Core::Url(serviceUrl); std::vector> perRetryPolicies; std::vector> perOperationPolicies; - perRetryPolicies.emplace_back(std::make_unique( - m_url.GetHost(), options.SecondaryHostForRetryReads)); perRetryPolicies.emplace_back(std::make_unique()); perOperationPolicies.emplace_back( std::make_unique(options.ApiVersion.ToString())); - perRetryPolicies.emplace_back(std::make_unique( - m_url.GetHost(), newOptions.SecondaryHostForRetryReads)); perRetryPolicies.emplace_back(std::make_unique()); { Azure::Core::Credentials::TokenRequestContext tokenContext; @@ -376,7 +379,7 @@ TableClient::TableClient( TableClient::TableClient( const std::string& tableName, - std::shared_ptr credential, + std::shared_ptr credential, std::string url, const TableClientOptions& options) : m_url(std::move(url)), m_tableName(tableName) @@ -384,8 +387,6 @@ TableClient::TableClient( { std::vector> perRetryPolicies; std::vector> perOperationPolicies; - perRetryPolicies.emplace_back(std::make_unique( - m_url.GetHost(), options.SecondaryHostForRetryReads)); perRetryPolicies.emplace_back(std::make_unique()); perOperationPolicies.emplace_back( std::make_unique(options.ApiVersion.ToString())); @@ -402,8 +403,6 @@ TableClient::TableClient( std::vector> perRetryPolicies2; std::vector> perOperationPolicies2; - perRetryPolicies2.emplace_back(std::make_unique( - m_url.GetHost(), newOptions.SecondaryHostForRetryReads)); perRetryPolicies2.emplace_back(std::make_unique()); perOperationPolicies2.emplace_back( std::make_unique(newOptions.ApiVersion.ToString())); @@ -464,9 +463,9 @@ Azure::Response TableServiceClient::CreateTable( Core::Http::Request request(Core::Http::HttpMethod::Post, url, &requestBody); - request.SetHeader("Content-Type", "application/json"); - request.SetHeader("Content-Length", std::to_string(requestBody.Length())); - request.SetHeader("Accept", "application/json;odata=fullmetadata"); + request.SetHeader(ContentTypeHeader, ContentTypeJson); + request.SetHeader(ContentLengthHeader, std::to_string(requestBody.Length())); + request.SetHeader(AcceptHeader, AcceptFullMeta); auto rawResponse = m_pipeline->Send(request, context); auto const httpStatusCode = rawResponse->GetStatusCode(); @@ -484,11 +483,11 @@ Azure::Response TableServiceClient::CreateTable( auto const jsonRoot = Core::Json::_internal::json::parse(responseBody.begin(), responseBody.end()); - response.TableName = jsonRoot["TableName"].get(); - response.EditLink = jsonRoot["odata.editLink"].get(); - response.Id = jsonRoot["odata.id"].get(); - response.Metadata = jsonRoot["odata.metadata"].get(); - response.Type = jsonRoot["odata.type"].get(); + response.TableName = jsonRoot[TableName].get(); + response.EditLink = jsonRoot[ODataEditLink].get(); + response.Id = jsonRoot[ODataId].get(); + response.Metadata = jsonRoot[ODataMeta].get(); + response.Type = jsonRoot[ODataType].get(); } } @@ -508,10 +507,10 @@ Models::QueryTablesPagedResponse TableServiceClient::QueryTables( auto url = m_url; url.AppendPath("Tables"); Core::Http::Request request(Core::Http::HttpMethod::Get, url); - request.SetHeader("Accept", "application/json;odata=fullmetadata"); + request.SetHeader(AcceptHeader, AcceptFullMeta); if (options.Prefix.HasValue()) { - request.GetUrl().AppendQueryParameter("If-Match", options.Prefix.Value()); + request.GetUrl().AppendQueryParameter(IfMatch, options.Prefix.Value()); } auto rawResponse = m_pipeline->Send(request, context); auto const httpStatusCode = rawResponse->GetStatusCode(); @@ -529,14 +528,14 @@ Models::QueryTablesPagedResponse TableServiceClient::QueryTables( { auto const jsonRoot = Core::Json::_internal::json::parse(responseBody.begin(), responseBody.end()); - std::string metadataLink = jsonRoot["odata.metadata"].get(); - for (auto value : jsonRoot["value"]) + std::string metadataLink = jsonRoot[ODataMeta].get(); + for (auto value : jsonRoot[Value]) { Models::Table table; - table.TableName = value["TableName"].get(); - table.EditLink = value["odata.editLink"].get(); - table.Id = value["odata.id"].get(); - table.Type = value["odata.type"].get(); + table.TableName = value[TableName].get(); + table.EditLink = value[ODataEditLink].get(); + table.Id = value[ODataId].get(); + table.Type = value[ODataType].get(); table.Metadata = metadataLink; response.Tables.emplace_back(std::move(table)); } @@ -566,15 +565,15 @@ Azure::Response TableClient::SetAccessPolicy { auto url = m_url; url.AppendPath(m_tableName); - url.AppendQueryParameter("comp", "acl"); + url.AppendQueryParameter(CompHeader, "acl"); std::string xmlBody = Serializers::SetAccessPolicy(tableAccessPolicy); Core::IO::MemoryBodyStream requestBody( reinterpret_cast(xmlBody.data()), xmlBody.length()); auto request = Core::Http::Request(Core::Http::HttpMethod::Put, url, &requestBody); - request.SetHeader("Content-Type", "application/xml; charset=UTF-8"); - request.SetHeader("Content-Length", std::to_string(requestBody.Length())); - request.GetUrl().AppendQueryParameter("comp", "acl"); + request.SetHeader(ContentTypeHeader, "application/xml; charset=UTF-8"); + request.SetHeader(ContentLengthHeader, std::to_string(requestBody.Length())); + request.GetUrl().AppendQueryParameter(CompHeader, "acl"); request.SetHeader("x-ms-version", "2019-12-12"); auto pRawResponse = m_pipeline->Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); @@ -592,7 +591,7 @@ Azure::Response TableClient::GetAccessPolicy( auto url = m_url; url.SetPath(""); url.AppendPath(m_tableName); - url.AppendQueryParameter("comp", "acl"); + url.AppendQueryParameter(CompHeader, "acl"); Core::Http::Request request(Core::Http::HttpMethod::Get, url); auto pRawResponse = m_pipeline->Send(request, context); @@ -613,12 +612,12 @@ Azure::Response TableServiceClient::DeleteTable( Core::Context const& context) { auto url = m_url; - url.AppendPath("Tables('" + tableName + "')"); + url.AppendPath("Tables('" + tableName + ClosingFragment); Core::Http::Request request(Core::Http::HttpMethod::Delete, url); - request.SetHeader("Content-Type", "application/json"); - request.SetHeader("Accept", "application/json;odata=fullmetadata"); + request.SetHeader(ContentTypeHeader, ContentTypeJson); + request.SetHeader(AcceptHeader, AcceptFullMeta); auto rawResponse = m_pipeline->Send(request, context); auto const httpStatusCode = rawResponse->GetStatusCode(); @@ -632,9 +631,9 @@ Azure::Response TableServiceClient::DeleteTable( return Response(std::move(response), std::move(rawResponse)); } -Azure::Response TableClient::CreateEntity( +Azure::Response TableClient::AddEntity( Models::TableEntity const& tableEntity, - Models::CreateEntityOptions const& options, + Models::AddEntityOptions const& options, Core::Context const& context) { (void)options; @@ -648,10 +647,10 @@ Azure::Response TableClient::CreateEntity( Core::Http::Request request(Core::Http::HttpMethod::Post, url, &requestBody); - request.SetHeader("Content-Type", "application/json"); - request.SetHeader("Content-Length", std::to_string(requestBody.Length())); - request.SetHeader("Accept", "application/json;odata=nometadata"); - request.SetHeader("Prefer", "return-no-content"); + request.SetHeader(ContentTypeHeader, ContentTypeJson); + request.SetHeader(ContentLengthHeader, std::to_string(requestBody.Length())); + request.SetHeader(AcceptHeader, AcceptFullMeta); + request.SetHeader(PreferHeader, PreferNoContent); auto rawResponse = m_pipeline->Send(request, context); auto const httpStatusCode = rawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::NoContent) @@ -659,9 +658,9 @@ Azure::Response TableClient::CreateEntity( throw Core::RequestFailedException(rawResponse); } - Models::CreateEntityResult response{}; + Models::AddEntityResult response{}; response.ETag = rawResponse->GetHeaders().at("ETag"); - return Response(std::move(response), std::move(rawResponse)); + return Response(std::move(response), std::move(rawResponse)); } Azure::Response TableClient::UpdateEntity( @@ -672,8 +671,9 @@ Azure::Response TableClient::UpdateEntity( (void)options; auto url = m_url; url.AppendPath( - m_tableName + "(PartitionKey='" + Azure::Core::Url::Encode(tableEntity.PartitionKey) - + "',RowKey='" + Azure::Core::Url::Encode(tableEntity.RowKey) + "')"); + m_tableName + PartitionKeyFragment + + Azure::Core::Url::Encode(tableEntity.GetPartitionKey().Value) + RowKeyFragment + + Azure::Core::Url::Encode(tableEntity.GetRowKey().Value) + ClosingFragment); std::string jsonBody = Serializers::UpdateEntity(tableEntity); @@ -682,17 +682,17 @@ Azure::Response TableClient::UpdateEntity( Core::Http::Request request(Core::Http::HttpMethod::Put, url, &requestBody); - request.SetHeader("Content-Type", "application/json"); - request.SetHeader("Content-Length", std::to_string(requestBody.Length())); - request.SetHeader("Accept", "application/json;odata=nometadata"); - request.SetHeader("Prefer", "return-no-content"); - if (tableEntity.ETag.HasValue()) + request.SetHeader(ContentTypeHeader, ContentTypeJson); + request.SetHeader(ContentLengthHeader, std::to_string(requestBody.Length())); + request.SetHeader(AcceptHeader, AcceptFullMeta); + request.SetHeader(PreferHeader, PreferNoContent); + if (!tableEntity.GetETag().Value.empty()) { - request.SetHeader("If-Match", tableEntity.ETag.Value()); + request.SetHeader(IfMatch, tableEntity.GetETag().Value); } else { - request.SetHeader("If-Match", "*"); + request.SetHeader(IfMatch, "*"); } auto rawResponse = m_pipeline->Send(request, context); @@ -716,8 +716,9 @@ Azure::Response TableClient::MergeEntity( (void)options; auto url = m_url; url.AppendPath( - m_tableName + "(PartitionKey='" + Azure::Core::Url::Encode(tableEntity.PartitionKey) - + "',RowKey='" + Azure::Core::Url::Encode(tableEntity.RowKey) + "')"); + m_tableName + PartitionKeyFragment + + Azure::Core::Url::Encode(tableEntity.GetPartitionKey().Value) + RowKeyFragment + + Azure::Core::Url::Encode(tableEntity.GetRowKey().Value) + ClosingFragment); std::string jsonBody = Serializers::MergeEntity(tableEntity); @@ -726,17 +727,17 @@ Azure::Response TableClient::MergeEntity( Core::Http::Request request(Core::Http::HttpMethod::Patch, url, &requestBody); - request.SetHeader("Content-Type", "application/json"); - request.SetHeader("Content-Length", std::to_string(requestBody.Length())); - request.SetHeader("Accept", "application/json;odata=nometadata"); - request.SetHeader("Prefer", "return-no-content"); - if (tableEntity.ETag.HasValue()) + request.SetHeader(ContentTypeHeader, ContentTypeJson); + request.SetHeader(ContentLengthHeader, std::to_string(requestBody.Length())); + request.SetHeader(AcceptHeader, AcceptFullMeta); + request.SetHeader(PreferHeader, PreferNoContent); + if (!tableEntity.GetETag().Value.empty()) { - request.SetHeader("If-Match", tableEntity.ETag.Value()); + request.SetHeader(IfMatch, tableEntity.GetETag().Value); } else { - request.SetHeader("If-Match", "*"); + request.SetHeader(IfMatch, "*"); } auto rawResponse = m_pipeline->Send(request, context); @@ -758,20 +759,21 @@ Azure::Response TableClient::DeleteEntity( { auto url = m_url; url.AppendPath( - m_tableName + "(PartitionKey='" + Azure::Core::Url::Encode(tableEntity.PartitionKey) - + "',RowKey='" + Azure::Core::Url::Encode(tableEntity.RowKey) + "')"); + m_tableName + PartitionKeyFragment + + Azure::Core::Url::Encode(tableEntity.GetPartitionKey().Value) + RowKeyFragment + + Azure::Core::Url::Encode(tableEntity.GetRowKey().Value) + ClosingFragment); Core::Http::Request request(Core::Http::HttpMethod::Delete, url); - if (tableEntity.ETag.HasValue()) + if (!tableEntity.GetETag().Value.empty()) { - request.SetHeader("If-Match", tableEntity.ETag.Value()); + request.SetHeader(IfMatch, tableEntity.GetETag().Value); } else { - request.SetHeader("If-Match", "*"); + request.SetHeader(IfMatch, "*"); } - request.SetHeader("Accept", "application/json;odata=nometadata"); + request.SetHeader(AcceptHeader, AcceptFullMeta); auto rawResponse = m_pipeline->Send(request, context); auto const httpStatusCode = rawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::NoContent) @@ -807,7 +809,7 @@ Azure::Response TableClient::UpsertEntity( } catch (const Azure::Core::RequestFailedException&) { - auto response = CreateEntity(tableEntity, Models::CreateEntityOptions(options), context); + auto response = AddEntity(tableEntity, Models::AddEntityOptions(options), context); return Azure::Response( Models::UpsertEntityResult(response.Value), std::move(response.RawResponse)); } @@ -827,11 +829,11 @@ Azure::Response TableClient::GetEntity( { auto url = m_url; url.AppendPath( - m_tableName + "(PartitionKey='" + Azure::Core::Url::Encode(partitionKey) + "',RowKey='" - + Azure::Core::Url::Encode(rowKey) + "')"); + m_tableName + PartitionKeyFragment + Azure::Core::Url::Encode(partitionKey) + RowKeyFragment + + Azure::Core::Url::Encode(rowKey) + ClosingFragment); Core::Http::Request request(Core::Http::HttpMethod::Get, url); - request.SetHeader("Accept", "application/json;odata=fullmetadata"); + request.SetHeader(AcceptHeader, AcceptFullMeta); auto rawResponse = m_pipeline->Send(request, context); auto const httpStatusCode = rawResponse->GetStatusCode(); @@ -881,7 +883,7 @@ Models::QueryEntitiesPagedResponse TableClient::QueryEntities( } Core::Http::Request request(Core::Http::HttpMethod::Get, url); - request.SetHeader("Accept", "application/json;odata=fullmetadata"); + request.SetHeader(AcceptHeader, AcceptFullMeta); auto rawResponse = m_pipeline->Send(request, context); auto const httpStatusCode = rawResponse->GetStatusCode(); @@ -909,13 +911,13 @@ Models::QueryEntitiesPagedResponse TableClient::QueryEntities( auto const jsonRoot = Core::Json::_internal::json::parse(responseBody.begin(), responseBody.end()); - if (!jsonRoot.contains("value")) + if (!jsonRoot.contains(Value)) { response.TableEntities.emplace_back(Serializers::DeserializeEntity(jsonRoot)); } else { - for (auto value : jsonRoot["value"]) + for (auto value : jsonRoot[Value]) { response.TableEntities.emplace_back(Serializers::DeserializeEntity(value)); } @@ -924,27 +926,24 @@ Models::QueryEntitiesPagedResponse TableClient::QueryEntities( return response; } -Transaction TableClient::CreateTransaction(std::string const& partitionKey) -{ - return Transaction(m_url.GetAbsoluteUrl(), m_tableName, partitionKey); -} - Azure::Response TableClient::SubmitTransaction( - Transaction& transaction, + std::vector const& steps, Core::Context const& context) { auto url = m_url; url.AppendPath("$batch"); + std::string batchId = "batch_" + Azure::Core::Uuid::CreateUuid().ToString(); + std::string changesetId = "changeset_" + Azure::Core::Uuid::CreateUuid().ToString(); - std::string body = transaction.PreparePayload(); + std::string body = PreparePayload(batchId, changesetId, steps); Core::IO::MemoryBodyStream requestBody( reinterpret_cast(body.data()), body.length()); Core::Http::Request request(Core::Http::HttpMethod::Post, url, &requestBody); - request.SetHeader("Content-Type", "multipart/mixed; boundary=" + transaction.GetBatchId()); - request.SetHeader("Accept", "application/json;odata=fullmetadata"); - request.SetHeader("Content-Length", std::to_string(requestBody.Length())); + request.SetHeader(ContentTypeHeader, "multipart/mixed; boundary=" + batchId); + request.SetHeader(AcceptHeader, AcceptFullMeta); + request.SetHeader(ContentLengthHeader, std::to_string(requestBody.Length())); request.SetHeader("Connection", "Keep-Alive"); request.SetHeader("DataServiceVersion", "3.0"); request.SetHeader("Accept-Charset", "UTF-8"); @@ -973,17 +972,17 @@ Azure::Response TableClient::SubmitTransaction( response.StatusCode = status; } - if (line.find("odata.error") != std::string::npos) + if (line.find(ODataError) != std::string::npos) { auto const jsonRoot = Core::Json::_internal::json::parse(line.begin(), line.end()); - if (jsonRoot["odata.error"].contains("code")) + if (jsonRoot[ODataError].contains("code")) { - error.Code = jsonRoot["odata.error"]["code"].get(); + error.Code = jsonRoot[ODataError]["code"].get(); } - if (jsonRoot["odata.error"].contains("message") - && jsonRoot["odata.error"]["message"].contains("value")) + if (jsonRoot[ODataError].contains("message") + && jsonRoot[ODataError]["message"].contains(Value)) { - error.Message = jsonRoot["odata.error"]["message"]["value"].get(); + error.Message = jsonRoot[ODataError]["message"][Value].get(); } } } @@ -994,3 +993,121 @@ Azure::Response TableClient::SubmitTransaction( } return Response(std::move(response), std::move(rawResponse)); } + +std::string TableClient::PreparePayload( + std::string const& batchId, + std::string const& changesetId, + std::vector const& steps) +{ + std::string accumulator + = "--" + batchId + "\nContent-Type: multipart/mixed; boundary=" + changesetId + "\n\n"; + + for (auto step : steps) + { + switch (step.Action) + { + case Models::TransactionActionType::Add: + accumulator += PrepAddEntity(changesetId, step.Entity); + break; + case Models::TransactionActionType::Delete: + accumulator += PrepDeleteEntity(changesetId, step.Entity); + break; + case Models::TransactionActionType::InsertMerge: + case Models::TransactionActionType::UpdateMerge: + accumulator += PrepMergeEntity(changesetId, step.Entity); + break; + case Models::TransactionActionType::InsertReplace: + case Models::TransactionActionType::UpdateReplace: + accumulator += PrepUpdateEntity(changesetId, step.Entity); + break; + } + } + + accumulator += "\n\n--" + changesetId + "--\n"; + accumulator += "--" + batchId + "\n"; + return accumulator; +} +std::string TableClient::PrepAddEntity(std::string const& changesetId, Models::TableEntity entity) +{ + std::string returnValue = "--" + changesetId + "\n"; + returnValue += "Content-Type: application/http\n"; + returnValue += "Content-Transfer-Encoding: binary\n\n"; + + returnValue += "POST " + m_url.GetAbsoluteUrl() + "/" + m_tableName + " HTTP/1.1\n"; + returnValue += "Content-Type: application/json\n"; + returnValue += "Accept: application/json;odata=minimalmetadata\n"; + returnValue += "Prefer: return-no-content\n"; + returnValue += "DataServiceVersion: 3.0;\n\n"; + returnValue += Serializers::CreateEntity(entity); + return returnValue; +} +std::string TableClient::PrepDeleteEntity( + std::string const& changesetId, + Models::TableEntity entity) +{ + std::string returnValue = "--" + changesetId + "\n"; + returnValue += "Content-Type: application/http\n"; + returnValue += "Content-Transfer-Encoding: binary\n\n"; + + returnValue += "DELETE " + m_url.GetAbsoluteUrl() + "/" + m_tableName + PartitionKeyFragment + + entity.GetPartitionKey().Value + RowKeyFragment + entity.GetRowKey().Value + ClosingFragment + + " HTTP/1.1\n"; + returnValue += "Accept: application/json;odata=minimalmetadata\n"; + // returnValue += "Prefer: return-no-content\n"; + returnValue += "DataServiceVersion: 3.0;\n"; + if (!entity.GetETag().Value.empty()) + { + returnValue += "If-Match: " + entity.GetETag().Value; + } + else + { + returnValue += "If-Match: *"; + } + returnValue += "\n"; + return returnValue; +} + +std::string TableClient::PrepMergeEntity(std::string const& changesetId, Models::TableEntity entity) +{ + std::string returnValue = "--" + changesetId + "\n"; + returnValue += "Content-Type: application/http\n"; + returnValue += "Content-Transfer-Encoding: binary\n\n"; + + returnValue += "MERGE " + m_url.GetAbsoluteUrl() + "/" + m_tableName + PartitionKeyFragment + + entity.GetPartitionKey().Value + RowKeyFragment + entity.GetRowKey().Value + ClosingFragment + + " HTTP/1.1\n"; + returnValue += "Content-Type: application/json\n"; + returnValue += "Accept: application/json;odata=minimalmetadata\n"; + returnValue += "DataServiceVersion: 3.0;\n\n"; + returnValue += Serializers::MergeEntity(entity); + + return returnValue; +} + +std::string TableClient::PrepUpdateEntity( + std::string const& changesetId, + Models::TableEntity entity) +{ + std::string returnValue = "--" + changesetId + "\n"; + returnValue += "Content-Type: application/http\n"; + returnValue += "Content-Transfer-Encoding: binary\n\n"; + + returnValue += "PUT " + m_url.GetAbsoluteUrl() + "/" + m_tableName + PartitionKeyFragment + + entity.GetPartitionKey().Value + RowKeyFragment + entity.GetRowKey().Value + ClosingFragment + + " HTTP/1.1\n"; + returnValue += "Content-Type: application/json\n"; + returnValue += "Accept: application/json;odata=minimalmetadata\n"; + returnValue += "Prefer: return-no-content\n"; + returnValue += "DataServiceVersion: 3.0;\n"; + if (!entity.GetETag().Value.empty()) + { + returnValue += "If-Match: " + entity.GetETag().Value; + } + else + { + returnValue += "If-Match: *"; + } + returnValue += "\n\n"; + returnValue += Serializers::UpdateEntity(entity); + return returnValue; +} diff --git a/sdk/tables/azure-data-tables/src/tables_sas_builder.cpp b/sdk/tables/azure-data-tables/src/tables_sas_builder.cpp index c52eb51b3..3992a1c29 100644 --- a/sdk/tables/azure-data-tables/src/tables_sas_builder.cpp +++ b/sdk/tables/azure-data-tables/src/tables_sas_builder.cpp @@ -37,7 +37,7 @@ namespace Azure { namespace Data { namespace Tables { namespace Sas { } std::string TablesSasBuilder::GenerateSasToken( - const Azure::Data::Tables::Credentials::SharedKeyCredential& credential) + const Azure::Data::Tables::Credentials::NamedKeyCredential& credential) { std::string canonicalName = Azure::Data::Tables::_detail::Cryptography::UrlUtils::UrlEncodeQueryParameter( diff --git a/sdk/tables/azure-data-tables/src/transactions.cpp b/sdk/tables/azure-data-tables/src/transactions.cpp deleted file mode 100644 index d7de54812..000000000 --- a/sdk/tables/azure-data-tables/src/transactions.cpp +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "azure/data/tables/transactions.hpp" - -#include "azure/data/tables/internal/serializers.hpp" - -using namespace Azure::Data::Tables; -using namespace Azure::Data::Tables::_detail; -void Transaction::CreateEntity(Models::TableEntity const& entity) -{ - Models::TableEntity _entity = entity; - _entity.PartitionKey = m_partitionKey; - m_steps.emplace_back( - Models::TransactionStep{Models::TransactionAction::InsertEntity, std::move(_entity)}); -} -void Transaction::DeleteEntity(Models::TableEntity const& entity) -{ - Models::TableEntity _entity = entity; - _entity.PartitionKey = m_partitionKey; - m_steps.emplace_back( - Models::TransactionStep{Models::TransactionAction::DeleteEntity, std::move(_entity)}); -} - -void Transaction::MergeEntity(Models::TableEntity const& entity) -{ - Models::TableEntity _entity = entity; - _entity.PartitionKey = m_partitionKey; - m_steps.emplace_back( - Models::TransactionStep{Models::TransactionAction::MergeEntity, std::move(_entity)}); -} -void Transaction::InsertMergeEntity(Models::TableEntity const& entity) -{ - Models::TableEntity _entity = entity; - _entity.PartitionKey = m_partitionKey; - m_steps.emplace_back( - Models::TransactionStep{Models::TransactionAction::MergeEntity, std::move(_entity)}); -} -void Transaction::UpdateEntity(Models::TableEntity const& entity) -{ - Models::TableEntity _entity = entity; - _entity.PartitionKey = m_partitionKey; - m_steps.emplace_back( - Models::TransactionStep{Models::TransactionAction::UpdateEntity, std::move(_entity)}); -} -void Transaction::InsertReplaceEntity(Models::TableEntity const& entity) -{ - Models::TableEntity _entity = entity; - _entity.PartitionKey = m_partitionKey; - m_steps.emplace_back( - Models::TransactionStep{Models::TransactionAction::UpdateEntity, std::move(_entity)}); -} -std::string Transaction::PreparePayload() -{ - std::string accumulator - = "--" + m_batchId + "\nContent-Type: multipart/mixed; boundary=" + m_changesetId + "\n\n"; - - for (auto step : m_steps) - { - switch (step.Action) - { - case Models::TransactionAction::InsertEntity: - accumulator += PrepCreateEntity(step.Entity); - break; - case Models::TransactionAction::DeleteEntity: - accumulator += PrepDeleteEntity(step.Entity); - break; - case Models::TransactionAction::InsertMergeEntity: - case Models::TransactionAction::MergeEntity: - accumulator += PrepMergeEntity(step.Entity); - break; - case Models::TransactionAction::InsertReplaceEntity: - case Models::TransactionAction::UpdateEntity: - accumulator += PrepUpdateEntity(step.Entity); - break; - } - } - - accumulator += "\n\n--" + m_changesetId + "--\n"; - accumulator += "--" + m_batchId + "\n"; - return accumulator; -} - -std::string Transaction::PrepCreateEntity(Models::TableEntity entity) -{ - std::string returnValue = "--" + m_changesetId + "\n"; - returnValue += "Content-Type: application/http\n"; - returnValue += "Content-Transfer-Encoding: binary\n\n"; - - returnValue += "POST " + m_url + "/" + m_tableName + " HTTP/1.1\n"; - returnValue += "Content-Type: application/json\n"; - returnValue += "Accept: application/json;odata=minimalmetadata\n"; - returnValue += "Prefer: return-no-content\n"; - returnValue += "DataServiceVersion: 3.0;\n\n"; - returnValue += Serializers::CreateEntity(entity); - return returnValue; -} - -std::string Transaction::PrepDeleteEntity(Models::TableEntity entity) -{ - std::string returnValue = "--" + m_changesetId + "\n"; - returnValue += "Content-Type: application/http\n"; - returnValue += "Content-Transfer-Encoding: binary\n\n"; - - returnValue += "DELETE " + m_url + "/" + m_tableName + "(PartitionKey='" + entity.PartitionKey - + "',RowKey='" + entity.RowKey + "')" + " HTTP/1.1\n"; - returnValue += "Accept: application/json;odata=minimalmetadata\n"; - // returnValue += "Prefer: return-no-content\n"; - returnValue += "DataServiceVersion: 3.0;\n"; - if (entity.ETag.HasValue()) - { - returnValue += "If-Match: " + entity.ETag.Value(); - } - else - { - returnValue += "If-Match: *"; - } - returnValue += "\n"; - return returnValue; -} - -std::string Transaction::PrepMergeEntity(Models::TableEntity entity) -{ - std::string returnValue = "--" + m_changesetId + "\n"; - returnValue += "Content-Type: application/http\n"; - returnValue += "Content-Transfer-Encoding: binary\n\n"; - - returnValue += "MERGE " + m_url + "/" + m_tableName + "(PartitionKey='" + entity.PartitionKey - + "',RowKey='" + entity.RowKey + "')" + " HTTP/1.1\n"; - returnValue += "Content-Type: application/json\n"; - returnValue += "Accept: application/json;odata=minimalmetadata\n"; - returnValue += "DataServiceVersion: 3.0;\n\n"; - returnValue += Serializers::MergeEntity(entity); - - return returnValue; -} - -std::string Transaction::PrepUpdateEntity(Models::TableEntity entity) -{ - std::string returnValue = "--" + m_changesetId + "\n"; - returnValue += "Content-Type: application/http\n"; - returnValue += "Content-Transfer-Encoding: binary\n\n"; - - returnValue += "PUT " + m_url + "/" + m_tableName + "(PartitionKey='" + entity.PartitionKey - + "',RowKey='" + entity.RowKey + "')" + " HTTP/1.1\n"; - returnValue += "Content-Type: application/json\n"; - returnValue += "Accept: application/json;odata=minimalmetadata\n"; - returnValue += "Prefer: return-no-content\n"; - returnValue += "DataServiceVersion: 3.0;\n"; - if (entity.ETag.HasValue()) - { - returnValue += "If-Match: " + entity.ETag.Value(); - } - else - { - returnValue += "If-Match: *"; - } - returnValue += "\n\n"; - returnValue += Serializers::UpdateEntity(entity); - return returnValue; -} diff --git a/sdk/tables/azure-data-tables/test/stress/tables_stress_test.cpp b/sdk/tables/azure-data-tables/test/stress/tables_stress_test.cpp index d89716adf..c9656176c 100644 --- a/sdk/tables/azure-data-tables/test/stress/tables_stress_test.cpp +++ b/sdk/tables/azure-data-tables/test/stress/tables_stress_test.cpp @@ -19,39 +19,51 @@ #include using namespace Azure::Data::Tables; +namespace Azure { namespace Data { namespace Tables { namespace StressTest { -std::string Transactions(int num) -{ - TableClient client("http://localhost:7777", "table"); - auto transaction = client.CreateTransaction("pk1"); - for (int i = 0; i < num; i++) - { - Models::TableEntity entity; - entity.PartitionKey = "pk1"; - entity.RowKey = "rk1"; - entity.Properties.emplace("prop1", "value1"); - entity.Properties.emplace("prop2", "value2"); - transaction.CreateEntity(entity); - transaction.DeleteEntity(entity); - transaction.UpdateEntity(entity); - transaction.MergeEntity(entity); - } - auto result = transaction.PreparePayload(); - return result; -} + class TransactionStressTest final { + public: + std::string Transactions(int num) + { + TableClient client("http://localhost:7777", "table"); + std::vector steps; + for (int i = 0; i < num; i++) + { + Models::TableEntity entity; + entity.SetPartitionKey("pk1"); + entity.SetRowKey("rk1"); + entity.Properties.emplace( + "prop1", Azure::Data::Tables::Models::TableEntityProperty("value1")); + entity.Properties.emplace( + "prop2", Azure::Data::Tables::Models::TableEntityProperty("value2")); + steps.emplace_back( + Models::TransactionStep{Models::TransactionActionType::InsertMerge, entity}); + + steps.emplace_back(Models::TransactionStep{Models::TransactionActionType::Delete, entity}); + steps.emplace_back( + Models::TransactionStep{Models::TransactionActionType::UpdateReplace, entity}); + steps.emplace_back( + Models::TransactionStep{Models::TransactionActionType::UpdateMerge, entity}); + } + auto result = client.PreparePayload("batch", "changeset", steps); + return result; + } + }; +}}}} // namespace Azure::Data::Tables::StressTest int main() { + Azure::Data::Tables::StressTest::TransactionStressTest test; std::cout << "--------------\tSTARTING TEST\t--------------" << std::endl; std::cout << "--------------\tPRE WARMUP\t--------------" << std::endl; - Transactions(WARMUP); + test.Transactions(WARMUP); std::cout << "--------------\tPOST WARMUP\t--------------" << std::endl; for (int i = 0; i < ROUNDS; i++) { std::cout << "--------------\tTEST ITERATION:" << i << "\t--------------" << std::endl; - Transactions(REQUESTS); + test.Transactions(REQUESTS); std::cout << "--------------\tDONE ITERATION:" << i << "\t--------------" << std::endl; } diff --git a/sdk/tables/azure-data-tables/test/ut/sas_test.cpp b/sdk/tables/azure-data-tables/test/ut/sas_test.cpp index 22a0b7a01..47e2853df 100644 --- a/sdk/tables/azure-data-tables/test/ut/sas_test.cpp +++ b/sdk/tables/azure-data-tables/test/ut/sas_test.cpp @@ -8,7 +8,7 @@ #include using namespace Azure::Data::Tables::Sas; -// cspell: words rwdxylacupitf raud bqft +// cspell: words rwdlau raud bqft namespace Azure { namespace Data { namespace Test { TEST(SasTest, TableSasBuilderTestAllSet) { @@ -27,7 +27,7 @@ namespace Azure { namespace Data { namespace Test { sasBuilder.PartitionKeyStart = "myStartPartitionKey"; sasBuilder.PartitionKeyEnd = "myEndPartitionKey"; std::string key = "accountKey"; - Azure::Data::Tables::Credentials::SharedKeyCredential cred( + Azure::Data::Tables::Credentials::NamedKeyCredential cred( "accountName", Azure::Core::Convert::Base64Encode(std::vector(key.begin(), key.end()))); auto sasToken = sasBuilder.GenerateSasToken(cred); @@ -56,7 +56,7 @@ namespace Azure { namespace Data { namespace Test { sasBuilder.TableName = "someTableName"; std::string key = "*"; - Azure::Data::Tables::Credentials::SharedKeyCredential cred( + Azure::Data::Tables::Credentials::NamedKeyCredential cred( "someaccount", Azure::Core::Convert::Base64Encode(std::vector(key.begin(), key.end()))); auto sasToken = sasBuilder.GenerateSasToken(cred); @@ -73,7 +73,7 @@ namespace Azure { namespace Data { namespace Test { sasBuilder.ExpiresOn = Azure::DateTime::Parse("2022-08-18T00:00:00Z", Azure::DateTime::DateFormat::Rfc3339); std::string key = "accountKey"; - Azure::Data::Tables::Credentials::SharedKeyCredential cred( + Azure::Data::Tables::Credentials::NamedKeyCredential cred( "accountName", Azure::Core::Convert::Base64Encode(std::vector(key.begin(), key.end()))); auto sasToken = sasBuilder.GenerateSasToken(cred); @@ -92,11 +92,11 @@ namespace Azure { namespace Data { namespace Test { = Azure::DateTime::Parse("2022-08-18T00:00:00Z", Azure::DateTime::DateFormat::Rfc3339); sasBuilder.IPRange = "iprange"; sasBuilder.EncryptionScope = "myScope"; - sasBuilder.ResourceTypes = AccountSasResource::All; + sasBuilder.ResourceTypes = AccountSasResourceType::All; sasBuilder.Services = AccountSasServices::All; std::string key = "accountKey"; - Azure::Data::Tables::Credentials::SharedKeyCredential cred( + Azure::Data::Tables::Credentials::NamedKeyCredential cred( "accountName", Azure::Core::Convert::Base64Encode(std::vector(key.begin(), key.end()))); auto sasToken = sasBuilder.GenerateSasToken(cred); @@ -106,10 +106,10 @@ namespace Azure { namespace Data { namespace Test { 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("sp"), "rwdlau"); EXPECT_EQ(sasParts.at("spr"), "https,http"); EXPECT_EQ(sasParts.at("srt"), "sco"); - EXPECT_EQ(sasParts.at("ss"), "bqft"); + EXPECT_EQ(sasParts.at("ss"), "t"); EXPECT_EQ(sasParts.at("st"), "2020-08-18T00:00:00Z"); EXPECT_EQ(sasParts.at("sv"), "2023-08-03"); } @@ -122,7 +122,7 @@ namespace Azure { namespace Data { namespace Test { = Azure::DateTime::Parse("2022-08-18T00:00:00Z", Azure::DateTime::DateFormat::Rfc3339); std::string key = "accountKey"; - Azure::Data::Tables::Credentials::SharedKeyCredential cred( + Azure::Data::Tables::Credentials::NamedKeyCredential cred( "accountName", Azure::Core::Convert::Base64Encode(std::vector(key.begin(), key.end()))); auto sasToken = sasBuilder.GenerateSasToken(cred); diff --git a/sdk/tables/azure-data-tables/test/ut/serializers_test.cpp b/sdk/tables/azure-data-tables/test/ut/serializers_test.cpp index 26405cbfb..7678ecd52 100644 --- a/sdk/tables/azure-data-tables/test/ut/serializers_test.cpp +++ b/sdk/tables/azure-data-tables/test/ut/serializers_test.cpp @@ -38,20 +38,20 @@ namespace Azure { namespace Data { namespace Test { policy.ServiceProperties.HourMetrics.Version = "1.0"; policy.ServiceProperties.HourMetrics.IsEnabled = true; policy.ServiceProperties.HourMetrics.IncludeApis = true; - policy.ServiceProperties.HourMetrics.RetentionPolicyDefinition.Days = 1; + policy.ServiceProperties.HourMetrics.RetentionPolicyDefinition.DataRetentionInDays = 1; policy.ServiceProperties.HourMetrics.RetentionPolicyDefinition.IsEnabled = true; policy.ServiceProperties.MinuteMetrics.Version = "1.0"; policy.ServiceProperties.MinuteMetrics.IsEnabled = true; policy.ServiceProperties.MinuteMetrics.IncludeApis = true; - policy.ServiceProperties.MinuteMetrics.RetentionPolicyDefinition.Days = 1; + policy.ServiceProperties.MinuteMetrics.RetentionPolicyDefinition.DataRetentionInDays = 1; policy.ServiceProperties.MinuteMetrics.RetentionPolicyDefinition.IsEnabled = true; policy.ServiceProperties.Logging.Version = "1.0"; policy.ServiceProperties.Logging.Delete = true; policy.ServiceProperties.Logging.Read = true; policy.ServiceProperties.Logging.Write = true; - policy.ServiceProperties.Logging.RetentionPolicyDefinition.Days = 1; + policy.ServiceProperties.Logging.RetentionPolicyDefinition.DataRetentionInDays = 1; policy.ServiceProperties.Logging.RetentionPolicyDefinition.IsEnabled = true; policy.ServiceProperties.Cors.emplace_back(CorsRule()); @@ -66,20 +66,20 @@ namespace Azure { namespace Data { namespace Test { EXPECT_EQ(data.HourMetrics.Version, "1.0"); EXPECT_EQ(data.HourMetrics.IsEnabled, true); EXPECT_EQ(data.HourMetrics.IncludeApis.Value(), true); - EXPECT_EQ(data.HourMetrics.RetentionPolicyDefinition.Days.Value(), 1); + EXPECT_EQ(data.HourMetrics.RetentionPolicyDefinition.DataRetentionInDays.Value(), 1); EXPECT_EQ(data.HourMetrics.RetentionPolicyDefinition.IsEnabled, true); EXPECT_EQ(data.MinuteMetrics.Version, "1.0"); EXPECT_EQ(data.MinuteMetrics.IsEnabled, true); EXPECT_EQ(data.MinuteMetrics.IncludeApis.Value(), true); - EXPECT_EQ(data.MinuteMetrics.RetentionPolicyDefinition.Days.Value(), 1); + EXPECT_EQ(data.MinuteMetrics.RetentionPolicyDefinition.DataRetentionInDays.Value(), 1); EXPECT_EQ(data.MinuteMetrics.RetentionPolicyDefinition.IsEnabled, true); EXPECT_EQ(data.Logging.Version, "1.0"); EXPECT_EQ(data.Logging.Delete, true); EXPECT_EQ(data.Logging.Read, true); EXPECT_EQ(data.Logging.Write, true); - EXPECT_EQ(data.Logging.RetentionPolicyDefinition.Days.Value(), 1); + EXPECT_EQ(data.Logging.RetentionPolicyDefinition.DataRetentionInDays.Value(), 1); EXPECT_EQ(data.Logging.RetentionPolicyDefinition.IsEnabled, true); EXPECT_EQ(data.Cors[0].AllowedOrigins, "*"); diff --git a/sdk/tables/azure-data-tables/test/ut/shared_key_lite_policy_test.cpp b/sdk/tables/azure-data-tables/test/ut/shared_key_lite_policy_test.cpp index c7bdb0d17..7df7c8d53 100644 --- a/sdk/tables/azure-data-tables/test/ut/shared_key_lite_policy_test.cpp +++ b/sdk/tables/azure-data-tables/test/ut/shared_key_lite_policy_test.cpp @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -#include "azure/data/tables/credentials/shared_key_credential.hpp" +#include "azure/data/tables/credentials/named_key_credential.hpp" #include "azure/data/tables/internal/cryptography/hmacsha256.hpp" #include "azure/data/tables/internal/policies/shared_key_lite_policy.hpp" @@ -27,7 +27,7 @@ namespace Azure { namespace Data { namespace Tables { namespace _internal { name std::vector(accountKey.begin(), accountKey.end())) + ";EndpointSuffix = core.windows.net "; - std::shared_ptr credential; + std::shared_ptr credential; auto parsedConnectionString = ParseConnectionString(connectionString); SharedKeyLitePolicy policy(parsedConnectionString.KeyCredential); @@ -54,7 +54,7 @@ namespace Azure { namespace Data { namespace Tables { namespace _internal { name std::vector(accountKey.begin(), accountKey.end())) + ";EndpointSuffix = core.windows.net "; - std::shared_ptr credential; + std::shared_ptr credential; auto parsedConnectionString = ParseConnectionString(connectionString); SharedKeyLitePolicy policy(parsedConnectionString.KeyCredential); @@ -73,7 +73,7 @@ namespace Azure { namespace Data { namespace Tables { namespace _internal { name std::vector(accountKey.begin(), accountKey.end())) + ";EndpointSuffix = core.windows.net "; - std::shared_ptr credential; + std::shared_ptr credential; auto parsedConnectionString = ParseConnectionString(connectionString); SharedKeyLitePolicy policy(parsedConnectionString.KeyCredential); 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 32561c3b9..0a84c9356 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 @@ -9,10 +9,21 @@ #include #include +#include +#include +// useful for debugging to avoid table conflicts when creating tables +// as it takes a while from then a table is deleted to when it can be recreated +// #define RANDOM_TABLE_NAME + +#ifdef RANDOM_TABLE_NAME +#include #include #include +#endif using namespace Azure::Data; +using namespace Azure::Data::Tables::Models; + namespace Azure { namespace Data { namespace Test { std::shared_ptr m_credential; void TablesClientTest::SetUp() @@ -29,19 +40,24 @@ namespace Azure { namespace Data { namespace Test { { auto clientOptions = InitStorageClientOptions(); auto tableClientOptions = InitStorageClientOptions(); - m_tableName = GetTestNameLowerCase(); +#ifdef RANDOM_TABLE_NAME + srand(static_cast(time(nullptr))); + int random_number = rand() % 1000 + 1; + m_tableName = m_tableName + std::to_string(random_number); +#endif + std::replace(m_tableName.begin(), m_tableName.end(), '-', '0'); switch (param) { case AuthType::ConnectionString: m_tableServiceClient = std::make_shared( Tables::TableServiceClient::CreateFromConnectionString( - GetEnv("STANDARD_STORAGE_CONNECTION_STRING"), clientOptions)); + GetConnectionString(), clientOptions)); m_tableClient = std::make_shared( Tables::TableClient::CreateFromConnectionString( - GetEnv("STANDARD_STORAGE_CONNECTION_STRING"), m_tableName, tableClientOptions)); + GetConnectionString(), m_tableName, tableClientOptions)); break; case AuthType::Key: m_credential = GetTestCredential(); @@ -57,11 +73,11 @@ namespace Azure { namespace Data { namespace Test { tableClientOptions)); break; case AuthType::SAS: - auto creds = std::make_shared( + auto creds = std::make_shared( GetAccountName(), GetAccountKey()); Azure::Data::Tables::Sas::AccountSasBuilder sasBuilder; sasBuilder.ExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60); - sasBuilder.ResourceTypes = Azure::Data::Tables::Sas::AccountSasResource::All; + sasBuilder.ResourceTypes = Azure::Data::Tables::Sas::AccountSasResourceType::All; sasBuilder.Services = Azure::Data::Tables::Sas::AccountSasServices::All; sasBuilder.Protocol = Azure::Data::Tables::Sas::SasProtocol::HttpsOnly; sasBuilder.SetPermissions(Azure::Data::Tables::Sas::AccountSasPermissions::All); @@ -262,20 +278,19 @@ namespace Azure { namespace Data { namespace Test { TEST_P(TablesClientTest, EntityCreate) { - if (GetParam() == AuthType::Key - && Azure::Core::_internal::StringExtensions::ToLower(GetEnv("AZURE_TEST_MODE")) == "live") + if (GetParam() == AuthType::Key) { EXPECT_TRUE(true); return; } Azure::Data::Tables::Models::TableEntity entity; - entity.PartitionKey = "P1"; - entity.RowKey = "R1"; - entity.Properties["Name"] = "Azure"; - entity.Properties["Product"] = "Tables"; + entity.SetPartitionKey("P1"); + entity.SetRowKey("R1"); + entity.Properties["Name"] = TableEntityProperty("Azure"); + entity.Properties["Product"] = TableEntityProperty("Tables"); auto createResponse = m_tableServiceClient->CreateTable(m_tableName); - auto response = m_tableClient->CreateEntity(entity); + auto response = m_tableClient->AddEntity(entity); EXPECT_EQ(response.RawResponse->GetStatusCode(), Azure::Core::Http::HttpStatusCode::NoContent); EXPECT_FALSE(response.Value.ETag.empty()); @@ -283,32 +298,31 @@ namespace Azure { namespace Data { namespace Test { TEST_P(TablesClientTest, EntityUpdate) { - if (GetParam() == AuthType::Key - && Azure::Core::_internal::StringExtensions::ToLower(GetEnv("AZURE_TEST_MODE")) == "live") + if (GetParam() == AuthType::Key) { EXPECT_TRUE(true); return; } Azure::Data::Tables::Models::TableEntity entity; - entity.PartitionKey = "P1"; - entity.RowKey = "R1"; - entity.Properties["Name"] = "Azure"; - entity.Properties["Product"] = "Tables"; + entity.SetPartitionKey("P1"); + entity.SetRowKey("R1"); + entity.Properties["Name"] = TableEntityProperty("Azure"); + entity.Properties["Product"] = TableEntityProperty("Tables"); auto createResponse = m_tableServiceClient->CreateTable(m_tableName); - auto response = m_tableClient->CreateEntity(entity); + auto response = m_tableClient->AddEntity(entity); EXPECT_EQ(response.RawResponse->GetStatusCode(), Azure::Core::Http::HttpStatusCode::NoContent); EXPECT_FALSE(response.Value.ETag.empty()); - entity.Properties["Product"] = "Tables2"; + entity.Properties["Product"] = TableEntityProperty("Tables2"); auto updateResponse = m_tableClient->UpdateEntity(entity); EXPECT_EQ( updateResponse.RawResponse->GetStatusCode(), Azure::Core::Http::HttpStatusCode::NoContent); EXPECT_FALSE(updateResponse.Value.ETag.empty()); - entity.Properties["Product"] = "Tables3"; - entity.ETag = updateResponse.Value.ETag; + entity.Properties["Product"] = TableEntityProperty("Tables3"); + entity.SetETag(updateResponse.Value.ETag); auto updateResponse2 = m_tableClient->UpdateEntity(entity); EXPECT_EQ( @@ -318,32 +332,31 @@ namespace Azure { namespace Data { namespace Test { TEST_P(TablesClientTest, EntityMerge) { - if (GetParam() == AuthType::Key - && Azure::Core::_internal::StringExtensions::ToLower(GetEnv("AZURE_TEST_MODE")) == "live") + if (GetParam() == AuthType::Key) { EXPECT_TRUE(true); return; } Azure::Data::Tables::Models::TableEntity entity; - entity.PartitionKey = "P1"; - entity.RowKey = "R1"; - entity.Properties["Name"] = "Azure"; - entity.Properties["Product"] = "Tables"; + entity.SetPartitionKey("P1"); + entity.SetRowKey("R1"); + entity.Properties["Name"] = TableEntityProperty("Azure"); + entity.Properties["Product"] = TableEntityProperty("Tables"); auto createResponse = m_tableServiceClient->CreateTable(m_tableName); - auto response = m_tableClient->CreateEntity(entity); + auto response = m_tableClient->AddEntity(entity); EXPECT_EQ(response.RawResponse->GetStatusCode(), Azure::Core::Http::HttpStatusCode::NoContent); EXPECT_FALSE(response.Value.ETag.empty()); - entity.Properties["Product2"] = "Tables2"; + entity.Properties["Product2"] = TableEntityProperty("Tables2"); auto updateResponse = m_tableClient->MergeEntity(entity); EXPECT_EQ( updateResponse.RawResponse->GetStatusCode(), Azure::Core::Http::HttpStatusCode::NoContent); EXPECT_FALSE(updateResponse.Value.ETag.empty()); - entity.Properties["Product3"] = "Tables3"; - entity.ETag = updateResponse.Value.ETag; + entity.Properties["Product3"] = TableEntityProperty("Tables3"); + entity.SetETag(updateResponse.Value.ETag); auto updateResponse2 = m_tableClient->MergeEntity(entity); EXPECT_EQ( @@ -353,34 +366,33 @@ namespace Azure { namespace Data { namespace Test { TEST_P(TablesClientTest, EntityDelete) { - if (GetParam() == AuthType::Key - && Azure::Core::_internal::StringExtensions::ToLower(GetEnv("AZURE_TEST_MODE")) == "live") + if (GetParam() == AuthType::Key) { EXPECT_TRUE(true); return; } Azure::Data::Tables::Models::TableEntity entity; - entity.PartitionKey = "P1"; - entity.RowKey = "R1"; - entity.Properties["Name"] = "Azure"; - entity.Properties["Product"] = "Tables"; + entity.SetPartitionKey("P1"); + entity.SetRowKey("R1"); + entity.Properties["Name"] = TableEntityProperty("Azure"); + entity.Properties["Product"] = TableEntityProperty("Tables"); auto createResponse = m_tableServiceClient->CreateTable(m_tableName); - auto response = m_tableClient->CreateEntity(entity); + auto response = m_tableClient->AddEntity(entity); EXPECT_EQ(response.RawResponse->GetStatusCode(), Azure::Core::Http::HttpStatusCode::NoContent); EXPECT_FALSE(response.Value.ETag.empty()); - entity.Properties["Product2"] = "Tables2"; + entity.Properties["Product2"] = TableEntityProperty("Tables2"); auto updateResponse = m_tableClient->DeleteEntity(entity); EXPECT_EQ( updateResponse.RawResponse->GetStatusCode(), Azure::Core::Http::HttpStatusCode::NoContent); - response = m_tableClient->CreateEntity(entity); + response = m_tableClient->AddEntity(entity); EXPECT_EQ(response.RawResponse->GetStatusCode(), Azure::Core::Http::HttpStatusCode::NoContent); EXPECT_FALSE(response.Value.ETag.empty()); - entity.Properties["Product3"] = "Tables3"; - entity.ETag = response.Value.ETag; + entity.Properties["Product3"] = TableEntityProperty("Tables3"); + entity.SetETag(response.Value.ETag); auto updateResponse2 = m_tableClient->DeleteEntity(entity); EXPECT_EQ( @@ -389,18 +401,17 @@ namespace Azure { namespace Data { namespace Test { TEST_P(TablesClientTest, EntityUpsert) { - if (GetParam() == AuthType::Key - && Azure::Core::_internal::StringExtensions::ToLower(GetEnv("AZURE_TEST_MODE")) == "live") + if (GetParam() == AuthType::Key) { EXPECT_TRUE(true); return; } Azure::Data::Tables::Models::TableEntity entity; - entity.PartitionKey = "P1"; - entity.RowKey = "R1"; - entity.Properties["Name"] = "Azure"; - entity.Properties["Product"] = "Tables"; + entity.SetPartitionKey("P1"); + entity.SetRowKey("R1"); + entity.Properties["Name"] = TableEntityProperty("Azure"); + entity.Properties["Product"] = TableEntityProperty("Tables"); auto createResponse = m_tableServiceClient->CreateTable(m_tableName); auto response = m_tableClient->UpsertEntity(entity); EXPECT_EQ(response.RawResponse->GetStatusCode(), Azure::Core::Http::HttpStatusCode::NoContent); @@ -409,7 +420,7 @@ namespace Azure { namespace Data { namespace Test { Azure::Data::Tables::Models::UpsertEntityOptions options; options.UpsertType = Azure::Data::Tables::Models::UpsertKind::Update; - entity.Properties["Product"] = "Tables2"; + entity.Properties["Product"] = TableEntityProperty("Tables2"); auto updateResponse = m_tableClient->MergeEntity(entity, options); EXPECT_EQ( @@ -418,8 +429,8 @@ namespace Azure { namespace Data { namespace Test { Azure::Data::Tables::Models::UpsertEntityOptions options2; options2.UpsertType = Azure::Data::Tables::Models::UpsertKind::Merge; - entity.Properties["Product3"] = "Tables3"; - entity.ETag = updateResponse.Value.ETag; + entity.Properties["Product3"] = TableEntityProperty("Tables3"); + entity.SetETag(updateResponse.Value.ETag); auto updateResponse2 = m_tableClient->MergeEntity(entity, options2); EXPECT_EQ( @@ -429,30 +440,29 @@ namespace Azure { namespace Data { namespace Test { TEST_P(TablesClientTest, EntityQuery) { - if (GetParam() == AuthType::Key - && Azure::Core::_internal::StringExtensions::ToLower(GetEnv("AZURE_TEST_MODE")) == "live") + if (GetParam() == AuthType::Key) { EXPECT_TRUE(true); return; } Azure::Data::Tables::Models::TableEntity entity; - entity.PartitionKey = "P1"; - entity.RowKey = "R1"; - entity.Properties["Name"] = "Azure"; - entity.Properties["Product"] = "Tables"; + entity.SetPartitionKey("P1"); + entity.SetRowKey("R1"); + entity.Properties["Name"] = TableEntityProperty("Azure"); + entity.Properties["Product"] = TableEntityProperty("Tables"); auto createResponse = m_tableServiceClient->CreateTable(m_tableName); - auto response = m_tableClient->CreateEntity(entity); + auto response = m_tableClient->AddEntity(entity); EXPECT_EQ(response.RawResponse->GetStatusCode(), Azure::Core::Http::HttpStatusCode::NoContent); EXPECT_FALSE(response.Value.ETag.empty()); - entity.Properties["Product"] = "Tables2"; - entity.RowKey = "R2"; - m_tableClient->CreateEntity(entity); + entity.Properties["Product"] = TableEntityProperty("Tables2"); + entity.SetRowKey("R2"); + m_tableClient->AddEntity(entity); - entity.Properties["Product"] = "Tables3"; - entity.RowKey = "R3"; - m_tableClient->CreateEntity(entity); + entity.Properties["Product"] = TableEntityProperty("Tables3"); + entity.SetRowKey("R3"); + m_tableClient->AddEntity(entity); Azure::Data::Tables::Models::QueryEntitiesOptions options; @@ -478,179 +488,196 @@ namespace Azure { namespace Data { namespace Test { } Azure::Data::Tables::Models::TableEntity entity; - entity.PartitionKey = "P1"; - entity.RowKey = "R1"; - entity.Properties["Name"] = "Azure"; - entity.Properties["Product"] = "Tables"; + entity.SetPartitionKey("P1"); + entity.SetRowKey("R1"); + entity.Properties["Name"] = TableEntityProperty("Azure"); + entity.Properties["Product"] = TableEntityProperty("Tables"); auto createResponse = m_tableServiceClient->CreateTable(m_tableName); - auto response = m_tableClient->CreateEntity(entity); + auto response = m_tableClient->AddEntity(entity); EXPECT_EQ(response.RawResponse->GetStatusCode(), Azure::Core::Http::HttpStatusCode::NoContent); EXPECT_FALSE(response.Value.ETag.empty()); - entity.Properties["Product"] = "Tables2"; - entity.RowKey = "R2"; - m_tableClient->CreateEntity(entity); + entity.Properties["Product"] = TableEntityProperty("Tables2"); + entity.SetRowKey("R2"); + m_tableClient->AddEntity(entity); - entity.Properties["Product"] = "Tables3"; - entity.RowKey = "R3"; - m_tableClient->CreateEntity(entity); + entity.Properties["Product"] = TableEntityProperty("Tables3"); + entity.SetRowKey("R3"); + m_tableClient->AddEntity(entity); std::string partitionKey = "P1"; std::string rowKey = "R1"; auto responseQuery = m_tableClient->GetEntity(partitionKey, rowKey); - EXPECT_EQ(responseQuery.Value.PartitionKey, "P1"); - EXPECT_EQ(responseQuery.Value.RowKey, "R1"); - EXPECT_EQ(responseQuery.Value.Properties["Name"], "Azure"); - EXPECT_EQ(responseQuery.Value.Properties["Product"], "Tables"); + EXPECT_EQ(responseQuery.Value.GetPartitionKey().Value, "P1"); + EXPECT_EQ(responseQuery.Value.GetRowKey().Value, "R1"); + EXPECT_EQ(responseQuery.Value.Properties["Name"].Value, "Azure"); + EXPECT_EQ(responseQuery.Value.Properties["Product"].Value, "Tables"); + EXPECT_EQ( + responseQuery.Value.Properties["Timestamp"].Type.Value(), TableEntityDataType::EdmDateTime); } TEST_P(TablesClientTest, TransactionCreateFail_LIVEONLY_) { + if (GetParam() == AuthType::SAS) + { + SkipTest(); + return; + } Azure::Data::Tables::Models::TableEntity entity; Azure::Data::Tables::Models::TableEntity entity2; - entity.PartitionKey = "P1"; - entity.RowKey = "R1"; - entity.Properties["Name"] = "Azure"; - entity.Properties["Product"] = "Tables"; - entity2.PartitionKey = "P1"; - entity2.RowKey = "R1"; - entity2.Properties["Name"] = "Azure"; - entity2.Properties["Product"] = "Tables"; + entity.SetPartitionKey("P1"); + entity.SetRowKey("R1"); + entity.Properties["Name"] = TableEntityProperty("Azure"); + entity.Properties["Product"] = TableEntityProperty("Tables"); + entity2.SetPartitionKey("P1"); + entity2.SetRowKey("R1"); + entity2.Properties["Name"] = TableEntityProperty("Azure"); + entity2.Properties["Product"] = TableEntityProperty("Tables"); auto createResponse = m_tableServiceClient->CreateTable(m_tableName); - auto transaction = m_tableClient->CreateTransaction("P1"); - transaction.CreateEntity(entity); - transaction.CreateEntity(entity2); + std::vector steps; + // conflicting entities in the same transaction + steps.emplace_back(Azure::Data::Tables::Models::TransactionStep{ + Azure::Data::Tables::Models::TransactionActionType::Add, entity}); + steps.emplace_back(Azure::Data::Tables::Models::TransactionStep{ + Azure::Data::Tables::Models::TransactionActionType::Add, entity2}); - auto response = m_tableClient->SubmitTransaction(transaction); + auto response = m_tableClient->SubmitTransaction(steps); EXPECT_TRUE(response.Value.Error.HasValue()); } TEST_P(TablesClientTest, TransactionCreateOK_LIVEONLY_) { - if (GetParam() == AuthType::Key - && Azure::Core::_internal::StringExtensions::ToLower(GetEnv("AZURE_TEST_MODE")) == "live") + if (GetParam() != AuthType::ConnectionString) { - EXPECT_TRUE(true); + SkipTest(); return; } Azure::Data::Tables::Models::TableEntity entity; Azure::Data::Tables::Models::TableEntity entity2; - entity.PartitionKey = "P1"; - entity.RowKey = "R1"; - entity.Properties["Name"] = "Azure"; - entity.Properties["Product"] = "Tables"; - entity2.PartitionKey = "P1"; - entity2.RowKey = "R2"; - entity2.Properties["Name"] = "Azure"; - entity2.Properties["Product"] = "Tables"; + entity.SetPartitionKey("P1"); + entity.SetRowKey("R1"); + entity.Properties["Name"] = TableEntityProperty("Azure"); + entity.Properties["Product"] = TableEntityProperty("Tables"); + entity2.SetPartitionKey("P1"); + entity2.SetRowKey("R2"); + entity2.Properties["Name"] = TableEntityProperty("Azure"); + entity2.Properties["Product"] = TableEntityProperty("Tables"); auto createResponse = m_tableServiceClient->CreateTable(m_tableName); - auto transaction = m_tableClient->CreateTransaction("P1"); - transaction.CreateEntity(entity); - transaction.CreateEntity(entity2); + std::vector steps; + // create two entities in the same transaction + steps.emplace_back(Azure::Data::Tables::Models::TransactionStep{ + Azure::Data::Tables::Models::TransactionActionType::Add, entity}); + steps.emplace_back(Azure::Data::Tables::Models::TransactionStep{ + Azure::Data::Tables::Models::TransactionActionType::Add, entity2}); - auto response = m_tableClient->SubmitTransaction(transaction); + auto response = m_tableClient->SubmitTransaction(steps); EXPECT_FALSE(response.Value.Error.HasValue()); } TEST_P(TablesClientTest, TransactionDelete_LIVEONLY_) { - if (GetParam() == AuthType::Key - && Azure::Core::_internal::StringExtensions::ToLower(GetEnv("AZURE_TEST_MODE")) == "live") + if (GetParam() != AuthType::ConnectionString) { - EXPECT_TRUE(true); + SkipTest(); return; } Azure::Data::Tables::Models::TableEntity entity; Azure::Data::Tables::Models::TableEntity entity2; - entity.PartitionKey = "P1"; - entity.RowKey = "R1"; - entity.Properties["Name"] = "Azure"; - entity.Properties["Product"] = "Tables"; - entity2.PartitionKey = "P1"; - entity2.RowKey = "R2"; - entity2.Properties["Name"] = "Azure"; - entity2.Properties["Product"] = "Tables"; + entity.SetPartitionKey("P1"); + entity.SetRowKey("R1"); + entity.Properties["Name"] = TableEntityProperty("Azure"); + entity.Properties["Product"] = TableEntityProperty("Tables"); + entity2.SetPartitionKey("P1"); + entity2.SetRowKey("R2"); + entity2.Properties["Name"] = TableEntityProperty("Azure"); + entity2.Properties["Product"] = TableEntityProperty("Tables"); auto createResponse = m_tableServiceClient->CreateTable(m_tableName); - auto transaction = m_tableClient->CreateTransaction("P1"); - transaction.CreateEntity(entity); - transaction.CreateEntity(entity2); + std::vector steps; - auto response = m_tableClient->SubmitTransaction(transaction); + steps.emplace_back(Azure::Data::Tables::Models::TransactionStep{ + Azure::Data::Tables::Models::TransactionActionType::Add, entity}); + steps.emplace_back(Azure::Data::Tables::Models::TransactionStep{ + Azure::Data::Tables::Models::TransactionActionType::Add, entity2}); - auto transaction2 = m_tableClient->CreateTransaction("P1"); + auto response = m_tableClient->SubmitTransaction(steps); - transaction2.DeleteEntity(entity); + steps.clear(); + // delete entity + steps.emplace_back(Azure::Data::Tables::Models::TransactionStep{ + Azure::Data::Tables::Models::TransactionActionType::Delete, entity2}); - response = m_tableClient->SubmitTransaction(transaction2); + response = m_tableClient->SubmitTransaction(steps); EXPECT_FALSE(response.Value.Error.HasValue()); } TEST_P(TablesClientTest, TransactionMerge_LIVEONLY_) { - if (GetParam() == AuthType::Key - && Azure::Core::_internal::StringExtensions::ToLower(GetEnv("AZURE_TEST_MODE")) == "live") + if (GetParam() != AuthType::ConnectionString) { - EXPECT_TRUE(true); + SkipTest(); return; } Azure::Data::Tables::Models::TableEntity entity; Azure::Data::Tables::Models::TableEntity entity2; - entity.PartitionKey = "P1"; - entity.RowKey = "R1"; - entity.Properties["Name"] = "Azure"; - entity.Properties["Product"] = "Tables"; - entity2.PartitionKey = "P1"; - entity2.RowKey = "R1"; - entity2.Properties["Name"] = "Azure2"; - entity2.Properties["Product"] = "Tables3"; + entity.SetPartitionKey("P1"); + entity.SetRowKey("R1"); + entity.Properties["Name"] = TableEntityProperty("Azure"); + entity.Properties["Product"] = TableEntityProperty("Tables"); + entity2.SetPartitionKey("P1"); + entity2.SetRowKey("R1"); + entity2.Properties["Name"] = TableEntityProperty("Azure2"); + entity2.Properties["Product"] = TableEntityProperty("Tables3"); auto createResponse = m_tableServiceClient->CreateTable(m_tableName); - auto transaction = m_tableClient->CreateTransaction("P1"); - transaction.CreateEntity(entity); + std::vector steps; - auto response = m_tableClient->SubmitTransaction(transaction); + steps.emplace_back(Azure::Data::Tables::Models::TransactionStep{ + Azure::Data::Tables::Models::TransactionActionType::Add, entity}); + auto response = m_tableClient->SubmitTransaction(steps); - auto transaction2 = m_tableClient->CreateTransaction("P1"); + steps.clear(); + // merge entity + steps.emplace_back(Azure::Data::Tables::Models::TransactionStep{ + Azure::Data::Tables::Models::TransactionActionType::UpdateMerge, entity2}); - transaction2.MergeEntity(entity2); + response = m_tableClient->SubmitTransaction(steps); - response = m_tableClient->SubmitTransaction(transaction2); EXPECT_FALSE(response.Value.Error.HasValue()); } TEST_P(TablesClientTest, TransactionUpdate_LIVEONLY_) { - if (GetParam() == AuthType::Key - && Azure::Core::_internal::StringExtensions::ToLower(GetEnv("AZURE_TEST_MODE")) == "live") + if (GetParam() != AuthType::ConnectionString) { - EXPECT_TRUE(true); + SkipTest(); return; } Azure::Data::Tables::Models::TableEntity entity; Azure::Data::Tables::Models::TableEntity entity2; - entity.PartitionKey = "P1"; - entity.RowKey = "R1"; - entity.Properties["Name"] = "Azure"; - entity.Properties["Product"] = "Tables"; - entity2.PartitionKey = "P1"; - entity2.RowKey = "R1"; - entity2.Properties["Name"] = "Azure2"; - entity2.Properties["Product"] = "Tables3"; + entity.SetPartitionKey("P1"); + entity.SetRowKey("R1"); + entity.Properties["Name"] = TableEntityProperty("Azure"); + entity.Properties["Product"] = TableEntityProperty("Tables"); + entity2.SetPartitionKey("P1"); + entity2.SetRowKey("R1"); + entity2.Properties["Name"] = TableEntityProperty("Azure2"); + entity2.Properties["Product"] = TableEntityProperty("Tables3"); auto createResponse = m_tableServiceClient->CreateTable(m_tableName); - auto transaction = m_tableClient->CreateTransaction("P1"); + std::vector steps; - transaction.CreateEntity(entity); + steps.emplace_back(Azure::Data::Tables::Models::TransactionStep{ + Azure::Data::Tables::Models::TransactionActionType::Add, entity}); + auto response = m_tableClient->SubmitTransaction(steps); - auto response = m_tableClient->SubmitTransaction(transaction); + steps.clear(); + // replace entity + steps.emplace_back(Azure::Data::Tables::Models::TransactionStep{ + Azure::Data::Tables::Models::TransactionActionType::UpdateReplace, entity2}); + response = m_tableClient->SubmitTransaction(steps); - auto transaction2 = m_tableClient->CreateTransaction("P1"); - - transaction2.UpdateEntity(entity2); - - response = m_tableClient->SubmitTransaction(transaction2); EXPECT_FALSE(response.Value.Error.HasValue()); } 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 0714a48da..e41e1ea01 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 @@ -4,7 +4,6 @@ #include "azure/data/tables/models.hpp" #include "azure/data/tables/tables_clients.hpp" -#include "azure/data/tables/transactions.hpp" #include "test/ut/test_base.hpp" namespace Azure { namespace Data { namespace Test { diff --git a/sdk/tables/azure-data-tables/test/ut/transactions_test.cpp b/sdk/tables/azure-data-tables/test/ut/transactions_test.cpp index bb1e9671d..67ec368c7 100644 --- a/sdk/tables/azure-data-tables/test/ut/transactions_test.cpp +++ b/sdk/tables/azure-data-tables/test/ut/transactions_test.cpp @@ -16,135 +16,137 @@ namespace Azure { namespace Data { namespace Test { TEST_F(TransactionsBodyTest, TransactionCreate) { - Tables::Transaction transaction(url, tableName, partitionKey); - EXPECT_EQ(transaction.GetPartitionKey(), partitionKey); - EXPECT_EQ(transaction.GetBatchId().substr(0, 6), "batch_"); - EXPECT_EQ(transaction.GetChangesetId().substr(0, 9), "changeset"); + std::vector steps; + TableClient client("http://localhost:7777", "table"); + + auto ser = client.PreparePayload("batch", "changeset", steps); + + EXPECT_EQ( + ser, + "--batch\nContent-Type: multipart/mixed; " + "boundary=changeset\n\n\n\n--changeset--\n--batch\n"); } - TEST_F(TransactionsBodyTest, TransactionBodyInsertOp) + TEST_F(TransactionsBodyTest, TransactionBodyAddOp) { - Tables::Transaction transaction(url, tableName, partitionKey); - Azure::Data::Tables::Models::TableEntity entity; - entity.RowKey = rowKey; - transaction.CreateEntity(entity); - EXPECT_EQ(transaction.GetSteps().size(), 1); - EXPECT_EQ(transaction.GetSteps()[0].Action, Models::TransactionAction::InsertEntity); - EXPECT_EQ(transaction.GetSteps()[0].Entity.RowKey, rowKey); - EXPECT_EQ(transaction.GetSteps()[0].Entity.PartitionKey, partitionKey); - auto serialized = transaction.PreparePayload(); - CheckTransactionBody(serialized, Models::TransactionAction::InsertEntity); - } - - TEST_F(TransactionsBodyTest, TransactionBodyDeleteOp) - { - Tables::Transaction transaction(url, tableName, partitionKey); + std::vector steps; + TableClient client(url, tableName); Azure::Data::Tables::Models::TableEntity entity; - entity.RowKey = rowKey; - transaction.DeleteEntity(entity); - EXPECT_EQ(transaction.GetSteps().size(), 1); - EXPECT_EQ(transaction.GetSteps()[0].Action, Models::TransactionAction::DeleteEntity); - EXPECT_EQ(transaction.GetSteps()[0].Entity.RowKey, rowKey); - EXPECT_EQ(transaction.GetSteps()[0].Entity.PartitionKey, partitionKey); - auto serialized = transaction.PreparePayload(); - CheckTransactionBody(serialized, Models::TransactionAction::DeleteEntity); - } + entity.SetRowKey(rowKey); + entity.SetPartitionKey(partitionKey); + steps.emplace_back(Models::TransactionStep{Models::TransactionActionType::Add, entity}); + auto serialized = client.PreparePayload("batch_", "changeset_1", steps); - TEST_F(TransactionsBodyTest, TransactionBodyMergeOp) - { - Tables::Transaction transaction(url, tableName, partitionKey); - - Azure::Data::Tables::Models::TableEntity entity; - entity.RowKey = rowKey; - transaction.MergeEntity(entity); - EXPECT_EQ(transaction.GetSteps().size(), 1); - EXPECT_EQ(transaction.GetSteps()[0].Action, Models::TransactionAction::MergeEntity); - EXPECT_EQ(transaction.GetSteps()[0].Entity.RowKey, rowKey); - EXPECT_EQ(transaction.GetSteps()[0].Entity.PartitionKey, partitionKey); - auto serialized = transaction.PreparePayload(); - CheckTransactionBody(serialized, Models::TransactionAction::MergeEntity); - } - - TEST_F(TransactionsBodyTest, TransactionBodyUpdateOp) - { - Tables::Transaction transaction(url, tableName, partitionKey); - Azure::Data::Tables::Models::TableEntity entity; - entity.RowKey = rowKey; - transaction.UpdateEntity(entity); - EXPECT_EQ(transaction.GetSteps().size(), 1); - EXPECT_EQ(transaction.GetSteps()[0].Action, Models::TransactionAction::UpdateEntity); - EXPECT_EQ(transaction.GetSteps()[0].Entity.RowKey, rowKey); - EXPECT_EQ(transaction.GetSteps()[0].Entity.PartitionKey, partitionKey); - auto serialized = transaction.PreparePayload(); - CheckTransactionBody(serialized, Models::TransactionAction::UpdateEntity); + CheckTransactionBody(serialized, Models::TransactionActionType::Add); } TEST_F(TransactionsBodyTest, TransactionBodyInsertMergeOp) { - Tables::Transaction transaction(url, tableName, partitionKey); + std::vector steps; + TableClient client(url, tableName); + Azure::Data::Tables::Models::TableEntity entity; - entity.RowKey = rowKey; - transaction.InsertMergeEntity(entity); - EXPECT_EQ(transaction.GetSteps().size(), 1); - EXPECT_EQ(transaction.GetSteps()[0].Action, Models::TransactionAction::MergeEntity); - EXPECT_EQ(transaction.GetSteps()[0].Entity.RowKey, rowKey); - EXPECT_EQ(transaction.GetSteps()[0].Entity.PartitionKey, partitionKey); - auto serialized = transaction.PreparePayload(); - CheckTransactionBody(serialized, Models::TransactionAction::InsertMergeEntity); + entity.SetRowKey(rowKey); + entity.SetPartitionKey(partitionKey); + steps.emplace_back(Models::TransactionStep{Models::TransactionActionType::InsertMerge, entity}); + auto serialized = client.PreparePayload("batch_", "changeset_1", steps); + + CheckTransactionBody(serialized, Models::TransactionActionType::InsertMerge); } TEST_F(TransactionsBodyTest, TransactionBodyInsertReplaceOp) { - Tables::Transaction transaction(url, tableName, partitionKey); + std::vector steps; + TableClient client(url, tableName); + Azure::Data::Tables::Models::TableEntity entity; - entity.RowKey = rowKey; - transaction.InsertReplaceEntity(entity); - EXPECT_EQ(transaction.GetSteps().size(), 1); - EXPECT_EQ(transaction.GetSteps()[0].Action, Models::TransactionAction::UpdateEntity); - EXPECT_EQ(transaction.GetSteps()[0].Entity.RowKey, rowKey); - EXPECT_EQ(transaction.GetSteps()[0].Entity.PartitionKey, partitionKey); - auto serialized = transaction.PreparePayload(); - CheckTransactionBody(serialized, Models::TransactionAction::InsertReplaceEntity); + entity.SetRowKey(rowKey); + entity.SetPartitionKey(partitionKey); + steps.emplace_back( + Models::TransactionStep{Models::TransactionActionType::InsertReplace, entity}); + auto serialized = client.PreparePayload("batch_", "changeset_1", steps); + + CheckTransactionBody(serialized, Models::TransactionActionType::InsertReplace); + } + + TEST_F(TransactionsBodyTest, TransactionBodyDeleteOp) + { + std::vector steps; + TableClient client(url, tableName); + + Azure::Data::Tables::Models::TableEntity entity; + entity.SetRowKey(rowKey); + entity.SetPartitionKey(partitionKey); + steps.emplace_back(Models::TransactionStep{Models::TransactionActionType::Delete, entity}); + auto serialized = client.PreparePayload("batch_", "changeset_1", steps); + CheckTransactionBody(serialized, Models::TransactionActionType::Delete); + } + + TEST_F(TransactionsBodyTest, TransactionBodyUpdateMergeOp) + { + std::vector steps; + TableClient client(url, tableName); + + Azure::Data::Tables::Models::TableEntity entity; + entity.SetRowKey(rowKey); + entity.SetPartitionKey(partitionKey); + steps.emplace_back(Models::TransactionStep{Models::TransactionActionType::UpdateMerge, entity}); + auto serialized = client.PreparePayload("batch_", "changeset_1", steps); + CheckTransactionBody(serialized, Models::TransactionActionType::UpdateMerge); + } + + TEST_F(TransactionsBodyTest, TransactionBodyUpdateReplaceOp) + { + std::vector steps; + TableClient client(url, tableName); + + Azure::Data::Tables::Models::TableEntity entity; + entity.SetRowKey(rowKey); + entity.SetPartitionKey(partitionKey); + steps.emplace_back( + Models::TransactionStep{Models::TransactionActionType::UpdateReplace, entity}); + auto serialized = client.PreparePayload("batch_", "changeset_1", steps); + CheckTransactionBody(serialized, Models::TransactionActionType::UpdateReplace); } void TransactionsBodyTest::CheckContentLines( std::vector const& lines, - Models::TransactionAction action) + Models::TransactionActionType action) { EXPECT_EQ(lines[0], "--" + changeset); EXPECT_EQ(lines[1], "Content-Type: application/http"); EXPECT_EQ(lines[2], "Content-Transfer-Encoding: binary"); switch (action) { - case Models::TransactionAction::InsertEntity: + case Models::TransactionActionType::Add: EXPECT_EQ(lines[4], "POST " + url + "/" + tableName + " HTTP/1.1"); break; - case Models::TransactionAction::DeleteEntity: + case Models::TransactionActionType::Delete: EXPECT_EQ( lines[4], "DELETE " + url + "/" + tableName + "(PartitionKey='" + partitionKey + "',RowKey='" + rowKey + "') HTTP/1.1"); break; - case Models::TransactionAction::MergeEntity: + case Models::TransactionActionType::UpdateMerge: EXPECT_EQ( lines[4], "MERGE " + url + "/" + tableName + "(PartitionKey='" + partitionKey + "',RowKey='" + rowKey + "') HTTP/1.1"); break; - case Models::TransactionAction::UpdateEntity: + case Models::TransactionActionType::UpdateReplace: EXPECT_EQ( lines[4], "PUT " + url + "/" + tableName + "(PartitionKey='" + partitionKey + "',RowKey='" + rowKey + "') HTTP/1.1"); break; - case Models::TransactionAction::InsertMergeEntity: + case Models::TransactionActionType::InsertMerge: EXPECT_EQ( lines[4], "MERGE " + url + "/" + tableName + "(PartitionKey='" + partitionKey + "',RowKey='" + rowKey + "') HTTP/1.1"); break; - case Models::TransactionAction::InsertReplaceEntity: + case Models::TransactionActionType::InsertReplace: EXPECT_EQ( lines[4], "PUT " + url + "/" + tableName + "(PartitionKey='" + partitionKey + "',RowKey='" @@ -155,7 +157,7 @@ namespace Azure { namespace Data { namespace Test { } void TransactionsBodyTest::CheckTransactionBody( std::string const& body, - Models::TransactionAction action) + Models::TransactionActionType action) { (void)action; std::stringstream ss(body); @@ -164,12 +166,11 @@ namespace Azure { namespace Data { namespace Test { // line1 EXPECT_EQ(line.substr(0, 8), "--batch_"); - EXPECT_EQ(line.size(), 44); + EXPECT_EQ(line.size(), 8); batch = line.substr(2, line.length() - 1); // line2 std::getline(ss, line, '\n'); - EXPECT_EQ(line.substr(0, 50), "Content-Type: multipart/mixed; boundary=changeset_"); - EXPECT_EQ(line.size(), 86); + EXPECT_EQ(line, "Content-Type: multipart/mixed; boundary=changeset_1"); changeset = line.substr(40, line.length() - 1); // line3 diff --git a/sdk/tables/azure-data-tables/test/ut/transactions_test.hpp b/sdk/tables/azure-data-tables/test/ut/transactions_test.hpp index 8a99fa7fc..be9700782 100644 --- a/sdk/tables/azure-data-tables/test/ut/transactions_test.hpp +++ b/sdk/tables/azure-data-tables/test/ut/transactions_test.hpp @@ -3,7 +3,6 @@ // Licensed under the MIT License. #include "azure/data/tables/tables_clients.hpp" -#include "azure/data/tables/transactions.hpp" #include "test/ut/test_base.hpp" namespace Azure { namespace Data { namespace Test { @@ -19,9 +18,9 @@ namespace Azure { namespace Data { namespace Test { std::string changeset; void CheckContentLines( std::vector const& lines, - Azure::Data::Tables::Models::TransactionAction action); + Azure::Data::Tables::Models::TransactionActionType action); void CheckTransactionBody( std::string const& body, - Azure::Data::Tables::Models::TransactionAction action); + Azure::Data::Tables::Models::TransactionActionType action); }; }}} // namespace Azure::Data::Test