Skip to content

Commit

Permalink
Merge pull request #802 from cagil-ozdemirag-mw/feature/multithreadin…
Browse files Browse the repository at this point in the history
…g_issues

Feature/multithreading issues
  • Loading branch information
abuzuhri authored Dec 19, 2024
2 parents 0242ae4 + 0daec75 commit 82684a3
Show file tree
Hide file tree
Showing 47 changed files with 690 additions and 444 deletions.
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,42 @@ AmazonConnection amazonConnection = new AmazonConnection(new AmazonCredential()

```

### Multithreaded connections
If multithreading, the following should be done to avoid inadvertantly passing incorrect token data between different threads:
```CSharp

// Note - you may also write and pass your own implementation of the IRateLimitingHandler interface if required
var connectionFactory = new AmazonMultithreadedConnectionFactory(
ClientId: "amzn1.application-XXX-client.XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
ClientSecret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
RefreshToken: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
// a singleton that handles rate limiting across multiple threads
rateLimitingHandler: new RateLimitingHandler());

// Then in each concurrent thread/request scope, a new connection can be created like so
var amazonConnection = connectionFactory.RequestScopedConnection(
marketPlaceId: "A2VIGQ35RCS4UG",
sellerId: "MySellerId",
// credentialConfiguration is an optional parameter that allows additional configuration of the AmazonCredential
credentialConfiguration: cred =>
{
cred.IsActiveLimitRate = true;
cred.IsDebugMode = true;
});

// or (remember either Marketplace OR Marketplace ID must be provided)
var amazonConnection = connectionFactory.RequestScopedConnection(
sellerId: "MySellerId",
credentialConfiguration: cred =>
{
cred.IsActiveLimitRate = true;
cred.IsDebugMode = true;
cred.Marketplace = MarketPlace.UnitedArabEmirates
});

```

### Configuration using a proxy
Please see [here](https://github.com/abuzuhri/Amazon-SP-API-CSharp/blob/main/Source/FikaAmazonAPI.SampleCode/Program.cs) for the relevant code file.
>```csharp
Expand Down
51 changes: 22 additions & 29 deletions Source/FikaAmazonAPI.SampleCode/Program.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Microsoft.Extensions.Configuration;
using FikaAmazonAPI.AmazonSpApiSDK.Models.FbaSmallandLight;
using FikaAmazonAPI.Utils;
using Microsoft.Extensions.Configuration;

namespace FikaAmazonAPI.SampleCode
{
Expand All @@ -12,40 +14,31 @@ static async Task Main(string[] args)
.AddUserSecrets<Program>()
.Build();

var connectionFactory = new AmazonMultithreadedConnectionFactory(
ClientId: config.GetSection("FikaAmazonAPI:ClientId").Value,
ClientSecret: config.GetSection("FikaAmazonAPI:ClientSecret").Value,
RefreshToken: config.GetSection("FikaAmazonAPI:RefreshToken").Value,
rateLimitingHandler: new RateLimitingHandler());

AmazonConnection amazonConnection = new AmazonConnection(new AmazonCredential()
var tasks = new[] { 1..10 }.Select(x =>
Task.Run(() =>
{
//AccessKey = config.GetSection("FikaAmazonAPI:AccessKey").Value,
//SecretKey = config.GetSection("FikaAmazonAPI:SecretKey").Value,
//RoleArn = config.GetSection("FikaAmazonAPI:RoleArn").Value,
ClientId = config.GetSection("FikaAmazonAPI:ClientId").Value,
ClientSecret = config.GetSection("FikaAmazonAPI:ClientSecret").Value,
RefreshToken = config.GetSection("FikaAmazonAPI:RefreshToken").Value,
MarketPlaceID = config.GetSection("FikaAmazonAPI:MarketPlaceID").Value,
SellerID = config.GetSection("FikaAmazonAPI:SellerId").Value,
IsDebugMode = true
});


ReportManagerSample reportManagerSample = new ReportManagerSample(amazonConnection);
reportManagerSample.CallReport();
//var error = amazonConnection.Reports.CreateReportAndDownloadFile(Utils.Constants.ReportTypes.GET_STRANDED_INVENTORY_UI_DATA);
//var dddd = amazonConnection.Reports.CreateReportAndDownloadFile(Utils.Constants.ReportTypes.GET_FBA_MYI_ALL_INVENTORY_DATA);
//var dddd = amazonConnection.Reports.CreateReportAndDownloadFile(Utils.Constants.ReportTypes.GET_FBA_MYI_UNSUPPRESSED_INVENTORY_DATA);
//ReportManager reportManager = new ReportManager(amazonConnection);

//var dddddd = reportManager.GetAFNInventoryQtyAsync().ConfigureAwait(false).GetAwaiter().GetResult();
var amazonConnection = connectionFactory.RequestScopedConnection(
marketPlaceId: config.GetSection("FikaAmazonAPI:MarketPlaceID").Value,
sellerId: config.GetSection("FikaAmazonAPI:SellerId").Value,
credentialConfiguration: cred =>
{
cred.IsActiveLimitRate = true;
cred.IsDebugMode = true;
});

ReportManagerSample reportManagerSample = new ReportManagerSample(amazonConnection);
reportManagerSample.CallReport();
}));

await Task.WhenAll(tasks);

Console.ReadLine();

}






}
}
83 changes: 45 additions & 38 deletions Source/FikaAmazonAPI/AmazonConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ public class AmazonConnection
{
private AmazonCredential Credentials { get; set; }

private IRateLimitingHandler RateLimitingHandler { get; }

public AppIntegrationsServiceV20240401 AppIntegrationsServiceV20240401 => this._AppIntegrationsServiceV20240401 ?? throw _NoCredentials;
public OrderService Orders => this._Orders ?? throw _NoCredentials;
public ReportService Reports => this._Reports ?? throw _NoCredentials;
Expand Down Expand Up @@ -91,10 +93,15 @@ public class AmazonConnection
private UnauthorizedAccessException _NoCredentials = new UnauthorizedAccessException($"Error, you cannot make calls to Amazon without credentials!");

public string RefNumber { get; set; }
public AmazonConnection(AmazonCredential Credentials, string RefNumber = null, CultureInfo? cultureInfo = null)
public AmazonConnection(
AmazonCredential Credentials,
IRateLimitingHandler rateLimitingHandler = null,
string RefNumber = null,
CultureInfo? cultureInfo = null)

Check warning on line 100 in Source/FikaAmazonAPI/AmazonConnection.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
{
this.Authenticate(Credentials);
this.RefNumber = RefNumber;
this.RateLimitingHandler = rateLimitingHandler ?? new RateLimitingHandler();
Thread.CurrentThread.CurrentCulture = cultureInfo ?? CultureInfo.CurrentCulture;
}

Expand All @@ -112,44 +119,44 @@ private void Init(AmazonCredential Credentials)

this.Credentials = Credentials;

this._Authorization = new AuthorizationService(this.Credentials);
this._AppIntegrationsServiceV20240401 = new AppIntegrationsServiceV20240401(this.Credentials);
this._Orders = new OrderService(this.Credentials);
this._Reports = new ReportService(this.Credentials);
this._Solicitations = new SolicitationService(this.Credentials);
this._Financials = new FinancialService(this.Credentials);
this._CatalogItems = new CatalogItemService(this.Credentials);
this._ProductPricing = new ProductPricingService(this.Credentials);
this._Authorization = new AuthorizationService(this.Credentials, this.RateLimitingHandler);
this._AppIntegrationsServiceV20240401 = new AppIntegrationsServiceV20240401(this.Credentials, this.RateLimitingHandler);
this._Orders = new OrderService(this.Credentials, this.RateLimitingHandler);
this._Reports = new ReportService(this.Credentials, this.RateLimitingHandler);
this._Solicitations = new SolicitationService(this.Credentials, this.RateLimitingHandler);
this._Financials = new FinancialService(this.Credentials, this.RateLimitingHandler);
this._CatalogItems = new CatalogItemService(this.Credentials, this.RateLimitingHandler);
this._ProductPricing = new ProductPricingService(this.Credentials, this.RateLimitingHandler);

this._FbaInbound = new FbaInboundService(this.Credentials);
this._FbaInventory = new FbaInventoryService(this.Credentials);
this._FbaOutbound = new FbaOutboundService(this.Credentials);
this._FbaSmallandLight = new FbaSmallandLightService(this.Credentials);
this._FbaInboundEligibility = new FbaInboundEligibilityService(this.Credentials);
this._EasyShip20220323 = new EasyShip20220323Service(this.Credentials);
this._AplusContent = new AplusContentService(this.Credentials);
this._Feed = new FeedService(this.Credentials);
this._ListingsItem = new ListingsItemService(this.Credentials);
this._Restrictions = new RestrictionService(this.Credentials);
this._MerchantFulfillment = new MerchantFulfillmentService(this.Credentials);
this._Messaging = new MessagingService(this.Credentials);
this._Notification = new NotificationService(this.Credentials);
this._ProductFee = new ProductFeeService(this.Credentials);
this._ProductType = new ProductTypeService(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._ShippingV2 = new ShippingServiceV2(this.Credentials);
this._Upload = new UploadService(this.Credentials);
this._Tokens = new TokenService(this.Credentials);
this._FulFillmentInbound = new FulFillmentInboundService(this.Credentials);
this._FulFillmentInboundv20240320 = new FulFillmentInboundServicev20240320(this.Credentials);
this._FulFillmentOutbound = new FulFillmentOutboundService(this.Credentials);
this._VendorDirectFulfillmentOrders = new VendorDirectFulfillmentOrderService(this.Credentials);
this._VendorOrders = new VendorOrderService(this.Credentials);
this._VendorTransactionStatus = new VendorTransactionStatusService(this.Credentials);
this._FbaInbound = new FbaInboundService(this.Credentials, this.RateLimitingHandler);
this._FbaInventory = new FbaInventoryService(this.Credentials, this.RateLimitingHandler);
this._FbaOutbound = new FbaOutboundService(this.Credentials, this.RateLimitingHandler);
this._FbaSmallandLight = new FbaSmallandLightService(this.Credentials, this.RateLimitingHandler);
this._FbaInboundEligibility = new FbaInboundEligibilityService(this.Credentials, this.RateLimitingHandler);
this._EasyShip20220323 = new EasyShip20220323Service(this.Credentials, this.RateLimitingHandler);
this._AplusContent = new AplusContentService(this.Credentials, this.RateLimitingHandler);
this._Feed = new FeedService(this.Credentials, this.RateLimitingHandler);
this._ListingsItem = new ListingsItemService(this.Credentials, this.RateLimitingHandler);
this._Restrictions = new RestrictionService(this.Credentials, this.RateLimitingHandler);
this._MerchantFulfillment = new MerchantFulfillmentService(this.Credentials, this.RateLimitingHandler);
this._Messaging = new MessagingService(this.Credentials, this.RateLimitingHandler);
this._Notification = new NotificationService(this.Credentials, this.RateLimitingHandler);
this._ProductFee = new ProductFeeService(this.Credentials, this.RateLimitingHandler);
this._ProductType = new ProductTypeService(this.Credentials, this.RateLimitingHandler);
this._Sales = new SalesService(this.Credentials, this.RateLimitingHandler);
this._Seller = new SellerService(this.Credentials, this.RateLimitingHandler);
this._Services = new ServicesService(this.Credentials, this.RateLimitingHandler);
this._ShipmentInvoicing = new ShipmentInvoicingService(this.Credentials, this.RateLimitingHandler);
this._Shipping = new ShippingService(this.Credentials, this.RateLimitingHandler);
this._ShippingV2 = new ShippingServiceV2(this.Credentials, this.RateLimitingHandler);
this._Upload = new UploadService(this.Credentials, this.RateLimitingHandler);
this._Tokens = new TokenService(this.Credentials, this.RateLimitingHandler);
this._FulFillmentInbound = new FulFillmentInboundService(this.Credentials, this.RateLimitingHandler);
this._FulFillmentInboundv20240320 = new FulFillmentInboundServicev20240320(this.Credentials, this.RateLimitingHandler);
this._FulFillmentOutbound = new FulFillmentOutboundService(this.Credentials, this.RateLimitingHandler);
this._VendorDirectFulfillmentOrders = new VendorDirectFulfillmentOrderService(this.Credentials, this.RateLimitingHandler);
this._VendorOrders = new VendorOrderService(this.Credentials, this.RateLimitingHandler);
this._VendorTransactionStatus = new VendorTransactionStatusService(this.Credentials, this.RateLimitingHandler);

AmazonCredential.DebugMode = this.Credentials.IsDebugMode;
}
Expand Down
4 changes: 1 addition & 3 deletions Source/FikaAmazonAPI/AmazonCredential.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using FikaAmazonAPI.AmazonSpApiSDK.Models.Token;
using FikaAmazonAPI.Utils;
using System.Collections.Generic;
using static FikaAmazonAPI.AmazonSpApiSDK.Models.Token.CacheTokenData;
using static FikaAmazonAPI.Utils.Constants;

Expand Down Expand Up @@ -53,11 +52,10 @@ public AWSAuthenticationTokenData GetAWSAuthenticationTokenData()
{
return CacheTokenData.GetAWSAuthenticationTokenData();
}

public void SetAWSAuthenticationTokenData(AWSAuthenticationTokenData tokenData)
{
CacheTokenData.SetAWSAuthenticationTokenData(tokenData);
}
internal Dictionary<RateLimitType, RateLimits> UsagePlansTimings { get; set; } = RateLimitsDefinitions.RateLimitsTime();

}
}
58 changes: 58 additions & 0 deletions Source/FikaAmazonAPI/AmazonMultithreadedConnectionFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using FikaAmazonAPI.Utils;
using System;
using System.Globalization;

