diff --git a/.github/workflows/dotnet-build-and-test.yml b/.github/workflows/dotnet-build-and-test.yml
new file mode 100644
index 000000000..ad34a345a
--- /dev/null
+++ b/.github/workflows/dotnet-build-and-test.yml
@@ -0,0 +1,29 @@
+name: .NET Build and Test
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ branches: [ main ]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: '6.0.x'
+
+ - name: Restore dependencies
+ run: dotnet restore
+
+ - name: Build
+ run: dotnet build --no-restore --configuration Release
+
+ - name: Test
+ run: dotnet test --no-build --verbosity normal --configuration Release
diff --git a/Src/VTEX/VTEXContext.cs b/Src/VTEX/VTEXContext.cs
index 8091a7023..5607d773a 100644
--- a/Src/VTEX/VTEXContext.cs
+++ b/Src/VTEX/VTEXContext.cs
@@ -1,1597 +1,1605 @@
-// ***********************************************************************
-// 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.Diagnostics.Contracts;
- using System.Globalization;
- using System.Linq;
- using System.Threading;
- using System.Threading.Tasks;
- using CrispyWaffle.Extensions;
- using CrispyWaffle.Log;
- using CrispyWaffle.Serialization;
- using Newtonsoft.Json;
- using VTEX.DataEntities;
- using VTEX.Enums;
- using VTEX.Extensions;
- using VTEX.GoodPractices;
- using VTEX.Health;
- using VTEX.Transport;
- using VTEX.Transport.Bridge;
-
- ///
- /// A VTEX Context, that consumes the VTEX Wrapper
- ///
- ///
- public sealed class VTEXContext : IDisposable
- {
- #region Private fields
-
- ///
- /// The wrapper
- ///
- private readonly VTEXWrapper _wrapper;
-
- #endregion
-
- #region ~Ctor
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// Name of the account.
- /// The application key.
- /// The application token.
- /// The cookie.
- /// appKey
- /// appToken
- public VTEXContext(string accountName, string appKey, string appToken, string cookie = null)
- {
- _wrapper = new VTEXWrapper(accountName);
- if (string.IsNullOrWhiteSpace(appKey))
- {
- throw new ArgumentNullException(nameof(appKey));
- }
-
- if (string.IsNullOrWhiteSpace(appToken))
- {
- throw new ArgumentNullException(nameof(appToken));
- }
-
- _wrapper.SetRestCredentials(appKey, appToken);
- if (string.IsNullOrWhiteSpace(cookie))
- {
- return;
- }
-
- _wrapper.SetVtexIdClientAuthCookie(cookie);
- }
-
- ///
- /// Retrieves a list of orders based on specified filtering criteria.
- ///
- /// The status of the orders to filter by (optional).
- /// The start date for filtering orders (optional).
- /// The end date for filtering orders (optional).
- /// The sales channel to filter by (optional).
- /// The affiliated ID to filter by (optional).
- /// The payment system name to filter by (optional).
- /// A generic query string for additional filtering (optional).
- /// An instance of containing the filtered orders.
- ///
- /// This method constructs a query string based on the provided parameters to filter the orders.
- /// It supports pagination and retrieves orders in pages of 50 until no more orders are found.
- /// The filtering criteria include order status, sales channel, affiliated ID, payment system name,
- /// and a date range defined by start and end dates. The results are logged indicating the number of orders found.
- ///
- private OrdersList GetOrdersListInternal(
- string status = null,
- DateTime? startDate = null,
- DateTime? endDate = null,
- string salesChannel = null,
- string affiliatedId = null,
- string paymentSystemName = null,
- string genericQuery = null
- )
- {
- OrdersList result = null;
- var currentPage = 1;
- var queryString = new Dictionary
- {
- { @"page", @"0" },
- { @"per_page", @"50" },
- };
- if (!string.IsNullOrWhiteSpace(status))
- {
- queryString.Add(@"f_status", status);
- }
-
- if (!string.IsNullOrWhiteSpace(salesChannel))
- {
- queryString.Add(@"f_salesChannel", salesChannel);
- }
-
- if (!string.IsNullOrWhiteSpace(affiliatedId))
- {
- queryString.Add(@"f_affiliateId", affiliatedId);
- }
-
- if (!string.IsNullOrWhiteSpace(paymentSystemName))
- {
- queryString.Add(@"f_paymentNames", paymentSystemName);
- }
-
- if (startDate.HasValue && endDate.HasValue)
- {
- queryString.Add(
- @"f_creationDate",
- $@"creationDate:[{startDate.Value.ToUniversalTime():s}Z TO {endDate.Value.ToUniversalTime():s}Z]"
- );
- }
-
- if (!string.IsNullOrWhiteSpace(genericQuery))
- {
- queryString.Add(@"q", genericQuery);
- }
-
- queryString.Add(@"orderBy", @"creationDate,asc");
- while (GetOrderListsValueInternal(queryString, currentPage, ref result))
- {
- currentPage++;
- }
-
- LogConsumer.Info("{0} orders found", result.List.Length);
- return result;
- }
-
- ///
- /// Gets the order lists value internal.
- ///
- /// The query string.
- /// The current page.
- /// The result.
- /// true if XXXX, false otherwise.
- ///
- private bool GetOrderListsValueInternal(
- Dictionary queryString,
- int currentPage,
- ref OrdersList result
- )
- {
- var json = string.Empty;
- try
- {
- LogConsumer.Trace("Getting page {0} of orders list", currentPage);
- queryString[@"page"] = currentPage.ToString(CultureInfo.InvariantCulture);
-
- json = _wrapper
- .ServiceInvokerAsync(
- HttpRequestMethod.GET,
- PlatformConstants.OmsOrders,
- CancellationToken.None,
- queryString
- )
- .Result;
- var temp = SerializerFactory.GetSerializer().Deserialize(json);
- if (result == null)
- {
- result = temp;
- }
- else
- {
- result.List = result.List.Concat(temp.List).ToArray();
- }
-
- if (temp.Paging.Pages == 1 || temp.Paging.CurrentPage >= temp.Paging.Pages)
- {
- return false;
- }
-
- if (currentPage == 1)
- {
- LogConsumer.Trace("{0} pages of orders list", temp.Paging.Pages);
- }
-
- return true;
- }
- catch (JsonSerializationException e)
- {
- throw new UnexpectedApiResponseException(json, e);
- }
- }
-
- ///
- /// Gets the orders by order's ids.
- ///
- /// The order's ids.
- /// IEnumerable<Order>.
- private IEnumerable GetOrdersInternal(IEnumerable ordersIds)
- {
- var list = new List();
- Parallel.ForEach(ordersIds, orderId => list.Add(GetOrder(orderId)));
- return list;
- }
-
- ///
- /// Get a order by order id
- ///
- /// The id of the order
- /// Order.
- ///
- ///
- private Order GetOrderInternal(string orderId)
- {
- LogConsumer.Trace("Getting order {0}", orderId);
- var json = _wrapper
- .ServiceInvokerAsync(
- HttpRequestMethod.GET,
- $"{PlatformConstants.OmsOrders}/{orderId}",
- CancellationToken.None
- )
- .Result;
- if (json == null)
- {
- return null;
- }
-
- try
- {
- var order = SerializerFactory.GetSerializer().Deserialize(json);
-
- #region Payment
-
- var transaction = order.PaymentData.Transactions.First();
- var payment = transaction.Payments.FirstOrDefault();
- if (
- payment != null
- && payment.PaymentSystem == 0
- && !string.IsNullOrWhiteSpace(order.AffiliateId)
- )
- {
- LogConsumer.Info(@"Marketplace {0}", order.AffiliateId);
- }
- else if (
- transaction.TransactionId != null
- && !transaction.TransactionId.Equals(
- @"NO-PAYMENT",
- StringComparison.InvariantCultureIgnoreCase
- )
- )
- {
- LogConsumer.Info(@"Bank bill {0}", order.Sequence);
- }
- else if (order.Totals.Sum(t => t.Value) == 0)
- {
- LogConsumer.Warning("Promotion / discount coupon - order subsidized");
- }
- else
- {
- throw new InvalidPaymentDataException(orderId);
- }
-
- #endregion
-
- #region Email
-
- if (!string.IsNullOrWhiteSpace(order.ClientProfileData.UserProfileId))
- {
- var client = SearchAsync(
- @"userId",
- order.ClientProfileData.UserProfileId,
- CancellationToken.None
- ).Result;
- if (client != null && !string.IsNullOrWhiteSpace(client.Email))
- {
- order.ClientProfileData.Email = client.Email;
- }
-
- if (
- order.ClientProfileData.Email.IndexOf(
- @"ct.vtex",
- StringComparison.InvariantCultureIgnoreCase
- ) != -1
- )
- {
- order.ClientProfileData.Email = @"pedido@editorainovacao.com.br";
- }
- }
-
- #endregion
-
- LogConsumer.Debug(order, $"vtex-order-{orderId}.js");
- var affiliated = string.IsNullOrWhiteSpace(order.AffiliateId)
- ? string.Empty
- : $" - Affiliated: {order.AffiliateId}";
- LogConsumer.Info(
- "Order: {0} - Sequence: {1} - Status: {2} - Sales channel: {3}{4}",
- order.OrderId,
- order.Sequence,
- order.Status.GetHumanReadableValue(),
- order.SalesChannel,
- affiliated
- );
- return order;
- }
- catch (JsonSerializationException e)
- {
- throw new UnexpectedApiResponseException(json, e);
- }
- }
-
- #endregion
-
- #region Public Methods
-
- #region OMS
-
- ///
- /// Gets the feed.
- ///
- /// The maximum lot.
- /// IEnumerable<OrderFeed>.
- public IEnumerable GetFeed(int maxLot = 20)
- {
- //VTEX limitation
- if (maxLot > 20)
- {
- maxLot = 20;
- }
-
- LogConsumer.Trace("Getting up to {0} events in order feed", maxLot);
- var json = _wrapper
- .ServiceInvokerAsync(
- HttpRequestMethod.GET,
- $"{PlatformConstants.OmsFeed}",
- CancellationToken.None,
- new Dictionary { { @"maxLot", maxLot.ToString() } }
- )
- .Result;
- return SerializerFactory.GetSerializer>().Deserialize(json);
- }
-
- ///
- /// Commits the feed.
- ///
- /// The feed.
- public void CommitFeed(OrderFeed feed)
- {
- LogConsumer.Trace("Commiting feed of order {0}", feed.OrderId);
- var data = (string)
- new OrderFeedCommit { CommitToken = feed.CommitToken }.GetSerializer();
- _wrapper
- .ServiceInvokerAsync(
- HttpRequestMethod.POST,
- $"{PlatformConstants.OmsFeed}confirm",
- CancellationToken.None,
- data: data
- )
- .Wait();
- }
-
- ///
- /// Get a order by order id
- ///
- /// The id of the order
- /// Order.
- ///
- public Order GetOrder(string orderId)
- {
- return GetOrderInternal(orderId);
- }
-
- ///
- /// Gets the orders list metadata.
- ///
- /// The status.
- /// IEnumerable<List>.
- public IEnumerable GetOrdersList(OrderStatus status)
- {
- LogConsumer.Warning("Getting orders with status {0}", status.GetHumanReadableValue());
- var orders = GetOrdersListInternal(status.GetInternalValue());
- return orders.List;
- }
-
- ///
- /// Get a Enumerable list of Order by status.
- ///
- /// The status of the orders to get
- /// IEnumerable<Order>.
- public IEnumerable GetOrders(OrderStatus status)
- {
- var ordersIds = GetOrdersList(status).Select(order => order.OrderId).ToList();
- if (ordersIds.Any())
- {
- return GetOrdersInternal(ordersIds);
- }
-
- LogConsumer.Warning("No orders with status {0} found", status.GetHumanReadableValue());
- return new Order[0];
- }
-
- ///
- /// Gets the orders list by a date range of order's placed date.
- ///
- /// The start date of the range.
- /// The end date of the range.
- /// IEnumerable<String>.
- public IEnumerable GetOrdersList(DateTime startDate, DateTime endDate)
- {
- LogConsumer.Warning("Getting orders between {0:G} and {1:G}", startDate, endDate);
- var orders = GetOrdersListInternal(startDate: startDate, endDate: endDate);
- return orders.List;
- }
-
- ///
- /// Get a Enumerable list of Order by a date range of order's placed date.
- ///
- /// The start date of the range
- /// The end date of the range
- /// IEnumerable<Order>.
- public IEnumerable GetOrders(DateTime startDate, DateTime endDate)
- {
- var ordersIds = GetOrdersList(startDate, endDate)
- .Select(order => order.OrderId)
- .ToList();
- if (ordersIds.Any())
- {
- return GetOrdersInternal(ordersIds);
- }
-
- LogConsumer.Warning("No orders between {0:G} and {1:G} found", startDate, endDate);
- return new Order[0];
- }
-
- ///
- /// Gets the orders list by status and date range of order's placed date.
- ///
- /// The status of orders to get.
- /// The start date of the range.
- /// The end date of the range.
- /// IEnumerable<String>.
- public IEnumerable GetOrdersList(
- OrderStatus status,
- DateTime startDate,
- DateTime endDate
- )
- {
- LogConsumer.Warning(
- "Getting orders with status {0} between {1:G} and {2:G}",
- status.GetHumanReadableValue(),
- startDate,
- endDate
- );
- var orders = GetOrdersListInternal(status.GetInternalValue(), startDate, endDate);
- return orders.List;
- }
-
- ///
- /// Get a Enumerable list of Order by status and date range of order's placed date.
- ///
- /// The status of orders to get.
- /// The start date of the range.
- /// The end date of the range.
- /// IEnumerable<Order>.
- public IEnumerable GetOrders(
- OrderStatus status,
- DateTime startDate,
- DateTime endDate
- )
- {
- var ordersIds = GetOrdersList(status, startDate, endDate)
- .Select(order => order.OrderId)
- .ToList();
- if (ordersIds.Any())
- {
- return GetOrdersInternal(ordersIds);
- }
-
- LogConsumer.Warning(
- "No order with status {0} between {1:G} and {2:G} found",
- status.GetHumanReadableValue(),
- startDate,
- endDate
- );
- return new Order[0];
- }
-
- ///
- /// Gets the orders list by status and affiliated identifier (AKA marketplace).
- ///
- /// The status of orders to get.
- /// The affiliated identifier
- /// IEnumerable<String>.
- public IEnumerable GetOrdersList(OrderStatus status, string affiliatedId)
- {
- LogConsumer.Warning(
- "Getting orders with status {0} and affiliated {1}",
- status.GetHumanReadableValue(),
- affiliatedId
- );
- var orders = GetOrdersListInternal(
- status.GetInternalValue(),
- affiliatedId: affiliatedId
- );
- return orders.List;
- }
-
- ///
- /// Get a Enumerable list of Order by status and affiliated identifier (AKA marketplace).
- ///
- /// The status of orders to get.
- /// The affiliated identifier
- /// IEnumerable<Order>.
- public IEnumerable GetOrders(OrderStatus status, string affiliatedId)
- {
- var ordersIds = GetOrdersList(status, affiliatedId)
- .Select(order => order.OrderId)
- .ToList();
- if (ordersIds.Any())
- {
- return GetOrdersInternal(ordersIds);
- }
-
- LogConsumer.Warning(
- "No order with status {0} and affiliated {1} found",
- status.GetHumanReadableValue(),
- affiliatedId
- );
- return new Order[0];
- }
-
- ///
- /// Gets the orders list by generic query (order id, client's document, sequence, etc).
- ///
- /// The query to lookup in orders.
- /// IEnumerable<String>.
- public IEnumerable GetOrdersList(string query)
- {
- LogConsumer.Warning("Getting orders with term '{0}'", query);
- var orders = GetOrdersListInternal(genericQuery: query);
- return orders.List;
- }
-
- ///
- /// Gets the orders by generic query (order identifier, client's document, sequence, etc).
- ///
- /// The query to lookup in orders.
- /// IEnumerable<Order>.
- public IEnumerable GetOrders(string query)
- {
- var ordersIds = GetOrdersList(query).Select(order => order.OrderId).ToList();
- if (ordersIds.Any())
- {
- return GetOrdersInternal(ordersIds);
- }
-
- LogConsumer.Warning("No orders with term '{0}' found", query);
- return new Order[0];
- }
-
- ///
- /// Gets the orders by the array of orders identifiers
- ///
- /// The orders ids.
- /// IEnumerable<Order>.
- public IEnumerable GetOrders(string[] ordersIds)
- {
- return GetOrdersInternal(ordersIds);
- }
-
- ///
- /// Cancels the order asynchronous.
- ///
- /// The order identifier.
- /// A Task<System.String> representing the asynchronous operation.
- /// Order {orderId} cannot be canceled because isn't in pending payment status on VTEX
- public async Task CancelOrderAsync(string orderId)
- {
- try
- {
- LogConsumer.Warning("Cancelling order {0}", orderId);
- var source = new CancellationTokenSource(new TimeSpan(0, 5, 0));
- var order = GetOrder(orderId);
- if (order.Status == OrderStatus.CANCELED)
- {
- return string.Empty;
- }
-
- if (
- order.Status != OrderStatus.PAYMENT_PENDING
- && order.Status != OrderStatus.AWAITING_AUTHORIZATION_TO_DISPATCH
- )
- {
- throw new InvalidOperationException(
- $"Order {orderId} cannot be canceled because isn't in pending payment status on VTEX"
- );
- }
-
- var json = await _wrapper
- .ServiceInvokerAsync(
- HttpRequestMethod.POST,
- $"{PlatformConstants.OmsOrders}/{orderId}/cancel",
- source.Token
- )
- .ConfigureAwait(false);
- var receipt = SerializerFactory
- .GetSerializer()
- .Deserialize(json);
- LogConsumer.Info(
- "Order {0} successfully canceled. Receipt: {1}",
- order.Sequence,
- receipt.Receipt
- );
- return receipt.Receipt;
- }
- catch (Exception e)
- {
- LogConsumer.Handle(new CancelOrderException(orderId, e));
- return string.Empty;
- }
- }
-
- ///
- /// Changes the order status asynchronous.
- ///
- /// The order identifier.
- /// The new status.
- /// A Task representing the asynchronous operation.
- ///
- public async Task ChangeOrderStatusAsync(string orderId, OrderStatus newStatus)
- {
- try
- {
- LogConsumer.Info(
- "Changing order {0} status to {1}",
- orderId,
- newStatus.GetHumanReadableValue()
- );
- var source = new CancellationTokenSource(new TimeSpan(0, 5, 0));
- var json = await _wrapper
- .ServiceInvokerAsync(
- HttpRequestMethod.POST,
- $"{PlatformConstants.OmsOrders}/{orderId}/changestate/{newStatus.GetInternalValue()}",
- source.Token
- )
- .ConfigureAwait(false);
- LogConsumer.Info(json);
- }
- catch (AggregateException e)
- {
- var ae = e.InnerExceptions.First();
- throw new ChangeStatusOrderException(
- orderId,
- newStatus.GetHumanReadableValue(),
- ae
- );
- }
- catch (Exception e)
- {
- throw new ChangeStatusOrderException(orderId, newStatus.GetHumanReadableValue(), e);
- }
- }
-
- ///
- /// Notifies the order paid asynchronous.
- ///
- /// The order identifier.
- /// A Task representing the asynchronous operation.
- public async Task NotifyOrderPaidAsync(string orderId)
- {
- try
- {
- LogConsumer.Info("Sending payment notification of order {0}", orderId);
- var source = new CancellationTokenSource(new TimeSpan(0, 5, 0));
- var order = GetOrder(orderId);
- if (
- order.Status != OrderStatus.PAYMENT_PENDING
- && order.Status != OrderStatus.AWAITING_AUTHORIZATION_TO_DISPATCH
- )
- {
- return;
- }
-
- if (order.Status == OrderStatus.AWAITING_AUTHORIZATION_TO_DISPATCH)
- {
- await ChangeOrderStatusAsync(order.OrderId, OrderStatus.AUTHORIZE_FULFILLMENT)
- .ConfigureAwait(false);
- return;
- }
- var paymentId = order.PaymentData.Transactions.First().Payments.First().Id;
- _wrapper
- .ServiceInvokerAsync(
- HttpRequestMethod.POST,
- $"{PlatformConstants.OmsOrders}/{order.OrderId}/payments/{paymentId}/payment-notification",
- source.Token
- )
- .Wait(source.Token);
- }
- catch (Exception e)
- {
- LogConsumer.Handle(new PaymentNotificationOrderException(orderId, e));
- }
- }
-
- ///
- /// Notifies the order shipped.
- ///
- /// The order identifier.
- /// The notification.
- ///
- public void NotifyOrderShipped(string orderId, ShippingNotification notification)
- {
- NotifyOrderShippedAsync(orderId, notification, CancellationToken.None).Wait();
- }
-
- ///
- /// Notifies the order shipped async.
- ///
- /// The order identifier.
- /// The notification.
- /// The token.
- /// A Task<System.String> representing the asynchronous operation.
- ///
- public async Task NotifyOrderShippedAsync(
- string orderId,
- ShippingNotification notification,
- CancellationToken token
- )
- {
- try
- {
- LogConsumer.Info("Sending shipping notification of order {0}", orderId);
- LogConsumer.Debug(
- notification,
- $"vtex-shipping-notification-{orderId}-{notification.InvoiceNumber}.js"
- );
- var json = await _wrapper
- .ServiceInvokerAsync(
- HttpRequestMethod.POST,
- $"{PlatformConstants.OmsInvoices}/{orderId}/invoice",
- token,
- data: (string)notification.GetSerializer()
- )
- .ConfigureAwait(false);
- var receipt = SerializerFactory.GetSerializer().Deserialize(json);
- LogConsumer.Trace(receipt.Receipt);
- return receipt.Receipt;
- }
- catch (AggregateException e)
- {
- var ae = e.InnerExceptions.First();
- throw new ShippingNotificationOrderException(orderId, ae);
- }
- catch (Exception e)
- {
- throw new ShippingNotificationOrderException(orderId, e);
- }
- }
-
- ///
- /// Notifies the order delivered
- ///
- /// The tracking.
- /// System.String.
- ///
- public async ValueTask NotifyOrderDelivered(Tracking tracking)
- {
- try
- {
- LogConsumer.Info("Sending tracking info of order {0}", tracking.OrderId);
- var source = new CancellationTokenSource(new TimeSpan(0, 5, 0));
- LogConsumer.Debug(
- tracking,
- $"vtex-tracking-info-{tracking.OrderId}-{tracking.InvoiceNumber}.js"
- );
- var json = await _wrapper
- .ServiceInvokerAsync(
- HttpRequestMethod.PUT,
- string.Format(
- PlatformConstants.OmsTracking,
- tracking.OrderId,
- tracking.InvoiceNumber
- ),
- source.Token,
- data: (string)tracking.GetSerializer()
- )
- .ConfigureAwait(false);
- var receipt = SerializerFactory.GetSerializer().Deserialize(json);
- LogConsumer.Trace(receipt.Receipt);
- return receipt.Receipt;
- }
- catch (Exception e)
- {
- throw new TrackingNotificationOrderException(tracking.OrderId, e);
- }
- }
-
- ///
- /// Updates the order invoice.
- ///
- /// The order identifier.
- /// The invoice identifier.
- /// The notification.
- ///
- public void UpdateOrderInvoice(
- string orderId,
- string invoiceId,
- ShippingNotificationPatch notification
- )
- {
- try
- {
- LogConsumer.Info("Patching fiscal invoice {1} of order {0}", orderId, invoiceId);
- var source = new CancellationTokenSource(new TimeSpan(0, 5, 0));
- LogConsumer.Debug(
- notification,
- $"vtex-shipping-notification-{orderId}-{invoiceId}.js"
- );
- var json = _wrapper
- .ServiceInvokerAsync(
- HttpRequestMethod.PATCH,
- $"{PlatformConstants.OmsOrders}/{orderId}/invoice/{invoiceId}",
- source.Token,
- data: (string)notification.GetSerializer()
- )
- .Result;
- var receipt = SerializerFactory.GetSerializer().Deserialize(json);
- LogConsumer.Trace(receipt.Receipt);
- }
- catch (Exception e)
- {
- throw new ShippingNotificationOrderException(orderId, e);
- }
- }
-
- ///
- /// Changes the order.
- ///
- /// The order identifier.
- /// The change.
- ///
- public void ChangeOrder(string orderId, ChangeOrder change)
- {
- try
- {
- LogConsumer.Info("Changing order {0}", orderId);
- LogConsumer.Debug(change, $"vtex-change-order-{orderId}.js");
- var json = _wrapper
- .ServiceInvokerAsync(
- HttpRequestMethod.POST,
- $"{PlatformConstants.OmsOrders}/{orderId}/changes",
- CancellationToken.None,
- data: (string)change.GetSerializer()
- )
- .Result;
- var receipt = SerializerFactory.GetSerializer().Deserialize(json);
- LogConsumer.Trace(receipt.Receipt);
- }
- catch (Exception e)
- {
- throw new ChangeOrderException(orderId, e);
- }
- }
-
- #endregion
-
- #region PCI Gateway
-
- ///
- /// Gets the transaction interactions.
- ///
- /// The transaction identifier.
- /// IEnumerable<TransactionInteraction>.
- ///
- [Pure]
- public IEnumerable GetTransactionInteractions(string transactionId)
- {
- try
- {
- LogConsumer.Info("Getting interactions of transaction {0}", transactionId);
- var json = _wrapper
- .ServiceInvokerAsync(
- HttpRequestMethod.GET,
- $"{PlatformConstants.PciTransactions}/{transactionId}/interactions",
- CancellationToken.None,
- restEndpoint: RequestEndpoint.PAYMENTS
- )
- .Result;
- return SerializerFactory
- .GetSerializer>()
- .Deserialize(json);
- }
- catch (Exception e)
- {
- throw new TransactionException(transactionId, e);
- }
- }
-
- #endregion
-
- #region Stock
-
- ///
- /// get sku reservations as an asynchronous operation.
- ///
- /// The sku identifier.
- /// The warehouse identifier.
- /// A Task<System.Int32> representing the asynchronous operation.
- public async Task GetSkuReservationsAsync(int skuId, string warehouseId)
- {
- try
- {
- LogConsumer.Info(
- "Getting reservations of SKU {0} in the warehouse {1}",
- skuId,
- warehouseId
- );
- var source = new CancellationTokenSource(new TimeSpan(0, 5, 0));
- var json = await _wrapper
- .ServiceInvokerAsync(
- HttpRequestMethod.GET,
- $"{PlatformConstants.LogReservations}/{warehouseId}/{skuId}",
- source.Token
- )
- .ConfigureAwait(false);
- var reservations = SerializerFactory
- .GetSerializer()
- .Deserialize(json);
- LogConsumer.Debug(reservations, $"vtex-sku-reservations-{skuId}.js");
- var total = !reservations.Items.Any() ? 0 : reservations.Items.Sum(r => r.Quantity);
- LogConsumer.Info(
- "The SKU {0} has {1} units reserved in warehouse {2}",
- skuId,
- total,
- warehouseId
- );
- return total;
- }
- catch (Exception e)
- {
- LogConsumer.Handle(new ProductExportException(skuId, e));
- return 0;
- }
- }
-
- ///
- /// Gets the sku inventory.
- ///
- /// The sku identifier.
- /// Inventory.
- public async Task GetSkuInventoryAsync(int skuId)
- {
- LogConsumer.Info("Getting inventory of SKU {0}", skuId);
- var source = new CancellationTokenSource(new TimeSpan(0, 5, 0));
- var json = await _wrapper
- .ServiceInvokerAsync(
- HttpRequestMethod.GET,
- $"{PlatformConstants.LogInventory}/{skuId}",
- source.Token,
- restEndpoint: RequestEndpoint.LOGISTICS
- )
- .ConfigureAwait(false);
- var inventory = SerializerFactory.GetSerializer().Deserialize(json);
- LogConsumer.Debug(inventory, $"vtex-sku-inventory-{skuId}.js");
- return inventory;
- }
-
- ///
- /// Updates the sku stock.
- ///
- /// The stock information.
- /// A Task representing the asynchronous operation.
- ///
- public async Task UpdateSkuStockAsync(StockInfo stockInfo)
- {
- try
- {
- if (stockInfo.Quantity < 0)
- {
- stockInfo.Quantity = 0;
- }
-
- stockInfo.DateUtcOnBalanceSystem = null;
- if (!stockInfo.UnlimitedQuantity)
- {
- stockInfo.Quantity += await GetSkuReservationsAsync(
- stockInfo.ItemId,
- stockInfo.WareHouseId
- )
- .ConfigureAwait(false);
- }
-
- LogConsumer.Info(
- "Updating inventory of SKU {0} on warehouse {1} with {2} units",
- stockInfo.ItemId,
- stockInfo.WareHouseId,
- stockInfo.Quantity
- );
- var source = new CancellationTokenSource(new TimeSpan(0, 5, 0));
- var data = @"[" + (string)stockInfo.GetSerializer() + @"]";
- LogConsumer.Debug(stockInfo, $"vtex-sku-stock-{stockInfo.ItemId}.js");
- await _wrapper
- .ServiceInvokerAsync(
- HttpRequestMethod.POST,
- PlatformConstants.LogWarehouses,
- source.Token,
- data: data
- )
- .ConfigureAwait(false);
- }
- catch (Exception e)
- {
- throw new UpdateStockInfoSKUException(stockInfo.ItemId, e);
- }
- }
-
- #endregion
-
- #region Pricing
-
- ///
- /// Get the prices for an SKU.
- /// It is possible that on the property "fixedPrices" exists a list of specific prices for Trade Policies and Minimum Quantities of the SKU.Fixed Prices may also be scheduled.
- ///
- /// The stock keeping unit identifier
- /// A task of price
- public async Task GetPriceAsync(int skuId)
- {
- LogConsumer.Info("Getting the price of sku {0}", skuId);
- var source = new CancellationTokenSource(new TimeSpan(0, 5, 0));
- try
- {
- var json = await _wrapper
- .ServiceInvokerAsync(
- HttpRequestMethod.GET,
- $@"{PlatformConstants.Pricing}/{skuId}",
- source.Token,
- restEndpoint: RequestEndpoint.API
- )
- .ConfigureAwait(false);
- return SerializerFactory.GetSerializer().Deserialize(json);
- }
- catch (UnexpectedApiResponseException e)
- {
- if (e.StatusCode == 404)
- {
- return new Price();
- }
-
- throw;
- }
- }
-
- ///
- /// This method will create or update an SKU Price.
- /// The property "basePrice" is the base selling price of the SKU.The property "fixedPrices" is an array where each item is a Fixed Price.
- /// The Fixed Price is the price of the SKU for an specific Trade Policy with an specific Minimum Quantity to be activated.
- /// A Fixed Price may optionally be scheduled by using the property dateRange.
- /// A Fixed Price may optionally overwrite the listPrice specified in the Base Price by using the inner property listPrice.
- /// If you don't have specific prices for different Trade Policies, you do not need to send the property fixedPrices.
- ///
- /// The price data
- /// The stock keeping unit identifier
- /// The cancellation token.
- /// A Task representing the asynchronous operation.
- ///
- public async Task UpdatePriceAsync(Price price, int skuId, CancellationToken token)
- {
- try
- {
- var oldPrice = await GetPriceAsync(skuId).ConfigureAwait(false);
- if (oldPrice?.FixedPrices != null && oldPrice.FixedPrices.Any())
- {
- await DeletePriceAsync(skuId, token).ConfigureAwait(false);
- }
-
- LogConsumer.Info(
- "Updating the price of sku {0} to {1} (list price: {2})",
- skuId,
- price.CostPrice.ToMonetary(),
- price.ListPrice.HasValue ? price.ListPrice.Value.ToMonetary() : "no"
- );
- await _wrapper
- .ServiceInvokerAsync(
- HttpRequestMethod.PUT,
- $@"{PlatformConstants.Pricing}/{skuId}",
- token,
- data: (string)price.GetSerializer(),
- restEndpoint: RequestEndpoint.API
- )
- .ConfigureAwait(false);
- }
- catch (Exception e)
- {
- throw new UpdatePriceInfoSkuException(skuId, e);
- }
- }
-
- ///
- /// Removes an SKU price.
- /// This action removes both Base Price and all available Fixed Prices for and SKU in all trade policies.
- ///
- /// The stock keeping unit identifier.
- /// The cancellation token.
- /// Task
- public async Task DeletePriceAsync(int skuId, CancellationToken token)
- {
- LogConsumer.Info("Deleting the price of sku {0}", skuId);
- await _wrapper
- .ServiceInvokerAsync(
- HttpRequestMethod.DELETE,
- $@"{PlatformConstants.Pricing}/{skuId}",
- token,
- restEndpoint: RequestEndpoint.API
- )
- .ConfigureAwait(false);
- }
-
- ///
- /// Retrieves a collection of bridge facets based on the specified query and optional keywords.
- ///
- /// The query string used to filter the bridge facets.
- /// Optional keywords to further refine the search for bridge facets.
- /// An enumerable collection of objects that match the specified query and keywords.
- ///
- /// This method constructs a query to fetch bridge facets from a remote service. It logs the action of retrieving facets
- /// and sets a timeout of 5 minutes for the operation. The method builds a dictionary of query parameters, including
- /// facets to retrieve and the specified query. If keywords are provided, they are added to the query parameters as well.
- /// The method then invokes the service asynchronously and deserializes the resulting JSON response into a list of
- /// objects. If an exception occurs during this process, a custom
- /// is thrown, encapsulating the original exception and the query that caused the failure.
- ///
- /// Thrown when an error occurs while retrieving bridge facets.
- [Pure]
- public IEnumerable GetBridgeFacets(
- [Localizable(false)] string query,
- [Localizable(false)] string keywords = null
- )
- {
- try
- {
- LogConsumer.Info(
- "Getting facets in bridge module that satisfy the condition '{0}'",
- query
- );
- var source = new CancellationTokenSource(new TimeSpan(0, 5, 0));
- var queryString = new Dictionary
- {
- { @"_facets", @"Origin,Status" },
- { @"_where", query },
- };
- if (!string.IsNullOrWhiteSpace(keywords))
- {
- queryString.Add(@"_keywords", $@"*{keywords}*");
- }
-
- var json = _wrapper
- .ServiceInvokerAsync(
- HttpRequestMethod.GET,
- $"{PlatformConstants.BridgeSearch}/facets",
- source.Token,
- queryString,
- restEndpoint: RequestEndpoint.BRIDGE
- )
- .Result;
- return SerializerFactory.GetSerializer>().Deserialize(json);
- }
- catch (Exception e)
- {
- throw new BridgeException(query, e);
- }
- }
-
- ///
- /// Retrieves a collection of bridge items based on the specified query parameters.
- ///
- /// The query string used to filter the bridge items.
- /// The sorting criteria for the returned items.
- /// Additional keywords to refine the search results.
- /// The starting point for the items to be retrieved.
- /// The maximum number of items to return.
- /// An enumerable collection of that match the specified criteria.
- ///
- /// This method interacts with an external service to fetch bridge items based on the provided query, sort, and keywords.
- /// It logs the request details and checks for an offset limit to avoid exceeding the maximum allowed items from the service.
- /// If the offset exceeds 10,000, a warning is logged, and an empty list is returned.
- /// The method uses a cancellation token to set a timeout for the service call, ensuring that it does not hang indefinitely.
- /// In case of an error during the service call, it throws a custom exception with details about the failure.
- ///
- ///
- /// Thrown when an error occurs while retrieving bridge items from the external service.
- ///
- [Pure]
- public IEnumerable GetBridgeItems(
- [Localizable(false)] string query,
- [Localizable(false)] string sort,
- [Localizable(false)] string keywords,
- int offSet,
- int limit
- )
- {
- try
- {
- LogConsumer.Info(
- "Getting {0} items from {1} in bridge module that satisfy the condition '{2}'",
- limit,
- offSet,
- query
- );
- if (offSet >= 10000)
- {
- LogConsumer.Warning(
- "Cannot get more than 10000 items from Bridge / Master Data (VTEX Elastic Search limitation)"
- );
- return new List();
- }
- var source = new CancellationTokenSource(new TimeSpan(0, 5, 0));
- var queryString = new Dictionary
- {
- { @"_where", query },
- { @"_sort", sort },
- { @"offSet", offSet.ToString() },
- { @"limit", limit.ToString() },
- };
- if (!string.IsNullOrWhiteSpace(keywords))
- {
- queryString.Add(@"_keywords", $@"*{keywords}*");
- }
-
- var json = _wrapper
- .ServiceInvokerAsync(
- HttpRequestMethod.GET,
- PlatformConstants.BridgeSearch,
- source.Token,
- queryString,
- restEndpoint: RequestEndpoint.BRIDGE
- )
- .Result;
- return SerializerFactory.GetSerializer>().Deserialize(json);
- }
- catch (AggregateException e)
- {
- throw new BridgeException(
- query,
- e.InnerExceptions.FirstOrDefault() ?? e.InnerException ?? e
- );
- }
- catch (Exception e)
- {
- throw new BridgeException(query, e);
- }
- }
-
- ///
- /// Gets all bridge items.
- ///
- /// The query.
- /// The sort.
- /// The keywords.
- /// Name of the facet.
- /// The facet value.
- /// IEnumerable<BridgeItem>.
- [Pure]
- public IEnumerable GetAllBridgeItems(
- [Localizable(false)] string query,
- [Localizable(false)] string sort,
- [Localizable(false)] string keywords,
- [Localizable(false)] string facetName,
- [Localizable(false)] string facetValue
- )
- {
- const int perPage = 100;
- var facets = GetBridgeFacets(query, keywords);
- var total = facets.Single(f => f.Field.Equals(facetName)).Facets[facetValue].ToInt32();
-
- var result = new List(total);
- var pages = (total / perPage) + 1;
- for (var x = 0; x < pages; x++)
- {
- result.AddRange(GetBridgeItems(query, sort, keywords, x * perPage, perPage));
- }
-
- return result;
- }
-
- #endregion
-
- #region Platform status
-
- ///
- /// Gets the platform status.
- ///
- /// IEnumerable<PlatformStatus>.
- public IEnumerable GetPlatformStatus()
- {
- return GetPlatformStatusAsync(CancellationToken.None).Result;
- }
-
- ///
- /// Gets the platform status asynchronous.
- ///
- /// The token.
- /// A Task<IEnumerable`1> representing the asynchronous operation.
- [Pure]
- public async Task> GetPlatformStatusAsync(
- CancellationToken token
- )
- {
- LogConsumer.Info("Getting platform status");
- var json = await _wrapper
- .ServiceInvokerAsync(
- HttpRequestMethod.GET,
- string.Empty,
- token,
- restEndpoint: RequestEndpoint.HEALTH
- )
- .ConfigureAwait(false);
- var status = SerializerFactory.GetSerializer>().Deserialize(json);
- LogConsumer.Debug(status, "vtex-platform-status.js");
- return status;
- }
-
- #endregion
-
- #region Order payments
-
- ///
- /// Gets the order payments.
- ///
- /// The transaction identifier.
- /// List<PciPayment>.
- [Pure]
- public List GetOrderPayments(string transactionId)
- {
- var json = _wrapper
- .ServiceInvokerAsync(
- HttpRequestMethod.GET,
- $"{PlatformConstants.PciTransactions}/{transactionId}/payments",
- CancellationToken.None,
- restEndpoint: RequestEndpoint.PAYMENTS
- )
- .Result;
- if (json == null)
- {
- return new List();
- }
-
- var data = SerializerFactory
- .GetCustomSerializer>(SerializerFormat.Json)
- .Deserialize(json);
- LogConsumer.Debug(data, $"vtex-order-payemnts-{transactionId}.js");
- return data;
- }
-
- #endregion
-
- #region Catalog
-
- #region Specification
-
- ///
- /// Gets the specification field asynchronous.
- ///
- /// The field identifier.
- /// The token.
- /// A Task<SpecificationField> representing the asynchronous operation.
- [Pure]
- public async Task GetSpecificationFieldAsync(
- int fieldId,
- CancellationToken token
- )
- {
- LogConsumer.Info("Getting field for the field id {0}", fieldId);
- var json = await _wrapper
- .ServiceInvokerAsync(
- HttpRequestMethod.GET,
- $@"{PlatformConstants.CatalogPub}/specification/fieldGet/{fieldId}",
- token
- )
- .ConfigureAwait(false);
- var field = SerializerFactory.GetSerializer().Deserialize(json);
- LogConsumer.Debug(field, $"vtex-specification-field-{fieldId}.js");
- return field;
- }
-
- ///
- /// Gets the specification field values asynchronous.
- ///
- /// The field identifier.
- /// The token.
- /// A Task<ICollection`1> representing the asynchronous operation.
- [Pure]
- public async Task> GetSpecificationFieldValuesAsync(
- int fieldId,
- CancellationToken token
- )
- {
- LogConsumer.Info("Getting field values for the field id {0}", fieldId);
- var json = await _wrapper
- .ServiceInvokerAsync(
- HttpRequestMethod.GET,
- $@"{PlatformConstants.CatalogPub}/specification/fieldvalue/{fieldId}",
- token
- )
- .ConfigureAwait(false);
- var fieldValues = SerializerFactory
- .GetSerializer>()
- .Deserialize(json);
- LogConsumer.Debug(fieldValues, $"vtex-specification-values-{fieldId}.js");
- return fieldValues;
- }
-
- ///
- /// Updates the product specification asynchronous.
- ///
- /// The specification.
- /// The product identifier.
- /// The token.
- /// A Task representing the asynchronous operation.
- public async Task UpdateProductSpecificationAsync(
- Specification specification,
- int productId,
- CancellationToken token
- )
- {
- await UpdateProductSpecificationsAsync(
- new List(new[] { specification }),
- productId,
- token
- )
- .ConfigureAwait(false);
- }
-
- ///
- /// Updates the product specifications asynchronous.
- ///
- /// The specifications list.
- /// The product identifier.
- /// The token.
- /// A Task representing the asynchronous operation.
- public async Task UpdateProductSpecificationsAsync(
- List specifications,
- int productId,
- CancellationToken token
- )
- {
- LogConsumer.Info(
- "Updating the specifications {1} of product {0}",
- productId,
- string.Join(@",", specifications.Select(s => s.Id))
- );
-
- var data = (string)specifications.GetSerializer();
- await _wrapper
- .ServiceInvokerAsync(
- HttpRequestMethod.POST,
- $@"{PlatformConstants.Catalog}/products/{productId}/specification",
- token,
- data: data
- )
- .ConfigureAwait(false);
- }
-
- ///
- /// Inserts the specification field value asynchronous.
- ///
- /// The field value.
- /// The token.
- /// A Task representing the asynchronous operation.
- public async Task InsertSpecificationFieldValueAsync(
- SpecificationFieldValue fieldValue,
- CancellationToken token
- )
- {
- LogConsumer.Info("Creating field value of field id {0}", fieldValue.FieldId);
- var data = (string)fieldValue.GetSerializer();
- await _wrapper
- .ServiceInvokerAsync(
- HttpRequestMethod.POST,
- $@"{PlatformConstants.Catalog}/specification/fieldValue",
- token,
- data: data
- )
- .ConfigureAwait(false);
- }
-
- ///
- /// Asynchronously searches for a data entity based on a specified field and value.
- ///
- /// The type of the data entity to search for, which must implement .
- /// The field of the data entity to search against.
- /// The value to search for in the specified field.
- /// A cancellation token to monitor for cancellation requests.
- /// A task that represents the asynchronous operation. The task result contains the found data entity of type or null if no entity is found.
- ///
- /// This method performs an asynchronous search for a data entity by sending a GET request to the specified endpoint.
- /// It constructs a query string using the provided field and value, and invokes a service to retrieve the data.
- /// If the search value is null or whitespace, an is thrown.
- /// In case of an unexpected API response, an is thrown, containing the JSON response and the original exception.
- /// The method logs the retrieved entity for debugging purposes.
- ///
- /// Thrown when is null or whitespace.
- /// Thrown when the API response is unexpected.
- [Pure]
- public async Task SearchAsync(
- string searchedField,
- string searchedValue,
- CancellationToken token
- )
- where TDataEntity : class, IDataEntity, new()
- {
- if (string.IsNullOrWhiteSpace(searchedValue))
- {
- throw new ArgumentNullException(nameof(searchedValue));
- }
-
- var queryString = new Dictionary
- {
- { searchedField, searchedValue },
- { @"_fields", @"_all" },
- };
- var json = string.Empty;
- try
- {
- var entityName = typeof(TDataEntity).GetDataEntityName();
- json = await _wrapper
- .ServiceInvokerAsync(
- HttpRequestMethod.GET,
- $@"dataentities/{entityName}/search/",
- token,
- queryString,
- restEndpoint: RequestEndpoint.MASTER_DATA
- )
- .ConfigureAwait(false);
- var entity = SerializerFactory
- .GetSerializer>()
- .Deserialize(json)
- .FirstOrDefault();
- if (entity == null)
- {
- return null;
- }
-
- LogConsumer.Debug(
- entity,
- $@"vtex-masterdata-entity-{entityName}-{searchedField}-{searchedValue}.js"
- );
- return entity;
- }
- catch (Exception e)
- {
- throw new UnexpectedApiResponseException(json, e);
- }
- }
-
- #endregion
-
- #endregion
-
- #endregion
-
- #region IDisposable
-
- ///
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- ///
- public void Dispose()
- {
- _wrapper.Dispose();
- }
-
- #endregion
- }
-}
+using System;
+using System.Collections.Generic;
+
+namespace VTEX
+{
+ public class Collection
+ {
+ public int Id { get; set; }
+ public string Name { get; set; }
+ }
+// ***********************************************************************
+// 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.Diagnostics.Contracts;
+ using System.Globalization;
+ using System.Linq;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using CrispyWaffle.Extensions;
+ using CrispyWaffle.Log;
+ using CrispyWaffle.Serialization;
+ using Newtonsoft.Json;
+ using VTEX.DataEntities;
+ using VTEX.Enums;
+ using VTEX.Extensions;
+ using VTEX.GoodPractices;
+ using VTEX.Health;
+ using VTEX.Transport;
+ using VTEX.Transport.Bridge;
+
+ ///
+ /// A VTEX Context, that consumes the VTEX Wrapper
+ ///
+ ///
+ public sealed class VTEXContext : IDisposable
+ {
+ #region Private fields
+
+ ///
+ /// The wrapper
+ ///
+ private readonly VTEXWrapper _wrapper;
+
+ #endregion
+
+ #region ~Ctor
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Name of the account.
+ /// The application key.
+ /// The application token.
+ /// The cookie.
+ /// appKey
+ /// appToken
+ public VTEXContext(string accountName, string appKey, string appToken, string cookie = null)
+ {
+ _wrapper = new VTEXWrapper(accountName);
+ if (string.IsNullOrWhiteSpace(appKey))
+ {
+ throw new ArgumentNullException(nameof(appKey));
+ }
+
+ if (string.IsNullOrWhiteSpace(appToken))
+ {
+ throw new ArgumentNullException(nameof(appToken));
+ }
+
+ _wrapper.SetRestCredentials(appKey, appToken);
+ if (string.IsNullOrWhiteSpace(cookie))
+ {
+ return;
+ }
+
+ _wrapper.SetVtexIdClientAuthCookie(cookie);
+ }
+
+ ///
+ /// Retrieves a list of orders based on specified filtering criteria.
+ ///
+ /// The status of the orders to filter by (optional).
+ /// The start date for filtering orders (optional).
+ /// The end date for filtering orders (optional).
+ /// The sales channel to filter by (optional).
+ /// The affiliated ID to filter by (optional).
+ /// The payment system name to filter by (optional).
+ /// A generic query string for additional filtering (optional).
+ /// An instance of containing the filtered orders.
+ ///
+ /// This method constructs a query string based on the provided parameters to filter the orders.
+ /// It supports pagination and retrieves orders in pages of 50 until no more orders are found.
+ /// The filtering criteria include order status, sales channel, affiliated ID, payment system name,
+ /// and a date range defined by start and end dates. The results are logged indicating the number of orders found.
+ ///
+ private OrdersList GetOrdersListInternal(
+ string status = null,
+ DateTime? startDate = null,
+ DateTime? endDate = null,
+ string salesChannel = null,
+ string affiliatedId = null,
+ string paymentSystemName = null,
+ string genericQuery = null
+ )
+ {
+ OrdersList result = null;
+ var currentPage = 1;
+ var queryString = new Dictionary
+ {
+ { @"page", @"0" },
+ { @"per_page", @"50" },
+ };
+ if (!string.IsNullOrWhiteSpace(status))
+ {
+ queryString.Add(@"f_status", status);
+ }
+
+ if (!string.IsNullOrWhiteSpace(salesChannel))
+ {
+ queryString.Add(@"f_salesChannel", salesChannel);
+ }
+
+ if (!string.IsNullOrWhiteSpace(affiliatedId))
+ {
+ queryString.Add(@"f_affiliateId", affiliatedId);
+ }
+
+ if (!string.IsNullOrWhiteSpace(paymentSystemName))
+ {
+ queryString.Add(@"f_paymentNames", paymentSystemName);
+ }
+
+ if (startDate.HasValue && endDate.HasValue)
+ {
+ queryString.Add(
+ @"f_creationDate",
+ $@"creationDate:[{startDate.Value.ToUniversalTime():s}Z TO {endDate.Value.ToUniversalTime():s}Z]"
+ );
+ }
+
+ if (!string.IsNullOrWhiteSpace(genericQuery))
+ {
+ queryString.Add(@"q", genericQuery);
+ }
+
+ queryString.Add(@"orderBy", @"creationDate,asc");
+ while (GetOrderListsValueInternal(queryString, currentPage, ref result))
+ {
+ currentPage++;
+ }
+
+ LogConsumer.Info("{0} orders found", result.List.Length);
+ return result;
+ }
+
+ ///
+ /// Gets the order lists value internal.
+ ///
+ /// The query string.
+ /// The current page.
+ /// The result.
+ /// true if XXXX, false otherwise.
+ ///
+ private bool GetOrderListsValueInternal(
+ Dictionary queryString,
+ int currentPage,
+ ref OrdersList result
+ )
+ {
+ var json = string.Empty;
+ try
+ {
+ LogConsumer.Trace("Getting page {0} of orders list", currentPage);
+ queryString[@"page"] = currentPage.ToString(CultureInfo.InvariantCulture);
+
+ json = _wrapper
+ .ServiceInvokerAsync(
+ HttpRequestMethod.GET,
+ PlatformConstants.OmsOrders,
+ CancellationToken.None,
+ queryString
+ )
+ .Result;
+ var temp = SerializerFactory.GetSerializer().Deserialize(json);
+ if (result == null)
+ {
+ result = temp;
+ }
+ else
+ {
+ result.List = result.List.Concat(temp.List).ToArray();
+ }
+
+ if (temp.Paging.Pages == 1 || temp.Paging.CurrentPage >= temp.Paging.Pages)
+ {
+ return false;
+ }
+
+ if (currentPage == 1)
+ {
+ LogConsumer.Trace("{0} pages of orders list", temp.Paging.Pages);
+ }
+
+ return true;
+ }
+ catch (JsonSerializationException e)
+ {
+ throw new UnexpectedApiResponseException(json, e);
+ }
+ }
+
+ ///
+ /// Gets the orders by order's ids.
+ ///
+ /// The order's ids.
+ /// IEnumerable<Order>.
+ private IEnumerable GetOrdersInternal(IEnumerable ordersIds)
+ {
+ var list = new List();
+ Parallel.ForEach(ordersIds, orderId => list.Add(GetOrder(orderId)));
+ return list;
+ }
+
+ ///
+ /// Get a order by order id
+ ///
+ /// The id of the order
+ /// Order.
+ ///
+ ///
+ private Order GetOrderInternal(string orderId)
+ {
+ LogConsumer.Trace("Getting order {0}", orderId);
+ var json = _wrapper
+ .ServiceInvokerAsync(
+ HttpRequestMethod.GET,
+ $"{PlatformConstants.OmsOrders}/{orderId}",
+ CancellationToken.None
+ )
+ .Result;
+ if (json == null)
+ {
+ return null;
+ }
+
+ try
+ {
+ var order = SerializerFactory.GetSerializer().Deserialize(json);
+
+ #region Payment
+
+ var transaction = order.PaymentData.Transactions.First();
+ var payment = transaction.Payments.FirstOrDefault();
+ if (
+ payment != null
+ && payment.PaymentSystem == 0
+ && !string.IsNullOrWhiteSpace(order.AffiliateId)
+ )
+ {
+ LogConsumer.Info(@"Marketplace {0}", order.AffiliateId);
+ }
+ else if (
+ transaction.TransactionId != null
+ && !transaction.TransactionId.Equals(
+ @"NO-PAYMENT",
+ StringComparison.InvariantCultureIgnoreCase
+ )
+ )
+ {
+ LogConsumer.Info(@"Bank bill {0}", order.Sequence);
+ }
+ else if (order.Totals.Sum(t => t.Value) == 0)
+ {
+ LogConsumer.Warning("Promotion / discount coupon - order subsidized");
+ }
+ else
+ {
+ throw new InvalidPaymentDataException(orderId);
+ }
+
+ #endregion
+
+ #region Email
+
+ if (!string.IsNullOrWhiteSpace(order.ClientProfileData.UserProfileId))
+ {
+ var client = SearchAsync(
+ @"userId",
+ order.ClientProfileData.UserProfileId,
+ CancellationToken.None
+ ).Result;
+ if (client != null && !string.IsNullOrWhiteSpace(client.Email))
+ {
+ order.ClientProfileData.Email = client.Email;
+ }
+
+ if (
+ order.ClientProfileData.Email.IndexOf(
+ @"ct.vtex",
+ StringComparison.InvariantCultureIgnoreCase
+ ) != -1
+ )
+ {
+ order.ClientProfileData.Email = @"pedido@editorainovacao.com.br";
+ }
+ }
+
+ #endregion
+
+ LogConsumer.Debug(order, $"vtex-order-{orderId}.js");
+ var affiliated = string.IsNullOrWhiteSpace(order.AffiliateId)
+ ? string.Empty
+ : $" - Affiliated: {order.AffiliateId}";
+ LogConsumer.Info(
+ "Order: {0} - Sequence: {1} - Status: {2} - Sales channel: {3}{4}",
+ order.OrderId,
+ order.Sequence,
+ order.Status.GetHumanReadableValue(),
+ order.SalesChannel,
+ affiliated
+ );
+ return order;
+ }
+ catch (JsonSerializationException e)
+ {
+ throw new UnexpectedApiResponseException(json, e);
+ }
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ #region OMS
+
+ ///
+ /// Gets the feed.
+ ///
+ /// The maximum lot.
+ /// IEnumerable<OrderFeed>.
+ public IEnumerable GetFeed(int maxLot = 20)
+ {
+ //VTEX limitation
+ if (maxLot > 20)
+ {
+ maxLot = 20;
+ }
+
+ LogConsumer.Trace("Getting up to {0} events in order feed", maxLot);
+ var json = _wrapper
+ .ServiceInvokerAsync(
+ HttpRequestMethod.GET,
+ $"{PlatformConstants.OmsFeed}",
+ CancellationToken.None,
+ new Dictionary { { @"maxLot", maxLot.ToString() } }
+ )
+ .Result;
+ return SerializerFactory.GetSerializer>().Deserialize(json);
+ }
+
+ ///
+ /// Commits the feed.
+ ///
+ /// The feed.
+ public void CommitFeed(OrderFeed feed)
+ {
+ LogConsumer.Trace("Commiting feed of order {0}", feed.OrderId);
+ var data = (string)
+ new OrderFeedCommit { CommitToken = feed.CommitToken }.GetSerializer();
+ _wrapper
+ .ServiceInvokerAsync(
+ HttpRequestMethod.POST,
+ $"{PlatformConstants.OmsFeed}confirm",
+ CancellationToken.None,
+ data: data
+ )
+ .Wait();
+ }
+
+ ///
+ /// Get a order by order id
+ ///
+ /// The id of the order
+ /// Order.
+ ///
+ public Order GetOrder(string orderId)
+ {
+ return GetOrderInternal(orderId);
+ }
+
+ ///
+ /// Gets the orders list metadata.
+ ///
+ /// The status.
+ /// IEnumerable<List>.
+ public IEnumerable GetOrdersList(OrderStatus status)
+ {
+ LogConsumer.Warning("Getting orders with status {0}", status.GetHumanReadableValue());
+ var orders = GetOrdersListInternal(status.GetInternalValue());
+ return orders.List;
+ }
+
+ ///
+ /// Get a Enumerable list of Order by status.
+ ///
+ /// The status of the orders to get
+ /// IEnumerable<Order>.
+ public IEnumerable GetOrders(OrderStatus status)
+ {
+ var ordersIds = GetOrdersList(status).Select(order => order.OrderId).ToList();
+ if (ordersIds.Any())
+ {
+ return GetOrdersInternal(ordersIds);
+ }
+
+ LogConsumer.Warning("No orders with status {0} found", status.GetHumanReadableValue());
+ return new Order[0];
+ }
+
+ ///
+ /// Gets the orders list by a date range of order's placed date.
+ ///
+ /// The start date of the range.
+ /// The end date of the range.
+ /// IEnumerable<String>.
+ public IEnumerable GetOrdersList(DateTime startDate, DateTime endDate)
+ {
+ LogConsumer.Warning("Getting orders between {0:G} and {1:G}", startDate, endDate);
+ var orders = GetOrdersListInternal(startDate: startDate, endDate: endDate);
+ return orders.List;
+ }
+
+ ///
+ /// Get a Enumerable list of Order by a date range of order's placed date.
+ ///
+ /// The start date of the range
+ /// The end date of the range
+ /// IEnumerable<Order>.
+ public IEnumerable GetOrders(DateTime startDate, DateTime endDate)
+ {
+ var ordersIds = GetOrdersList(startDate, endDate)
+ .Select(order => order.OrderId)
+ .ToList();
+ if (ordersIds.Any())
+ {
+ return GetOrdersInternal(ordersIds);
+ }
+
+ LogConsumer.Warning("No orders between {0:G} and {1:G} found", startDate, endDate);
+ return new Order[0];
+ }
+
+ ///
+ /// Gets the orders list by status and date range of order's placed date.
+ ///
+ /// The status of orders to get.
+ /// The start date of the range.
+ /// The end date of the range.
+ /// IEnumerable<String>.
+ public IEnumerable GetOrdersList(
+ OrderStatus status,
+ DateTime startDate,
+ DateTime endDate
+ )
+ {
+ LogConsumer.Warning(
+ "Getting orders with status {0} between {1:G} and {2:G}",
+ status.GetHumanReadableValue(),
+ startDate,
+ endDate
+ );
+ var orders = GetOrdersListInternal(status.GetInternalValue(), startDate, endDate);
+ return orders.List;
+ }
+
+ ///
+ /// Get a Enumerable list of Order by status and date range of order's placed date.
+ ///
+ /// The status of orders to get.
+ /// The start date of the range.
+ /// The end date of the range.
+ /// IEnumerable<Order>.
+ public IEnumerable GetOrders(
+ OrderStatus status,
+ DateTime startDate,
+ DateTime endDate
+ )
+ {
+ var ordersIds = GetOrdersList(status, startDate, endDate)
+ .Select(order => order.OrderId)
+ .ToList();
+ if (ordersIds.Any())
+ {
+ return GetOrdersInternal(ordersIds);
+ }
+
+ LogConsumer.Warning(
+ "No order with status {0} between {1:G} and {2:G} found",
+ status.GetHumanReadableValue(),
+ startDate,
+ endDate
+ );
+ return new Order[0];
+ }
+
+ ///
+ /// Gets the orders list by status and affiliated identifier (AKA marketplace).
+ ///
+ /// The status of orders to get.
+ /// The affiliated identifier
+ /// IEnumerable<String>.
+ public IEnumerable GetOrdersList(OrderStatus status, string affiliatedId)
+ {
+ LogConsumer.Warning(
+ "Getting orders with status {0} and affiliated {1}",
+ status.GetHumanReadableValue(),
+ affiliatedId
+ );
+ var orders = GetOrdersListInternal(
+ status.GetInternalValue(),
+ affiliatedId: affiliatedId
+ );
+ return orders.List;
+ }
+
+ ///
+ /// Get a Enumerable list of Order by status and affiliated identifier (AKA marketplace).
+ ///
+ /// The status of orders to get.
+ /// The affiliated identifier
+ /// IEnumerable<Order>.
+ public IEnumerable GetOrders(OrderStatus status, string affiliatedId)
+ {
+ var ordersIds = GetOrdersList(status, affiliatedId)
+ .Select(order => order.OrderId)
+ .ToList();
+ if (ordersIds.Any())
+ {
+ return GetOrdersInternal(ordersIds);
+ }
+
+ LogConsumer.Warning(
+ "No order with status {0} and affiliated {1} found",
+ status.GetHumanReadableValue(),
+ affiliatedId
+ );
+ return new Order[0];
+ }
+
+ ///
+ /// Gets the orders list by generic query (order id, client's document, sequence, etc).
+ ///
+ /// The query to lookup in orders.
+ /// IEnumerable<String>.
+ public IEnumerable GetOrdersList(string query)
+ {
+ LogConsumer.Warning("Getting orders with term '{0}'", query);
+ var orders = GetOrdersListInternal(genericQuery: query);
+ return orders.List;
+ }
+
+ ///
+ /// Gets the orders by generic query (order identifier, client's document, sequence, etc).
+ ///
+ /// The query to lookup in orders.
+ /// IEnumerable<Order>.
+ public IEnumerable GetOrders(string query)
+ {
+ var ordersIds = GetOrdersList(query).Select(order => order.OrderId).ToList();
+ if (ordersIds.Any())
+ {
+ return GetOrdersInternal(ordersIds);
+ }
+
+ LogConsumer.Warning("No orders with term '{0}' found", query);
+ return new Order[0];
+ }
+
+ ///
+ /// Gets the orders by the array of orders identifiers
+ ///
+ /// The orders ids.
+ /// IEnumerable<Order>.
+ public IEnumerable GetOrders(string[] ordersIds)
+ {
+ return GetOrdersInternal(ordersIds);
+ }
+
+ ///
+ /// Cancels the order asynchronous.
+ ///
+ /// The order identifier.
+ /// A Task<System.String> representing the asynchronous operation.
+ /// Order {orderId} cannot be canceled because isn't in pending payment status on VTEX
+ public async Task CancelOrderAsync(string orderId)
+ {
+ try
+ {
+ LogConsumer.Warning("Cancelling order {0}", orderId);
+ var source = new CancellationTokenSource(new TimeSpan(0, 5, 0));
+ var order = GetOrder(orderId);
+ if (order.Status == OrderStatus.CANCELED)
+ {
+ return string.Empty;
+ }
+
+ if (
+ order.Status != OrderStatus.PAYMENT_PENDING
+ && order.Status != OrderStatus.AWAITING_AUTHORIZATION_TO_DISPATCH
+ )
+ {
+ throw new InvalidOperationException(
+ $"Order {orderId} cannot be canceled because isn't in pending payment status on VTEX"
+ );
+ }
+
+ var json = await _wrapper
+ .ServiceInvokerAsync(
+ HttpRequestMethod.POST,
+ $"{PlatformConstants.OmsOrders}/{orderId}/cancel",
+ source.Token
+ )
+ .ConfigureAwait(false);
+ var receipt = SerializerFactory
+ .GetSerializer()
+ .Deserialize(json);
+ LogConsumer.Info(
+ "Order {0} successfully canceled. Receipt: {1}",
+ order.Sequence,
+ receipt.Receipt
+ );
+ return receipt.Receipt;
+ }
+ catch (Exception e)
+ {
+ LogConsumer.Handle(new CancelOrderException(orderId, e));
+ return string.Empty;
+ }
+ }
+
+ ///
+ /// Changes the order status asynchronous.
+ ///
+ /// The order identifier.
+ /// The new status.
+ /// A Task representing the asynchronous operation.
+ ///
+ public async Task ChangeOrderStatusAsync(string orderId, OrderStatus newStatus)
+ {
+ try
+ {
+ LogConsumer.Info(
+ "Changing order {0} status to {1}",
+ orderId,
+ newStatus.GetHumanReadableValue()
+ );
+ var source = new CancellationTokenSource(new TimeSpan(0, 5, 0));
+ var json = await _wrapper
+ .ServiceInvokerAsync(
+ HttpRequestMethod.POST,
+ $"{PlatformConstants.OmsOrders}/{orderId}/changestate/{newStatus.GetInternalValue()}",
+ source.Token
+ )
+ .ConfigureAwait(false);
+ LogConsumer.Info(json);
+ }
+ catch (AggregateException e)
+ {
+ var ae = e.InnerExceptions.First();
+ throw new ChangeStatusOrderException(
+ orderId,
+ newStatus.GetHumanReadableValue(),
+ ae
+ );
+ }
+ catch (Exception e)
+ {
+ throw new ChangeStatusOrderException(orderId, newStatus.GetHumanReadableValue(), e);
+ }
+ }
+
+ ///
+ /// Notifies the order paid asynchronous.
+ ///
+ /// The order identifier.
+ /// A Task representing the asynchronous operation.
+ public async Task NotifyOrderPaidAsync(string orderId)
+ {
+ try
+ {
+ LogConsumer.Info("Sending payment notification of order {0}", orderId);
+ var source = new CancellationTokenSource(new TimeSpan(0, 5, 0));
+ var order = GetOrder(orderId);
+ if (
+ order.Status != OrderStatus.PAYMENT_PENDING
+ && order.Status != OrderStatus.AWAITING_AUTHORIZATION_TO_DISPATCH
+ )
+ {
+ return;
+ }
+
+ if (order.Status == OrderStatus.AWAITING_AUTHORIZATION_TO_DISPATCH)
+ {
+ await ChangeOrderStatusAsync(order.OrderId, OrderStatus.AUTHORIZE_FULFILLMENT)
+ .ConfigureAwait(false);
+ return;
+ }
+ var paymentId = order.PaymentData.Transactions.First().Payments.First().Id;
+ _wrapper
+ .ServiceInvokerAsync(
+ HttpRequestMethod.POST,
+ $"{PlatformConstants.OmsOrders}/{order.OrderId}/payments/{paymentId}/payment-notification",
+ source.Token
+ )
+ .Wait(source.Token);
+ }
+ catch (Exception e)
+ {
+ LogConsumer.Handle(new PaymentNotificationOrderException(orderId, e));
+ }
+ }
+
+ ///
+ /// Notifies the order shipped.
+ ///
+ /// The order identifier.
+ /// The notification.
+ ///
+ public void NotifyOrderShipped(string orderId, ShippingNotification notification)
+ {
+ NotifyOrderShippedAsync(orderId, notification, CancellationToken.None).Wait();
+ }
+
+ ///
+ /// Notifies the order shipped async.
+ ///
+ /// The order identifier.
+ /// The notification.
+ /// The token.
+ /// A Task<System.String> representing the asynchronous operation.
+ ///
+ public async Task NotifyOrderShippedAsync(
+ string orderId,
+ ShippingNotification notification,
+ CancellationToken token
+ )
+ {
+ try
+ {
+ LogConsumer.Info("Sending shipping notification of order {0}", orderId);
+ LogConsumer.Debug(
+ notification,
+ $"vtex-shipping-notification-{orderId}-{notification.InvoiceNumber}.js"
+ );
+ var json = await _wrapper
+ .ServiceInvokerAsync(
+ HttpRequestMethod.POST,
+ $"{PlatformConstants.OmsInvoices}/{orderId}/invoice",
+ token,
+ data: (string)notification.GetSerializer()
+ )
+ .ConfigureAwait(false);
+ var receipt = SerializerFactory.GetSerializer().Deserialize(json);
+ LogConsumer.Trace(receipt.Receipt);
+ return receipt.Receipt;
+ }
+ catch (AggregateException e)
+ {
+ var ae = e.InnerExceptions.First();
+ throw new ShippingNotificationOrderException(orderId, ae);
+ }
+ catch (Exception e)
+ {
+ throw new ShippingNotificationOrderException(orderId, e);
+ }
+ }
+
+ ///
+ /// Notifies the order delivered
+ ///
+ /// The tracking.
+ /// System.String.
+ ///
+ public async ValueTask NotifyOrderDelivered(Tracking tracking)
+ {
+ try
+ {
+ LogConsumer.Info("Sending tracking info of order {0}", tracking.OrderId);
+ var source = new CancellationTokenSource(new TimeSpan(0, 5, 0));
+ LogConsumer.Debug(
+ tracking,
+ $"vtex-tracking-info-{tracking.OrderId}-{tracking.InvoiceNumber}.js"
+ );
+ var json = await _wrapper
+ .ServiceInvokerAsync(
+ HttpRequestMethod.PUT,
+ string.Format(
+ PlatformConstants.OmsTracking,
+ tracking.OrderId,
+ tracking.InvoiceNumber
+ ),
+ source.Token,
+ data: (string)tracking.GetSerializer()
+ )
+ .ConfigureAwait(false);
+ var receipt = SerializerFactory.GetSerializer().Deserialize(json);
+ LogConsumer.Trace(receipt.Receipt);
+ return receipt.Receipt;
+ }
+ catch (Exception e)
+ {
+ throw new TrackingNotificationOrderException(tracking.OrderId, e);
+ }
+ }
+
+ ///
+ /// Updates the order invoice.
+ ///
+ /// The order identifier.
+ /// The invoice identifier.
+ /// The notification.
+ ///
+ public void UpdateOrderInvoice(
+ string orderId,
+ string invoiceId,
+ ShippingNotificationPatch notification
+ )
+ {
+ try
+ {
+ LogConsumer.Info("Patching fiscal invoice {1} of order {0}", orderId, invoiceId);
+ var source = new CancellationTokenSource(new TimeSpan(0, 5, 0));
+ LogConsumer.Debug(
+ notification,
+ $"vtex-shipping-notification-{orderId}-{invoiceId}.js"
+ );
+ var json = _wrapper
+ .ServiceInvokerAsync(
+ HttpRequestMethod.PATCH,
+ $"{PlatformConstants.OmsOrders}/{orderId}/invoice/{invoiceId}",
+ source.Token,
+ data: (string)notification.GetSerializer()
+ )
+ .Result;
+ var receipt = SerializerFactory.GetSerializer().Deserialize(json);
+ LogConsumer.Trace(receipt.Receipt);
+ }
+ catch (Exception e)
+ {
+ throw new ShippingNotificationOrderException(orderId, e);
+ }
+ }
+
+ ///
+ /// Changes the order.
+ ///
+ /// The order identifier.
+ /// The change.
+ ///
+ public void ChangeOrder(string orderId, ChangeOrder change)
+ {
+ try
+ {
+ LogConsumer.Info("Changing order {0}", orderId);
+ LogConsumer.Debug(change, $"vtex-change-order-{orderId}.js");
+ var json = _wrapper
+ .ServiceInvokerAsync(
+ HttpRequestMethod.POST,
+ $"{PlatformConstants.OmsOrders}/{orderId}/changes",
+ CancellationToken.None,
+ data: (string)change.GetSerializer()
+ )
+ .Result;
+ var receipt = SerializerFactory.GetSerializer().Deserialize(json);
+ LogConsumer.Trace(receipt.Receipt);
+ }
+ catch (Exception e)
+ {
+ throw new ChangeOrderException(orderId, e);
+ }
+ }
+
+ #endregion
+
+ #region PCI Gateway
+
+ ///
+ /// Gets the transaction interactions.
+ ///
+ /// The transaction identifier.
+ /// IEnumerable<TransactionInteraction>.
+ ///
+ [Pure]
+ public IEnumerable GetTransactionInteractions(string transactionId)
+ {
+ try
+ {
+ LogConsumer.Info("Getting interactions of transaction {0}", transactionId);
+ var json = _wrapper
+ .ServiceInvokerAsync(
+ HttpRequestMethod.GET,
+ $"{PlatformConstants.PciTransactions}/{transactionId}/interactions",
+ CancellationToken.None,
+ restEndpoint: RequestEndpoint.PAYMENTS
+ )
+ .Result;
+ return SerializerFactory
+ .GetSerializer>()
+ .Deserialize(json);
+ }
+ catch (Exception e)
+ {
+ throw new TransactionException(transactionId, e);
+ }
+ }
+
+ #endregion
+
+ #region Stock
+
+ ///
+ /// get sku reservations as an asynchronous operation.
+ ///
+ /// The sku identifier.
+ /// The warehouse identifier.
+ /// A Task<System.Int32> representing the asynchronous operation.
+ public async Task GetSkuReservationsAsync(int skuId, string warehouseId)
+ {
+ try
+ {
+ LogConsumer.Info(
+ "Getting reservations of SKU {0} in the warehouse {1}",
+ skuId,
+ warehouseId
+ );
+ var source = new CancellationTokenSource(new TimeSpan(0, 5, 0));
+ var json = await _wrapper
+ .ServiceInvokerAsync(
+ HttpRequestMethod.GET,
+ $"{PlatformConstants.LogReservations}/{warehouseId}/{skuId}",
+ source.Token
+ )
+ .ConfigureAwait(false);
+ var reservations = SerializerFactory
+ .GetSerializer()
+ .Deserialize(json);
+ LogConsumer.Debug(reservations, $"vtex-sku-reservations-{skuId}.js");
+ var total = !reservations.Items.Any() ? 0 : reservations.Items.Sum(r => r.Quantity);
+ LogConsumer.Info(
+ "The SKU {0} has {1} units reserved in warehouse {2}",
+ skuId,
+ total,
+ warehouseId
+ );
+ return total;
+ }
+ catch (Exception e)
+ {
+ LogConsumer.Handle(new ProductExportException(skuId, e));
+ return 0;
+ }
+ }
+
+ ///
+ /// Gets the sku inventory.
+ ///
+ /// The sku identifier.
+ /// Inventory.
+ public async Task GetSkuInventoryAsync(int skuId)
+ {
+ LogConsumer.Info("Getting inventory of SKU {0}", skuId);
+ var source = new CancellationTokenSource(new TimeSpan(0, 5, 0));
+ var json = await _wrapper
+ .ServiceInvokerAsync(
+ HttpRequestMethod.GET,
+ $"{PlatformConstants.LogInventory}/{skuId}",
+ source.Token,
+ restEndpoint: RequestEndpoint.LOGISTICS
+ )
+ .ConfigureAwait(false);
+ var inventory = SerializerFactory.GetSerializer().Deserialize(json);
+ LogConsumer.Debug(inventory, $"vtex-sku-inventory-{skuId}.js");
+ return inventory;
+ }
+
+ ///
+ /// Updates the sku stock.
+ ///
+ /// The stock information.
+ /// A Task representing the asynchronous operation.
+ ///
+ public async Task UpdateSkuStockAsync(StockInfo stockInfo)
+ {
+ try
+ {
+ if (stockInfo.Quantity < 0)
+ {
+ stockInfo.Quantity = 0;
+ }
+
+ stockInfo.DateUtcOnBalanceSystem = null;
+ if (!stockInfo.UnlimitedQuantity)
+ {
+ stockInfo.Quantity += await GetSkuReservationsAsync(
+ stockInfo.ItemId,
+ stockInfo.WareHouseId
+ )
+ .ConfigureAwait(false);
+ }
+
+ LogConsumer.Info(
+ "Updating inventory of SKU {0} on warehouse {1} with {2} units",
+ stockInfo.ItemId,
+ stockInfo.WareHouseId,
+ stockInfo.Quantity
+ );
+ var source = new CancellationTokenSource(new TimeSpan(0, 5, 0));
+ var data = @"[" + (string)stockInfo.GetSerializer() + @"]";
+ LogConsumer.Debug(stockInfo, $"vtex-sku-stock-{stockInfo.ItemId}.js");
+ await _wrapper
+ .ServiceInvokerAsync(
+ HttpRequestMethod.POST,
+ PlatformConstants.LogWarehouses,
+ source.Token,
+ data: data
+ )
+ .ConfigureAwait(false);
+ }
+ catch (Exception e)
+ {
+ throw new UpdateStockInfoSKUException(stockInfo.ItemId, e);
+ }
+ }
+
+ #endregion
+
+ #region Pricing
+
+ ///
+ /// Get the prices for an SKU.
+ /// It is possible that on the property "fixedPrices" exists a list of specific prices for Trade Policies and Minimum Quantities of the SKU.Fixed Prices may also be scheduled.
+ ///
+ /// The stock keeping unit identifier
+ /// A task of price
+ public async Task GetPriceAsync(int skuId)
+ {
+ LogConsumer.Info("Getting the price of sku {0}", skuId);
+ var source = new CancellationTokenSource(new TimeSpan(0, 5, 0));
+ try
+ {
+ var json = await _wrapper
+ .ServiceInvokerAsync(
+ HttpRequestMethod.GET,
+ $@"{PlatformConstants.Pricing}/{skuId}",
+ source.Token,
+ restEndpoint: RequestEndpoint.API
+ )
+ .ConfigureAwait(false);
+ return SerializerFactory.GetSerializer().Deserialize(json);
+ }
+ catch (UnexpectedApiResponseException e)
+ {
+ if (e.StatusCode == 404)
+ {
+ return new Price();
+ }
+
+ throw;
+ }
+ }
+
+ ///
+ /// This method will create or update an SKU Price.
+ /// The property "basePrice" is the base selling price of the SKU.The property "fixedPrices" is an array where each item is a Fixed Price.
+ /// The Fixed Price is the price of the SKU for an specific Trade Policy with an specific Minimum Quantity to be activated.
+ /// A Fixed Price may optionally be scheduled by using the property dateRange.
+ /// A Fixed Price may optionally overwrite the listPrice specified in the Base Price by using the inner property listPrice.
+ /// If you don't have specific prices for different Trade Policies, you do not need to send the property fixedPrices.
+ ///
+ /// The price data
+ /// The stock keeping unit identifier
+ /// The cancellation token.
+ /// A Task representing the asynchronous operation.
+ ///
+ public async Task UpdatePriceAsync(Price price, int skuId, CancellationToken token)
+ {
+ try
+ {
+ var oldPrice = await GetPriceAsync(skuId).ConfigureAwait(false);
+ if (oldPrice?.FixedPrices != null && oldPrice.FixedPrices.Any())
+ {
+ await DeletePriceAsync(skuId, token).ConfigureAwait(false);
+ }
+
+ LogConsumer.Info(
+ "Updating the price of sku {0} to {1} (list price: {2})",
+ skuId,
+ price.CostPrice.ToMonetary(),
+ price.ListPrice.HasValue ? price.ListPrice.Value.ToMonetary() : "no"
+ );
+ await _wrapper
+ .ServiceInvokerAsync(
+ HttpRequestMethod.PUT,
+ $@"{PlatformConstants.Pricing}/{skuId}",
+ token,
+ data: (string)price.GetSerializer(),
+ restEndpoint: RequestEndpoint.API
+ )
+ .ConfigureAwait(false);
+ }
+ catch (Exception e)
+ {
+ throw new UpdatePriceInfoSkuException(skuId, e);
+ }
+ }
+
+ ///
+ /// Removes an SKU price.
+ /// This action removes both Base Price and all available Fixed Prices for and SKU in all trade policies.
+ ///
+ /// The stock keeping unit identifier.
+ /// The cancellation token.
+ /// Task
+ public async Task DeletePriceAsync(int skuId, CancellationToken token)
+ {
+ LogConsumer.Info("Deleting the price of sku {0}", skuId);
+ await _wrapper
+ .ServiceInvokerAsync(
+ HttpRequestMethod.DELETE,
+ $@"{PlatformConstants.Pricing}/{skuId}",
+ token,
+ restEndpoint: RequestEndpoint.API
+ )
+ .ConfigureAwait(false);
+ }
+
+ ///
+ /// Retrieves a collection of bridge facets based on the specified query and optional keywords.
+ ///
+ /// The query string used to filter the bridge facets.
+ /// Optional keywords to further refine the search for bridge facets.
+ /// An enumerable collection of objects that match the specified query and keywords.
+ ///
+ /// This method constructs a query to fetch bridge facets from a remote service. It logs the action of retrieving facets
+ /// and sets a timeout of 5 minutes for the operation. The method builds a dictionary of query parameters, including
+ /// facets to retrieve and the specified query. If keywords are provided, they are added to the query parameters as well.
+ /// The method then invokes the service asynchronously and deserializes the resulting JSON response into a list of
+ /// objects. If an exception occurs during this process, a custom
+ /// is thrown, encapsulating the original exception and the query that caused the failure.
+ ///
+ /// Thrown when an error occurs while retrieving bridge facets.
+ [Pure]
+ public IEnumerable GetBridgeFacets(
+ [Localizable(false)] string query,
+ [Localizable(false)] string keywords = null
+ )
+ {
+ try
+ {
+ LogConsumer.Info(
+ "Getting facets in bridge module that satisfy the condition '{0}'",
+ query
+ );
+ var source = new CancellationTokenSource(new TimeSpan(0, 5, 0));
+ var queryString = new Dictionary
+ {
+ { @"_facets", @"Origin,Status" },
+ { @"_where", query },
+ };
+ if (!string.IsNullOrWhiteSpace(keywords))
+ {
+ queryString.Add(@"_keywords", $@"*{keywords}*");
+ }
+
+ var json = _wrapper
+ .ServiceInvokerAsync(
+ HttpRequestMethod.GET,
+ $"{PlatformConstants.BridgeSearch}/facets",
+ source.Token,
+ queryString,
+ restEndpoint: RequestEndpoint.BRIDGE
+ )
+ .Result;
+ return SerializerFactory.GetSerializer>().Deserialize(json);
+ }
+ catch (Exception e)
+ {
+ throw new BridgeException(query, e);
+ }
+ }
+
+ ///
+ /// Retrieves a collection of bridge items based on the specified query parameters.
+ ///
+ /// The query string used to filter the bridge items.
+ /// The sorting criteria for the returned items.
+ /// Additional keywords to refine the search results.
+ /// The starting point for the items to be retrieved.
+ /// The maximum number of items to return.
+ /// An enumerable collection of that match the specified criteria.
+ ///
+ /// This method interacts with an external service to fetch bridge items based on the provided query, sort, and keywords.
+ /// It logs the request details and checks for an offset limit to avoid exceeding the maximum allowed items from the service.
+ /// If the offset exceeds 10,000, a warning is logged, and an empty list is returned.
+ /// The method uses a cancellation token to set a timeout for the service call, ensuring that it does not hang indefinitely.
+ /// In case of an error during the service call, it throws a custom exception with details about the failure.
+ ///
+ ///
+ /// Thrown when an error occurs while retrieving bridge items from the external service.
+ ///
+ [Pure]
+ public IEnumerable GetBridgeItems(
+ [Localizable(false)] string query,
+ [Localizable(false)] string sort,
+ [Localizable(false)] string keywords,
+ int offSet,
+ int limit
+ )
+ {
+ try
+ {
+ LogConsumer.Info(
+ "Getting {0} items from {1} in bridge module that satisfy the condition '{2}'",
+ limit,
+ offSet,
+ query
+ );
+ if (offSet >= 10000)
+ {
+ LogConsumer.Warning(
+ "Cannot get more than 10000 items from Bridge / Master Data (VTEX Elastic Search limitation)"
+ );
+ return new List();
+ }
+ var source = new CancellationTokenSource(new TimeSpan(0, 5, 0));
+ var queryString = new Dictionary
+ {
+ { @"_where", query },
+ { @"_sort", sort },
+ { @"offSet", offSet.ToString() },
+ { @"limit", limit.ToString() },
+ };
+ if (!string.IsNullOrWhiteSpace(keywords))
+ {
+ queryString.Add(@"_keywords", $@"*{keywords}*");
+ }
+
+ var json = _wrapper
+ .ServiceInvokerAsync(
+ HttpRequestMethod.GET,
+ PlatformConstants.BridgeSearch,
+ source.Token,
+ queryString,
+ restEndpoint: RequestEndpoint.BRIDGE
+ )
+ .Result;
+ return SerializerFactory.GetSerializer>().Deserialize(json);
+ }
+ catch (AggregateException e)
+ {
+ throw new BridgeException(
+ query,
+ e.InnerExceptions.FirstOrDefault() ?? e.InnerException ?? e
+ );
+ }
+ catch (Exception e)
+ {
+ throw new BridgeException(query, e);
+ }
+ }
+
+ ///
+ /// Gets all bridge items.
+ ///
+ /// The query.
+ /// The sort.
+ /// The keywords.
+ /// Name of the facet.
+ /// The facet value.
+ /// IEnumerable<BridgeItem>.
+ [Pure]
+ public IEnumerable GetAllBridgeItems(
+ [Localizable(false)] string query,
+ [Localizable(false)] string sort,
+ [Localizable(false)] string keywords,
+ [Localizable(false)] string facetName,
+ [Localizable(false)] string facetValue
+ )
+ {
+ const int perPage = 100;
+ var facets = GetBridgeFacets(query, keywords);
+ var total = facets.Single(f => f.Field.Equals(facetName)).Facets[facetValue].ToInt32();
+
+ var result = new List(total);
+ var pages = (total / perPage) + 1;
+ for (var x = 0; x < pages; x++)
+ {
+ result.AddRange(GetBridgeItems(query, sort, keywords, x * perPage, perPage));
+ }
+
+ return result;
+ }
+
+ #endregion
+
+ #region Platform status
+
+ ///
+ /// Gets the platform status.
+ ///
+ /// IEnumerable<PlatformStatus>.
+ public IEnumerable GetPlatformStatus()
+ {
+ return GetPlatformStatusAsync(CancellationToken.None).Result;
+ }
+
+ ///
+ /// Gets the platform status asynchronous.
+ ///
+ /// The token.
+ /// A Task<IEnumerable`1> representing the asynchronous operation.
+ [Pure]
+ public async Task> GetPlatformStatusAsync(
+ CancellationToken token
+ )
+ {
+ LogConsumer.Info("Getting platform status");
+ var json = await _wrapper
+ .ServiceInvokerAsync(
+ HttpRequestMethod.GET,
+ string.Empty,
+ token,
+ restEndpoint: RequestEndpoint.HEALTH
+ )
+ .ConfigureAwait(false);
+ var status = SerializerFactory.GetSerializer>().Deserialize(json);
+ LogConsumer.Debug(status, "vtex-platform-status.js");
+ return status;
+ }
+
+ #endregion
+
+ #region Order payments
+
+ ///
+ /// Gets the order payments.
+ ///
+ /// The transaction identifier.
+ /// List<PciPayment>.
+ [Pure]
+ public List GetOrderPayments(string transactionId)
+ {
+ var json = _wrapper
+ .ServiceInvokerAsync(
+ HttpRequestMethod.GET,
+ $"{PlatformConstants.PciTransactions}/{transactionId}/payments",
+ CancellationToken.None,
+ restEndpoint: RequestEndpoint.PAYMENTS
+ )
+ .Result;
+ if (json == null)
+ {
+ return new List();
+ }
+
+ var data = SerializerFactory
+ .GetCustomSerializer>(SerializerFormat.Json)
+ .Deserialize(json);
+ LogConsumer.Debug(data, $"vtex-order-payemnts-{transactionId}.js");
+ return data;
+ }
+
+ #endregion
+
+ #region Catalog
+
+ #region Specification
+
+ ///
+ /// Gets the specification field asynchronous.
+ ///
+ /// The field identifier.
+ /// The token.
+ /// A Task<SpecificationField> representing the asynchronous operation.
+ [Pure]
+ public async Task GetSpecificationFieldAsync(
+ int fieldId,
+ CancellationToken token
+ )
+ {
+ LogConsumer.Info("Getting field for the field id {0}", fieldId);
+ var json = await _wrapper
+ .ServiceInvokerAsync(
+ HttpRequestMethod.GET,
+ $@"{PlatformConstants.CatalogPub}/specification/fieldGet/{fieldId}",
+ token
+ )
+ .ConfigureAwait(false);
+ var field = SerializerFactory.GetSerializer().Deserialize(json);
+ LogConsumer.Debug(field, $"vtex-specification-field-{fieldId}.js");
+ return field;
+ }
+
+ ///
+ /// Gets the specification field values asynchronous.
+ ///
+ /// The field identifier.
+ /// The token.
+ /// A Task<ICollection`1> representing the asynchronous operation.
+ [Pure]
+ public async Task> GetSpecificationFieldValuesAsync(
+ int fieldId,
+ CancellationToken token
+ )
+ {
+ LogConsumer.Info("Getting field values for the field id {0}", fieldId);
+ var json = await _wrapper
+ .ServiceInvokerAsync(
+ HttpRequestMethod.GET,
+ $@"{PlatformConstants.CatalogPub}/specification/fieldvalue/{fieldId}",
+ token
+ )
+ .ConfigureAwait(false);
+ var fieldValues = SerializerFactory
+ .GetSerializer>()
+ .Deserialize(json);
+ LogConsumer.Debug(fieldValues, $"vtex-specification-values-{fieldId}.js");
+ return fieldValues;
+ }
+
+ ///
+ /// Updates the product specification asynchronous.
+ ///
+ /// The specification.
+ /// The product identifier.
+ /// The token.
+ /// A Task representing the asynchronous operation.
+ public async Task UpdateProductSpecificationAsync(
+ Specification specification,
+ int productId,
+ CancellationToken token
+ )
+ {
+ await UpdateProductSpecificationsAsync(
+ new List(new[] { specification }),
+ productId,
+ token
+ )
+ .ConfigureAwait(false);
+ }
+
+ ///
+ /// Updates the product specifications asynchronous.
+ ///
+ /// The specifications list.
+ /// The product identifier.
+ /// The token.
+ /// A Task representing the asynchronous operation.
+ public async Task UpdateProductSpecificationsAsync(
+ List specifications,
+ int productId,
+ CancellationToken token
+ )
+ {
+ LogConsumer.Info(
+ "Updating the specifications {1} of product {0}",
+ productId,
+ string.Join(@",", specifications.Select(s => s.Id))
+ );
+
+ var data = (string)specifications.GetSerializer();
+ await _wrapper
+ .ServiceInvokerAsync(
+ HttpRequestMethod.POST,
+ $@"{PlatformConstants.Catalog}/products/{productId}/specification",
+ token,
+ data: data
+ )
+ .ConfigureAwait(false);
+ }
+
+ ///
+ /// Inserts the specification field value asynchronous.
+ ///
+ /// The field value.
+ /// The token.
+ /// A Task representing the asynchronous operation.
+ public async Task InsertSpecificationFieldValueAsync(
+ SpecificationFieldValue fieldValue,
+ CancellationToken token
+ )
+ {
+ LogConsumer.Info("Creating field value of field id {0}", fieldValue.FieldId);
+ var data = (string)fieldValue.GetSerializer();
+ await _wrapper
+ .ServiceInvokerAsync(
+ HttpRequestMethod.POST,
+ $@"{PlatformConstants.Catalog}/specification/fieldValue",
+ token,
+ data: data
+ )
+ .ConfigureAwait(false);
+ }
+
+ ///
+ /// Asynchronously searches for a data entity based on a specified field and value.
+ ///
+ /// The type of the data entity to search for, which must implement .
+ /// The field of the data entity to search against.
+ /// The value to search for in the specified field.
+ /// A cancellation token to monitor for cancellation requests.
+ /// A task that represents the asynchronous operation. The task result contains the found data entity of type or null if no entity is found.
+ ///
+ /// This method performs an asynchronous search for a data entity by sending a GET request to the specified endpoint.
+ /// It constructs a query string using the provided field and value, and invokes a service to retrieve the data.
+ /// If the search value is null or whitespace, an is thrown.
+ /// In case of an unexpected API response, an is thrown, containing the JSON response and the original exception.
+ /// The method logs the retrieved entity for debugging purposes.
+ ///
+ /// Thrown when is null or whitespace.
+ /// Thrown when the API response is unexpected.
+ [Pure]
+ public async Task SearchAsync(
+ string searchedField,
+ string searchedValue,
+ CancellationToken token
+ )
+ where TDataEntity : class, IDataEntity, new()
+ {
+ if (string.IsNullOrWhiteSpace(searchedValue))
+ {
+ throw new ArgumentNullException(nameof(searchedValue));
+ }
+
+ var queryString = new Dictionary
+ {
+ { searchedField, searchedValue },
+ { @"_fields", @"_all" },
+ };
+ var json = string.Empty;
+ try
+ {
+ var entityName = typeof(TDataEntity).GetDataEntityName();
+ json = await _wrapper
+ .ServiceInvokerAsync(
+ HttpRequestMethod.GET,
+ $@"dataentities/{entityName}/search/",
+ token,
+ queryString,
+ restEndpoint: RequestEndpoint.MASTER_DATA
+ )
+ .ConfigureAwait(false);
+ var entity = SerializerFactory
+ .GetSerializer>()
+ .Deserialize(json)
+ .FirstOrDefault();
+ if (entity == null)
+ {
+ return null;
+ }
+
+ LogConsumer.Debug(
+ entity,
+ $@"vtex-masterdata-entity-{entityName}-{searchedField}-{searchedValue}.js"
+ );
+ return entity;
+ }
+ catch (Exception e)
+ {
+ throw new UnexpectedApiResponseException(json, e);
+ }
+ }
+
+ #endregion
+
+ #endregion
+
+ #endregion
+
+ #region IDisposable
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ public void Dispose()
+ {
+ _wrapper.Dispose();
+ }
+
+ #endregion
diff --git a/Src/VTEX/VTEXWrapper.cs b/Src/VTEX/VTEXWrapper.cs
index 3140fe613..10520a1b3 100644
--- a/Src/VTEX/VTEXWrapper.cs
+++ b/Src/VTEX/VTEXWrapper.cs
@@ -1,531 +1,621 @@
-// ***********************************************************************
-// 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.
+//
+//
+using System.Net.Http.Headers;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+namespace VTEX
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using CrispyWaffle.Extensions;
+using CrispyWaffle.Log;
+
+ ///
+ /// 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
+
+ ///
+
+ /// Retrieves a list of all collections.
+ ///
+ /// A cancellation token to observe while waiting for the task to complete.
+ /// A task that represents the asynchronous operation, containing the list of collections as a string.
+ public async Task GetCollectionsAsync(CancellationToken token)
+ {
+ return await ServiceInvokerAsync(HttpRequestMethod.GET, "collections", token);
+ }
+
+
+ ///
+ /// Updates an existing collection.
+ ///
+ /// The identifier of the collection to be updated.
+ /// The data representing the updated collection.
+ /// A cancellation token to observe while waiting for the task to complete.
+ /// A task that represents the asynchronous operation, containing the response as a string.
+ public async Task UpdateCollectionAsync(int id, string data, CancellationToken token)
+ {
+
+ return await ServiceInvokerAsync(HttpRequestMethod.PUT, $"collections/{id}", token, data: data);
+}
+ /// Deletes a collection.
+ ///
+ /// The identifier of the collection to be deleted.
+ /// A cancellation token to observe while waiting for the task to complete.
+ /// A task that represents the asynchronous operation, containing the response as a string.
+ public async Task DeleteCollectionAsync(int id, CancellationToken token)
+ {
+ return await ServiceInvokerAsync(HttpRequestMethod.DELETE, $"collections/{id}", token);
+
+ }
+ ///
+ /// Creates a new collection.
+ ///
+ /// The data representing the new collection to be created.
+ /// A cancellation token to observe while waiting for the task to complete.
+ /// A task that represents the asynchronous operation, containing the response as a string.
+ public async Task CreateCollectionAsync(string data, CancellationToken token)
+ {
+ return await ServiceInvokerAsync(HttpRequestMethod.POST, "collections", token, data: data);
+
+ }
+
+ ///
+ /// 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.
+ 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
+