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:
parent
acb6d97f7c
commit
8c34ec83ab
@ -2,5 +2,5 @@
|
||||
"AssetsRepo": "Azure/azure-sdk-assets",
|
||||
"AssetsRepoPrefixPath": "cpp",
|
||||
"TagPrefix": "cpp/tables",
|
||||
"Tag": "cpp/tables_69657814a3"
|
||||
"Tag": "cpp/tables_55f4cc2c1e"
|
||||
}
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
@ -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>
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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.
|
||||
*/
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
|
||||
@ -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";
|
||||
}
|
||||
|
||||
@ -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");
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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, "*");
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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());
|
||||
}
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user