// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
// Code generated by Microsoft (R) TypeSpec Code Generator.
// Changes may cause incorrect behavior and will be lost if the code is regenerated.

#include "azure/data/appconfiguration/configuration_client.hpp"

#include "private/package_version.hpp"

#include <azure/core/exception.hpp>
#include <azure/core/http/http.hpp>
#include <azure/core/http/http_status_code.hpp>
#include <azure/core/http/policies/policy.hpp>
#include <azure/core/internal/json/json.hpp>
#include <azure/core/io/body_stream.hpp>

#include <functional>

using namespace Azure::Data::AppConfiguration;
using namespace Azure::Data::AppConfiguration::_detail;

GetKeysResponseContentType const
    GetKeysResponseContentType::ApplicationVndMicrosoftAppconfigKeysetJson{
        "application/vnd.microsoft.appconfig.keyset+json"};
GetKeysResponseContentType const GetKeysResponseContentType::ApplicationProblemJson{
    "application/problem+json"};

GetKeyValuesResponseContentType const
    GetKeyValuesResponseContentType::ApplicationVndMicrosoftAppconfigKvsetJson{
        "application/vnd.microsoft.appconfig.kvset+json"};
GetKeyValuesResponseContentType const GetKeyValuesResponseContentType::ApplicationProblemJson{
    "application/problem+json"};

KeyValueFields const KeyValueFields::Key{"key"};
KeyValueFields const KeyValueFields::Label{"label"};
KeyValueFields const KeyValueFields::ContentType{"content_type"};
KeyValueFields const KeyValueFields::Value{"value"};
KeyValueFields const KeyValueFields::LastModified{"last_modified"};
KeyValueFields const KeyValueFields::Tags{"tags"};
KeyValueFields const KeyValueFields::Locked{"locked"};
KeyValueFields const KeyValueFields::Etag{"etag"};

PutKeyValueRequestContentType const
    PutKeyValueRequestContentType::ApplicationVndMicrosoftAppconfigKvJson{
        "application/vnd.microsoft.appconfig.kv+json"};
PutKeyValueRequestContentType const
    PutKeyValueRequestContentType::ApplicationVndMicrosoftAppconfigKvsetJson{
        "application/vnd.microsoft.appconfig.kvset+json"};
PutKeyValueRequestContentType const PutKeyValueRequestContentType::ApplicationJson{
    "application/json"};
PutKeyValueRequestContentType const PutKeyValueRequestContentType::TextJson{"text/json"};
PutKeyValueRequestContentType const PutKeyValueRequestContentType::ApplicationZeroZeroJson{
    "application/*+json"};
PutKeyValueRequestContentType const PutKeyValueRequestContentType::ApplicationJsonPatchJson{
    "application/json-patch+json"};

SnapshotStatus const SnapshotStatus::Provisioning{"provisioning"};
SnapshotStatus const SnapshotStatus::Ready{"ready"};
SnapshotStatus const SnapshotStatus::Archived{"archived"};
SnapshotStatus const SnapshotStatus::Failed{"failed"};

CompositionType const CompositionType::Key{"key"};
CompositionType const CompositionType::KeyLabel{"key_label"};

GetSnapshotsResponseContentType const
    GetSnapshotsResponseContentType::ApplicationVndMicrosoftAppconfigSnapshotsetJson{
        "application/vnd.microsoft.appconfig.snapshotset+json"};
GetSnapshotsResponseContentType const GetSnapshotsResponseContentType::ApplicationProblemJson{
    "application/problem+json"};

SnapshotFields const SnapshotFields::Name{"name"};
SnapshotFields const SnapshotFields::Status{"status"};
SnapshotFields const SnapshotFields::Filters{"filters"};
SnapshotFields const SnapshotFields::CompositionType{"composition_type"};
SnapshotFields const SnapshotFields::Created{"created"};
SnapshotFields const SnapshotFields::Expires{"expires"};
SnapshotFields const SnapshotFields::RetentionPeriod{"retention_period"};
SnapshotFields const SnapshotFields::Size{"size"};
SnapshotFields const SnapshotFields::ItemsCount{"items_count"};
SnapshotFields const SnapshotFields::Tags{"tags"};
SnapshotFields const SnapshotFields::Etag{"etag"};

GetSnapshotResponseContentType const
    GetSnapshotResponseContentType::ApplicationVndMicrosoftAppconfigSnapshotJson{
        "application/vnd.microsoft.appconfig.snapshot+json"};
GetSnapshotResponseContentType const GetSnapshotResponseContentType::ApplicationProblemJson{
    "application/problem+json"};

OperationState const OperationState::NotStarted{"NotStarted"};
OperationState const OperationState::Running{"Running"};
OperationState const OperationState::Succeeded{"Succeeded"};
OperationState const OperationState::Failed{"Failed"};
OperationState const OperationState::Canceled{"Canceled"};

CreateSnapshotResponseContentType const
    CreateSnapshotResponseContentType::ApplicationVndMicrosoftAppconfigSnapshotJson{
        "application/vnd.microsoft.appconfig.snapshot+json"};
CreateSnapshotResponseContentType const CreateSnapshotResponseContentType::ApplicationProblemJson{
    "application/problem+json"};

CreateSnapshotRequestContentType const
    CreateSnapshotRequestContentType::ApplicationVndMicrosoftAppconfigSnapshotJson{
        "application/vnd.microsoft.appconfig.snapshot+json"};
CreateSnapshotRequestContentType const CreateSnapshotRequestContentType::ApplicationJson{
    "application/json"};

UpdateSnapshotResponseContentType const
    UpdateSnapshotResponseContentType::ApplicationVndMicrosoftAppconfigSnapshotJson{
        "application/vnd.microsoft.appconfig.snapshot+json"};
UpdateSnapshotResponseContentType const UpdateSnapshotResponseContentType::ApplicationProblemJson{
    "application/problem+json"};

UpdateSnapshotRequestContentType const UpdateSnapshotRequestContentType::ApplicationMergePatchJson{
    "application/merge-patch+json"};
UpdateSnapshotRequestContentType const UpdateSnapshotRequestContentType::ApplicationJson{
    "application/json"};

GetLabelsResponseContentType const
    GetLabelsResponseContentType::ApplicationVndMicrosoftAppconfigLabelsetJson{
        "application/vnd.microsoft.appconfig.labelset+json"};
GetLabelsResponseContentType const GetLabelsResponseContentType::ApplicationProblemJson{
    "application/problem+json"};

LabelFields const LabelFields::Name{"name"};

GetRevisionsResponseContentType const
    GetRevisionsResponseContentType::ApplicationVndMicrosoftAppconfigKvsetJson{
        "application/vnd.microsoft.appconfig.kvset+json"};
GetRevisionsResponseContentType const GetRevisionsResponseContentType::ApplicationProblemJson{
    "application/problem+json"};

void GetKeysPagedResponse::OnNextPage(Core::Context const& context)
{
  auto const pageToken = this->NextPageToken;
  this->m_options.NextPageToken = pageToken.Value();
  *this = this->m_client->GetKeys(this->m_accept, this->m_options, context);
  this->CurrentPageToken = pageToken.Value();
}

void GetKeyValuesPagedResponse::OnNextPage(Core::Context const& context)
{
  auto const pageToken = this->NextPageToken;
  this->m_options.NextPageToken = pageToken.Value();
  *this = this->m_client->GetKeyValues(this->m_accept, this->m_options, context);
  this->CurrentPageToken = pageToken.Value();
}

void GetSnapshotsPagedResponse::OnNextPage(Core::Context const& context)
{
  auto const pageToken = this->NextPageToken;
  this->m_options.NextPageToken = pageToken.Value();
  *this = this->m_client->GetSnapshots(this->m_accept, this->m_options, context);
  this->CurrentPageToken = pageToken.Value();
}

void GetLabelsPagedResponse::OnNextPage(Core::Context const& context)
{
  auto const pageToken = this->NextPageToken;
  this->m_options.NextPageToken = pageToken.Value();
  *this = this->m_client->GetLabels(this->m_accept, this->m_options, context);
  this->CurrentPageToken = pageToken.Value();
}

void GetRevisionsPagedResponse::OnNextPage(Core::Context const& context)
{
  auto const pageToken = this->NextPageToken;
  this->m_options.NextPageToken = pageToken.Value();
  *this = this->m_client->GetRevisions(this->m_accept, this->m_options, context);
  this->CurrentPageToken = pageToken.Value();
}

ConfigurationClient::ConfigurationClient(
    std::string const& url,
    const std::shared_ptr<const Core::Credentials::TokenCredential>& credential,
    ConfigurationClientOptions options)
    : m_url(url), m_apiVersion(options.ApiVersion)
{
  std::vector<std::unique_ptr<Core::Http::Policies::HttpPolicy>> perRetryPolicies;
  {
    Core::Credentials::TokenRequestContext tokenRequestContext;
    tokenRequestContext.Scopes = {"https://azconfig.io/.default"};
    perRetryPolicies.emplace_back(
        std::make_unique<Core::Http::Policies::_internal::BearerTokenAuthenticationPolicy>(
            credential, tokenRequestContext));
  }

  m_pipeline.reset(new Core::Http::_internal::HttpPipeline(
      {}, "data-appconfiguration", PackageVersion::ToString(), std::move(perRetryPolicies), {}));
}

std::string ConfigurationClient::GetUrl() const { return m_url.GetAbsoluteUrl(); }

