SP-API stock reconciliation

Amazon had depreciated a number of reports that were used for stock reconciliation. Application now uses the new fba ledger report to reconcile. It is currently untested, as this requires data from Amazon. Methods that require testing will return a 'NotImplementedException'.

Also, removed the depreciated ILMerge and replaced with ILRepack.

Plus much more tidying up, and improvements.
This commit is contained in:
Bobbie Hodgetts
2024-05-07 08:24:00 +01:00
committed by GitHub
parent 2f919d7b5a
commit 91ef9acc78
1272 changed files with 4944 additions and 2773311 deletions
@@ -12,6 +12,7 @@ namespace bnhtrade.Core.Data.Amazon.ProductFee
{
public class GetFeeEstimate
{
private Logic.Log.LogEvent log = new Logic.Log.LogEvent();
private AmazonConnection amznConn = new SpApiConnection().Connection;
public string CurrencyCode { get; set; } = "GBP";
@@ -56,7 +57,7 @@ namespace bnhtrade.Core.Data.Amazon.ProductFee
if (result.Error.Type != null)
{ sb.AppendLine("Type: " + result.Error.Type); }
new Logic.Log.LogEvent().EventLogInsert("Error running GetProductEstimateFee for ASIN:" + asin + ", further details attached.", 1, sb.ToString());
log.LogError("Error running GetProductEstimateFee for ASIN:" + asin + ", further details attached.", sb.ToString());
throw new Exception("ProductFeeEstimate error, check logs for further details");
}
@@ -17,7 +17,7 @@ namespace bnhtrade.Core.Data.Amazon.Report
{
// Amazon will throttle (return 'FATAL' error) when requesting this report twice within 15 minutes
DownloadSnapshotReport(reportMaxAge);
DownloadReport(reportMaxAge);
}
}
}
@@ -0,0 +1,91 @@
using CsvHelper;
using FikaAmazonAPI.AmazonSpApiSDK.Models.FulfillmentOutbound;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using static FikaAmazonAPI.Utils.Constants;
using System.Linq;
using CsvHelper.Configuration;
using NUnit.Framework.Interfaces;
namespace bnhtrade.Core.Data.Amazon.Report
{
public class FbaInventoryLedgerDetailed : ReportLogic
{
List<Model.Import.AmazonFbaInventoryLedgerDetail> resultList = null;
/// <summary>
/// Get report will download the file to local storage. Use this to transpose the data to a model class.
/// </summary>
public List<Model.Import.AmazonFbaInventoryLedgerDetail> ResultList
{
get
{
if (resultList == null)
{
TransposeFileToClass();
}
return resultList;
}
}
public bool IsSetResultList
{
get { return IsSetReportFilePath; }
}
public FbaInventoryLedgerDetailed() : base(ReportTypes.GET_LEDGER_DETAIL_VIEW_DATA)
{
}
public new void Init()
{
base.Init();
resultList = null;
}
public void GetReport(DateTime utcDataStartTime, DateTime utcDataEndTime)
{
// 18 months max historical transactions for report
DownloadTimePeriodReport(utcDataStartTime, utcDataEndTime, 0, 547);
}
private void TransposeFileToClass()
{
var transposeResult = new List<Model.Import.AmazonFbaInventoryLedgerDetail>();
if (IsSetReportFilePath == false)
{
resultList = transposeResult;
}
else
{
using (var reader = new StreamReader(ReportFilePath))
{
// setup csvhelper
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
Delimiter = "\t",
};
using (var csvreader = new CsvReader(reader, config))
{
// check header count
csvreader.Read();
csvreader.ReadHeader();
int columns = csvreader.HeaderRecord.Count();
if (columns < 15)
throw new Exception("Malformed report 'GET_LEDGER_DETAIL_VIEW_DATA' does not have enough headers");
if (columns > 15)
log.LogError("New column in 'GET_LEDGER_DETAIL_VIEW_DATA'");
// transpose the data
transposeResult = csvreader.GetRecords<Model.Import.AmazonFbaInventoryLedgerDetail>().ToList();
resultList = transposeResult;
}
}
}
}
}
}
@@ -1,11 +1,13 @@
using bnhtrade.Core.Data.Amazon.SellingPartnerAPI;
using FikaAmazonAPI;
using FikaAmazonAPI.AmazonSpApiSDK.Models.Reports;
using FikaAmazonAPI.Parameter.Report;
using FikaAmazonAPI.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using static FikaAmazonAPI.Utils.Constants;
@@ -13,11 +15,35 @@ namespace bnhtrade.Core.Data.Amazon.Report
{
public class ReportLogic
{
public enum ProcessingStatus
{
NULL,
INQUEUE,
INPROGRESS,
CANCELLED,
DONE,
FATAL
}
private bool _isSetReportProcessingStatus = false;
private AmazonConnection amznConn = new SpApiConnection().Connection;
private FikaAmazonAPI.AmazonSpApiSDK.Models.Reports.Report report;
private FikaAmazonAPI.AmazonSpApiSDK.Models.Reports.Report report = new FikaAmazonAPI.AmazonSpApiSDK.Models.Reports.Report();
private ReportTypes reportType;
protected Logic.Log.LogEvent log = new Logic.Log.LogEvent();
public ProcessingStatus ReportProcessingStatus { get; private set; }
public bool IsSetReportProcessingStatus
{
get
{
if (ReportProcessingStatus == ProcessingStatus.NULL)
return false;
else
return true;
}
}
public DateTime? ReportCreatedTime
{
get
@@ -27,13 +53,19 @@ namespace bnhtrade.Core.Data.Amazon.Report
}
}
/// <summary>
/// The report has completed, but there is no data (and hence no file) to download.
/// </summary>
public bool ReportDoneNoData { get; private set; }
/// <summary>
/// File path the the local location of the file
/// </summary>
public string ReportFilePath { get; private set; }
private FikaAmazonAPI.Utils.MarketplaceIds MarketPlaceIds { get; set; }
public bool ReportFilePathIsSet
public bool IsSetReportFilePath
{
get { return !string.IsNullOrWhiteSpace(ReportFilePath); }
}
@@ -43,35 +75,45 @@ namespace bnhtrade.Core.Data.Amazon.Report
this.reportType = reportType;
this.MarketPlaceIds = new MarketplaceIds();
this.MarketPlaceIds.Add(amznConn.GetCurrentMarketplace.ID);
Innit();
Init();
}
private void Innit()
protected void Init()
{
ReportDoneNoData = false;
ReportFilePath = null;
report = null;
ReportProcessingStatus = ProcessingStatus.NULL;
}
/// <summary>
/// Always use this function to report and it's filepath, this ensures the two correlate
/// For reports that require a start and end period to report over.
/// </summary>
/// <param name="report"></param>
/// <param name="reportFilePath"></param>
private void SetFilePath(FikaAmazonAPI.AmazonSpApiSDK.Models.Reports.Report report, string reportFilePath)
{
this.report = report;
this.ReportFilePath = reportFilePath;
}
protected void DownloadTimePeriodReport(DateTime utcDataStartTime, DateTime utcDataEndTime, int maxDateRangeDays)
/// <param name="utcDataStartTime"></param>
/// <param name="utcDataEndTime"></param>
/// <param name="maxDateRangeDays">The max time in days between the start and end date time</param>
/// <param name="maxDataAgeDays">The max time that Amazon retains the data for, in days</param>
/// <exception cref="Exception"></exception>
protected void DownloadTimePeriodReport(DateTime utcDataStartTime, DateTime utcDataEndTime, int maxDateRangeDays, int maxDataAgeDays = 0)
{
if (utcDataStartTime.Kind != DateTimeKind.Utc || utcDataEndTime.Kind != DateTimeKind.Utc)
throw new Exception("Report period time should be set to UTC");
throw new Exception("Report period time has to be set to UTC");
if (maxDateRangeDays > 0 && (utcDataEndTime - utcDataStartTime).TotalDays > maxDateRangeDays)
throw new Exception("Date range for report is greater than the maximum allowable");
if (maxDataAgeDays < 0)
{
throw new Exception("Max data age in days cannot be less than zero");
}
//else if(maxDataAgeDays > 0)
//{
// if (utcDataStartTime < DateTime.UtcNow.AddDays(maxDataAgeDays * -1))
// {
// throw new Exception("Max historical data for " + reportType.ToString() + " is " + maxDataAgeDays + " days");
// }
//}
var specification = new ParameterCreateReportSpecification();
specification.marketplaceIds = this.MarketPlaceIds;
specification.reportType = reportType;
@@ -81,7 +123,11 @@ namespace bnhtrade.Core.Data.Amazon.Report
DownloadReport(specification, false);
}
protected void DownloadSnapshotReport(int snapshotMaxAge)
/// <summary>
/// Use this to download reports that do not have a time period element, they are as things stand now.
/// </summary>
/// <param name="snapshotMaxAge">In minutes, the minimum time between report requests. Amazon will return 'FATAL' if a report is re-requested in too short a time period </param>
protected void DownloadReport(int snapshotMaxAge)
{
var specification = new ParameterCreateReportSpecification();
specification.marketplaceIds = this.MarketPlaceIds;
@@ -90,64 +136,120 @@ namespace bnhtrade.Core.Data.Amazon.Report
DownloadReport(specification, true, snapshotMaxAge);
}
/// <summary>
/// The main body of the class, where the all the work is done
/// </summary>
/// <param name="specification">the specification built by the supporting methods</param>
/// <param name="isSnapshotReport">ie not a time period report, but an 'as things stand now' report</param>
/// <param name="snapshotMaxAge">time in minutes</param>
/// <exception cref="Exception"></exception>
private void DownloadReport(ParameterCreateReportSpecification specification, bool isSnapshotReport, int snapshotMaxAge = 0)
{
log.LogInformation("Starting report requesting from Amazon SP-API for " + specification.reportType.ToString());
// Amazon create report is throttled to 1 report every 15 minutes
Innit();
Init();
// check for existing report with same specification
string reportId = ExistingReportSearch(specification);
string reportId = ExistingReportCheck(specification);
// no matching report? then create one
if (reportId == null)
{ reportId = amznConn.Reports.CreateReport(specification); }
var filePath = amznConn.Reports.GetReportFile(reportId);
var report = amznConn.Reports.GetReport(reportId); // filePath and report need to be called in this order
WaitWhileProcessing(reportId);
if (!string.IsNullOrWhiteSpace(filePath))
// some real time report will return 'fatal' if the report has already recently been requested, test for this
if (isSnapshotReport && report.ProcessingStatus == FikaAmazonAPI.AmazonSpApiSDK.Models.Reports.Report.ProcessingStatusEnum.FATAL)
{
SetFilePath(report, filePath);
bool aSuitableReportExists = SnapshotReportThrottleHanderler(ref reportId, specification, snapshotMaxAge);
if (aSuitableReportExists == true)
{
log.LogWarning("Create amazon report returned 'FATAL' due to a report with the same parameters already existing, requesting the existing report");
WaitWhileProcessing(reportId);
}
}
// test for processing status
if (report.ProcessingStatus == FikaAmazonAPI.AmazonSpApiSDK.Models.Reports.Report.ProcessingStatusEnum.DONE)
{
ReportProcessingStatus = ProcessingStatus.DONE;
log.LogInformation("Report proccessing status is 'DONE' (ReportId:" + report.ReportId + ").");
if (string.IsNullOrEmpty(report.ReportDocumentId))
throw new Exception("Report processing done, yet there's no report document. Write code to handle this");
}
else if (report.ProcessingStatus == FikaAmazonAPI.AmazonSpApiSDK.Models.Reports.Report.ProcessingStatusEnum.CANCELLED)
{
ReportProcessingStatus = ProcessingStatus.CANCELLED;
// From SPAPI Documentation: The report was cancelled. There are two ways a report can be cancelled:
// an explicit cancellation request before the report starts processing, or an automatic cancellation
// if there is no data to return.
log.Initialise();
log.LogInformation("ReportID:" + reportId + " returned 'CANCELLED', if a cancel request hasn't been made then there is no data in the report.");
ReportDoneNoData = true;
return;
}
else
else if (report.ProcessingStatus == FikaAmazonAPI.AmazonSpApiSDK.Models.Reports.Report.ProcessingStatusEnum.FATAL)
{
// test for 'Fatal' return when report has recently been requested.
report = amznConn.Reports.GetReport(reportId);
if (report.ProcessingStatus == FikaAmazonAPI.AmazonSpApiSDK.Models.Reports.Report.ProcessingStatusEnum.CANCELLED)
ReportProcessingStatus = ProcessingStatus.FATAL;
// AMazon does sometime return a file with information as to why fatal was returned contained within, test for this
if (string.IsNullOrEmpty(report.ReportDocumentId))
{
// From SPAPI Documentation: The report was cancelled. There are two ways a report can be cancelled:
// an explicit cancellation request before the report starts processing, or an automatic cancellation
// if there is no data to return.
log.Initialise();
log.LogInformation("ReportID:" + reportId + " returned 'CANCELLED' status (no data).");
ReportDoneNoData = true;
log.LogError("ReportID:" + reportId + " returned 'FATAL', no further information was recived.");
return;
}
else if (report.ProcessingStatus == FikaAmazonAPI.AmazonSpApiSDK.Models.Reports.Report.ProcessingStatusEnum.FATAL)
{
if (isSnapshotReport)
{
SnapahotThrottleHanderler(ref report, specification, snapshotMaxAge);
filePath = amznConn.Reports.GetReportFile(report.ReportId);
if (string.IsNullOrWhiteSpace(filePath))
{
LogReportError(report, "Error while retriving report " + reportType.ToString());
throw new Exception("Error while retriving report " + reportType.ToString());
}
SetFilePath(report, filePath);
return;
}
else
{
throw new Exception("Report processing status returned 'FATAL' report#" + reportType.ToString());
}
}
}
throw new Exception("Error while retriving " + reportType.ToString() + " report, nothing should be getting here!!");
// if we get here, onto downloading the file
if (string.IsNullOrEmpty(report.ReportDocumentId))
throw new Exception("ReportDocumentId field is null or empty.");
// as file url expires after 5 minutes, request it just before download
UI.Console.WriteLine("Requesting download url for Amazon report (ReportId:" + reportId + ").");
var amazonFileInfo = amznConn.Reports.GetReportDocument(report.ReportDocumentId);
// download report from url
log.LogInformation("Downloading Amazon report #" + reportId, "Report Document ID: " + amazonFileInfo.ReportDocumentId);
byte[] response = null;
using (var webClient = new System.Net.WebClient())
{
response = webClient.DownloadData(amazonFileInfo.Url);
}
// decrypt report
UI.Console.WriteLine("Processing download (ReportId:" + reportId + ").");
if (amazonFileInfo.EncryptionDetails != null)
{
UI.Console.WriteLine("Decrypting report ReportId:" + reportId + ").", false);
byte[] key = Encoding.ASCII.GetBytes(amazonFileInfo.EncryptionDetails.Key);
byte[] iv = Encoding.ASCII.GetBytes(amazonFileInfo.EncryptionDetails.InitializationVector);
response = Logic.Utilities.File.DecryptByte(key, iv, response);
}
// decompress
if (amazonFileInfo.CompressionAlgorithm != null)
{
UI.Console.WriteLine("Decompressing report ReportId:" + reportId + ").", false);
response = Logic.Utilities.File.Decompress(response);
}
string reportString = Encoding.ASCII.GetString(response);
// if fatal returned, give file contents in log
if (report.ProcessingStatus == FikaAmazonAPI.AmazonSpApiSDK.Models.Reports.Report.ProcessingStatusEnum.FATAL)
{
log.LogError("Downloading reportId:" + report.ReportId + " returned 'FATAL'. See long detail for further details", reportString);
return;
}
// save to file
string reportFilePath = Config.GetTempFileDirectoryPath() + @"\SP-API-Reports\ " + report.ReportType.ToString() + " reportId_" + reportId + ".txt";
System.IO.File.WriteAllText(reportFilePath, reportString);
log.LogInformation("Amazon report #" + reportId + " sucessfully saved to disk.");
this.ReportFilePath = reportFilePath;
}
/// <summary>
@@ -155,12 +257,12 @@ namespace bnhtrade.Core.Data.Amazon.Report
/// </summary>
/// <param name="report">Report to test for throttled response</param>
/// <param name="specification">The specification that created to throttle report.</param>
private void SnapahotThrottleHanderler(ref FikaAmazonAPI.AmazonSpApiSDK.Models.Reports.Report report, ParameterCreateReportSpecification specification, int snapshotMaxAge)
private bool SnapshotReportThrottleHanderler(ref string reportId, ParameterCreateReportSpecification specification, int snapshotMaxAgeMinutes)
{
if (report.ProcessingStatus != FikaAmazonAPI.AmazonSpApiSDK.Models.Reports.Report.ProcessingStatusEnum.FATAL)
return;
throw new Exception("Incorrect use of class, processing status must be fatal");
if (snapshotMaxAge <= 0)
if (snapshotMaxAgeMinutes <= 0)
{
// report not avaiable
throw new Exception( reportType.ToString() + " report not avaibale for requested timeframe");
@@ -169,26 +271,31 @@ namespace bnhtrade.Core.Data.Amazon.Report
// search for valid reports with required timeframe
var parameters = new ParameterReportList();
parameters.reportTypes = new List<ReportTypes> { specification.reportType };
parameters.createdSince = report.CreatedTime.Value.AddMinutes(-snapshotMaxAge);
parameters.createdSince = report.CreatedTime.Value.AddMinutes(-snapshotMaxAgeMinutes);
parameters.marketplaceIds = specification.marketplaceIds;
var reports = amznConn.Reports.GetReports(parameters);
if (reports == null || !reports.Any())
{
return;
return false;
}
foreach (var item in reports)
{
if (item.ProcessingStatus != FikaAmazonAPI.AmazonSpApiSDK.Models.Reports.Report.ProcessingStatusEnum.CANCELLED
&& item.ProcessingStatus != FikaAmazonAPI.AmazonSpApiSDK.Models.Reports.Report.ProcessingStatusEnum.FATAL)
if (item.ProcessingStatus != FikaAmazonAPI.AmazonSpApiSDK.Models.Reports.Report.ProcessingStatusEnum.DONE
|| item.ProcessingStatus != FikaAmazonAPI.AmazonSpApiSDK.Models.Reports.Report.ProcessingStatusEnum.INPROGRESS
|| item.ProcessingStatus != FikaAmazonAPI.AmazonSpApiSDK.Models.Reports.Report.ProcessingStatusEnum.INQUEUE)
{
log.LogInformation("Amazon declinded requested report:" + item.ReportType.ToString() + " as another with same parameters already existes, substituting reportId " + reportId + " for " + item.ReportId);
reportId = item.ReportId;
report = item;
return true;
}
}
return false;
}
private string ExistingReportSearch(ParameterCreateReportSpecification specification)
private string ExistingReportCheck(ParameterCreateReportSpecification specification)
{
// amazon may return 'fatal' due to report already existing and/or report request matches an recent existing request
@@ -215,6 +322,44 @@ namespace bnhtrade.Core.Data.Amazon.Report
return null;
}
private void WaitWhileProcessing(string reportId)
{
// set accordingly
int waitPeriodIncrement = (1000 * 10);
int waitPeriodIncrementMax = (1000 * 60);
int waitPeriod = 0;
int waitPeriodTotal = 0;
int waitPeriodTotalTimeout = (1000 * 1200); // 20 minutes
while (waitPeriodTotal < waitPeriodTotalTimeout)
{
report = amznConn.Reports.GetReport(reportId);
if (report.ProcessingStatus == FikaAmazonAPI.AmazonSpApiSDK.Models.Reports.Report.ProcessingStatusEnum.INQUEUE)
{
ReportProcessingStatus = ProcessingStatus.INQUEUE;
}
else if (report.ProcessingStatus == FikaAmazonAPI.AmazonSpApiSDK.Models.Reports.Report.ProcessingStatusEnum.INPROGRESS)
{
ReportProcessingStatus = ProcessingStatus.INPROGRESS;
}
else
{
return;
}
waitPeriod = waitPeriod + waitPeriodIncrement;
if (waitPeriod > waitPeriodIncrementMax)
waitPeriod = waitPeriodIncrementMax;
UI.Console.Wait("Report processing status " + ReportProcessingStatus + ". Check in {0} seconds...", waitPeriod);
waitPeriodTotal = waitPeriodTotal + waitPeriod;
}
throw new Exception("Amazon report processing for reportId:" + report.ReportId + " timeout after " + (waitPeriodTotal / 60000) + " minutes.");
}
private void LogReportError(FikaAmazonAPI.AmazonSpApiSDK.Models.Reports.Report report, string errorTitle)
{
log.Initialise();
@@ -1,122 +0,0 @@
////using bnhtrade.Core.Data.Amazon.SellingPartnerAPI.Services;
//using FikaAmazonAPI.Services;
//using System;
//using System.Collections.Generic;
//using System.Text;
//namespace bnhtrade.Core.Data.Amazon.SellingPartnerAPI
//{
// public class AmazonConnection
// {
// private AmazonCredential Credentials { get; set; }
// public FbaInboundService FbaInbound => this._FbaInbound ?? throw _NoCredentials;
// public FbaInventoryService FbaInventory => this._FbaInventory ?? throw _NoCredentials;
// public FulFillmentInboundService FulFillmentInbound => this._FulFillmentInbound ?? throw _NoCredentials;
// public ProductFeeService ProductFee => this._ProductFee ?? throw _NoCredentials;
// public ProductPricingService ProductPricing => this._ProductPricing ?? throw _NoCredentials;
// public ReportService Reports => this._Reports ?? throw _NoCredentials;
// //public AplusContentService AplusContent => this._AplusContent ?? throw _NoCredentials;
// //public AuthorizationService Authorization => this._Authorization ?? throw _NoCredentials;
// //public CatalogItemService CatalogItem => this._CatalogItems ?? throw _NoCredentials;
// //public FbaInboundEligibilityService FbaInboundEligibility => this._FbaInboundEligibility ?? throw _NoCredentials;
// //public FbaOutboundService FbaOutbound => this._FbaOutbound ?? throw _NoCredentials;
// //public FbaSmallandLightService FbaSmallandLight => this._FbaSmallandLight ?? throw _NoCredentials;
// //public FeedService Feed => this._Feed ?? throw _NoCredentials;
// //public FinancialService Financial => this._Financials ?? throw _NoCredentials;
// //public FulFillmentOutboundService FulFillmentOutbound => this._FulFillmentOutbound ?? throw _NoCredentials;
// //public ListingsItemService ListingsItem => this._ListingsItem ?? throw _NoCredentials;
// //public MerchantFulfillmentService MerchantFulfillment => this._MerchantFulfillment ?? throw _NoCredentials;
// //public MessagingService Messaging => this._Messaging ?? throw _NoCredentials;
// //public NotificationService Notification => this._Notification ?? throw _NoCredentials;
// //public OrderService Orders => this._Orders ?? throw _NoCredentials;
// //public SalesService Sales => this._Sales ?? throw _NoCredentials;
// //public SellerService Seller => this._Seller ?? throw _NoCredentials;
// //public ServicesService Services => this._Services ?? throw _NoCredentials;
// //public ShipmentInvoicingService ShipmentInvoicing => this._ShipmentInvoicing ?? throw _NoCredentials;
// //public ShippingService Shipping => this._Shipping ?? throw _NoCredentials;
// //public SolicitationService Solicitations => this._Solicitations ?? throw _NoCredentials;
// //public TokenService Tokens => this._Tokens ?? throw _NoCredentials;
// //public UploadService Upload => this._Upload ?? throw _NoCredentials;
// private FbaInboundService _FbaInbound { get; set; }
// private FbaInventoryService _FbaInventory { get; set; }
// private FulFillmentInboundService _FulFillmentInbound { get; set; }
// private ProductFeeService _ProductFee { get; set; }
// private ProductPricingService _ProductPricing { get; set; }
// private ReportService _Reports { get; set; }
// //private AplusContentService _AplusContent { get; set; }
// //private AuthorizationService _Authorization { get; set; }
// //private CatalogItemService _CatalogItems { get; set; }
// //private FbaInboundEligibilityService _FbaInboundEligibility { get; set; }
// //private FbaOutboundService _FbaOutbound { get; set; }
// //private FbaSmallandLightService _FbaSmallandLight { get; set; }
// private FeedService _Feed { get; set; }
// //private FinancialService _Financials { get; set; }
// //private FulFillmentOutboundService _FulFillmentOutbound { get; set; }
// //private ListingsItemService _ListingsItem { get; set; }
// //private MerchantFulfillmentService _MerchantFulfillment { get; set; }
// //private MessagingService _Messaging { get; set; }
// //private NotificationService _Notification { get; set; }
// //private OrderService _Orders { get; set; }
// //private SalesService _Sales { get; set; }
// //private SellerService _Seller { get; set; }
// //private ServicesService _Services { get; set; }
// //private ShipmentInvoicingService _ShipmentInvoicing { get; set; }
// //private ShippingService _Shipping { get; set; }
// //private SolicitationService _Solicitations { get; set; }
// //private TokenService _Tokens { get; set; }
// //private UploadService _Upload { get; set; }
// private UnauthorizedAccessException _NoCredentials = new UnauthorizedAccessException($"Error, you cannot make calls to Amazon without credentials!");
// public AmazonConnection(AmazonCredential Credentials)
// {
// this.Authenticate(Credentials);
// }
// public void Authenticate(AmazonCredential Credentials)
// {
// if (this.Credentials == default(AmazonCredential))
// Init(Credentials);
// else
// throw new InvalidOperationException("Error, you are already authenticated to amazon in this AmazonConnection, dispose of this connection and create a new one to connect to a different account.");
// }
// private void Init(AmazonCredential Credentials)
// {
// this.Credentials = Credentials;
// this._FbaInbound = new FbaInboundService(this.Credentials);
// this._FbaInventory = new FbaInventoryService(this.Credentials);
// this._FulFillmentInbound = new FulFillmentInboundService(this.Credentials);
// this._ProductFee = new ProductFeeService(this.Credentials);
// this._ProductPricing = new ProductPricingService(this.Credentials);
// this._Reports = new ReportService(this.Credentials);
// //this._AplusContent = new AplusContentService(this.Credentials);
// //this._CatalogItems = new CatalogItemService(this.Credentials);
// //this._FbaInboundEligibility = new FbaInboundEligibilityService(this.Credentials);
// //this._FbaOutbound = new FbaOutboundService(this.Credentials);
// //this._FbaSmallandLight = new FbaSmallandLightService(this.Credentials);
// this._Feed = new FeedService(this.Credentials);
// //this._Financials = new FinancialService(this.Credentials);
// //this._FulFillmentOutbound= new FulFillmentOutboundService(this.Credentials);
// //this._ListingsItem = new ListingsItemService(this.Credentials);
// //this._MerchantFulfillment = new MerchantFulfillmentService(this.Credentials);
// //this._Messaging= new MessagingService(this.Credentials);
// //this._Notification= new NotificationService(this.Credentials);
// //this._Orders = new OrderService(this.Credentials);
// //this._Sales= new SalesService(this.Credentials);
// //this._Seller= new SellerService(this.Credentials);
// //this._Services= new ServicesService(this.Credentials);
// //this._ShipmentInvoicing= new ShipmentInvoicingService(this.Credentials);
// //this._Shipping= new ShippingService(this.Credentials);
// //this._Solicitations = new SolicitationService(this.Credentials);
// //this._Tokens= new TokenService(this.Credentials);
// //this._Upload= new UploadService(this.Credentials);
// }
// }
//}
@@ -1,103 +0,0 @@
//using bnhtrade.Core.Data.Amazon.SellingPartnerAPI.SDK.Models.Token;
//using bnhtrade.Core.Data.Amazon.SellingPartnerAPI.Utils;
using FikaAmazonAPI.AmazonSpApiSDK.Models.Token;
using FikaAmazonAPI.Utils;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Text;
using static FikaAmazonAPI.AmazonSpApiSDK.Models.Token.CacheTokenData;
using static FikaAmazonAPI.Utils.Constants;
//using static bnhtrade.Core.Data.Amazon.SellingPartnerAPI.SDK.Models.Token.CacheTokenData;
//using static bnhtrade.Core.Data.Amazon.SellingPartnerAPI.Utils.Constants;
namespace bnhtrade.Core.Data.Amazon.SellingPartnerAPI
{
public class AmazonCredential
{
public string AccessKey { get; set; }
public string SecretKey { get; set; }
public string RoleArn { get; set; }
public string ClientId { get; set; }
public string ClientSecret { get; set; }
public string RefreshToken { get; set; }
public MarketPlace MarketPlace { get; set; }
private CacheTokenData CacheTokenData { get; set; } = new CacheTokenData();
public bool IsActiveLimitRate { get; set; } = true;
public Environments Environment { get; set; } = Environments.Production;
public AmazonCredential()
{
// attempt to retrive credentials from app.local.config
try
{
Innit(ConfigurationManager.AppSettings["SpapiAccessKey"]
, ConfigurationManager.AppSettings["SpapiSecretKey"]
, ConfigurationManager.AppSettings["SpapiRoleArn"]
, ConfigurationManager.AppSettings["SpapiClientId"]
, ConfigurationManager.AppSettings["SpapiClientSecret"]
, ConfigurationManager.AppSettings["SpapiRefreshToken"]);
if (!string.IsNullOrWhiteSpace(ConfigurationManager.AppSettings["SpapiMarketplaceId"]))
{
this.MarketPlace = MarketPlace.UnitedKingdom;
}
}
catch (Exception ex)
{
throw new Exception("Unable to retirve Amazon SP API credentials: " + ex.Message);
}
}
private AmazonCredential(string accessKey, string secretKey, string roleArn, string clientId, string clientSecret, string refreshToken)
{
// Use other method by default, however can change this method back to public is the need arises.
Innit(accessKey
, secretKey
, roleArn
, clientId
, clientSecret
, refreshToken);
}
public AmazonCredential(Core.Model.Credentials.AmazonSPAPI userCredentials)
{
Innit(userCredentials.AccessKey
, userCredentials.SecretKey
, userCredentials.RoleArn
, userCredentials.ClientId
, userCredentials.ClientSecret
, userCredentials.RefreshToken);
}
private void Innit(string accessKey, string secretKey, string roleArn, string clientId, string clientSecret, string refreshToken)
{
this.AccessKey = accessKey;
this.SecretKey = secretKey;
this.RoleArn = roleArn;
this.ClientId = clientId;
this.ClientSecret = clientSecret;
this.RefreshToken = refreshToken;
}
public TokenResponse GetToken(TokenDataType tokenDataType)
{
return CacheTokenData.GetToken(tokenDataType);
}
public void SetToken(TokenDataType tokenDataType, TokenResponse token)
{
CacheTokenData.SetToken(tokenDataType, token);
}
public AWSAuthenticationTokenData GetAWSAuthenticationTokenData()
{
return CacheTokenData.GetAWSAuthenticationTokenData();
}
public void SetAWSAuthenticationTokenData(AWSAuthenticationTokenData tokenData)
{
CacheTokenData.SetAWSAuthenticationTokenData(tokenData);
}
}
}
@@ -12,11 +12,12 @@ namespace bnhtrade.Core.Data.Amazon.SellingPartnerAPI
{
public MarketPlace MarketPlaceId()
{
return MarketPlace.GetMarketPlaceByID(ConfigurationManager.AppSettings["SpapiMarketplaceId"]);
return MarketPlace.GetMarketPlaceByID(MarketPlaceIdAsString());
}
public string MarketPlaceIdAsString()
{
return ConfigurationManager.AppSettings["SpapiMarketplaceId"];
var config = new Config().GetConfiguration();
return config.AppSettings.Settings["SpapiMarketplaceId"].Value;
}
}
}
@@ -7,13 +7,13 @@
//{
// public class ReportService : RequestService
// {
// private Dictionary<string, RestSharp.RestResponse<SDK.Models.Reports.Report>> reportList;
// private Dictionary<string, RestSharp.RestResponse<FikaAmazonAPI.AmazonSpApiSDK.Models.Reports.Report>> reportList;
// private Dictionary<string, string> filePath;
// private bool fatalEncounter = false;
// protected Logic.Log.LogEvent log = new Logic.Log.LogEvent();
// private int reportRequestCount = 0;
// public ReportService() : base (new AmazonCredential())
// public ReportService() : base(new AmazonCredential())
// {
// Innit();
// }
@@ -260,25 +260,25 @@
// // as file url expires after 5 minutes, request just before download
// UI.Console.WriteLine("Requesting download url for Amazon report (ReportId:" + reportId + ").");
// var reportDocument = GetReportDocument(reportList[reportId].Data.ReportDocumentId);
// var amazonFileInfo = GetReportDocument(reportList[reportId].Data.ReportDocumentId);
// // download report from url
// log.LogInformation("Downloading Amazon report #" + reportId, "Report Document ID: " + reportDocument.ReportDocumentId);
// log.LogInformation("Downloading Amazon report #" + reportId, "Report Document ID: " + amazonFileInfo.ReportDocumentId);
// var response = new byte[0];
// using (var webClient = new System.Net.WebClient())
// {
// response = webClient.DownloadData(reportDocument.Url);
// response = webClient.DownloadData(amazonFileInfo.Url);
// }
// string report = null;
// // decrypt report
// UI.Console.WriteLine("Processing download (ReportId:" + reportId + ").");
// if (reportDocument.EncryptionDetails != null)
// if (amazonFileInfo.EncryptionDetails != null)
// {
// byte[] key = Encoding.ASCII.GetBytes(reportDocument.EncryptionDetails.Key);
// byte[] iv = Encoding.ASCII.GetBytes(reportDocument.EncryptionDetails.InitializationVector);
// report = Logic.Utilities.FileTransform.DecryptString(key, iv, response);
// byte[] key = Encoding.ASCII.GetBytes(amazonFileInfo.EncryptionDetails.Key);
// byte[] iv = Encoding.ASCII.GetBytes(amazonFileInfo.EncryptionDetails.InitializationVector);
// report = Logic.Utilities.File.DecryptString(key, iv, response);
// }
// else
// {
@@ -286,9 +286,9 @@
// }
// // decompress
// if (reportDocument.CompressionAlgorithm != null)
// if (amazonFileInfo.CompressionAlgorithm != null)
// {
// report = Logic.Utilities.FileTransform.Decompress(report);
// report = Logic.Utilities.File.Decompress(report);
// }
// // save to file
@@ -26,17 +26,20 @@ namespace bnhtrade.Core.Data.Amazon.SellingPartnerAPI
{
// attempt to create credential object from app.local.config
var credential = new FikaAmazonAPI.AmazonCredential();
var config = new Config().GetConfiguration();
try
{
string dataSource = config.AppSettings.Settings["DbDataSource"].Value;
credential = new FikaAmazonAPI.AmazonCredential();
credential.AccessKey = ConfigurationManager.AppSettings["SpapiAccessKey"];
credential.SecretKey = ConfigurationManager.AppSettings["SpapiSecretKey"];
credential.RoleArn = ConfigurationManager.AppSettings["SpapiRoleArn"];
credential.ClientId = ConfigurationManager.AppSettings["SpapiClientId"];
credential.ClientSecret = ConfigurationManager.AppSettings["SpapiClientSecret"];
credential.RefreshToken = ConfigurationManager.AppSettings["SpapiRefreshToken"];
credential.MarketPlaceID = ConfigurationManager.AppSettings["SpapiMarketplaceId"];
credential.IsDebugMode = true;
//credential.AccessKey = config.AppSettings.Settings["SpapiAccessKey"].Value;
//credential.SecretKey = config.AppSettings.Settings["SpapiSecretKey"].Value;
//credential.RoleArn = config.AppSettings.Settings["SpapiRoleArn"].Value;
credential.ClientId = config.AppSettings.Settings["SpapiClientId"].Value;
credential.ClientSecret = config.AppSettings.Settings["SpapiClientSecret"].Value;
credential.RefreshToken = config.AppSettings.Settings["SpapiRefreshToken"].Value;
credential.MarketPlaceID = config.AppSettings.Settings["SpapiMarketplaceId"].Value;
//credential.IsDebugMode = true;
credential.MaxThrottledRetryCount = 3;
}
catch (Exception ex)