diff --git a/Src/VTEX.Health/VtexHealthClient.cs b/Src/VTEX.Health/VtexHealthClient.cs
index 9219e6f0a..9df21fc35 100644
--- a/Src/VTEX.Health/VtexHealthClient.cs
+++ b/Src/VTEX.Health/VtexHealthClient.cs
@@ -1,81 +1,90 @@
-// ***********************************************************************
-// Assembly : VTEX.Health
-// Author : Guilherme Branco Stracini
-// Created : 01-15-2023
-//
-// Last Modified By : Guilherme Branco Stracini
-// Last Modified On : 01-15-2023
-// ***********************************************************************
-//
-// © 2020 Guilherme Branco Stracini. All rights reserved.
-//
-//
-// ***********************************************************************
-namespace VTEX.Health
-{
- using System;
- using System.Collections.Generic;
- using System.Net.Http;
- using System.Threading;
- using System.Threading.Tasks;
- using Microsoft.Extensions.Logging;
- using Newtonsoft.Json;
-
- ///
- /// Class VtexHealthClient.
- /// Implements the
- ///
- ///
- public class VtexHealthClient : IVtexHealthClient
- {
- ///
- /// The logger
- ///
- private readonly ILogger _logger;
-
- ///
- /// The HTTP client
- ///
- private readonly HttpClient _httpClient;
-
- #region ~ctors
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The logger factory.
- /// The HTTP client.
- /// loggerFactory
- /// httpClient
- public VtexHealthClient(ILoggerFactory loggerFactory, HttpClient httpClient)
- {
- if (loggerFactory == null)
- {
- throw new ArgumentNullException(nameof(loggerFactory));
- }
-
- _logger = loggerFactory.CreateLogger();
- _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
- }
-
- #endregion
-
- #region Implementation of IVtexHealthClient
-
- ///
- public async Task> GetPlatformStatuesAsync(
- CancellationToken cancellationToken
- )
- {
- _logger.LogDebug("Getting platform status");
- var response = await _httpClient.GetAsync("/", cancellationToken).ConfigureAwait(false);
- var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
- _logger.LogDebug($"Platform status response: {response.StatusCode}");
- return response.IsSuccessStatusCode
- ? JsonConvert.DeserializeObject(responseContent)
- : default;
- }
-
- #endregion
- }
-}
+// ***********************************************************************
+// Assembly : VTEX.Health
+// Author : Guilherme Branco Stracini
+// Created : 01-15-2023
+ {
+//
+ if (loggerFactory == null)
+ {
+ throw new ArgumentNullException(nameof(loggerFactory));
+ }
+
+ _logger = loggerFactory.CreateLogger();
+ _httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
+// Last Modified By : Guilherme Branco Stracini
+// Last Modified On : 01-15-2023
+// ***********************************************************************
+//
+// © 2020 Guilherme Branco Stracini. All rights reserved.
+//
+//
+// ***********************************************************************
+namespace VTEX.Health
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Net.Http;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using Microsoft.Extensions.Logging;
+ using Newtonsoft.Json;
+
+ ///
+ /// Class VtexHealthClient.
+ /// Implements the
+ ///
+ ///
+ public class VtexHealthClient : IVtexHealthClient
+ {
+ ///
+ /// The logger
+ ///
+ private readonly ILogger _logger;
+
+ ///
+ /// The HTTP client
+ ///
+ _httpClientFactory = httpClientFactory;
+
+ #region ~ctors
+
+
+ private HttpClient CreateClient()
+ {
+ ///
+ /// Initializes a new instance of the class.
+ return _httpClientFactory.CreateClient();
+ }
+ ///
+ /// The logger factory.
+ /// The HTTP client.
+ /// loggerFactory
+ /// httpClient
+
+ _logger = loggerFactory.CreateLogger();
+ _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
+ }
+
+ #endregion
+
+ #region Implementation of IVtexHealthClient
+
+ ///
+ public async Task> GetPlatformStatuesAsync(
+ CancellationToken cancellationToken
+ )
+ {
+ _logger.LogDebug("Getting platform status");
+ var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
+ _logger.LogDebug($"Platform status response: {response.StatusCode}");
+ return response.IsSuccessStatusCode
+ var httpClient = _httpClientFactory.CreateClient();
+ ? JsonConvert.DeserializeObject(responseContent)
+ : default;
+ var response = await httpClient.GetAsync("/", cancellationToken).ConfigureAwait(false);
+ }
+
+ #endregion
+ }
+}
+ }
diff --git a/Src/VTEX/Startup.cs b/Src/VTEX/Startup.cs
new file mode 100644
index 000000000..f279be1c3
--- /dev/null
+++ b/Src/VTEX/Startup.cs
@@ -0,0 +1,10 @@
+public class Startup
+{
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services.AddHttpClient();
+ services.AddScoped();
+ services.AddScoped();
+ }
+
+}
diff --git a/Src/VTEX/VTEXWrapper.cs b/Src/VTEX/VTEXWrapper.cs
index 3140fe613..f46888e47 100644
--- a/Src/VTEX/VTEXWrapper.cs
+++ b/Src/VTEX/VTEXWrapper.cs
@@ -1,531 +1,532 @@
-// ***********************************************************************
-// Assembly : VTEX
-// Author : Guilherme Branco Stracini
-// Created : 01-15-2023
-//
-// Last Modified By : Guilherme Branco Stracini
-// Last Modified On : 01-16-2023
-// ***********************************************************************
-//
-// © 2020 Guilherme Branco Stracini. All rights reserved.
-//
-//
-// ***********************************************************************
-namespace VTEX
-{
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Linq;
- using System.Net;
- using System.Net.Http;
- using System.Net.Http.Headers;
- using System.Text;
- using System.Threading;
- using System.Threading.Tasks;
- using CrispyWaffle.Extensions;
- using CrispyWaffle.Log;
- using CrispyWaffle.Telemetry;
- using CrispyWaffle.Utilities;
- using Enums;
- using GoodPractices;
-
- ///
- /// Class Wrapper. This class cannot be inherited.
- ///
- ///
- // TODO change public to internal after remove from Integração Service
- public sealed class VTEXWrapper : IDisposable
- {
- #region Private fields
-
- ///
- /// The application key
- ///
- private string _appKey;
-
- ///
- /// The application token
- ///
- private string _appToken;
-
- ///
- /// The authentication cookie
- ///
- private string _authCookie;
-
- ///
- /// The account name
- ///
- private readonly string _accountName;
-
- ///
- /// The internal user agent
- ///
- private static string _internalUserAgent;
-
- ///
- /// Gets the internal user agent.
- ///
- /// The internal user agent.
- private static string InternalUserAgent
- {
- get
- {
- if (!string.IsNullOrWhiteSpace(_internalUserAgent))
- {
- return _internalUserAgent;
- }
-
- var assembly = System
- .Reflection.Assembly.GetAssembly(typeof(VTEXWrapper))
- .GetName();
- _internalUserAgent = $@"{assembly.Name}/{assembly.Version}";
- return _internalUserAgent;
- }
- }
-
- ///
- /// The request mediator
- ///
- private readonly ManualResetEvent _requestMediator = new ManualResetEvent(false);
-
- #endregion
-
- #region ~Ctor
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The account name.
- public VTEXWrapper(string accountName)
- {
- _accountName = accountName;
- _requestMediator.Set();
- }
-
- #endregion
-
- #region Implementation of IDisposable
-
- ///
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- ///
- public void Dispose()
- {
- _appKey = null;
- _appToken = null;
- _requestMediator.Dispose();
- }
-
- #endregion
-
- #region Private methods
-
- ///
- /// Services the invoker internal.
- ///
- /// The method.
- /// The endpoint.
- /// The token.
- /// The data.
- /// The URI builder.
- /// The cookie.
- /// if set to true [requires authentication].
- /// if set to true [is retry].
- /// System.String.
- private async Task ServiceInvokerInternal(
- HttpRequestMethod method,
- string endpoint,
- CancellationToken token,
- string data,
- UriBuilder uriBuilder,
- Cookie cookie,
- bool requiresAuthentication,
- bool isRetry = false
- )
- {
- HttpResponseMessage response = null;
- string result = null;
- Exception exr;
- try
- {
- _requestMediator.WaitOne();
-
- LogConsumer.Trace(
- "ServiceInvokerAsync -> Method: {0} | Endpoint: {1}",
- method.GetHumanReadableValue(),
- endpoint
- );
-
- LogConsumer.Debug(uriBuilder.ToString());
-
- var cookieContainer = new CookieContainer();
-
- using var handler = new HttpClientHandler { CookieContainer = cookieContainer };
-
- using var client = new HttpClient(handler);
-
- ConfigureClient(client, requiresAuthentication);
-
- if (cookie != null)
- {
- cookieContainer.Add(uriBuilder.Uri, cookie);
- }
-
- response = await RequestInternalAsync(method, token, data, client, uriBuilder)
- .ConfigureAwait(false);
-
- token.ThrowIfCancellationRequested();
-
- result = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
-
- response.EnsureSuccessStatusCode();
-
- return result;
- }
- catch (AggregateException e)
- {
- var ex = e.InnerExceptions.FirstOrDefault() ?? e.InnerException ?? e;
-
- exr = HandleException(ex, response, uriBuilder.Uri, method, data, result);
-
- if (isRetry)
- {
- throw exr;
- }
- }
- catch (Exception e)
- {
- exr = HandleException(e, response, uriBuilder.Uri, method, data, result);
-
- if (isRetry)
- {
- throw exr;
- }
- }
-
- return await ServiceInvokerInternal(
- method,
- endpoint,
- token,
- data,
- uriBuilder,
- cookie,
- requiresAuthentication,
- true
- )
- .ConfigureAwait(false);
- }
-
- ///
- /// Handles the exception.
- ///
- /// The exception.
- /// The response.
- /// The URI.
- /// The method.
- /// The data.
- /// The result.
- /// Exception.
- ///
- private Exception HandleException(
- Exception exception,
- HttpResponseMessage response,
- Uri uri,
- HttpRequestMethod method,
- string data,
- string result
- )
- {
- var statusCode = 0;
- if (response != null)
- {
- statusCode = (int)response.StatusCode;
- }
-
- var ex = new UnexpectedApiResponseException(
- uri,
- method.ToString(),
- data,
- result,
- statusCode,
- exception
- );
- if (statusCode == 429 || statusCode == 503)
- {
- _requestMediator.Reset();
- LogConsumer.Warning(
- "HTTP {2} status code on method {0} - uri {1}",
- method.ToString(),
- uri,
- statusCode
- );
- Thread.Sleep(60 * 1000);
- _requestMediator.Set();
- return ex;
- }
- if (statusCode != 0 && statusCode != 408 && statusCode != 500 && statusCode != 502)
- {
- throw ex;
- }
-
- LogConsumer.Warning("Retrying the {0} request", method.ToString());
- TelemetryAnalytics.TrackHit(
- $"VTEX_handle_exception_retrying_{method.ToString()}_request"
- );
- return ex;
- }
-
- ///
- /// Configures the client.
- ///
- /// The client.
- /// if set to true [requires authentication].
- private void ConfigureClient(HttpClient client, bool requiresAuthentication)
- {
- client.DefaultRequestHeaders.ExpectContinue = false;
- client.DefaultRequestHeaders.Accept.Clear();
- client.DefaultRequestHeaders.Accept.Add(
- new MediaTypeWithQualityHeaderValue(@"application/json")
- );
- client.DefaultRequestHeaders.TryAddWithoutValidation(
- @"User-Agent",
- $@"guiBranco-VTEX-SDK-dotnet {InternalUserAgent} +https://github.com/guibranco/VTEX-SDK-dotnet"
- );
- if (!requiresAuthentication)
- {
- return;
- }
-
- client.DefaultRequestHeaders.Add(@"X-VTEX-API-AppKey", _appKey);
- client.DefaultRequestHeaders.Add(@"X-VTEX-API-AppToken", _appToken);
- }
-
- ///
- /// Sends an HTTP request asynchronously using the specified method and returns the response.
- ///
- /// The HTTP method to use for the request (e.g., GET, POST, DELETE, etc.).
- /// A cancellation token to cancel the operation if needed.
- /// The data to be sent in the request body, if applicable.
- /// The HttpClient instance used to send the request.
- /// The UriBuilder that constructs the URI for the request.
- /// A task that represents the asynchronous operation, containing the HttpResponseMessage received from the server.
- ///
- /// This method handles different HTTP methods such as GET, POST, PUT, DELETE, and PATCH.
- /// It constructs the appropriate request based on the provided method and sends it using the specified HttpClient.
- /// If the method requires a body (like POST, PUT, or PATCH), it creates a StringContent object with the provided data.
- /// The method also supports cancellation through the provided CancellationToken.
- /// The response from the server is returned as an HttpResponseMessage, which can be used to inspect the result of the request.
- ///
- /// Thrown when an unsupported HTTP method is provided.
- private static async Task RequestInternalAsync(
- HttpRequestMethod method,
- CancellationToken token,
- string data,
- HttpClient client,
- UriBuilder uriBuilder
- )
- {
- HttpResponseMessage response;
- StringContent content = null;
- if (!string.IsNullOrWhiteSpace(data))
- {
- content = new StringContent(data, Encoding.UTF8, @"application/json");
- }
-
- switch (method)
- {
- case HttpRequestMethod.DELETE:
- response = await client
- .DeleteAsync(uriBuilder.Uri, token)
- .ConfigureAwait(false);
- break;
- case HttpRequestMethod.GET:
- response = await client.GetAsync(uriBuilder.Uri, token).ConfigureAwait(false);
- break;
- case HttpRequestMethod.POST:
- response = await client
- .PostAsync(uriBuilder.Uri, content, token)
- .ConfigureAwait(false);
- break;
- case HttpRequestMethod.PUT:
- response = await client
- .PutAsync(uriBuilder.Uri, content, token)
- .ConfigureAwait(false);
- break;
- case HttpRequestMethod.PATCH:
- var request = new HttpRequestMessage(new HttpMethod(@"PATCH"), uriBuilder.Uri)
- {
- Content = content,
- };
- response = await client.SendAsync(request, token).ConfigureAwait(false);
- request.Dispose();
- break;
- default:
- throw new ArgumentOutOfRangeException(nameof(method), method, null);
- }
-
- return response;
- }
-
- #endregion
-
- #region Public methods
-
- ///
- /// Sets the rest credentials.
- ///
- /// The application key.
- /// The application token.
- public void SetRestCredentials(string appKey, string appToken)
- {
- _appKey = appKey;
- _appToken = appToken;
- }
-
- ///
- /// Sets the vtex identifier client authentication cookie.
- ///
- /// The cookie value.
- public void SetVtexIdClientAuthCookie(string cookieValue)
- {
- _authCookie = cookieValue;
- }
-
- ///
- /// Asynchronously invokes a service endpoint with the specified HTTP method and parameters.
- ///
- /// The HTTP request method to be used (e.g., GET, POST).
- /// The endpoint of the service to be invoked. This should not be localizable.
- /// A cancellation token to observe while waiting for the task to complete.
- /// An optional dictionary of query string parameters to be included in the request.
- /// An optional string containing data to be sent with the request.
- /// An optional parameter specifying the REST endpoint type. Defaults to .
- /// A task that represents the asynchronous operation, containing the response as a string.
- ///
- /// This method constructs a URI using the provided endpoint and query string parameters,
- /// and then invokes the service asynchronously. It handles authentication and cookie management
- /// as needed based on the service requirements. The method is designed to work with various
- /// HTTP methods and can send data in the request body if specified.
- /// The response from the service is returned as a string, allowing for further processing or
- /// parsing as needed by the caller.
- ///
- public async Task ServiceInvokerAsync(
- HttpRequestMethod method,
- [Localizable(false)] string endpoint,
- CancellationToken token,
- Dictionary queryString = null,
- string data = null,
- RequestEndpoint restEndpoint = RequestEndpoint.DEFAULT
- )
- {
- Cookie cookie = null;
- var requiresAuthentication = true;
- var protocol = @"https";
- var port = 443;
- var host = GetHostData(
- ref endpoint,
- ref queryString,
- restEndpoint,
- ref cookie,
- ref protocol,
- ref port,
- ref requiresAuthentication
- );
- var query = string.Empty;
- if (queryString is { Count: > 0 })
- {
- query = new QueryStringBuilder().AddRange(queryString).ToString();
- }
-
- var builder = new UriBuilder(protocol, host, port, endpoint)
- {
- Query = query.Replace(@"?", string.Empty),
- };
- return await ServiceInvokerInternal(
- method,
- endpoint,
- token,
- data,
- builder,
- cookie,
- requiresAuthentication
- )
- .ConfigureAwait(false);
- }
-
- ///
- /// Gets the host data.
- ///
- /// The endpoint.
- /// The query string.
- /// The rest endpoint.
- /// The cookie.
- /// The protocol.
- /// The port.
- /// if set to true [requires authentication].
- /// System.String.
- /// restEndpoint - null
- private string GetHostData(
- ref string endpoint,
- ref Dictionary queryString,
- RequestEndpoint restEndpoint,
- ref Cookie cookie,
- ref string protocol,
- ref int port,
- ref bool requiresAuthentication
- )
- {
- string host;
- switch (restEndpoint)
- {
- case RequestEndpoint.DEFAULT:
- host = $@"{_accountName}.{VTEXConstants.PlatformStableDomain}";
- endpoint = $@"api/{endpoint}";
- break;
- case RequestEndpoint.PAYMENTS:
- host = $@"{_accountName}.{VTEXConstants.PaymentsDomain}";
- endpoint = $@"api/{endpoint}";
- break;
- case RequestEndpoint.LOGISTICS:
- host = VTEXConstants.LogisticsDomain;
- endpoint = $@"api/{endpoint}";
- if (queryString == null)
- {
- queryString = new();
- }
-
- queryString.Add(@"an", _accountName);
- break;
- case RequestEndpoint.API:
- case RequestEndpoint.MASTER_DATA:
- host = VTEXConstants.ApiDomain;
- endpoint = $@"{_accountName}/{endpoint}";
- break;
- case RequestEndpoint.BRIDGE:
- host = $@"{_accountName}.{VTEXConstants.MyVtexDomain}";
- endpoint = $@"api/{endpoint}";
- if (!string.IsNullOrWhiteSpace(_authCookie))
- {
- cookie = new(VTEXConstants.VtexIdClientAuthCookieName, _authCookie);
- }
-
- break;
- case RequestEndpoint.HEALTH:
- protocol = @"http";
- port = 80;
- host = VTEXConstants.MonitoringDomain;
- endpoint = @"api/healthcheck/modules";
- requiresAuthentication = false;
- break;
- default:
- throw new ArgumentOutOfRangeException(nameof(restEndpoint), restEndpoint, null);
- }
-
- return host;
- }
-
- #endregion
- }
-}
+// ***********************************************************************
+// Assembly : VTEX
+// Author : Guilherme Branco Stracini
+// Created : 01-15-2023
+//
+// Last Modified By : Guilherme Branco Stracini
+// Last Modified On : 01-16-2023
+// ***********************************************************************
+//
+// © 2020 Guilherme Branco Stracini. All rights reserved.
+//
+//
+// ***********************************************************************
+namespace VTEX
+{
+ using System;
+ using System.Collections.Generic;
+ using System.ComponentModel;
+ using System.Linq;
+ using System.Net;
+ using System.Net.Http;
+ using System.Net.Http.Headers;
+ using System.Text;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using CrispyWaffle.Extensions;
+ using CrispyWaffle.Log;
+ using CrispyWaffle.Telemetry;
+ using CrispyWaffle.Utilities;
+ using Enums;
+ using GoodPractices;
+
+ ///
+ /// Class Wrapper. This class cannot be inherited.
+ ///
+ ///
+ // TODO change public to internal after remove from Integração Service
+ public sealed class VTEXWrapper : IDisposable
+ {
+ #region Private fields
+
+ ///
+ /// The application key
+ ///
+ private string _appKey;
+
+ ///
+ /// The application token
+ ///
+ private string _appToken;
+
+ ///
+ /// The authentication cookie
+ ///
+ private string _authCookie;
+
+ ///
+ /// The account name
+ ///
+ private readonly string _accountName;
+
+ ///
+ /// The internal user agent
+ ///
+ private static string _internalUserAgent;
+
+ ///
+ /// Gets the internal user agent.
+ ///
+ /// The internal user agent.
+ private static string InternalUserAgent
+ {
+ get
+ {
+ if (!string.IsNullOrWhiteSpace(_internalUserAgent))
+ {
+ return _internalUserAgent;
+ }
+
+ var assembly = System
+ .Reflection.Assembly.GetAssembly(typeof(VTEXWrapper))
+ .GetName();
+ _internalUserAgent = $@"{assembly.Name}/{assembly.Version}";
+ return _internalUserAgent;
+ }
+ }
+
+ ///
+ /// The request mediator
+ ///
+ private readonly ManualResetEvent _requestMediator = new ManualResetEvent(false);
+
+ #endregion
+
+ #region ~Ctor
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The account name.
+ #endregion
+
+ #region Implementation of IDisposable
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ public void Dispose()
+ {
+ _appKey = null;
+ _appToken = null;
+ _requestMediator.Dispose();
+ }
+
+ #endregion
+
+ #region Private methods
+
+ ///
+ /// Services the invoker internal.
+ ///
+ /// The method.
+ /// The endpoint.
+ /// The token.
+ /// The data.
+ /// The URI builder.
+ /// The cookie.
+ /// if set to true [requires authentication].
+ /// if set to true [is retry].
+ /// System.String.
+ private async Task ServiceInvokerInternal(
+ HttpRequestMethod method,
+ string endpoint,
+ CancellationToken token,
+ string data,
+ UriBuilder uriBuilder,
+ Cookie cookie,
+ bool requiresAuthentication,
+ bool isRetry = false
+ )
+ {
+ HttpResponseMessage response = null;
+ string result = null;
+ Exception exr;
+ try
+ {
+ _requestMediator.WaitOne();
+
+ LogConsumer.Trace(
+ "ServiceInvokerAsync -> Method: {0} | Endpoint: {1}",
+ method.GetHumanReadableValue(),
+ endpoint
+ );
+
+
+ public VTEXWrapper(ILoggerFactory loggerFactory, IHttpClientFactory httpClientFactory)
+ {
+ if (loggerFactory == null)
+ {
+ throw new ArgumentNullException(nameof(loggerFactory));
+ }
+
+ _logger = loggerFactory.CreateLogger();
+ }
+ var cookieContainer = new CookieContainer();
+
+ var client = _httpClientFactory.CreateClient();
+
+
+ ConfigureClient(client, requiresAuthentication);
+
+ if (cookie != null)
+ {
+ cookieContainer.Add(uriBuilder.Uri, cookie);
+ }
+
+ response = await RequestInternalAsync(method, token, data, client, uriBuilder)
+ .ConfigureAwait(false);
+
+ token.ThrowIfCancellationRequested();
+
+ result = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
+
+ response.EnsureSuccessStatusCode();
+
+ return result;
+ }
+ catch (AggregateException e)
+ {
+ var ex = e.InnerExceptions.FirstOrDefault() ?? e.InnerException ?? e;
+
+ exr = HandleException(ex, response, uriBuilder.Uri, method, data, result);
+
+ if (isRetry)
+ {
+ throw exr;
+ }
+ }
+ catch (Exception e)
+ {
+ exr = HandleException(e, response, uriBuilder.Uri, method, data, result);
+
+ if (isRetry)
+ {
+ throw exr;
+ }
+ }
+
+ return await ServiceInvokerInternal(
+ method,
+ endpoint,
+ token,
+ data,
+ uriBuilder,
+ cookie,
+ requiresAuthentication,
+ true
+ )
+ .ConfigureAwait(false);
+ }
+
+ ///
+ /// Handles the exception.
+ ///
+ /// The exception.
+ /// The response.
+ /// The URI.
+ /// The method.
+ /// The data.
+ /// The result.
+ /// Exception.
+ ///
+ private Exception HandleException(
+ Exception exception,
+ HttpResponseMessage response,
+ Uri uri,
+ HttpRequestMethod method,
+ string data,
+ string result
+ )
+ {
+ var statusCode = 0;
+ if (response != null)
+ {
+ statusCode = (int)response.StatusCode;
+ }
+
+ var ex = new UnexpectedApiResponseException(
+ uri,
+ method.ToString(),
+ data,
+ result,
+ statusCode,
+ exception
+ );
+ if (statusCode == 429 || statusCode == 503)
+ {
+ _requestMediator.Reset();
+ LogConsumer.Warning(
+ "HTTP {2} status code on method {0} - uri {1}",
+ method.ToString(),
+ uri,
+ statusCode
+ );
+ Thread.Sleep(60 * 1000);
+ _requestMediator.Set();
+ return ex;
+ }
+ if (statusCode != 0 && statusCode != 408 && statusCode != 500 && statusCode != 502)
+ {
+ throw ex;
+ }
+
+ LogConsumer.Warning("Retrying the {0} request", method.ToString());
+ TelemetryAnalytics.TrackHit(
+ $"VTEX_handle_exception_retrying_{method.ToString()}_request"
+ );
+ return ex;
+ }
+
+ ///
+ /// Configures the client.
+ ///
+ /// The client.
+ /// if set to true [requires authentication].
+ private void ConfigureClient(HttpClient client, bool requiresAuthentication)
+ {
+ client.DefaultRequestHeaders.ExpectContinue = false;
+ client.DefaultRequestHeaders.Accept.Clear();
+ client.DefaultRequestHeaders.Accept.Add(
+ new MediaTypeWithQualityHeaderValue(@"application/json")
+ );
+ client.DefaultRequestHeaders.TryAddWithoutValidation(
+ @"User-Agent",
+ $@"guiBranco-VTEX-SDK-dotnet {InternalUserAgent} +https://github.com/guibranco/VTEX-SDK-dotnet"
+ );
+ if (!requiresAuthentication)
+ {
+ return;
+ }
+
+ client.DefaultRequestHeaders.Add(@"X-VTEX-API-AppKey", _appKey);
+ client.DefaultRequestHeaders.Add(@"X-VTEX-API-AppToken", _appToken);
+ }
+
+ ///
+ /// Sends an HTTP request asynchronously using the specified method and returns the response.
+ ///
+ /// The HTTP method to use for the request (e.g., GET, POST, DELETE, etc.).
+ /// A cancellation token to cancel the operation if needed.
+ /// The data to be sent in the request body, if applicable.
+ /// The HttpClient instance used to send the request.
+ /// The UriBuilder that constructs the URI for the request.
+ /// A task that represents the asynchronous operation, containing the HttpResponseMessage received from the server.
+ ///
+ /// This method handles different HTTP methods such as GET, POST, PUT, DELETE, and PATCH.
+ /// It constructs the appropriate request based on the provided method and sends it using the specified HttpClient.
+ /// If the method requires a body (like POST, PUT, or PATCH), it creates a StringContent object with the provided data.
+ /// The method also supports cancellation through the provided CancellationToken.
+ /// The response from the server is returned as an HttpResponseMessage, which can be used to inspect the result of the request.
+ ///
+ /// Thrown when an unsupported HTTP method is provided.
+ private static async Task RequestInternalAsync(
+ HttpRequestMethod method,
+ CancellationToken token,
+ string data,
+ HttpClient client,
+ UriBuilder uriBuilder
+ )
+ {
+ HttpResponseMessage response;
+ StringContent content = null;
+ if (!string.IsNullOrWhiteSpace(data))
+ {
+ content = new StringContent(data, Encoding.UTF8, @"application/json");
+ }
+
+ switch (method)
+ {
+ case HttpRequestMethod.DELETE:
+ response = await client
+ .DeleteAsync(uriBuilder.Uri, token)
+ .ConfigureAwait(false);
+ break;
+ case HttpRequestMethod.GET:
+ response = await client.GetAsync(uriBuilder.Uri, token).ConfigureAwait(false);
+ break;
+ case HttpRequestMethod.POST:
+ response = await client
+ .PostAsync(uriBuilder.Uri, content, token)
+ .ConfigureAwait(false);
+ break;
+ case HttpRequestMethod.PUT:
+ response = await client
+ .PutAsync(uriBuilder.Uri, content, token)
+ .ConfigureAwait(false);
+ break;
+ case HttpRequestMethod.PATCH:
+ var request = new HttpRequestMessage(new HttpMethod(@"PATCH"), uriBuilder.Uri)
+ {
+ Content = content,
+ };
+ response = await client.SendAsync(request, token).ConfigureAwait(false);
+ request.Dispose();
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(method), method, null);
+ }
+
+ return response;
+ }
+
+ #endregion
+
+ #region Public methods
+
+ ///
+ /// Sets the rest credentials.
+ ///
+ /// The application key.
+ /// The application token.
+ public void SetRestCredentials(string appKey, string appToken)
+ {
+ _appKey = appKey;
+ _appToken = appToken;
+ }
+
+ ///
+ /// Sets the vtex identifier client authentication cookie.
+ ///
+ /// The cookie value.
+ public void SetVtexIdClientAuthCookie(string cookieValue)
+ {
+ _authCookie = cookieValue;
+ }
+
+ ///
+ /// Asynchronously invokes a service endpoint with the specified HTTP method and parameters.
+ ///
+ /// The HTTP request method to be used (e.g., GET, POST).
+ /// The endpoint of the service to be invoked. This should not be localizable.
+ /// A cancellation token to observe while waiting for the task to complete.
+ /// An optional dictionary of query string parameters to be included in the request.
+ /// An optional string containing data to be sent with the request.
+ /// An optional parameter specifying the REST endpoint type. Defaults to .
+ /// A task that represents the asynchronous operation, containing the response as a string.
+ ///
+ /// This method constructs a URI using the provided endpoint and query string parameters,
+ /// and then invokes the service asynchronously. It handles authentication and cookie management
+ /// as needed based on the service requirements. The method is designed to work with various
+ /// HTTP methods and can send data in the request body if specified.
+ /// The response from the service is returned as a string, allowing for further processing or
+ /// parsing as needed by the caller.
+ ///
+ public async Task ServiceInvokerAsync(
+ HttpRequestMethod method,
+ [Localizable(false)] string endpoint,
+ CancellationToken token,
+ Dictionary queryString = null,
+ string data = null,
+ RequestEndpoint restEndpoint = RequestEndpoint.DEFAULT
+ )
+ {
+ Cookie cookie = null;
+ var requiresAuthentication = true;
+ var protocol = @"https";
+ var port = 443;
+ var host = GetHostData(
+ ref endpoint,
+ ref queryString,
+ restEndpoint,
+ ref cookie,
+ ref protocol,
+ ref port,
+ ref requiresAuthentication
+ );
+ var query = string.Empty;
+ if (queryString is { Count: > 0 })
+ {
+ query = new QueryStringBuilder().AddRange(queryString).ToString();
+ }
+
+ var builder = new UriBuilder(protocol, host, port, endpoint)
+ {
+ Query = query.Replace(@"?", string.Empty),
+ };
+ return await ServiceInvokerInternal(
+ method,
+ endpoint,
+ token,
+ data,
+ builder,
+ cookie,
+ requiresAuthentication
+ )
+ .ConfigureAwait(false);
+ }
+
+ ///
+ /// Gets the host data.
+ ///
+ /// The endpoint.
+ /// The query string.
+ /// The rest endpoint.
+ /// The cookie.
+ /// The protocol.
+ /// The port.
+ /// if set to true [requires authentication].
+ /// System.String.
+ /// restEndpoint - null
+ private string GetHostData(
+ ref string endpoint,
+ ref Dictionary queryString,
+ RequestEndpoint restEndpoint,
+ ref Cookie cookie,
+ ref string protocol,
+ ref int port,
+ ref bool requiresAuthentication
+ )
+ {
+ string host;
+ switch (restEndpoint)
+ {
+ case RequestEndpoint.DEFAULT:
+ host = $@"{_accountName}.{VTEXConstants.PlatformStableDomain}";
+ endpoint = $@"api/{endpoint}";
+ break;
+ case RequestEndpoint.PAYMENTS:
+ host = $@"{_accountName}.{VTEXConstants.PaymentsDomain}";
+ endpoint = $@"api/{endpoint}";
+ break;
+ case RequestEndpoint.LOGISTICS:
+ host = VTEXConstants.LogisticsDomain;
+ endpoint = $@"api/{endpoint}";
+ if (queryString == null)
+ {
+ queryString = new();
+ }
+
+ queryString.Add(@"an", _accountName);
+ break;
+ case RequestEndpoint.API:
+ case RequestEndpoint.MASTER_DATA:
+ host = VTEXConstants.ApiDomain;
+ endpoint = $@"{_accountName}/{endpoint}";
+ break;
+ case RequestEndpoint.BRIDGE:
+ host = $@"{_accountName}.{VTEXConstants.MyVtexDomain}";
+ endpoint = $@"api/{endpoint}";
+ if (!string.IsNullOrWhiteSpace(_authCookie))
+ {
+ cookie = new(VTEXConstants.VtexIdClientAuthCookieName, _authCookie);
+ }
+
+ break;
+ case RequestEndpoint.HEALTH:
+ protocol = @"http";
+ port = 80;
+ host = VTEXConstants.MonitoringDomain;
+ endpoint = @"api/healthcheck/modules";
+ requiresAuthentication = false;
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(restEndpoint), restEndpoint, null);
+ }
+
+ return host;
+ }
+
+ #endregion
+ }
+}