GetKeysPagedResponse ConfigurationClient::GetKeys(
    std::string const& accept,
    GetKeysOptions const& options,
    Core::Context const& context) const
{
  auto url = m_url;
  url.AppendPath("keys");

  url.AppendQueryParameter("api-version", Core::Url::Encode(m_apiVersion));
  url.AppendQueryParameter("name", Core::Url::Encode(options.Name));
  url.AppendQueryParameter("After", Core::Url::Encode(options.After));
  if (options.NextPageToken.find("https://") == 0 || options.NextPageToken.find("http://") == 0)
  {
    url = Core::Url(options.NextPageToken);
    url.SetPort(m_url.GetPort());
  }
  else if (!options.NextPageToken.empty())
  {
    url.SetPath(
        options.NextPageToken[0] == '/' ? options.NextPageToken.substr(1) : options.NextPageToken);
  }

  Core::Http::Request request(Core::Http::HttpMethod::Get, url);

  request.SetHeader("Accept", accept);
  request.SetHeader("Sync-Token", options.SyncToken);
  request.SetHeader("Accept-Datetime", options.AcceptDatetime);
  auto rawResponse = m_pipeline->Send(request, context);
  auto const httpStatusCode = rawResponse->GetStatusCode();

  if (httpStatusCode != Core::Http::HttpStatusCode::Ok)
  {
    throw Core::RequestFailedException(rawResponse);
  }

  GetKeysPagedResponse response{};
  response.m_client = std::make_shared<ConfigurationClient>(*this);
  response.m_accept = accept;
  response.m_options = options;
  {
    auto const& responseBody = rawResponse->GetBody();
    if (responseBody.size() > 0)
    {
      auto const jsonRoot
          = Core::Json::_internal::json::parse(responseBody.begin(), responseBody.end());
      if (jsonRoot.contains("@nextLink") && !jsonRoot["@nextLink"].is_null())
      {
        response.NextPageToken = jsonRoot["@nextLink"].get<std::string>();
      }

      if (jsonRoot.contains("items"))
      {
        response.Items = std::vector<Key>{};

        for (auto const& jsonItem : jsonRoot["items"])
        {
          Key vectorItem{};

          vectorItem.Name = jsonItem["name"].get<std::string>();

          response.Items.Value().emplace_back(std::move(vectorItem));
        }
      }
    }
  }

  response.SyncToken = rawResponse->GetHeaders().at("Sync-Token");
  response.ContentType = GetKeysResponseContentType(rawResponse->GetHeaders().at("Content-Type"));

  response.RawResponse = std::move(rawResponse);

  return response;
}

Azure::Response<CheckKeysResult> ConfigurationClient::CheckKeys(
    CheckKeysOptions const& options,
    Core::Context const& context) const
{
  auto url = m_url;
  url.AppendPath("keys");

  url.AppendQueryParameter("api-version", Core::Url::Encode(m_apiVersion));
  url.AppendQueryParameter("name", Core::Url::Encode(options.Name));
  url.AppendQueryParameter("After", Core::Url::Encode(options.After));
  Core::Http::Request request(Core::Http::HttpMethod::Head, url);

  request.SetHeader("Accept", "application/json");

  request.SetHeader("Sync-Token", options.SyncToken);
  request.SetHeader("Accept-Datetime", options.AcceptDatetime);
  request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.ToString());
  auto rawResponse = m_pipeline->Send(request, context);
  auto const httpStatusCode = rawResponse->GetStatusCode();

  if (httpStatusCode != Core::Http::HttpStatusCode::Ok)
  {
    throw Core::RequestFailedException(rawResponse);
  }

  CheckKeysResult response{};
  response.SyncToken = rawResponse->GetHeaders().at("Sync-Token");

  return Response<CheckKeysResult>(std::move(response), std::move(rawResponse));
}

GetKeyValuesPagedResponse ConfigurationClient::GetKeyValues(
    std::string const& accept,
    GetKeyValuesOptions const& options,
    Core::Context const& context) const
{
  auto url = m_url;
  url.AppendPath("kv");

  url.AppendQueryParameter("api-version", Core::Url::Encode(m_apiVersion));
  url.AppendQueryParameter("key", Core::Url::Encode(options.Key));
  url.AppendQueryParameter("label", Core::Url::Encode(options.Label));
  url.AppendQueryParameter("After", Core::Url::Encode(options.After));
  {
    std::string valueStr;
    for (size_t i = 0; i < options.Select.size(); ++i)
    {
      if (i != 0)
      {
        valueStr += ',';
      }

      valueStr += Core::Url::Encode(options.Select[i].ToString());
    }

    url.AppendQueryParameter("$Select", valueStr);
  }

  url.AppendQueryParameter("snapshot", Core::Url::Encode(options.Snapshot));
  {
    std::string valueStr;
    for (size_t i = 0; i < options.Tags.size(); ++i)
    {
      if (i != 0)
      {
        valueStr += "&tags=";
      }

      valueStr += Core::Url::Encode(options.Tags[i]);
    }

    url.AppendQueryParameter("tags", valueStr);
  }

  if (options.NextPageToken.find("https://") == 0 || options.NextPageToken.find("http://") == 0)
  {
    url = Core::Url(options.NextPageToken);
    url.SetPort(m_url.GetPort());
  }
  else if (!options.NextPageToken.empty())
  {
    url.SetPath(
        options.NextPageToken[0] == '/' ? options.NextPageToken.substr(1) : options.NextPageToken);
  }

  Core::Http::Request request(Core::Http::HttpMethod::Get, url);

  request.SetHeader("Accept", accept);
  request.SetHeader("Sync-Token", options.SyncToken);
  request.SetHeader("Accept-Datetime", options.AcceptDatetime);
  request.SetHeader("If-Match", options.IfMatch);
  request.SetHeader("If-None-Match", options.IfNoneMatch);
  auto rawResponse = m_pipeline->Send(request, context);
  auto const httpStatusCode = rawResponse->GetStatusCode();

  if (httpStatusCode != Core::Http::HttpStatusCode::Ok)
  {
    throw Core::RequestFailedException(rawResponse);
  }

  GetKeyValuesPagedResponse response{};
  response.m_client = std::make_shared<ConfigurationClient>(*this);
  response.m_accept = accept;
  response.m_options = options;
  {
    auto const& responseBody = rawResponse->GetBody();
    if (responseBody.size() > 0)
    {
      auto const jsonRoot
          = Core::Json::_internal::json::parse(responseBody.begin(), responseBody.end());
      if (jsonRoot.contains("@nextLink") && !jsonRoot["@nextLink"].is_null())
      {
        response.NextPageToken = jsonRoot["@nextLink"].get<std::string>();
      }

      if (jsonRoot.contains("items"))
      {
        response.Items = std::vector<KeyValue>{};

        for (auto const& jsonItem : jsonRoot["items"])
        {
          KeyValue vectorItem{};

          vectorItem.Key = jsonItem["key"].get<std::string>();

          if (jsonItem.contains("label") && !jsonItem["label"].is_null())
          {
            vectorItem.Label = jsonItem["label"].get<std::string>();
          }

          if (jsonItem.contains("content_type") && !jsonItem["content_type"].is_null())
          {
            vectorItem.ContentType = jsonItem["content_type"].get<std::string>();
          }

          if (jsonItem.contains("value") && !jsonItem["value"].is_null())
          {
            vectorItem.Value = jsonItem["value"].get<std::string>();
          }

          if (jsonItem.contains("last_modified") && !jsonItem["last_modified"].is_null())
          {
            vectorItem.LastModified = jsonItem["last_modified"].get<std::string>();
          }

          if (jsonItem.contains("tags"))
          {
            vectorItem.Tags = std::map<std::string, std::string>{};

            for (auto const& kv : jsonItem["tags"].items())
            {
              std::string value{};
              value = kv.value().get<std::string>();
              vectorItem.Tags.Value().emplace(kv.key(), value);
            }
          }

          if (jsonItem.contains("locked") && !jsonItem["locked"].is_null())
          {
            vectorItem.Locked = jsonItem["locked"].is_string()
                ? (jsonItem["locked"].get<std::string>() == "true")
                : jsonItem["locked"].get<bool>();
          }

          if (jsonItem.contains("etag") && !jsonItem["etag"].is_null())
          {
            vectorItem.Etag = jsonItem["etag"].get<std::string>();
          }

          response.Items.Value().emplace_back(std::move(vectorItem));
        }
      }
      if (jsonRoot.contains("etag") && !jsonRoot["etag"].is_null())
      {
        response.Etag = jsonRoot["etag"].get<std::string>();
      }
    }
  }

  response.SyncToken = rawResponse->GetHeaders().at("Sync-Token");
  response.ETag = rawResponse->GetHeaders().at("ETag");
  response.ContentType
      = GetKeyValuesResponseContentType(rawResponse->GetHeaders().at("Content-Type"));

  response.RawResponse = std::move(rawResponse);

  return response;
}

Azure::Response<CheckKeyValuesResult> ConfigurationClient::CheckKeyValues(
    CheckKeyValuesOptions const& options,
    Core::Context const& context) const
{
  auto url = m_url;
  url.AppendPath("kv");

  url.AppendQueryParameter("api-version", Core::Url::Encode(m_apiVersion));
  url.AppendQueryParameter("key", Core::Url::Encode(options.Key));
  url.AppendQueryParameter("label", Core::Url::Encode(options.Label));
  url.AppendQueryParameter("After", Core::Url::Encode(options.After));
  {
    std::string valueStr;
    for (size_t i = 0; i < options.Select.size(); ++i)
    {
      if (i != 0)
      {
        valueStr += ',';
      }

      valueStr += Core::Url::Encode(options.Select[i].ToString());
    }

    url.AppendQueryParameter("$Select", valueStr);
  }

  url.AppendQueryParameter("snapshot", Core::Url::Encode(options.Snapshot));
  {
    std::string valueStr;
    for (size_t i = 0; i < options.Tags.size(); ++i)
    {
      if (i != 0)
      {
        valueStr += "&tags=";
      }

      valueStr += Core::Url::Encode(options.Tags[i]);
    }

    url.AppendQueryParameter("tags", valueStr);
  }

  Core::Http::Request request(Core::Http::HttpMethod::Head, url);

  request.SetHeader("Accept", "application/json");

  request.SetHeader("Sync-Token", options.SyncToken);
  request.SetHeader("Accept-Datetime", options.AcceptDatetime);
  request.SetHeader("If-Match", options.IfMatch);
  request.SetHeader("If-None-Match", options.IfNoneMatch);
  request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.ToString());
  auto rawResponse = m_pipeline->Send(request, context);
  auto const httpStatusCode = rawResponse->GetStatusCode();

  if (httpStatusCode != Core::Http::HttpStatusCode::Ok)
  {
    throw Core::RequestFailedException(rawResponse);
  }

  CheckKeyValuesResult response{};
  response.SyncToken = rawResponse->GetHeaders().at("Sync-Token");
  response.ETag = rawResponse->GetHeaders().at("ETag");

  return Response<CheckKeyValuesResult>(std::move(response), std::move(rawResponse));
}