namespace FikaAmazonAPI
{
public class AmazonMultithreadedConnectionFactory
{
private readonly string _accessKey;
private readonly string _secretKey;
private readonly string _roleArn;
private readonly string _clientId;
private readonly string _clientSecret;
private readonly string _refreshToken;
private readonly string _proxyAddress;

private readonly IRateLimitingHandler _rateLimitingHandler;

public AmazonMultithreadedConnectionFactory(
string ClientId,
string ClientSecret,
string RefreshToken,
string AccessKey = null,
string SecretKey = null,
string RoleArn = null,
string ProxyAddress = null,
IRateLimitingHandler rateLimitingHandler = null)
{
_accessKey = AccessKey;
_secretKey = SecretKey;
_roleArn = RoleArn;
_clientId = ClientId;
_clientSecret = ClientSecret;
_refreshToken = RefreshToken;
_proxyAddress = ProxyAddress;

_rateLimitingHandler = rateLimitingHandler ?? new RateLimitingHandler();
}

public AmazonConnection RequestScopedConnection(
string marketPlaceId = null,
string sellerId = null,
string refCode = null,
Action<AmazonCredential> credentialConfiguration = null,
CultureInfo cultureInfo = null)
{
// need to create distinct credential/connection here so that token caching in credential is predicably kept within scope
var credential = new AmazonCredential(_accessKey, _secretKey, _roleArn, _clientId, _clientSecret, _refreshToken, _proxyAddress)
{
MarketPlaceID = marketPlaceId,
SellerID = sellerId,
};
credentialConfiguration?.Invoke(credential);

return new AmazonConnection(credential, _rateLimitingHandler, refCode, cultureInfo);
}
}
}
6 changes: 4 additions & 2 deletions Source/FikaAmazonAPI/Services/AplusContentService.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
namespace FikaAmazonAPI.Services
using FikaAmazonAPI.Utils;

