diff --git a/Documents/API/EPCIS 2.0/Queries/Subscribe to query.bru b/Documents/API/EPCIS 2.0/Queries/Subscribe to query.bru new file mode 100644 index 00000000..ba6eb042 --- /dev/null +++ b/Documents/API/EPCIS 2.0/Queries/Subscribe to query.bru @@ -0,0 +1,46 @@ +meta { + name: Subscribe to query + type: http + seq: 5 +} + +delete { + url: {{baseUrl}}/queries/UniqueQueryName/subscriptions/c8f1e6d9-252b-4bd5-b0a3-f8f1094cd1a3 + body: json + auth: basic +} + +auth:basic { + username: {{username}} + password: {{password}} +} + +body:json { + { + "dest": "https://localhost:5001/test", + "reportIfEmpty": false, + "stream": true + } +} + +vars:pre-request { + queryName: UniqueQueryName +} + +assert { + res.status: eq 201 + res.headers.location: isDefined +} + +tests { + test("Should contain the location header", function() { + expect(res.headers.location).to.equals("queries/UniqueQueryName"); + }); + +} + +docs { + # Creates a named EPCIS events query. + + Creating a named query creates a view on the events in the repository, accessible through its events resource. To obtain the named query results, the client can use the URL in the `Location` header. The client can also use this URL to start a query subscription immediately after creating the query. +} diff --git a/Documents/API/EPCIS 2.0/Queries/Unsubscribe.bru b/Documents/API/EPCIS 2.0/Queries/Unsubscribe.bru new file mode 100644 index 00000000..ef3ae0df --- /dev/null +++ b/Documents/API/EPCIS 2.0/Queries/Unsubscribe.bru @@ -0,0 +1,46 @@ +meta { + name: Unsubscribe + type: http + seq: 6 +} + +delete { + url: {{baseUrl}}/queries/UniqueQueryName/subscriptions/c8f1e6d9-252b-4bd5-b0a3-f8f1094cd1a3 + body: json + auth: basic +} + +auth:basic { + username: {{username}} + password: {{password}} +} + +body:json { + { + "dest": "https://localhost:5001/test", + "reportIfEmpty": false, + "stream": true + } +} + +vars:pre-request { + queryName: UniqueQueryName +} + +assert { + res.status: eq 201 + res.headers.location: isDefined +} + +tests { + test("Should contain the location header", function() { + expect(res.headers.location).to.equals("queries/UniqueQueryName"); + }); + +} + +docs { + # Creates a named EPCIS events query. + + Creating a named query creates a view on the events in the repository, accessible through its events resource. To obtain the named query results, the client can use the URL in the `Location` header. The client can also use this URL to start a query subscription immediately after creating the query. +} diff --git a/src/FasTnT.Domain/Model/Subscriptions/SubscriptionSchedule.cs b/src/FasTnT.Domain/Model/Subscriptions/SubscriptionSchedule.cs index f7500d6c..ed2475dc 100644 --- a/src/FasTnT.Domain/Model/Subscriptions/SubscriptionSchedule.cs +++ b/src/FasTnT.Domain/Model/Subscriptions/SubscriptionSchedule.cs @@ -1,11 +1,11 @@ namespace FasTnT.Domain.Model.Subscriptions; -public class SubscriptionSchedule +public sealed record SubscriptionSchedule { - public string Second { get; set; } - public string Minute { get; set; } - public string Hour { get; set; } - public string DayOfMonth { get; set; } - public string Month { get; set; } - public string DayOfWeek { get; set; } + public string Second { get; init; } + public string Minute { get; init; } + public string Hour { get; init; } + public string DayOfMonth { get; init; } + public string Month { get; init; } + public string DayOfWeek { get; init; } } diff --git a/src/FasTnT.Host/Endpoints/SubscriptionEndpoints.cs b/src/FasTnT.Host/Endpoints/SubscriptionEndpoints.cs index f65d862b..fe25351a 100644 --- a/src/FasTnT.Host/Endpoints/SubscriptionEndpoints.cs +++ b/src/FasTnT.Host/Endpoints/SubscriptionEndpoints.cs @@ -38,7 +38,7 @@ private static async Task DeleteSubscription(string name, Subscriptions return Results.NoContent(); } - private static async Task SubscribeRequest(SubscriptionRequest request, QueriesHandler queryDetails, SubscriptionsHandler subscribe, CancellationToken cancellationToken) + private static async Task SubscribeRequest(SubscriptionRequest request, QueriesHandler queryDetails, SubscriptionsHandler handler, CancellationToken cancellationToken) { var query = await queryDetails.GetQueryDetailsAsync(request.QueryName, cancellationToken); @@ -46,7 +46,7 @@ private static async Task SubscribeRequest(SubscriptionRequest request, request.Subscription.Name = Guid.NewGuid().ToString(); request.Subscription.Parameters.AddRange(query.Parameters); - var response = await subscribe.RegisterSubscriptionAsync(request.Subscription, cancellationToken); + var response = await handler.RegisterSubscriptionAsync(request.Subscription, cancellationToken); return Results.Created($"queries/{query}/subscriptions/{response.Name}", null); } diff --git a/src/FasTnT.Host/Subscriptions/Schedulers/CronSubscriptionScheduler.cs b/src/FasTnT.Host/Subscriptions/Schedulers/CronSubscriptionScheduler.cs index 59dfa9d9..636dd65b 100644 --- a/src/FasTnT.Host/Subscriptions/Schedulers/CronSubscriptionScheduler.cs +++ b/src/FasTnT.Host/Subscriptions/Schedulers/CronSubscriptionScheduler.cs @@ -2,10 +2,10 @@ namespace FasTnT.Host.Subscriptions.Schedulers; -public class CronSubscriptionScheduler(SubscriptionSchedule schedule) : SubscriptionScheduler +public sealed class CronSubscriptionScheduler(SubscriptionSchedule schedule) : SubscriptionScheduler { internal readonly ScheduleEntry - Seconds = ScheduleEntry.Parse(schedule.Second, 0, 60), + Seconds = ScheduleEntry.Parse(schedule.Second, 0, 59), Minutes = ScheduleEntry.Parse(schedule.Minute, 0, 59), Hours = ScheduleEntry.Parse(schedule.Hour, 0, 23), DayOfMonth = ScheduleEntry.Parse(schedule.DayOfMonth, 1, 31), @@ -80,7 +80,7 @@ private static DateTime GetNextTentative(DateTime tentative, Func return tentative; } - internal class ScheduleEntry + internal sealed record ScheduleEntry { private readonly List _values = []; private readonly int _minValue, _maxValue; diff --git a/src/FasTnT.Host/Subscriptions/Schedulers/TriggeredSubscriptionScheduler.cs b/src/FasTnT.Host/Subscriptions/Schedulers/TriggeredSubscriptionScheduler.cs index e9e82b30..d7459cf3 100644 --- a/src/FasTnT.Host/Subscriptions/Schedulers/TriggeredSubscriptionScheduler.cs +++ b/src/FasTnT.Host/Subscriptions/Schedulers/TriggeredSubscriptionScheduler.cs @@ -1,11 +1,14 @@ namespace FasTnT.Host.Subscriptions.Schedulers; -public class TriggeredSubscriptionScheduler : SubscriptionScheduler +public sealed class TriggeredSubscriptionScheduler : SubscriptionScheduler { + // The default delay to run triggered subscriptions in case no even was received + private static readonly TimeSpan Delay = TimeSpan.FromMinutes(2); + public override void ComputeNextExecution(DateTime startDate) { - // Trigger every streaming subscription at least every 30 seconds in case a request was missed. - NextComputedExecution = DateTime.UtcNow.AddSeconds(30); + // Trigger every streaming subscription with a regular schedule in case a capture operation was missed or failed. + NextComputedExecution = DateTime.UtcNow + Delay; } public override bool IsDue()