Azure::Response<GetKeyValueResult> ConfigurationClient::GetKeyValue(
    std::string const& key,
    std::string const& accept,
    GetKeyValueOptions const& options,
    Core::Context const& context) const
{
  auto url = m_url;
  url.AppendPath("kv/");
  url.AppendPath(!key.empty() ? Core::Url::Encode(key) : "null");

  url.AppendQueryParameter("api-version", Core::Url::Encode(m_apiVersion));
  url.AppendQueryParameter("label", Core::Url::Encode(options.Label));
  {
    std::string valueStr;
    for (size_t i = 0; i < options.Select.size(); ++i)
    {
      if (i != 0)
      {
        valueStr += ',';
      }

      valueStr += Core::Url::Encode(options.Select[i].ToString());
    }

    url.AppendQueryParameter("$Select", valueStr);
  }

  Core::Http::Request request(Core::Http::HttpMethod::Get, url);

  request.SetHeader("Accept", accept);
  request.SetHeader("Sync-Token", options.SyncToken);
  request.SetHeader("Accept-Datetime", options.AcceptDatetime);
  request.SetHeader("If-Match", options.IfMatch);
  request.SetHeader("If-None-Match", options.IfNoneMatch);
  request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.ToString());
  auto rawResponse = m_pipeline->Send(request, context);
  auto const httpStatusCode = rawResponse->GetStatusCode();

  if (httpStatusCode != Core::Http::HttpStatusCode::Ok)
  {
    throw Core::RequestFailedException(rawResponse);
  }

  GetKeyValueResult response{};
  {
    auto const& responseBody = rawResponse->GetBody();
    if (responseBody.size() > 0)
    {
      auto const jsonRoot
          = Core::Json::_internal::json::parse(responseBody.begin(), responseBody.end());

      response.Key = jsonRoot["key"].get<std::string>();

      if (jsonRoot.contains("label") && !jsonRoot["label"].is_null())
      {
        response.Label = jsonRoot["label"].get<std::string>();
      }

      if (jsonRoot.contains("content_type") && !jsonRoot["content_type"].is_null())
      {
        response.ContentType = jsonRoot["content_type"].get<std::string>();
      }

      if (jsonRoot.contains("value") && !jsonRoot["value"].is_null())
      {
        response.Value = jsonRoot["value"].get<std::string>();
      }

      if (jsonRoot.contains("last_modified") && !jsonRoot["last_modified"].is_null())
      {
        response.LastModified = jsonRoot["last_modified"].get<std::string>();
      }

      if (jsonRoot.contains("tags"))
      {
        response.Tags = std::map<std::string, std::string>{};

        for (auto const& kv : jsonRoot["tags"].items())
        {
          std::string value{};
          value = kv.value().get<std::string>();
          response.Tags.Value().emplace(kv.key(), value);
        }
      }

      if (jsonRoot.contains("locked") && !jsonRoot["locked"].is_null())
      {
        response.Locked = jsonRoot["locked"].is_string()
            ? (jsonRoot["locked"].get<std::string>() == "true")
            : jsonRoot["locked"].get<bool>();
      }

      if (jsonRoot.contains("etag") && !jsonRoot["etag"].is_null())
      {
        response.Etag = jsonRoot["etag"].get<std::string>();
      }
    }
  }

  response.XMsClientRequestId
      = Core::Uuid::Parse(rawResponse->GetHeaders().at("x-ms-client-request-id"));
  response.SyncToken = rawResponse->GetHeaders().at("Sync-Token");
  response.ETag = rawResponse->GetHeaders().at("ETag");

  return Response<GetKeyValueResult>(std::move(response), std::move(rawResponse));
}

Azure::Response<PutKeyValueResult> ConfigurationClient::PutKeyValue(
    PutKeyValueRequestContentType const& contentType,
    std::string const& key,
    std::string const& accept,
    PutKeyValueOptions const& options,
    Core::Context const& context) const
{
  auto url = m_url;
  url.AppendPath("kv/");
  url.AppendPath(!key.empty() ? Core::Url::Encode(key) : "null");

  url.AppendQueryParameter("api-version", Core::Url::Encode(m_apiVersion));
  url.AppendQueryParameter("label", Core::Url::Encode(options.Label));
  std::string jsonBody;
  {
    auto jsonRoot = Core::Json::_internal::json::object();

    jsonRoot["key"] = options.Entity.Key;

    if (options.Entity.Label.HasValue())
    {
      jsonRoot["label"] = options.Entity.Label.Value();
    }

    if (options.Entity.ContentType.HasValue())
    {
      jsonRoot["content_type"] = options.Entity.ContentType.Value();
    }

    if (options.Entity.Value.HasValue())
    {
      jsonRoot["value"] = options.Entity.Value.Value();
    }

    if (options.Entity.LastModified.HasValue())
    {
      jsonRoot["last_modified"] = options.Entity.LastModified.Value();
    }
    if (options.Entity.Tags.HasValue())
    {
      jsonRoot["tags"] = Core::Json::_internal::json::object();

      for (auto const& kv : options.Entity.Tags.Value())
      {
        jsonRoot["tags"][kv.first] = kv.second;
      }
    }

    if (options.Entity.Locked.HasValue())
    {
      jsonRoot["locked"] = options.Entity.Locked.Value();
    }

    if (options.Entity.Etag.HasValue())
    {
      jsonRoot["etag"] = options.Entity.Etag.Value();
    }

    jsonBody = jsonRoot.dump();
  }

  Core::IO::MemoryBodyStream requestBody(
      reinterpret_cast<std::uint8_t const*>(jsonBody.data()), jsonBody.length());

  Core::Http::Request request(Core::Http::HttpMethod::Put, url, &requestBody);

  request.SetHeader("Content-Type", contentType.ToString());
  request.SetHeader("Accept", accept);
  request.SetHeader("Sync-Token", options.SyncToken);
  request.SetHeader("If-Match", options.IfMatch);
  request.SetHeader("If-None-Match", options.IfNoneMatch);
  request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.ToString());
  request.SetHeader("Content-Length", std::to_string(requestBody.Length()));

  auto rawResponse = m_pipeline->Send(request, context);
  auto const httpStatusCode = rawResponse->GetStatusCode();

  if (httpStatusCode != Core::Http::HttpStatusCode::Ok)
  {
    throw Core::RequestFailedException(rawResponse);
  }

  PutKeyValueResult response{};
  {
    auto const& responseBody = rawResponse->GetBody();
    if (responseBody.size() > 0)
    {
      auto const jsonRoot
          = Core::Json::_internal::json::parse(responseBody.begin(), responseBody.end());

      response.Key = jsonRoot["key"].get<std::string>();

      if (jsonRoot.contains("label") && !jsonRoot["label"].is_null())
      {
        response.Label = jsonRoot["label"].get<std::string>();
      }

      if (jsonRoot.contains("content_type") && !jsonRoot["content_type"].is_null())
      {
        response.ContentType = jsonRoot["content_type"].get<std::string>();
      }

      if (jsonRoot.contains("value") && !jsonRoot["value"].is_null())
      {
        response.Value = jsonRoot["value"].get<std::string>();
      }

      if (jsonRoot.contains("last_modified") && !jsonRoot["last_modified"].is_null())
      {
        response.LastModified = jsonRoot["last_modified"].get<std::string>();
      }

      if (jsonRoot.contains("tags"))
      {
        response.Tags = std::map<std::string, std::string>{};

        for (auto const& kv : jsonRoot["tags"].items())
        {
          std::string value{};
          value = kv.value().get<std::string>();
          response.Tags.Value().emplace(kv.key(), value);
        }
      }

      if (jsonRoot.contains("locked") && !jsonRoot["locked"].is_null())
      {
        response.Locked = jsonRoot["locked"].is_string()
            ? (jsonRoot["locked"].get<std::string>() == "true")
            : jsonRoot["locked"].get<bool>();
      }

      if (jsonRoot.contains("etag") && !jsonRoot["etag"].is_null())
      {
        response.Etag = jsonRoot["etag"].get<std::string>();
      }
    }
  }

  response.SyncToken = rawResponse->GetHeaders().at("Sync-Token");
  response.ETag = rawResponse->GetHeaders().at("ETag");

  return Response<PutKeyValueResult>(std::move(response), std::move(rawResponse));
}