namespace FikaAmazonAPI.Services
{

public class AplusContentService : RequestService
{

public AplusContentService(AmazonCredential amazonCredential) : base(amazonCredential)
public AplusContentService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler)
{

}
Expand Down
2 changes: 1 addition & 1 deletion Source/FikaAmazonAPI/Services/AppIntegrationsV20240401.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace FikaAmazonAPI.Services
{
public class AppIntegrationsServiceV20240401: RequestService
{
public AppIntegrationsServiceV20240401(AmazonCredential amazonCredential) : base(amazonCredential)
public AppIntegrationsServiceV20240401(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler)
{

}
Expand Down
3 changes: 2 additions & 1 deletion Source/FikaAmazonAPI/Services/AuthorizationService.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using FikaAmazonAPI.AmazonSpApiSDK.Models.Authorization;
using FikaAmazonAPI.AmazonSpApiSDK.Models.Token;
using FikaAmazonAPI.Parameter.Authorization;
using FikaAmazonAPI.Utils;
using System.Threading;
using System.Threading.Tasks;
using static FikaAmazonAPI.AmazonSpApiSDK.Models.Token.CacheTokenData;
Expand All @@ -9,7 +10,7 @@ namespace FikaAmazonAPI.Services
{
public class AuthorizationService : RequestService
{
public AuthorizationService(AmazonCredential amazonCredential) : base(amazonCredential)
public AuthorizationService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler)
{
}
public string GetAuthorizationCode(ParameterAuthorizationCode parameterGetOrderMetrics) =>
Expand Down
2 changes: 1 addition & 1 deletion Source/FikaAmazonAPI/Services/CatalogItemService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace FikaAmazonAPI.Services
{
public class CatalogItemService : RequestService
{
public CatalogItemService(AmazonCredential amazonCredential) : base(amazonCredential)
public CatalogItemService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler)
{

}
Expand Down
2 changes: 1 addition & 1 deletion Source/FikaAmazonAPI/Services/EasyShip20220323Service.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace FikaAmazonAPI.Services
{
public class EasyShip20220323Service : RequestService
{
public EasyShip20220323Service(AmazonCredential amazonCredential) : base(amazonCredential)
public EasyShip20220323Service(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler)
{

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace FikaAmazonAPI.Services
{
public class FbaInboundEligibilityService : RequestService
{
public FbaInboundEligibilityService(AmazonCredential amazonCredential) : base(amazonCredential)
public FbaInboundEligibilityService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler)
{

}
Expand Down
6 changes: 4 additions & 2 deletions Source/FikaAmazonAPI/Services/FbaInboundService.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
namespace FikaAmazonAPI.Services
using FikaAmazonAPI.Utils;

namespace FikaAmazonAPI.Services
{
public class FbaInboundService : RequestService
{
public FbaInboundService(AmazonCredential amazonCredential) : base(amazonCredential)
public FbaInboundService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler)
{

}
Expand Down
2 changes: 1 addition & 1 deletion Source/FikaAmazonAPI/Services/FbaInventoryService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace FikaAmazonAPI.Services
public class FbaInventoryService : RequestService
{

public FbaInventoryService(AmazonCredential amazonCredential) : base(amazonCredential)
public FbaInventoryService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler)
{

}
Expand Down
Loading

0 comments on commit 82684a3

Please sign in to comment.