Api comments (#5481)

* transactions and some other comments

* accouunt sas resource type

* ghf

* Get/set part/row key

* accessors

* jyugdg

* done, need add samples

* typo

* clangs, typo

* fsd

* assets

* no keys, need investigate test recordings

* doc fix

* data types reflected in properties

* dswa

* wqeq

* remove shared key policy and switch to secondary

* dsdsa

* PR comments

* das

* clang
This commit is contained in:
George Arama 2024-04-05 09:48:59 -07:00 committed by GitHub
parent acb6d97f7c
commit 8c34ec83ab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 885 additions and 1216 deletions

View File

@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "cpp",
"TagPrefix": "cpp/tables",
"Tag": "cpp/tables_69657814a3"
"Tag": "cpp/tables_55f4cc2c1e"
}

View File

@ -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()

View File

@ -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"

View File

@ -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 <azure/core/datetime.hpp>
@ -12,7 +12,8 @@
#include <string>
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;

View File

@ -9,7 +9,6 @@
#include <mutex>
#include <string>
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<SharedKeyCredential> KeyCredential;
std::shared_ptr<NamedKeyCredential> KeyCredential;
};
ConnectionStringParts ParseConnectionString(const std::string& connectionString);

View File

@ -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 <type_traits>

View File

@ -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/core/http/policies/policy.hpp>
@ -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<Credentials::SharedKeyCredential> credential)
explicit SharedKeyLitePolicy(std::shared_ptr<Credentials::NamedKeyCredential> 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<Credentials::SharedKeyCredential> m_credential;
std::shared_ptr<Credentials::NamedKeyCredential> m_credential;
};
}}}}} // namespace Azure::Data::Tables::_detail::Policies

View File

@ -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 <azure/core/http/policies/policy.hpp>
#include <memory>
#include <string>
namespace Azure { namespace Data { namespace Tables { namespace _detail { namespace Policies {
class SharedKeyPolicy final : public Core::Http::Policies::HttpPolicy {
public:
explicit SharedKeyPolicy(std::shared_ptr<Credentials::SharedKeyCredential> credential)
: m_credential{std::move(credential)}
{
}
~SharedKeyPolicy() override {}
std::unique_ptr<HttpPolicy> Clone() const override
{
return std::make_unique<SharedKeyPolicy>(m_credential);
}
std::unique_ptr<Core::Http::RawResponse> 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<Credentials::SharedKeyCredential> m_credential;
};
}}}}} // namespace Azure::Data::Tables::_detail::Policies

View File