Azure::Response<DeleteKeyValueResult> ConfigurationClient::DeleteKeyValue(
    std::string const& key,
    std::string const& accept,
    DeleteKeyValueOptions const& options,
    Core::Context const& context) const
{
  auto url = m_url;
  url.AppendPath("kv/");
  url.AppendPath(!key.empty() ? Core::Url::Encode(key) : "null");

  url.AppendQueryParameter("api-version", Core::Url::Encode(m_apiVersion));
  url.AppendQueryParameter("label", Core::Url::Encode(options.Label));
  Core::Http::Request request(Core::Http::HttpMethod::Delete, url);

  request.SetHeader("Accept", accept);
  request.SetHeader("Sync-Token", options.SyncToken);
  request.SetHeader("If-Match", options.IfMatch);
  request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.ToString());
  auto rawResponse = m_pipeline->Send(request, context);
  auto const httpStatusCode = rawResponse->GetStatusCode();

  if (httpStatusCode != Core::Http::HttpStatusCode::NoContent
      && httpStatusCode != Core::Http::HttpStatusCode::Ok)
  {
    throw Core::RequestFailedException(rawResponse);
  }

  DeleteKeyValueResult response{};
  {
    auto const& responseBody = rawResponse->GetBody();
    if (responseBody.size() > 0)
    {
      auto const jsonRoot
          = Core::Json::_internal::json::parse(responseBody.begin(), responseBody.end());

      response.Key = jsonRoot["key"].get<std::string>();

      if (jsonRoot.contains("label") && !jsonRoot["label"].is_null())
      {
        response.Label = jsonRoot["label"].get<std::string>();
      }

      if (jsonRoot.contains("content_type") && !jsonRoot["content_type"].is_null())
      {
        response.ContentType = jsonRoot["content_type"].get<std::string>();
      }

      if (jsonRoot.contains("value") && !jsonRoot["value"].is_null())
      {
        response.Value = jsonRoot["value"].get<std::string>();
      }

      if (jsonRoot.contains("last_modified") && !jsonRoot["last_modified"].is_null())
      {
        response.LastModified = jsonRoot["last_modified"].get<std::string>();
      }

      if (jsonRoot.contains("tags"))
      {
        response.Tags = std::map<std::string, std::string>{};

        for (auto const& kv : jsonRoot["tags"].items())
        {
          std::string value{};
          value = kv.value().get<std::string>();
          response.Tags.Value().emplace(kv.key(), value);
        }
      }

      if (jsonRoot.contains("locked") && !jsonRoot["locked"].is_null())
      {
        response.Locked = jsonRoot["locked"].is_string()
            ? (jsonRoot["locked"].get<std::string>() == "true")
            : jsonRoot["locked"].get<bool>();
      }

      if (jsonRoot.contains("etag") && !jsonRoot["etag"].is_null())
      {
        response.Etag = jsonRoot["etag"].get<std::string>();
      }
    }
  }

  response.SyncToken = rawResponse->GetHeaders().at("Sync-Token");
  response.ETag = rawResponse->GetHeaders().at("ETag");

  return Response<DeleteKeyValueResult>(std::move(response), std::move(rawResponse));
}

Azure::Response<CheckKeyValueResult> ConfigurationClient::CheckKeyValue(
    std::string const& key,
    CheckKeyValueOptions const& options,
    Core::Context const& context) const
{
  auto url = m_url;
  url.AppendPath("kv/");
  url.AppendPath(!key.empty() ? Core::Url::Encode(key) : "null");

  url.AppendQueryParameter("api-version", Core::Url::Encode(m_apiVersion));
  url.AppendQueryParameter("label", Core::Url::Encode(options.Label));
  {
    std::string valueStr;
    for (size_t i = 0; i < options.Select.size(); ++i)
    {
      if (i != 0)
      {
        valueStr += ',';
      }

      valueStr += Core::Url::Encode(options.Select[i].ToString());
    }

    url.AppendQueryParameter("$Select", valueStr);
  }

  Core::Http::Request request(Core::Http::HttpMethod::Head, url);

  request.SetHeader("Accept", "application/json");

  request.SetHeader("Sync-Token", options.SyncToken);
  request.SetHeader("Accept-Datetime", options.AcceptDatetime);
  request.SetHeader("If-Match", options.IfMatch);
  request.SetHeader("If-None-Match", options.IfNoneMatch);
  request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.ToString());
  auto rawResponse = m_pipeline->Send(request, context);
  auto const httpStatusCode = rawResponse->GetStatusCode();

  if (httpStatusCode != Core::Http::HttpStatusCode::Ok)
  {
    throw Core::RequestFailedException(rawResponse);
  }

  CheckKeyValueResult response{};
  response.SyncToken = rawResponse->GetHeaders().at("Sync-Token");
  response.ETag = rawResponse->GetHeaders().at("ETag");

  return Response<CheckKeyValueResult>(std::move(response), std::move(rawResponse));
}

GetSnapshotsPagedResponse ConfigurationClient::GetSnapshots(
    std::string const& accept,
    GetSnapshotsOptions const& options,
    Core::Context const& context) const
{
  auto url = m_url;
  url.AppendPath("snapshots");

  url.AppendQueryParameter("api-version", Core::Url::Encode(m_apiVersion));
  url.AppendQueryParameter("name", Core::Url::Encode(options.Name));
  url.AppendQueryParameter("After", Core::Url::Encode(options.After));
  {
    std::string valueStr;
    for (size_t i = 0; i < options.Select.size(); ++i)
    {
      if (i != 0)
      {
        valueStr += ',';
      }

      valueStr += Core::Url::Encode(options.Select[i].ToString());
    }

    url.AppendQueryParameter("$Select", valueStr);
  }

  {
    std::string valueStr;
    for (size_t i = 0; i < options.Status.size(); ++i)
    {
      if (i != 0)
      {
        valueStr += ',';
      }

      valueStr += Core::Url::Encode(options.Status[i].ToString());
    }

    url.AppendQueryParameter("status", valueStr);
  }

  if (options.NextPageToken.find("https://") == 0 || options.NextPageToken.find("http://") == 0)
  {
    url = Core::Url(options.NextPageToken);
    url.SetPort(m_url.GetPort());
  }
  else if (!options.NextPageToken.empty())
  {
    url.SetPath(
        options.NextPageToken[0] == '/' ? options.NextPageToken.substr(1) : options.NextPageToken);
  }

  Core::Http::Request request(Core::Http::HttpMethod::Get, url);

  request.SetHeader("Accept", accept);
  request.SetHeader("Sync-Token", options.SyncToken);
  auto rawResponse = m_pipeline->Send(request, context);
  auto const httpStatusCode = rawResponse->GetStatusCode();

  if (httpStatusCode != Core::Http::HttpStatusCode::Ok)
  {
    throw Core::RequestFailedException(rawResponse);
  }

  GetSnapshotsPagedResponse response{};
  response.m_client = std::make_shared<ConfigurationClient>(*this);
  response.m_accept = accept;
  response.m_options = options;
  {
    auto const& responseBody = rawResponse->GetBody();
    if (responseBody.size() > 0)
    {
      auto const jsonRoot
          = Core::Json::_internal::json::parse(responseBody.begin(), responseBody.end());
      if (jsonRoot.contains("@nextLink") && !jsonRoot["@nextLink"].is_null())
      {
        response.NextPageToken = jsonRoot["@nextLink"].get<std::string>();
      }

      if (jsonRoot.contains("items"))
      {
        response.Items = std::vector<Snapshot>{};

        for (auto const& jsonItem : jsonRoot["items"])
        {
          Snapshot vectorItem{};

          vectorItem.Name = jsonItem["name"].get<std::string>();

          if (jsonItem.contains("status") && !jsonItem["status"].is_null())
          {
            vectorItem.Status = SnapshotStatus(jsonItem["status"].get<std::string>());
          }

          for (auto const& jsonItemNested : jsonItem["filters"])
          {
            KeyValueFilter vecItem{};

            vecItem.Key = jsonItemNested["key"].get<std::string>();

            if (jsonItemNested.contains("label") && !jsonItemNested["label"].is_null())
            {
              vecItem.Label = jsonItemNested["label"].get<std::string>();
            }

            if (jsonItemNested.contains("tags"))
            {
              vecItem.Tags = std::vector<std::string>{};

              for (auto const& jsonItemNestedAgain : jsonItemNested["tags"])
              {
                std::string vi{};

                vi = jsonItemNestedAgain.get<std::string>();

                vecItem.Tags.Value().emplace_back(std::move(vi));
              }
            }

            vectorItem.Filters.emplace_back(std::move(vecItem));
          }

          if (jsonItem.contains("composition_type") && !jsonItem["composition_type"].is_null())
          {
            vectorItem.CompositionType
                = CompositionType(jsonItem["composition_type"].get<std::string>());
          }

          if (jsonItem.contains("created") && !jsonItem["created"].is_null())
          {
            vectorItem.Created = jsonItem["created"].get<std::string>();
          }

          if (jsonItem.contains("expires") && !jsonItem["expires"].is_null())
          {
            vectorItem.Expires = jsonItem["expires"].get<std::string>();
          }

          if (jsonItem.contains("retention_period") && !jsonItem["retention_period"].is_null())
          {
            vectorItem.RetentionPeriod = jsonItem["retention_period"].is_string()
                ? std::stoll(jsonItem["retention_period"].get<std::string>())
                : jsonItem["retention_period"].get<std::int64_t>();
          }

          if (jsonItem.contains("size") && !jsonItem["size"].is_null())
          {
            vectorItem.Size = jsonItem["size"].is_string()
                ? std::stoll(jsonItem["size"].get<std::string>())
                : jsonItem["size"].get<std::int64_t>();
          }

          if (jsonItem.contains("items_count") && !jsonItem["items_count"].is_null())
          {
            vectorItem.ItemsCount = jsonItem["items_count"].is_string()
                ? std::stoll(jsonItem["items_count"].get<std::string>())
                : jsonItem["items_count"].get<std::int64_t>();
          }

          if (jsonItem.contains("tags"))
          {
            vectorItem.Tags = std::map<std::string, std::string>{};

            for (auto const& kv : jsonItem["tags"].items())
            {
              std::string value{};
              value = kv.value().get<std::string>();
              vectorItem.Tags.Value().emplace(kv.key(), value);
            }
          }

          if (jsonItem.contains("etag") && !jsonItem["etag"].is_null())
          {
            vectorItem.Etag = jsonItem["etag"].get<std::string>();
          }

          response.Items.Value().emplace_back(std::move(vectorItem));
        }
      }
    }
  }

  response.SyncToken = rawResponse->GetHeaders().at("Sync-Token");
  response.ContentType
      = GetSnapshotsResponseContentType(rawResponse->GetHeaders().at("Content-Type"));

  response.RawResponse = std::move(rawResponse);

  return response;
}

