Skip to content

Commit 8617c7f

Browse files
committed
Improve notification SQS
1 parent 1c0d86a commit 8617c7f

5 files changed

Lines changed: 169 additions & 51 deletions

File tree

README.md

Lines changed: 86 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ This is an API Binding in .Net C# for the new Amazon Selling Partner API.
55

66
This library is based on the output of [swagger-codegen](https://app.swaggerhub.com/home) with the [OpenAPI files provided by Amazon (Models)](https://github.com/amzn/selling-partner-api-models/tree/main/models) and has been modified by the contributors.
77

8-
The purpose of this package is to have an easy way of getting started with the Amazon Selling Partner API using C#, you can watch this 📷 [Youtube](https://www.youtube.com/watch?v=1gZJBCoMr70) 📣 video for easy start your project
8+
The purpose of this package is to have an easy way of getting started with the Amazon Selling Partner API using C#. You can watch this 📷 [YouTube](https://www.youtube.com/watch?v=1gZJBCoMr70) 📣 video to get started quickly.
99

1010
---
1111
### Requirements
@@ -76,8 +76,10 @@ Install-Package CSharpAmazonSpAPI
7676

7777
---
7878
## Keys
79-
To get all keys needed you need to follow this step [Creating and configuring IAM policies and entities](https://developer-docs.amazon.com/sp-api/docs/creating-and-configuring-iam-policies-and-entities) and then you need to [Registering your Application](https://developer-docs.amazon.com/sp-api/docs/registering-your-application) then [Authorizing Selling Partner API applications
80-
](https://developer-docs.amazon.com/sp-api/docs/authorizing-selling-partner-api-applications#step-1-request-a-login-with-amazon-access-token)
79+
To get all the keys you need, follow these steps:
80+
1. [Create and configure IAM policies and entities](https://developer-docs.amazon.com/sp-api/docs/creating-and-configuring-iam-policies-and-entities)
81+
2. [Register your Application](https://developer-docs.amazon.com/sp-api/docs/registering-your-application)
82+
3. [Authorize Selling Partner API applications](https://developer-docs.amazon.com/sp-api/docs/authorizing-selling-partner-api-applications#step-1-request-a-login-with-amazon-access-token)
8183

8284

8385
| Name | Description |
@@ -88,15 +90,15 @@ To get all keys needed you need to follow this step [Creating and configuring IA
8890
| RefreshToken | Check how to get [RefreshToken](https://github.com/amzn/selling-partner-api-docs/blob/main/guides/en-US/developer-guide/SellingPartnerApiDeveloperGuide.md#Self-authorization) |
8991

9092

91-
For more information about keys please check [New Amazon doc for create keys Developer ](https://developer-docs.amazon.com/sp-api/docs/creating-and-configuring-iam-policies-and-entities) , If you are not registered as developer please [Register](https://developer.amazonservices.com/) to be able to create application.
93+
For more information about keys, check the [Amazon developer documentation](https://developer-docs.amazon.com/sp-api/docs/creating-and-configuring-iam-policies-and-entities). If you are not registered as a developer, please [Register](https://developer.amazonservices.com/) to be able to create an application.
9294

9395
---
9496
## Usage
9597

9698
> ### Please be aware there has been a change to the _Orders.GetOrderAddress()_ method please reference the new sample code for more details.
9799
98100
### Configuration
99-
You can configure a connection like so please see [Here](https://github.com/abuzuhri/Amazon-SP-API-CSharp/blob/main/Source/FikaAmazonAPI.SampleCode/Program.cs) for the relevant code file.
101+
You can configure a connection as shown below. See [Here](https://github.com/abuzuhri/Amazon-SP-API-CSharp/blob/main/Source/FikaAmazonAPI.SampleCode/Program.cs) for the relevant code file.
100102
```CSharp
101103
AmazonConnection amazonConnection = new AmazonConnection(new AmazonCredential()
102104
{
@@ -136,7 +138,8 @@ Please see [here](https://github.com/abuzuhri/Amazon-SP-API-CSharp/blob/main/Sou
136138
>>
137139
>> ***This is not required and will operate normally without the ProxyAddress being set.***
138140
139-
### Order List, For more orders sample please check [Here](https://github.com/abuzuhri/Amazon-SP-API-CSharp/blob/main/Source/FikaAmazonAPI.SampleCode/ReportsSample.cs).
141+
### Order List
142+
For more order samples, please check [Here](https://github.com/abuzuhri/Amazon-SP-API-CSharp/blob/main/Source/FikaAmazonAPI.SampleCode/OrdersSample.cs).
140143
```CSharp
141144
ParameterOrderList searchOrderList = new ParameterOrderList();
142145
searchOrderList.CreatedAfter = DateTime.UtcNow.AddMinutes(-600000);
@@ -174,7 +177,7 @@ var orders = _amazonConnection.Orders.GetOrders(parameterOrderList);
174177
175178
```
176179
177-
### Order List with parameter including PII data Advance (if you want to get specific data Elements object only)
180+
### Order List with parameter including PII data — Advanced (if you want to get specific data elements only)
178181
```CSharp
179182
var parameterOrderList = new ParameterOrderList
180183
{
@@ -219,7 +222,8 @@ var orders = amazonConnection.Orders.GetOrders
219222
);
220223
```
221224
222-
### Report List, For more report sample please check [Here](https://github.com/abuzuhri/Amazon-SP-API-CSharp/blob/main/Source/FikaAmazonAPI.SampleCode/OrdersSample.cs).
225+
### Report List
226+
For more report samples, please check [Here](https://github.com/abuzuhri/Amazon-SP-API-CSharp/blob/main/Source/FikaAmazonAPI.SampleCode/ReportsSample.cs).
223227
```CSharp
224228
var parameters = new ParameterReportList();
225229
parameters.pageSize = 100;
@@ -257,7 +261,7 @@ var data = amazonConnection.Reports.GetReportDocument("50039018869997",true);
257261
258262
259263
### Report Manager 🚀🧑‍🚀✨
260-
Easy way to get the report you need and convert the file return from amazon to class or list, this feature only ready for some reports as its will take much times to finish for [All report type](https://github.com/amzn/selling-partner-api-docs/blob/main/references/reports-api/reporttype-values.md) ....
264+
An easy way to get the report you need and convert the file returned from Amazon to a class or list. This feature is only available for some reports, as it takes significant effort to cover all [report types](https://github.com/amzn/selling-partner-api-docs/blob/main/references/reports-api/reporttype-values.md).
261265
```CSharp
262266
ReportManager reportManager = new ReportManager(amazonConnection);
263267
var products = reportManager.GetProducts(); //GET_MERCHANT_LISTINGS_ALL_DATA
@@ -369,7 +373,8 @@ var data = amazonConnection.ProductPricing.GetCompetitivePricing(
369373
```
370374
371375
372-
### Notifications Create Destination, For more Notifications sample please check [Here](https://github.com/abuzuhri/Amazon-SP-API-CSharp/blob/main/Source/FikaAmazonAPI.SampleCode/NotificationsSample.cs).
376+
### Notifications — Create Destination
377+
For more notification samples, please check [Here](https://github.com/abuzuhri/Amazon-SP-API-CSharp/blob/main/Source/FikaAmazonAPI.SampleCode/NotificationsSample.cs).
373378
```CSharp
374379
375380
//EventBridge
@@ -395,7 +400,8 @@ var dataSqs = amazonConnection.Notification.CreateDestination(
395400
});
396401
```
397402
398-
### Notifications Create Subscription, For more Notifications sample please check [Here](https://github.com/abuzuhri/Amazon-SP-API-CSharp/blob/main/Source/FikaAmazonAPI.SampleCode/NotificationsSample.cs).
403+
### Notifications — Create Subscription
404+
For more notification samples, please check [Here](https://github.com/abuzuhri/Amazon-SP-API-CSharp/blob/main/Source/FikaAmazonAPI.SampleCode/NotificationsSample.cs).
399405
```CSharp
400406
401407
//SQS
@@ -408,39 +414,80 @@ var result = amazonConnection.Notification.CreateSubscription(
408414
});
409415
```
410416
411-
### Notifications read messages
417+
### Notifications — Read Messages
412418
```CSharp
413419
414-
var SQS_URL = "https://sqs.us-east-2.amazonaws.com/9999999999999/IUSER_SQS";
415-
ParameterMessageReceiver param = new ParameterMessageReceiver(
416-
Environment.GetEnvironmentVariable("AccessKey"),
417-
Environment.GetEnvironmentVariable("SecretKey"),
418-
SQS_URL, Amazon.RegionEndpoint.USEast2);
420+
var SQS_URL = Environment.GetEnvironmentVariable("SQS_URL");
421+
var param = new ParameterMessageReceiver(
422+
Environment.GetEnvironmentVariable("AccessKey"),
423+
Environment.GetEnvironmentVariable("SecretKey"),
424+
SQS_URL,
425+
Amazon.RegionEndpoint.USEast2,
426+
WaitTimeSeconds: 20); // Enable SQS long polling to reduce empty receives and cost
419427
420-
CustomMessageReceiver messageReceiver = new CustomMessageReceiver();
428+
var messageReceiver = new CustomMessageReceiver();
421429
430+
// Use CancellationToken for graceful shutdown and wrap in a restart loop
431+
// so the listener recovers from transient errors automatically.
432+
var cts = new CancellationTokenSource();
433+
while (!cts.Token.IsCancellationRequested)
434+
{
435+
try
436+
{
437+
await amazonConnection.Notification.StartReceivingNotificationMessagesAsync(
438+
param, messageReceiver, cancellationToken: cts.Token);
439+
}
440+
catch (OperationCanceledException) when (cts.Token.IsCancellationRequested)
441+
{
442+
break; // Graceful shutdown
443+
}
444+
catch (Exception ex)
445+
{
446+
Console.WriteLine($"Notification listener crashed, restarting in 10s: {ex.Message}");
447+
await Task.Delay(TimeSpan.FromSeconds(10), cts.Token);
448+
}
449+
}
422450
423-
amazonConnection.Notification.StartReceivingNotificationMessages(param, messageReceiver);
424451
public class CustomMessageReceiver : IMessageReceiver
425452
{
426-
public void ErrorCatch(Exception ex)
427-
{
428-
//Your code here
429-
}
453+
// Track processed notification IDs to handle SQS duplicate delivery
454+
private readonly ConcurrentDictionary<string, byte> _processedNotificationIds = new();
455+
private readonly ConcurrentQueue<string> _idQueue = new();
456+
private const int MaxTrackedIds = 10_000;
430457
431-
public void NewMessageRevicedTriger(NotificationMessageResponce message)
432-
{
433-
//Your Code here
434-
}
458+
public void ErrorCatch(Exception ex)
459+
{
460+
Console.WriteLine($"Notification error: {ex.Message}");
461+
}
462+
463+
public void NewMessageRevicedTriger(NotificationMessageResponce message)
464+
{
465+
// Deduplicate: SQS standard queues may deliver the same message more than once
466+
var notificationId = message?.NotificationMetadata?.NotificationId;
467+
if (notificationId != null && !_processedNotificationIds.TryAdd(notificationId, 0))
468+
return;
469+
470+
// Cap the dedup cache so it doesn't grow forever
471+
if (notificationId != null)
472+
{
473+
_idQueue.Enqueue(notificationId);
474+
while (_idQueue.Count > MaxTrackedIds && _idQueue.TryDequeue(out var oldId))
475+
_processedNotificationIds.TryRemove(oldId, out _);
476+
}
477+
478+
//Your Code here
479+
}
435480
}
436481
437482
```
438483
439484
### Feed Submit
440-
Here full sample for submit feed to change price and generate XML and get final report for result same as in [doc](https://github.com/amzn/selling-partner-api-docs/blob/main/guides/en-US/use-case-guides/feeds-api-use-case-guide/feeds-api-use-case-guide_2021-06-30.md).
441-
Notes: not all [feed type](https://github.com/amzn/selling-partner-api-docs/blob/main/references/feeds-api/feedtype-values.md) finished as it's big work and effort but all classes are partial for easy change and you can generate XML outside and use our library to get data, now we support only submit existing product, change quantity and change price , I list most of XSD here Source\FikaAmazonAPI\ConstructFeed\xsd its will help you easy generate class and add it in your app to generate final feed xml.
485+
Here is a full sample for submitting a feed to change price, generate XML, and get the final processing report, same as in the [documentation](https://github.com/amzn/selling-partner-api-docs/blob/main/guides/en-US/use-case-guides/feeds-api-use-case-guide/feeds-api-use-case-guide_2021-06-30.md).
486+
487+
> **Note:** Not all [feed types](https://github.com/amzn/selling-partner-api-docs/blob/main/references/feeds-api/feedtype-values.md) are implemented yet. All classes are `partial` for easy extension — you can generate XML outside the library and use it to submit data. Currently supported: submit existing product, change quantity, and change price. Most XSD files are listed in `Source\FikaAmazonAPI\ConstructFeed\xsd` to help you generate classes for your app.
442488
443-
#### Feed Submit for change price , For more Feed sample please check [Here](https://github.com/abuzuhri/Amazon-SP-API-CSharp/blob/main/Source/FikaAmazonAPI.SampleCode/FeedsSample.cs).
489+
#### Feed Submit — Change Price
490+
For more feed samples, please check [Here](https://github.com/abuzuhri/Amazon-SP-API-CSharp/blob/main/Source/FikaAmazonAPI.SampleCode/FeedsSample.cs).
444491
```CSharp
445492
ConstructFeedService createDocument = new ConstructFeedService("{SellerID}", "1.02");
446493
@@ -628,7 +675,7 @@ var processingReport = await amazonConnection.Feed.GetJsonFeedDocumentProcessing
628675
```
629676
630677
631-
#### Feed Submit for change Quantity
678+
#### Feed Submit — Change Quantity
632679
```CSharp
633680
ConstructFeedService createDocument = new ConstructFeedService("{SellerID}", "1.02");
634681
@@ -658,7 +705,7 @@ var processingReport = amazonConnection.Feed.GetFeedDocumentProcessingReport(out
658705
659706
```
660707
661-
#### Feed Submit for change ProdcutImage
708+
#### Feed Submit — Change Product Image
662709
```CSharp
663710
public void SubmitFeedProductImage()
664711
{
@@ -678,7 +725,7 @@ public void SubmitFeedProductImage()
678725
}
679726
```
680727
681-
#### Feed Submit for change FULFILLMENT DATA (add tracking number for shipment)
728+
#### Feed Submit — Fulfillment Data (add tracking number for shipment)
682729
```CSharp
683730
684731
ConstructFeedService createDocument = new ConstructFeedService("{sellerId}", "1.02");
@@ -704,7 +751,7 @@ list.Add(new OrderFulfillmentMessage()
704751
```
705752
706753
707-
#### Feed Submit for change Order Adjustments
754+
#### Feed Submit Order Adjustments
708755
```CSharp
709756
public void SubmitFeedOrderAdjustment()
710757
{
@@ -750,11 +797,11 @@ public void SubmitFeedOrderAdjustment()
750797
Please read this doc to get all information about this limitation
751798
https://developer-docs.amazon.com/sp-api/docs/usage-plans-and-rate-limits
752799
753-
we calc waiting time by read x-amzn-RateLimit-Limit header
800+
We calculate the waiting time by reading the `x-amzn-RateLimit-Limit` header:
754801
755802
`int sleepTime = (int)((1 / header["x-amzn-RateLimit-Limit"] ) * 1000);`
756803
757-
You can also disable libary from handelling limitaion by set IsActiveLimitRate=false in AmazonCredential
804+
You can also disable the library's rate limit handling by setting `IsActiveLimitRate = false` in `AmazonCredential`:
758805
```CSharp
759806
var amazonConnection = new AmazonConnection(new AmazonCredential()
760807
{
@@ -766,8 +813,8 @@ var amazonConnection = new AmazonConnection(new AmazonCredential()
766813
767814
---
768815
769-
## Enable debug mode
770-
You can also enable log for all http request and response you can set IsDebugMode=true in AmazonCredential
816+
## Enable Debug Mode
817+
You can enable logging for all HTTP requests and responses by setting `IsDebugMode = true` in `AmazonCredential`:
771818
```CSharp
772819
var amazonConnection = new AmazonConnection(new AmazonCredential()
773820
{
@@ -779,7 +826,7 @@ var amazonConnection = new AmazonConnection(new AmazonCredential()
779826
780827
---
781828
782-
## Get restrictions before try to add new lists
829+
## Get Restrictions Before Adding New Listings
783830
784831
```CSharp
785832
var result = amazonConnection.Restrictions.GetListingsRestrictions(

Source/FikaAmazonAPI.SampleCode/NotificationsSample.cs

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
using FikaAmazonAPI.NotificationMessages;
22
using FikaAmazonAPI.Parameter.Notification;
33
using System;
4+
using System.Collections.Concurrent;
45
using System.Collections.Generic;
56
using System.Linq;
67
using System.Text;
8+
using System.Threading;
79
using System.Threading.Tasks;
810
using static FikaAmazonAPI.Utils.Constants;
911

@@ -72,26 +74,83 @@ public void CreateSubscription()
7274
}
7375

7476

75-
public void StartReceivingNotificationMessages()
77+
public async Task StartReceivingNotificationMessagesAsync(CancellationToken cancellationToken)
7678
{
77-
var SQL_URL = "https://sqs.us-east-2.amazonaws.com/239917024027/ICANL_SQS";
78-
ParameterMessageReceiver param = new ParameterMessageReceiver(Environment.GetEnvironmentVariable("AccessKey"), Environment.GetEnvironmentVariable("SecretKey"), SQL_URL, Amazon.RegionEndpoint.USEast2);
79-
80-
CustomMessageReceiver messageReceiver = new CustomMessageReceiver();
81-
82-
83-
amazonConnection.Notification.StartReceivingNotificationMessages(param, messageReceiver);
79+
// Prevent unobserved task exceptions from crashing the process.
80+
// The library fires off ProcessAnyOfferChangedMessage without await,
81+
// so any exception in that path becomes an unobserved task exception.
82+
TaskScheduler.UnobservedTaskException += (sender, e) =>
83+
{
84+
Console.WriteLine($"Unobserved task exception caught: {e.Exception?.Message}");
85+
e.SetObserved();
86+
};
87+
88+
var SQS_URL = Environment.GetEnvironmentVariable("SQS_URL");
89+
var param = new ParameterMessageReceiver(
90+
Environment.GetEnvironmentVariable("AccessKey"),
91+
Environment.GetEnvironmentVariable("SecretKey"),
92+
SQS_URL,
93+
Amazon.RegionEndpoint.USEast2,
94+
WaitTimeSeconds: 20); // Enable SQS long polling to reduce empty receives and cost
95+
96+
var messageReceiver = new CustomMessageReceiver();
97+
98+
// Wrap in a restart loop so the listener recovers from any fatal error
99+
// (e.g. network drop, SQS client disposed, transient AWS failures)
100+
while (!cancellationToken.IsCancellationRequested)
101+
{
102+
try
103+
{
104+
await amazonConnection.Notification.StartReceivingNotificationMessagesAsync(
105+
param, messageReceiver, cancellationToken: cancellationToken);
106+
}
107+
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
108+
{
109+
// Graceful shutdown requested
110+
break;
111+
}
112+
catch (Exception ex)
113+
{
114+
Console.WriteLine($"Notification listener crashed, restarting in 10s: {ex.Message}");
115+
try
116+
{
117+
await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken);
118+
}
119+
catch (OperationCanceledException)
120+
{
121+
break;
122+
}
123+
}
124+
}
84125
}
85126
public class CustomMessageReceiver : IMessageReceiver
86127
{
128+
// Track processed notification IDs to handle SQS duplicate delivery.
129+
// Use a queue to cap memory — keep only the most recent IDs.
130+
private readonly ConcurrentDictionary<string, byte> _processedNotificationIds = new();
131+
private readonly ConcurrentQueue<string> _idQueue = new();
132+
private const int MaxTrackedIds = 10_000;
87133

88134
public void ErrorCatch(Exception ex)
89135
{
90-
//Your code here
136+
Console.WriteLine($"Notification error: {ex.Message}");
91137
}
92138

93139
public void NewMessageRevicedTriger(NotificationMessageResponce message)
94140
{
141+
// Deduplicate: SQS standard queues may deliver the same message more than once
142+
var notificationId = message?.NotificationMetadata?.NotificationId;
143+
if (notificationId != null && !_processedNotificationIds.TryAdd(notificationId, 0))
144+
return;
145+
146+
// Cap the dedup cache so it doesn't grow forever
147+
if (notificationId != null)
148+
{
149+
_idQueue.Enqueue(notificationId);
150+
while (_idQueue.Count > MaxTrackedIds && _idQueue.TryDequeue(out var oldId))
151+
_processedNotificationIds.TryRemove(oldId, out _);
152+
}
153+
95154
//Your Code here
96155
}
97156
}

Source/FikaAmazonAPI.SampleCode/Program.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using FikaAmazonAPI.AmazonSpApiSDK.Models.FulfillmentInbound;
2+
using FikaAmazonAPI.ReportGeneration;
23
using FikaAmazonAPI.Utils;
34
using Microsoft.Extensions.Configuration;
45
using Microsoft.Extensions.Logging;
@@ -27,6 +28,9 @@ static async Task Main(string[] args)
2728
IsDebugMode = true
2829
});
2930

31+
ReportManager reportManager = new ReportManager(amazonConnection);
32+
33+
var feedbacks = reportManager.GetFeedbackFromDays(180); //GET_SELLER_FEEDBACK_DATA
3034

3135
FeedsSample feedsSample = new FeedsSample(amazonConnection);
3236
await feedsSample.SubmitFeedDELETE_JSONAsync("B07HMBFZCZ .2");

0 commit comments

Comments
 (0)