@ -1,44 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#pragma once
#include "azure/data/tables/dll_import_export.hpp"
#include <azure/core/http/policies/policy.hpp>
#include <memory>
#include <string>
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<bool>(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<Azure::Core::Http::Policies::HttpPolicy> Clone() const override
{
return std::make_unique<SwitchToSecondaryPolicy>(*this);
}
std::unique_ptr<Azure::Core::Http::RawResponse> 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

View File

@ -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<std::int32_t> Days;
Nullable<std::int32_t> 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<TableEntityDataType> 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<std::string, std::string> Properties;
/**
* ETag
*/
Azure::Nullable<std::string> ETag;
std::map<std::string, TableEntityProperty> 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.
*/

View File

@ -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 <azure/core/credentials/credentials.hpp>
#include <azure/core/http/http.hpp>
@ -21,6 +20,21 @@
#include <utility>
#include <vector>
#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<Azure::Data::Tables::Credentials::SharedKeyCredential> credential,
std::shared_ptr<Azure::Data::Tables::Credentials::NamedKeyCredential> 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<Models::CreateEntityResult> CreateEntity(
Response<Models::AddEntityResult> 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<Models::SubmitTransactionResult> SubmitTransaction(
Transaction& transaction,
std::vector<Models::TransactionStep> 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<Models::TransactionStep> 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<Core::Http::_internal::HttpPipeline> 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<Azure::Data::Tables::Credentials::SharedKeyCredential> credential,
std::shared_ptr<Azure::Data::Tables::Credentials::NamedKeyCredential> credential,
const TableClientOptions& options = {});
/**
@ -488,9 +530,23 @@ namespace Azure { namespace Data { namespace Tables {
Response<Models::PreflightCheckResult> 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<Core::Http::_internal::HttpPipeline> m_pipeline;
std::shared_ptr<Core::Credentials::TokenCredential> m_tokenCredential;
std::shared_ptr<Azure::Data::Tables::Credentials::NamedKeyCredential> m_namedKeyCredential;
Core::Url m_url;
};
}}} // namespace Azure::Data::Tables

View File

@ -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 <azure/core/datetime.hpp>
@ -14,28 +14,6 @@
#include <type_traits>
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);

View File

@ -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 <azure/core/url.hpp>
#include <azure/core/uuid.hpp>
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<Models::TransactionStep> 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<Models::TransactionStep> m_steps;
std::string m_batchId;
std::string m_changesetId;
};
}}} // namespace Azure::Data::Tables

View File

@ -8,6 +8,10 @@
#include <stdexcept>
#include <thread>
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

View File

@ -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";
}

View File

@ -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 <algorithm>
@ -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<SharedKeyCredential>(accountName, accountKey);
= std::make_shared<NamedKeyCredential>(accountName, accountKey);
}
std::string sas = getWithDefault(connectionStringMap, "SharedAccessSignature");

View File

@ -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 <azure/core/base64.hpp>
#include <azure/core/cryptography/hash.hpp>
#include <azure/core/http/http.hpp>
#include <azure/core/internal/strings.hpp>
#include <algorithm>
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<std::pair<std::string, std::string>> 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<uint8_t>(string_to_sign.begin(), string_to_sign.end()),
Azure::Core::Convert::Base64Decode(m_credential->GetAccountKey())));
}
}}}}} // namespace Azure::Data::Tables::_detail::Policies

View File

@ -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<Azure::Core::Http::RawResponse> SwitchToSecondaryPolicy::Send(
Azure::Core::Http::Request& request,
Azure::Core::Http::Policies::NextHttpPolicy nextPolicy,
const Azure::Core::Context& context) const
{
std::shared_ptr<bool> 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

View File

@ -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::map<std::string, std::string>>();
std::vector<std::string> erasable;
for (auto property : properties)
{
tableEntity.PartitionKey = json["PartitionKey"].get<std::string>();
}
if (json.contains("RowKey"))
{
tableEntity.RowKey = json["RowKey"].get<std::string>();
}
if (json.contains("odata.etag"))
{
tableEntity.ETag = json["odata.etag"].get<std::string>();
}
for (auto properties : json.get<std::map<std::string, std::string>>())
{
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<Azure::Data::Tables::Models::TableEntityDataType>(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

View File

@ -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 <sstream>
#include <string>
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<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> perRetryPolicies;
std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> perOperationPolicies;
perRetryPolicies.emplace_back(std::make_unique<SwitchToSecondaryPolicy>(
m_url.GetHost(), newOptions.SecondaryHostForRetryReads));
perRetryPolicies.emplace_back(std::make_unique<TimeoutPolicy>());
perOperationPolicies.emplace_back(
std::make_unique<ServiceVersionPolicy>(newOptions.ApiVersion.ToString()));
@ -44,8 +41,6 @@ TableServiceClient::TableServiceClient(
m_url = Azure::Core::Url(serviceUrl);
std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> perRetryPolicies;
std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> perOperationPolicies;
perRetryPolicies.emplace_back(std::make_unique<SwitchToSecondaryPolicy>(
m_url.GetHost(), options.SecondaryHostForRetryReads));
perRetryPolicies.emplace_back(std::make_unique<TimeoutPolicy>());
perOperationPolicies.emplace_back(
std::make_unique<ServiceVersionPolicy>(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<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> perRetryPolicies;
std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> perOperationPolicies;
perRetryPolicies.emplace_back(std::make_unique<SwitchToSecondaryPolicy>(
m_url.GetHost(), options.SecondaryHostForRetryReads));
perRetryPolicies.emplace_back(std::make_unique<TimeoutPolicy>());
perOperationPolicies.emplace_back(
std::make_unique<ServiceVersionPolicy>(options.ApiVersion.ToString()));
perRetryPolicies.emplace_back(std::make_unique<SwitchToSecondaryPolicy>(
m_url.GetHost(), newOptions.SecondaryHostForRetryReads));
perRetryPolicies.emplace_back(std::make_unique<TimeoutPolicy>());
{
Azure::Core::Credentials::TokenRequestContext tokenContext;
@ -98,14 +90,13 @@ TableServiceClient::TableServiceClient(
TableServiceClient::TableServiceClient(
const std::string& serviceUrl,
std::shared_ptr<Azure::Data::Tables::Credentials::SharedKeyCredential> credential,
std::shared_ptr<Azure::Data::Tables::Credentials::NamedKeyCredential> credential,
const TableClientOptions& options)
: m_url(Azure::Core::Url(serviceUrl))
{
m_namedKeyCredential = credential;
std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> perRetryPolicies;
std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> perOperationPolicies;
perRetryPolicies.emplace_back(std::make_unique<SwitchToSecondaryPolicy>(
m_url.GetHost(), options.SecondaryHostForRetryReads));
perRetryPolicies.emplace_back(std::make_unique<TimeoutPolicy>());
perOperationPolicies.emplace_back(
std::make_unique<ServiceVersionPolicy>(options.ApiVersion.ToString()));
@ -122,8 +113,6 @@ TableServiceClient::TableServiceClient(
std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> perRetryPolicies2;
std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> perOperationPolicies2;
perRetryPolicies2.emplace_back(std::make_unique<SwitchToSecondaryPolicy>(
m_url.GetHost(), newOptions.SecondaryHostForRetryReads));
perRetryPolicies2.emplace_back(std::make_unique<TimeoutPolicy>());
perOperationPolicies2.emplace_back(
std::make_unique<ServiceVersionPolicy>(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<Models::PreflightCheckResult> 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<Models::PreflightCheckResult> TableServiceClient::PreflightCheck
return Response<Models::PreflightCheckResult>(std::move(response), std::move(rawResponse));
}
Azure::Response<Models::SetServicePropertiesResult> TableServiceClient::SetServiceProperties(
Models::SetServicePropertiesOptions const& options,
Core::Context const& context)
@ -189,15 +198,15 @@ Azure::Response<Models::SetServicePropertiesResult> 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<std::uint8_t const*>(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<Models::TableServiceProperties> 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<Models::ServiceStatistics> 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<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> perRetryPolicies;
std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> perOperationPolicies;
perRetryPolicies.emplace_back(std::make_unique<SwitchToSecondaryPolicy>(
m_url.GetHost(), options.SecondaryHostForRetryReads));
perRetryPolicies.emplace_back(std::make_unique<TimeoutPolicy>());
perOperationPolicies.emplace_back(
std::make_unique<ServiceVersionPolicy>(options.ApiVersion.ToString()));
@ -346,14 +353,10 @@ TableClient::TableClient(
m_url = Azure::Core::Url(serviceUrl);
std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> perRetryPolicies;
std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> perOperationPolicies;
perRetryPolicies.emplace_back(std::make_unique<SwitchToSecondaryPolicy>(
m_url.GetHost(), options.SecondaryHostForRetryReads));
perRetryPolicies.emplace_back(std::make_unique<TimeoutPolicy>());
perOperationPolicies.emplace_back(
std::make_unique<ServiceVersionPolicy>(options.ApiVersion.ToString()));
perRetryPolicies.emplace_back(std::make_unique<SwitchToSecondaryPolicy>(
m_url.GetHost(), newOptions.SecondaryHostForRetryReads));
perRetryPolicies.emplace_back(std::make_unique<TimeoutPolicy>());
{
Azure::Core::Credentials::TokenRequestContext tokenContext;
@ -376,7 +379,7 @@ TableClient::TableClient(
TableClient::TableClient(
const std::string& tableName,
std::shared_ptr<Azure::Data::Tables::Credentials::SharedKeyCredential> credential,
std::shared_ptr<Azure::Data::Tables::Credentials::NamedKeyCredential> credential,
std::string url,
const TableClientOptions& options)
: m_url(std::move(url)), m_tableName(tableName)
@ -384,8 +387,6 @@ TableClient::TableClient(
{
std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> perRetryPolicies;
std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> perOperationPolicies;
perRetryPolicies.emplace_back(std::make_unique<SwitchToSecondaryPolicy>(
m_url.GetHost(), options.SecondaryHostForRetryReads));
perRetryPolicies.emplace_back(std::make_unique<TimeoutPolicy>());
perOperationPolicies.emplace_back(
std::make_unique<ServiceVersionPolicy>(options.ApiVersion.ToString()));
@ -402,8 +403,6 @@ TableClient::TableClient(
std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> perRetryPolicies2;
std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> perOperationPolicies2;
perRetryPolicies2.emplace_back(std::make_unique<SwitchToSecondaryPolicy>(
m_url.GetHost(), newOptions.SecondaryHostForRetryReads));
perRetryPolicies2.emplace_back(std::make_unique<TimeoutPolicy>());
perOperationPolicies2.emplace_back(
std::make_unique<ServiceVersionPolicy>(newOptions.ApiVersion.ToString()));
@ -464,9 +463,9 @@ Azure::Response<Models::Table> 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<Models::Table> TableServiceClient::CreateTable(
auto const jsonRoot
= Core::Json::_internal::json::parse(responseBody.begin(), responseBody.end());
response.TableName = jsonRoot["TableName"].get<std::string>();
response.EditLink = jsonRoot["odata.editLink"].get<std::string>();
response.Id = jsonRoot["odata.id"].get<std::string>();
response.Metadata = jsonRoot["odata.metadata"].get<std::string>();
response.Type = jsonRoot["odata.type"].get<std::string>();
response.TableName = jsonRoot[TableName].get<std::string>();
response.EditLink = jsonRoot[ODataEditLink].get<std::string>();
response.Id = jsonRoot[ODataId].get<std::string>();
response.Metadata = jsonRoot[ODataMeta].get<std::string>();
response.Type = jsonRoot[ODataType].get<std::string>();
}
}
@ -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<std::string>();
for (auto value : jsonRoot["value"])
std::string metadataLink = jsonRoot[ODataMeta].get<std::string>();
for (auto value : jsonRoot[Value])
{
Models::Table table;
table.TableName = value["TableName"].get<std::string>();
table.EditLink = value["odata.editLink"].get<std::string>();
table.Id = value["odata.id"].get<std::string>();
table.Type = value["odata.type"].get<std::string>();
table.TableName = value[TableName].get<std::string>();
table.EditLink = value[ODataEditLink].get<std::string>();
table.Id = value[ODataId].get<std::string>();
table.Type = value[ODataType].get<std::string>();
table.Metadata = metadataLink;
response.Tables.emplace_back(std::move(table));
}
@ -566,15 +565,15 @@ Azure::Response<Models::SetTableAccessPolicyResult> 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<const uint8_t*>(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<Models::TableAccessPolicy> 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<Models::DeleteTableResult> 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<Models::DeleteTableResult> TableServiceClient::DeleteTable(
return Response<Models::DeleteTableResult>(std::move(response), std::move(rawResponse));
}
Azure::Response<Models::CreateEntityResult> TableClient::CreateEntity(
Azure::Response<Models::AddEntityResult> 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<Models::CreateEntityResult> 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<Models::CreateEntityResult> TableClient::CreateEntity(
throw Core::RequestFailedException(rawResponse);
}
Models::CreateEntityResult response{};
Models::AddEntityResult response{};
response.ETag = rawResponse->GetHeaders().at("ETag");
return Response<Models::CreateEntityResult>(std::move(response), std::move(rawResponse));
return Response<Models::AddEntityResult>(std::move(response), std::move(rawResponse));
}
Azure::Response<Models::UpdateEntityResult> TableClient::UpdateEntity(
@ -672,8 +671,9 @@ Azure::Response<Models::UpdateEntityResult> 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<Models::UpdateEntityResult> 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<Models::MergeEntityResult> 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<Models::MergeEntityResult> 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<Models::DeleteEntityResult> 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<Models::UpsertEntityResult> 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>(
Models::UpsertEntityResult(response.Value), std::move(response.RawResponse));
}
@ -827,11 +829,11 @@ Azure::Response<Models::TableEntity> 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<Models::SubmitTransactionResult> TableClient::SubmitTransaction(
Transaction& transaction,
std::vector<Models::TransactionStep> 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<std::uint8_t const*>(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<Models::SubmitTransactionResult> 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<std::string>();
error.Code = jsonRoot[ODataError]["code"].get<std::string>();
}
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<std::string>();
error.Message = jsonRoot[ODataError]["message"][Value].get<std::string>();
}
}
}
@ -994,3 +993,121 @@ Azure::Response<Models::SubmitTransactionResult> TableClient::SubmitTransaction(
}
return Response<Models::SubmitTransactionResult>(std::move(response), std::move(rawResponse));
}
std::string TableClient::PreparePayload(
std::string const& batchId,
std::string const& changesetId,
std::vector<Models::TransactionStep> 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;
}

View File

@ -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(

View File

@ -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;
}

View File

@ -19,39 +19,51 @@
#include <iostream>
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<Models::TransactionStep> 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;
}

View File

@ -8,7 +8,7 @@
#include <thread>
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<uint8_t>(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<uint8_t>(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<uint8_t>(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<uint8_t>(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<uint8_t>(key.begin(), key.end())));
auto sasToken = sasBuilder.GenerateSasToken(cred);

View File

@ -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, "*");

View File

@ -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<uint8_t>(accountKey.begin(), accountKey.end()))
+ ";EndpointSuffix = core.windows.net ";
std::shared_ptr<SharedKeyCredential> credential;
std::shared_ptr<NamedKeyCredential> credential;
auto parsedConnectionString = ParseConnectionString(connectionString);
SharedKeyLitePolicy policy(parsedConnectionString.KeyCredential);
@ -54,7 +54,7 @@ namespace Azure { namespace Data { namespace Tables { namespace _internal { name
std::vector<uint8_t>(accountKey.begin(), accountKey.end()))
+ ";EndpointSuffix = core.windows.net ";
std::shared_ptr<SharedKeyCredential> credential;
std::shared_ptr<NamedKeyCredential> credential;
auto parsedConnectionString = ParseConnectionString(connectionString);
SharedKeyLitePolicy policy(parsedConnectionString.KeyCredential);
@ -73,7 +73,7 @@ namespace Azure { namespace Data { namespace Tables { namespace _internal { name
std::vector<uint8_t>(accountKey.begin(), accountKey.end()))
+ ";EndpointSuffix = core.windows.net ";
std::shared_ptr<SharedKeyCredential> credential;
std::shared_ptr<NamedKeyCredential> credential;
auto parsedConnectionString = ParseConnectionString(connectionString);
SharedKeyLitePolicy policy(parsedConnectionString.KeyCredential);

View File

@ -9,10 +9,21 @@
#include <azure/core/internal/strings.hpp>
#include <chrono>
#include <cstdlib>
#include <ctime>
// 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 <iostream>
#include <string>
#include <thread>
#endif
using namespace Azure::Data;
using namespace Azure::Data::Tables::Models;
namespace Azure { namespace Data { namespace Test {
std::shared_ptr<Azure::Core::Credentials::TokenCredential> m_credential;
void TablesClientTest::SetUp()
@ -29,19 +40,24 @@ namespace Azure { namespace Data { namespace Test {
{
auto clientOptions = InitStorageClientOptions<Tables::TableClientOptions>();
auto tableClientOptions = InitStorageClientOptions<Tables::TableClientOptions>();
m_tableName = GetTestNameLowerCase();
#ifdef RANDOM_TABLE_NAME
srand(static_cast<unsigned>(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>(
Tables::TableServiceClient::CreateFromConnectionString(
GetEnv("STANDARD_STORAGE_CONNECTION_STRING"), clientOptions));
GetConnectionString(), clientOptions));
m_tableClient = std::make_shared<Tables::TableClient>(
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<Azure::Data::Tables::Credentials::SharedKeyCredential>(
auto creds = std::make_shared<Azure::Data::Tables::Credentials::NamedKeyCredential>(
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<Azure::Data::Tables::Models::TransactionStep> 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<Azure::Data::Tables::Models::TransactionStep> 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<Azure::Data::Tables::Models::TransactionStep> 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<Azure::Data::Tables::Models::TransactionStep> 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<Azure::Data::Tables::Models::TransactionStep> 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());
}

View File

@ -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 {

View File

@ -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<Models::TransactionStep> 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<Models::TransactionStep> 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<Models::TransactionStep> 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<Models::TransactionStep> 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<Models::TransactionStep> 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<Models::TransactionStep> 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<Models::TransactionStep> 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<std::string> 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

View File

@ -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<std::string> 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