Azure::Response<CheckSnapshotsResult> ConfigurationClient::CheckSnapshots(
    CheckSnapshotsOptions const& options,
    Core::Context const& context) const
{
  auto url = m_url;
  url.AppendPath("snapshots");

  url.AppendQueryParameter("api-version", Core::Url::Encode(m_apiVersion));
  url.AppendQueryParameter("After", Core::Url::Encode(options.After));
  Core::Http::Request request(Core::Http::HttpMethod::Head, url);

  request.SetHeader("Accept", "application/json");

  request.SetHeader("Sync-Token", options.SyncToken);
  request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.ToString());
  auto rawResponse = m_pipeline->Send(request, context);
  auto const httpStatusCode = rawResponse->GetStatusCode();

  if (httpStatusCode != Core::Http::HttpStatusCode::Ok)
  {
    throw Core::RequestFailedException(rawResponse);
  }

  CheckSnapshotsResult response{};
  response.SyncToken = rawResponse->GetHeaders().at("Sync-Token");

  return Response<CheckSnapshotsResult>(std::move(response), std::move(rawResponse));
}

Azure::Response<GetSnapshotResult> ConfigurationClient::GetSnapshot(
    std::string const& name,
    std::string const& accept,
    GetSnapshotOptions const& options,
    Core::Context const& context) const
{
  auto url = m_url;
  url.AppendPath("snapshots/");
  url.AppendPath(!name.empty() ? Core::Url::Encode(name) : "null");

  url.AppendQueryParameter("api-version", Core::Url::Encode(m_apiVersion));
  {
    std::string valueStr;
    for (size_t i = 0; i < options.Select.size(); ++i)
    {
      if (i != 0)
      {
        valueStr += ',';
      }

      valueStr += Core::Url::Encode(options.Select[i].ToString());
    }

    url.AppendQueryParameter("$Select", valueStr);
  }

  Core::Http::Request request(Core::Http::HttpMethod::Get, url);

  request.SetHeader("Accept", accept);
  request.SetHeader("Sync-Token", options.SyncToken);
  request.SetHeader("If-Match", options.IfMatch);
  request.SetHeader("If-None-Match", options.IfNoneMatch);
  request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.ToString());
  auto rawResponse = m_pipeline->Send(request, context);
  auto const httpStatusCode = rawResponse->GetStatusCode();

  if (httpStatusCode != Core::Http::HttpStatusCode::Ok)
  {
    throw Core::RequestFailedException(rawResponse);
  }

  GetSnapshotResult response{};
  {
    auto const& responseBody = rawResponse->GetBody();
    if (responseBody.size() > 0)
    {
      auto const jsonRoot
          = Core::Json::_internal::json::parse(responseBody.begin(), responseBody.end());

      response.Name = jsonRoot["name"].get<std::string>();

      if (jsonRoot.contains("status") && !jsonRoot["status"].is_null())
      {
        response.Status = SnapshotStatus(jsonRoot["status"].get<std::string>());
      }

      for (auto const& jsonItem : jsonRoot["filters"])
      {
        KeyValueFilter vectorItem{};

        vectorItem.Key = jsonItem["key"].get<std::string>();

        if (jsonItem.contains("label") && !jsonItem["label"].is_null())
        {
          vectorItem.Label = jsonItem["label"].get<std::string>();
        }

        if (jsonItem.contains("tags"))
        {
          vectorItem.Tags = std::vector<std::string>{};

          for (auto const& jsonItemNested : jsonItem["tags"])
          {
            std::string vecItem{};

            vecItem = jsonItemNested.get<std::string>();

            vectorItem.Tags.Value().emplace_back(std::move(vecItem));
          }
        }

        response.Filters.emplace_back(std::move(vectorItem));
      }

      if (jsonRoot.contains("composition_type") && !jsonRoot["composition_type"].is_null())
      {
        response.CompositionType = CompositionType(jsonRoot["composition_type"].get<std::string>());
      }

      if (jsonRoot.contains("created") && !jsonRoot["created"].is_null())
      {
        response.Created = jsonRoot["created"].get<std::string>();
      }

      if (jsonRoot.contains("expires") && !jsonRoot["expires"].is_null())
      {
        response.Expires = jsonRoot["expires"].get<std::string>();
      }

      if (jsonRoot.contains("retention_period") && !jsonRoot["retention_period"].is_null())
      {
        response.RetentionPeriod = jsonRoot["retention_period"].is_string()
            ? std::stoll(jsonRoot["retention_period"].get<std::string>())
            : jsonRoot["retention_period"].get<std::int64_t>();
      }

      if (jsonRoot.contains("size") && !jsonRoot["size"].is_null())
      {
        response.Size = jsonRoot["size"].is_string()
            ? std::stoll(jsonRoot["size"].get<std::string>())
            : jsonRoot["size"].get<std::int64_t>();
      }

      if (jsonRoot.contains("items_count") && !jsonRoot["items_count"].is_null())
      {
        response.ItemsCount = jsonRoot["items_count"].is_string()
            ? std::stoll(jsonRoot["items_count"].get<std::string>())
            : jsonRoot["items_count"].get<std::int64_t>();
      }

      if (jsonRoot.contains("tags"))
      {
        response.Tags = std::map<std::string, std::string>{};

        for (auto const& kv : jsonRoot["tags"].items())
        {
          std::string value{};
          value = kv.value().get<std::string>();
          response.Tags.Value().emplace(kv.key(), value);
        }
      }

      if (jsonRoot.contains("etag") && !jsonRoot["etag"].is_null())
      {
        response.Etag = jsonRoot["etag"].get<std::string>();
      }
    }
  }

  response.SyncToken = rawResponse->GetHeaders().at("Sync-Token");
  response.ETag = rawResponse->GetHeaders().at("ETag");
  response.Link = rawResponse->GetHeaders().at("Link");
  response.XMsClientRequestId
      = Core::Uuid::Parse(rawResponse->GetHeaders().at("x-ms-client-request-id"));
  response.ContentType
      = GetSnapshotResponseContentType(rawResponse->GetHeaders().at("Content-Type"));

  return Response<GetSnapshotResult>(std::move(response), std::move(rawResponse));
}

Azure::Response<OperationDetails> ConfigurationClient::GetOperationDetails(
    std::string const& snapshot,
    GetOperationDetailsOptions const& options,
    Core::Context const& context) const
{
  auto url = m_url;
  url.AppendPath("operations");

  url.AppendQueryParameter("api-version", Core::Url::Encode(m_apiVersion));
  url.AppendQueryParameter("snapshot", Core::Url::Encode(snapshot));
  Core::Http::Request request(Core::Http::HttpMethod::Get, url);

  request.SetHeader("Accept", "application/json");

  request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.ToString());
  auto rawResponse = m_pipeline->Send(request, context);
  auto const httpStatusCode = rawResponse->GetStatusCode();

  if (httpStatusCode != Core::Http::HttpStatusCode::Ok)
  {
    throw Core::RequestFailedException(rawResponse);
  }

  OperationDetails response{};
  {
    auto const& responseBody = rawResponse->GetBody();
    if (responseBody.size() > 0)
    {
      auto const jsonRoot
          = Core::Json::_internal::json::parse(responseBody.begin(), responseBody.end());

      response.Id = jsonRoot["id"].get<std::string>();

      response.Status = OperationState(jsonRoot["status"].get<std::string>());

      std::function<void(Error&, Core::Json::_internal::json const&)> const deserializeError
          = [&](Error& error, Core::Json::_internal::json const& jsonItem) {
              error.Code = jsonItem["code"].get<std::string>();

              error.Message = jsonItem["message"].get<std::string>();

              if (jsonItem.contains("target") && !jsonItem["target"].is_null())
              {
                error.Target = jsonItem["target"].get<std::string>();
              }

              if (jsonItem.contains("details"))
              {
                error.Details = std::vector<Error>{};

                for (auto const& jsonItemNested : jsonItem["details"])
                {
                  Error vectorItem{};

                  deserializeError(vectorItem, jsonItemNested);

                  error.Details.Value().emplace_back(std::move(vectorItem));
                }
              }

              std::function<void(InnerError&, Core::Json::_internal::json const&)> const
                  deserializeInnerError
                  = [&](InnerError& innerError, Core::Json::_internal::json const& jsonItem) {
                      if (jsonItem.contains("code") && !jsonItem["code"].is_null())
                      {
                        innerError.Code = jsonItem["code"].get<std::string>();
                      }

                      std::function<void(
                          std::unique_ptr<InnerError>&, Core::Json::_internal::json const&)> const
                          deserializeInnerErrorPtr
                          = [&](std::unique_ptr<InnerError>& innerError,
                                Core::Json::_internal::json const& jsonItem) {
                              if (jsonItem.contains("code") && !jsonItem["code"].is_null())
                              {
                                if (!innerError)
                                {
                                  innerError = std::make_unique<InnerError>();
                                }

                                innerError->Code = jsonItem["code"].get<std::string>();
                              }

                              deserializeInnerErrorPtr(
                                  innerError->Innererror, jsonItem["innererror"]);
                            };
                      deserializeInnerErrorPtr(innerError.Innererror, jsonItem["innererror"]);
                    };
              deserializeInnerError(error.Innererror, jsonItem["innererror"]);
            };
      deserializeError(response.Error, jsonRoot["error"]);
    }
  }

  return Response<OperationDetails>(std::move(response), std::move(rawResponse));
}

Azure::Response<CreateSnapshotResult> ConfigurationClient::CreateSnapshot(
    CreateSnapshotRequestContentType const& contentType,
    std::string const& name,
    std::string const& accept,
    Snapshot const& entity,
    CreateSnapshotOptions const& options,
    Core::Context const& context) const
{
  auto url = m_url;
  url.AppendPath("snapshots/");
  url.AppendPath(!name.empty() ? Core::Url::Encode(name) : "null");

  url.AppendQueryParameter("api-version", Core::Url::Encode(m_apiVersion));
  std::string jsonBody;
  {
    auto jsonRoot = Core::Json::_internal::json::object();

    jsonRoot["name"] = entity.Name;

    if (entity.Status.HasValue())
    {
      jsonRoot["status"] = entity.Status.Value().ToString();
    }
    for (size_t i = 0; i < entity.Filters.size(); ++i)
    {
      jsonRoot["filters"][i]["key"] = entity.Filters[i].Key;

      if (entity.Filters[i].Label.HasValue())
      {
        jsonRoot["filters"][i]["label"] = entity.Filters[i].Label.Value();
      }
      if (entity.Filters[i].Tags.HasValue())
      {
        jsonRoot["filters"][i]["tags"] = Core::Json::_internal::json::array();

        for (size_t j = 0; j < entity.Filters[i].Tags.Value().size(); ++j)
        {
          jsonRoot["filters"][i]["tags"][j] = entity.Filters[i].Tags.Value()[j];
        }
      }
    }

    if (entity.CompositionType.HasValue())
    {
      jsonRoot["composition_type"] = entity.CompositionType.Value().ToString();
    }

    if (entity.Created.HasValue())
    {
      jsonRoot["created"] = entity.Created.Value();
    }

    if (entity.Expires.HasValue())
    {
      jsonRoot["expires"] = entity.Expires.Value();
    }

    if (entity.RetentionPeriod.HasValue())
    {
      jsonRoot["retention_period"] = entity.RetentionPeriod.Value();
    }

    if (entity.Size.HasValue())
    {
      jsonRoot["size"] = entity.Size.Value();
    }

    if (entity.ItemsCount.HasValue())
    {
      jsonRoot["items_count"] = entity.ItemsCount.Value();
    }
    if (entity.Tags.HasValue())
    {
      jsonRoot["tags"] = Core::Json::_internal::json::object();

      for (auto const& kv : entity.Tags.Value())
      {
        jsonRoot["tags"][kv.first] = kv.second;
      }
    }

    if (entity.Etag.HasValue())
    {
      jsonRoot["etag"] = entity.Etag.Value();
    }

    jsonBody = jsonRoot.dump();
  }

  Core::IO::MemoryBodyStream requestBody(
      reinterpret_cast<std::uint8_t const*>(jsonBody.data()), jsonBody.length());

  Core::Http::Request request(Core::Http::HttpMethod::Put, url, &requestBody);

  request.SetHeader("Content-Type", contentType.ToString());
  request.SetHeader("Accept", accept);
  request.SetHeader("Sync-Token", options.SyncToken);
  request.SetHeader("Content-Length", std::to_string(requestBody.Length()));

  auto rawResponse = m_pipeline->Send(request, context);
  auto const httpStatusCode = rawResponse->GetStatusCode();

  if (httpStatusCode != Core::Http::HttpStatusCode::Created)
  {
    throw Core::RequestFailedException(rawResponse);
  }

  CreateSnapshotResult response{};
  {
    auto const& responseBody = rawResponse->GetBody();
    if (responseBody.size() > 0)
    {
      auto const jsonRoot
          = Core::Json::_internal::json::parse(responseBody.begin(), responseBody.end());

      response.Name = jsonRoot["name"].get<std::string>();

      if (jsonRoot.contains("status") && !jsonRoot["status"].is_null())
      {
        response.Status = SnapshotStatus(jsonRoot["status"].get<std::string>());
      }

      for (auto const& jsonItem : jsonRoot["filters"])
      {
        KeyValueFilter vectorItem{};

        vectorItem.Key = jsonItem["key"].get<std::string>();

        if (jsonItem.contains("label") && !jsonItem["label"].is_null())
        {
          vectorItem.Label = jsonItem["label"].get<std::string>();
        }

        if (jsonItem.contains("tags"))
        {
          vectorItem.Tags = std::vector<std::string>{};

          for (auto const& jsonItemNested : jsonItem["tags"])
          {
            std::string vecItem{};

            vecItem = jsonItemNested.get<std::string>();

            vectorItem.Tags.Value().emplace_back(std::move(vecItem));
          }
        }

        response.Filters.emplace_back(std::move(vectorItem));
      }

      if (jsonRoot.contains("composition_type") && !jsonRoot["composition_type"].is_null())
      {
        response.CompositionType = CompositionType(jsonRoot["composition_type"].get<std::string>());
      }

      if (jsonRoot.contains("created") && !jsonRoot["created"].is_null())
      {
        response.Created = jsonRoot["created"].get<std::string>();
      }

      if (jsonRoot.contains("expires") && !jsonRoot["expires"].is_null())
      {
        response.Expires = jsonRoot["expires"].get<std::string>();
      }

      if (jsonRoot.contains("retention_period") && !jsonRoot["retention_period"].is_null())
      {
        response.RetentionPeriod = jsonRoot["retention_period"].is_string()
            ? std::stoll(jsonRoot["retention_period"].get<std::string>())
            : jsonRoot["retention_period"].get<std::int64_t>();
      }

      if (jsonRoot.contains("size") && !jsonRoot["size"].is_null())
      {
        response.Size = jsonRoot["size"].is_string()
            ? std::stoll(jsonRoot["size"].get<std::string>())
            : jsonRoot["size"].get<std::int64_t>();
      }

      if (jsonRoot.contains("items_count") && !jsonRoot["items_count"].is_null())
      {
        response.ItemsCount = jsonRoot["items_count"].is_string()
            ? std::stoll(jsonRoot["items_count"].get<std::string>())
            : jsonRoot["items_count"].get<std::int64_t>();
      }

      if (jsonRoot.contains("tags"))
      {
        response.Tags = std::map<std::string, std::string>{};

        for (auto const& kv : jsonRoot["tags"].items())
        {
          std::string value{};
          value = kv.value().get<std::string>();
          response.Tags.Value().emplace(kv.key(), value);
        }
      }

      if (jsonRoot.contains("etag") && !jsonRoot["etag"].is_null())
      {
        response.Etag = jsonRoot["etag"].get<std::string>();
      }
    }
  }

  response.SyncToken = rawResponse->GetHeaders().at("Sync-Token");
  response.ETag = rawResponse->GetHeaders().at("ETag");
  response.Link = rawResponse->GetHeaders().at("Link");
  response.OperationLocation = Core::Url(rawResponse->GetHeaders().at("Operation-Location"));
  response.ContentType
      = CreateSnapshotResponseContentType(rawResponse->GetHeaders().at("Content-Type"));

  return Response<CreateSnapshotResult>(std::move(response), std::move(rawResponse));
}

Azure::Response<UpdateSnapshotResult> ConfigurationClient::UpdateSnapshot(
    UpdateSnapshotRequestContentType const& contentType,
    std::string const& name,
    std::string const& accept,
    SnapshotUpdateParameters const& entity,
    UpdateSnapshotOptions const& options,
    Core::Context const& context) const
{
  auto url = m_url;
  url.AppendPath("snapshots/");
  url.AppendPath(!name.empty() ? Core::Url::Encode(name) : "null");

  url.AppendQueryParameter("api-version", Core::Url::Encode(m_apiVersion));
  std::string jsonBody;
  {
    auto jsonRoot = Core::Json::_internal::json::object();

    if (entity.Status.HasValue())
    {
      jsonRoot["status"] = entity.Status.Value().ToString();
    }

    jsonBody = jsonRoot.dump();
  }

  Core::IO::MemoryBodyStream requestBody(
      reinterpret_cast<std::uint8_t const*>(jsonBody.data()), jsonBody.length());

  Core::Http::Request request(Core::Http::HttpMethod::Patch, url, &requestBody);

  request.SetHeader("Content-Type", contentType.ToString());
  request.SetHeader("Accept", accept);
  request.SetHeader("Sync-Token", options.SyncToken);
  request.SetHeader("If-Match", options.IfMatch);
  request.SetHeader("If-None-Match", options.IfNoneMatch);
  request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.ToString());
  request.SetHeader("Content-Length", std::to_string(requestBody.Length()));

  auto rawResponse = m_pipeline->Send(request, context);
  auto const httpStatusCode = rawResponse->GetStatusCode();

  if (httpStatusCode != Core::Http::HttpStatusCode::Ok)
  {
    throw Core::RequestFailedException(rawResponse);
  }

  UpdateSnapshotResult response{};
  {
    auto const& responseBody = rawResponse->GetBody();
    if (responseBody.size() > 0)
    {
      auto const jsonRoot
          = Core::Json::_internal::json::parse(responseBody.begin(), responseBody.end());

      response.Name = jsonRoot["name"].get<std::string>();

      if (jsonRoot.contains("status") && !jsonRoot["status"].is_null())
      {
        response.Status = SnapshotStatus(jsonRoot["status"].get<std::string>());
      }

      for (auto const& jsonItem : jsonRoot["filters"])
      {
        KeyValueFilter vectorItem{};

        vectorItem.Key = jsonItem["key"].get<std::string>();

        if (jsonItem.contains("label") && !jsonItem["label"].is_null())
        {
          vectorItem.Label = jsonItem["label"].get<std::string>();
        }

        if (jsonItem.contains("tags"))
        {
          vectorItem.Tags = std::vector<std::string>{};

          for (auto const& jsonItemNested : jsonItem["tags"])
          {
            std::string vecItem{};

            vecItem = jsonItemNested.get<std::string>();

            vectorItem.Tags.Value().emplace_back(std::move(vecItem));
          }
        }

        response.Filters.emplace_back(std::move(vectorItem));
      }

      if (jsonRoot.contains("composition_type") && !jsonRoot["composition_type"].is_null())
      {
        response.CompositionType = CompositionType(jsonRoot["composition_type"].get<std::string>());
      }

      if (jsonRoot.contains("created") && !jsonRoot["created"].is_null())
      {
        response.Created = jsonRoot["created"].get<std::string>();
      }

      if (jsonRoot.contains("expires") && !jsonRoot["expires"].is_null())
      {
        response.Expires = jsonRoot["expires"].get<std::string>();
      }

      if (jsonRoot.contains("retention_period") && !jsonRoot["retention_period"].is_null())
      {
        response.RetentionPeriod = jsonRoot["retention_period"].is_string()
            ? std::stoll(jsonRoot["retention_period"].get<std::string>())
            : jsonRoot["retention_period"].get<std::int64_t>();
      }

      if (jsonRoot.contains("size") && !jsonRoot["size"].is_null())
      {
        response.Size = jsonRoot["size"].is_string()
            ? std::stoll(jsonRoot["size"].get<std::string>())
            : jsonRoot["size"].get<std::int64_t>();
      }

      if (jsonRoot.contains("items_count") && !jsonRoot["items_count"].is_null())
      {
        response.ItemsCount = jsonRoot["items_count"].is_string()
            ? std::stoll(jsonRoot["items_count"].get<std::string>())
            : jsonRoot["items_count"].get<std::int64_t>();
      }

      if (jsonRoot.contains("tags"))
      {
        response.Tags = std::map<std::string, std::string>{};

        for (auto const& kv : jsonRoot["tags"].items())
        {
          std::string value{};
          value = kv.value().get<std::string>();
          response.Tags.Value().emplace(kv.key(), value);
        }
      }

      if (jsonRoot.contains("etag") && !jsonRoot["etag"].is_null())
      {
        response.Etag = jsonRoot["etag"].get<std::string>();
      }
    }
  }

  response.SyncToken = rawResponse->GetHeaders().at("Sync-Token");
  response.ETag = rawResponse->GetHeaders().at("ETag");
  response.Link = rawResponse->GetHeaders().at("Link");
  response.ContentType
      = UpdateSnapshotResponseContentType(rawResponse->GetHeaders().at("Content-Type"));

  return Response<UpdateSnapshotResult>(std::move(response), std::move(rawResponse));
}

Azure::Response<CheckSnapshotResult> ConfigurationClient::CheckSnapshot(
    std::string const& name,
    CheckSnapshotOptions const& options,
    Core::Context const& context) const
{
  auto url = m_url;
  url.AppendPath("snapshots/");
  url.AppendPath(!name.empty() ? Core::Url::Encode(name) : "null");

  url.AppendQueryParameter("api-version", Core::Url::Encode(m_apiVersion));
  Core::Http::Request request(Core::Http::HttpMethod::Head, url);

  request.SetHeader("Accept", "application/json");

  request.SetHeader("Sync-Token", options.SyncToken);
  request.SetHeader("If-Match", options.IfMatch);
  request.SetHeader("If-None-Match", options.IfNoneMatch);
  request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.ToString());
  auto rawResponse = m_pipeline->Send(request, context);
  auto const httpStatusCode = rawResponse->GetStatusCode();

  if (httpStatusCode != Core::Http::HttpStatusCode::Ok)
  {
    throw Core::RequestFailedException(rawResponse);
  }

  CheckSnapshotResult response{};
  response.SyncToken = rawResponse->GetHeaders().at("Sync-Token");
  response.ETag = rawResponse->GetHeaders().at("ETag");
  response.Link = rawResponse->GetHeaders().at("Link");

  return Response<CheckSnapshotResult>(std::move(response), std::move(rawResponse));
}

GetLabelsPagedResponse ConfigurationClient::GetLabels(
    std::string const& accept,
    GetLabelsOptions const& options,
    Core::Context const& context) const
{
  auto url = m_url;
  url.AppendPath("labels");

  url.AppendQueryParameter("api-version", Core::Url::Encode(m_apiVersion));
  url.AppendQueryParameter("name", Core::Url::Encode(options.Name));
  url.AppendQueryParameter("After", Core::Url::Encode(options.After));
  {
    std::string valueStr;
    for (size_t i = 0; i < options.Select.size(); ++i)
    {
      if (i != 0)
      {
        valueStr += ',';
      }

      valueStr += Core::Url::Encode(options.Select[i].ToString());
    }

    url.AppendQueryParameter("$Select", valueStr);
  }

  if (options.NextPageToken.find("https://") == 0 || options.NextPageToken.find("http://") == 0)
  {
    url = Core::Url(options.NextPageToken);
    url.SetPort(m_url.GetPort());
  }
  else if (!options.NextPageToken.empty())
  {
    url.SetPath(
        options.NextPageToken[0] == '/' ? options.NextPageToken.substr(1) : options.NextPageToken);
  }

  Core::Http::Request request(Core::Http::HttpMethod::Get, url);

  request.SetHeader("Accept", accept);
  request.SetHeader("Sync-Token", options.SyncToken);
  request.SetHeader("Accept-Datetime", options.AcceptDatetime);
  request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.ToString());
  auto rawResponse = m_pipeline->Send(request, context);
  auto const httpStatusCode = rawResponse->GetStatusCode();

  if (httpStatusCode != Core::Http::HttpStatusCode::Ok)
  {
    throw Core::RequestFailedException(rawResponse);
  }

  GetLabelsPagedResponse response{};
  response.m_client = std::make_shared<ConfigurationClient>(*this);
  response.m_accept = accept;
  response.m_options = options;
  {
    auto const& responseBody = rawResponse->GetBody();
    if (responseBody.size() > 0)
    {
      auto const jsonRoot
          = Core::Json::_internal::json::parse(responseBody.begin(), responseBody.end());
      if (jsonRoot.contains("@nextLink") && !jsonRoot["@nextLink"].is_null())
      {
        response.NextPageToken = jsonRoot["@nextLink"].get<std::string>();
      }

      if (jsonRoot.contains("items"))
      {
        response.Items = std::vector<Label>{};

        for (auto const& jsonItem : jsonRoot["items"])
        {
          Label vectorItem{};

          if (jsonItem.contains("name") && !jsonItem["name"].is_null())
          {
            vectorItem.Name = jsonItem["name"].get<std::string>();
          }

          response.Items.Value().emplace_back(std::move(vectorItem));
        }
      }
    }
  }

  response.SyncToken = rawResponse->GetHeaders().at("Sync-Token");
  response.ContentType = GetLabelsResponseContentType(rawResponse->GetHeaders().at("Content-Type"));

  response.RawResponse = std::move(rawResponse);

  return response;
}

Azure::Response<CheckLabelsResult> ConfigurationClient::CheckLabels(
    CheckLabelsOptions const& options,
    Core::Context const& context) const
{
  auto url = m_url;
  url.AppendPath("labels");

  url.AppendQueryParameter("api-version", Core::Url::Encode(m_apiVersion));
  url.AppendQueryParameter("name", Core::Url::Encode(options.Name));
  url.AppendQueryParameter("After", Core::Url::Encode(options.After));
  {
    std::string valueStr;
    for (size_t i = 0; i < options.Select.size(); ++i)
    {
      if (i != 0)
      {
        valueStr += ',';
      }

      valueStr += Core::Url::Encode(options.Select[i].ToString());
    }

    url.AppendQueryParameter("$Select", valueStr);
  }

  Core::Http::Request request(Core::Http::HttpMethod::Head, url);

  request.SetHeader("Accept", "application/json");

  request.SetHeader("Sync-Token", options.SyncToken);
  request.SetHeader("Accept-Datetime", options.AcceptDatetime);
  request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.ToString());
  auto rawResponse = m_pipeline->Send(request, context);
  auto const httpStatusCode = rawResponse->GetStatusCode();

  if (httpStatusCode != Core::Http::HttpStatusCode::Ok)
  {
    throw Core::RequestFailedException(rawResponse);
  }

  CheckLabelsResult response{};
  response.SyncToken = rawResponse->GetHeaders().at("Sync-Token");

  return Response<CheckLabelsResult>(std::move(response), std::move(rawResponse));
}

Azure::Response<PutLockResult> ConfigurationClient::PutLock(
    std::string const& key,
    std::string const& accept,
    PutLockOptions const& options,
    Core::Context const& context) const
{
  auto url = m_url;
  url.AppendPath("locks/");
  url.AppendPath(!key.empty() ? Core::Url::Encode(key) : "null");

  url.AppendQueryParameter("api-version", Core::Url::Encode(m_apiVersion));
  url.AppendQueryParameter("label", Core::Url::Encode(options.Label));
  Core::Http::Request request(Core::Http::HttpMethod::Put, url);

  request.SetHeader("Accept", accept);
  request.SetHeader("Sync-Token", options.SyncToken);
  request.SetHeader("If-Match", options.IfMatch);
  request.SetHeader("If-None-Match", options.IfNoneMatch);
  request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.ToString());
  auto rawResponse = m_pipeline->Send(request, context);
  auto const httpStatusCode = rawResponse->GetStatusCode();

  if (httpStatusCode != Core::Http::HttpStatusCode::Ok)
  {
    throw Core::RequestFailedException(rawResponse);
  }

  PutLockResult response{};
  {
    auto const& responseBody = rawResponse->GetBody();
    if (responseBody.size() > 0)
    {
      auto const jsonRoot
          = Core::Json::_internal::json::parse(responseBody.begin(), responseBody.end());

      response.Key = jsonRoot["key"].get<std::string>();

      if (jsonRoot.contains("label") && !jsonRoot["label"].is_null())
      {
        response.Label = jsonRoot["label"].get<std::string>();
      }

      if (jsonRoot.contains("content_type") && !jsonRoot["content_type"].is_null())
      {
        response.ContentType = jsonRoot["content_type"].get<std::string>();
      }

      if (jsonRoot.contains("value") && !jsonRoot["value"].is_null())
      {
        response.Value = jsonRoot["value"].get<std::string>();
      }

      if (jsonRoot.contains("last_modified") && !jsonRoot["last_modified"].is_null())
      {
        response.LastModified = jsonRoot["last_modified"].get<std::string>();
      }

      if (jsonRoot.contains("tags"))
      {
        response.Tags = std::map<std::string, std::string>{};

        for (auto const& kv : jsonRoot["tags"].items())
        {
          std::string value{};
          value = kv.value().get<std::string>();
          response.Tags.Value().emplace(kv.key(), value);
        }
      }

      if (jsonRoot.contains("locked") && !jsonRoot["locked"].is_null())
      {
        response.Locked = jsonRoot["locked"].is_string()
            ? (jsonRoot["locked"].get<std::string>() == "true")
            : jsonRoot["locked"].get<bool>();
      }

      if (jsonRoot.contains("etag") && !jsonRoot["etag"].is_null())
      {
        response.Etag = jsonRoot["etag"].get<std::string>();
      }
    }
  }

  response.SyncToken = rawResponse->GetHeaders().at("Sync-Token");
  response.ETag = rawResponse->GetHeaders().at("ETag");

  return Response<PutLockResult>(std::move(response), std::move(rawResponse));
}

Azure::Response<DeleteLockResult> ConfigurationClient::DeleteLock(
    std::string const& key,
    std::string const& accept,
    DeleteLockOptions const& options,
    Core::Context const& context) const
{
  auto url = m_url;
  url.AppendPath("locks/");
  url.AppendPath(!key.empty() ? Core::Url::Encode(key) : "null");

  url.AppendQueryParameter("api-version", Core::Url::Encode(m_apiVersion));
  url.AppendQueryParameter("label", Core::Url::Encode(options.Label));
  Core::Http::Request request(Core::Http::HttpMethod::Delete, url);

  request.SetHeader("Accept", accept);
  request.SetHeader("Sync-Token", options.SyncToken);
  request.SetHeader("If-Match", options.IfMatch);
  request.SetHeader("If-None-Match", options.IfNoneMatch);
  request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.ToString());
  auto rawResponse = m_pipeline->Send(request, context);
  auto const httpStatusCode = rawResponse->GetStatusCode();

  if (httpStatusCode != Core::Http::HttpStatusCode::Ok)
  {
    throw Core::RequestFailedException(rawResponse);
  }

  DeleteLockResult response{};
  {
    auto const& responseBody = rawResponse->GetBody();
    if (responseBody.size() > 0)
    {
      auto const jsonRoot
          = Core::Json::_internal::json::parse(responseBody.begin(), responseBody.end());

      response.Key = jsonRoot["key"].get<std::string>();

      if (jsonRoot.contains("label") && !jsonRoot["label"].is_null())
      {
        response.Label = jsonRoot["label"].get<std::string>();
      }

      if (jsonRoot.contains("content_type") && !jsonRoot["content_type"].is_null())
      {
        response.ContentType = jsonRoot["content_type"].get<std::string>();
      }

      if (jsonRoot.contains("value") && !jsonRoot["value"].is_null())
      {
        response.Value = jsonRoot["value"].get<std::string>();
      }

      if (jsonRoot.contains("last_modified") && !jsonRoot["last_modified"].is_null())
      {
        response.LastModified = jsonRoot["last_modified"].get<std::string>();
      }

      if (jsonRoot.contains("tags"))
      {
        response.Tags = std::map<std::string, std::string>{};

        for (auto const& kv : jsonRoot["tags"].items())
        {
          std::string value{};
          value = kv.value().get<std::string>();
          response.Tags.Value().emplace(kv.key(), value);
        }
      }

      if (jsonRoot.contains("locked") && !jsonRoot["locked"].is_null())
      {
        response.Locked = jsonRoot["locked"].is_string()
            ? (jsonRoot["locked"].get<std::string>() == "true")
            : jsonRoot["locked"].get<bool>();
      }

      if (jsonRoot.contains("etag") && !jsonRoot["etag"].is_null())
      {
        response.Etag = jsonRoot["etag"].get<std::string>();
      }
    }
  }

  response.SyncToken = rawResponse->GetHeaders().at("Sync-Token");
  response.ETag = rawResponse->GetHeaders().at("ETag");

  return Response<DeleteLockResult>(std::move(response), std::move(rawResponse));
}

GetRevisionsPagedResponse ConfigurationClient::GetRevisions(
    std::string const& accept,
    GetRevisionsOptions const& options,
    Core::Context const& context) const
{
  auto url = m_url;
  url.AppendPath("revisions");

  url.AppendQueryParameter("api-version", Core::Url::Encode(m_apiVersion));
  url.AppendQueryParameter("key", Core::Url::Encode(options.Key));
  url.AppendQueryParameter("label", Core::Url::Encode(options.Label));
  url.AppendQueryParameter("After", Core::Url::Encode(options.After));
  {
    std::string valueStr;
    for (size_t i = 0; i < options.Select.size(); ++i)
    {
      if (i != 0)
      {
        valueStr += ',';
      }

      valueStr += Core::Url::Encode(options.Select[i].ToString());
    }

    url.AppendQueryParameter("$Select", valueStr);
  }

  {
    std::string valueStr;
    for (size_t i = 0; i < options.Tags.size(); ++i)
    {
      if (i != 0)
      {
        valueStr += "&tags=";
      }

      valueStr += Core::Url::Encode(options.Tags[i]);
    }

    url.AppendQueryParameter("tags", valueStr);
  }

  if (options.NextPageToken.find("https://") == 0 || options.NextPageToken.find("http://") == 0)
  {
    url = Core::Url(options.NextPageToken);
    url.SetPort(m_url.GetPort());
  }
  else if (!options.NextPageToken.empty())
  {
    url.SetPath(
        options.NextPageToken[0] == '/' ? options.NextPageToken.substr(1) : options.NextPageToken);
  }

  Core::Http::Request request(Core::Http::HttpMethod::Get, url);

  request.SetHeader("Accept", accept);
  request.SetHeader("Sync-Token", options.SyncToken);
  request.SetHeader("Accept-Datetime", options.AcceptDatetime);
  request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.ToString());
  auto rawResponse = m_pipeline->Send(request, context);
  auto const httpStatusCode = rawResponse->GetStatusCode();

  if (httpStatusCode != Core::Http::HttpStatusCode::Ok)
  {
    throw Core::RequestFailedException(rawResponse);
  }

  GetRevisionsPagedResponse response{};
  response.m_client = std::make_shared<ConfigurationClient>(*this);
  response.m_accept = accept;
  response.m_options = options;
  {
    auto const& responseBody = rawResponse->GetBody();
    if (responseBody.size() > 0)
    {
      auto const jsonRoot
          = Core::Json::_internal::json::parse(responseBody.begin(), responseBody.end());
      if (jsonRoot.contains("@nextLink") && !jsonRoot["@nextLink"].is_null())
      {
        response.NextPageToken = jsonRoot["@nextLink"].get<std::string>();
      }

      if (jsonRoot.contains("items"))
      {
        response.Items = std::vector<KeyValue>{};

        for (auto const& jsonItem : jsonRoot["items"])
        {
          KeyValue vectorItem{};

          vectorItem.Key = jsonItem["key"].get<std::string>();

          if (jsonItem.contains("label") && !jsonItem["label"].is_null())
          {
            vectorItem.Label = jsonItem["label"].get<std::string>();
          }

          if (jsonItem.contains("content_type") && !jsonItem["content_type"].is_null())
          {
            vectorItem.ContentType = jsonItem["content_type"].get<std::string>();
          }

          if (jsonItem.contains("value") && !jsonItem["value"].is_null())
          {
            vectorItem.Value = jsonItem["value"].get<std::string>();
          }

          if (jsonItem.contains("last_modified") && !jsonItem["last_modified"].is_null())
          {
            vectorItem.LastModified = jsonItem["last_modified"].get<std::string>();
          }

          if (jsonItem.contains("tags"))
          {
            vectorItem.Tags = std::map<std::string, std::string>{};

            for (auto const& kv : jsonItem["tags"].items())
            {
              std::string value{};
              value = kv.value().get<std::string>();
              vectorItem.Tags.Value().emplace(kv.key(), value);
            }
          }

          if (jsonItem.contains("locked") && !jsonItem["locked"].is_null())
          {
            vectorItem.Locked = jsonItem["locked"].is_string()
                ? (jsonItem["locked"].get<std::string>() == "true")
                : jsonItem["locked"].get<bool>();
          }

          if (jsonItem.contains("etag") && !jsonItem["etag"].is_null())
          {
            vectorItem.Etag = jsonItem["etag"].get<std::string>();
          }

          response.Items.Value().emplace_back(std::move(vectorItem));
        }
      }
      if (jsonRoot.contains("etag") && !jsonRoot["etag"].is_null())
      {
        response.Etag = jsonRoot["etag"].get<std::string>();
      }
    }
  }

  response.SyncToken = rawResponse->GetHeaders().at("Sync-Token");
  response.ETag = rawResponse->GetHeaders().at("ETag");
  response.ContentType
      = GetRevisionsResponseContentType(rawResponse->GetHeaders().at("Content-Type"));

  response.RawResponse = std::move(rawResponse);

  return response;
}

Azure::Response<CheckRevisionsResult> ConfigurationClient::CheckRevisions(
    CheckRevisionsOptions const& options,
    Core::Context const& context) const
{
  auto url = m_url;
  url.AppendPath("revisions");

  url.AppendQueryParameter("api-version", Core::Url::Encode(m_apiVersion));
  url.AppendQueryParameter("key", Core::Url::Encode(options.Key));
  url.AppendQueryParameter("label", Core::Url::Encode(options.Label));
  url.AppendQueryParameter("After", Core::Url::Encode(options.After));
  {
    std::string valueStr;
    for (size_t i = 0; i < options.Select.size(); ++i)
    {
      if (i != 0)
      {
        valueStr += ',';
      }

      valueStr += Core::Url::Encode(options.Select[i].ToString());
    }

    url.AppendQueryParameter("$Select", valueStr);
  }

  {
    std::string valueStr;
    for (size_t i = 0; i < options.Tags.size(); ++i)
    {
      if (i != 0)
      {
        valueStr += "&tags=";
      }

      valueStr += Core::Url::Encode(options.Tags[i]);
    }

    url.AppendQueryParameter("tags", valueStr);
  }

  Core::Http::Request request(Core::Http::HttpMethod::Head, url);

  request.SetHeader("Accept", "application/json");

  request.SetHeader("Sync-Token", options.SyncToken);
  request.SetHeader("Accept-Datetime", options.AcceptDatetime);
  request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.ToString());
  auto rawResponse = m_pipeline->Send(request, context);
  auto const httpStatusCode = rawResponse->GetStatusCode();

  if (httpStatusCode != Core::Http::HttpStatusCode::Ok)
  {
    throw Core::RequestFailedException(rawResponse);
  }

  CheckRevisionsResult response{};
  response.SyncToken = rawResponse->GetHeaders().at("Sync-Token");
  response.ETag = rawResponse->GetHeaders().at("ETag");

  return Response<CheckRevisionsResult>(std::move(response), std::move(rawResponse));
}
