From 8be8eb52bcc8a44565f0340c9c35fab67019e10b Mon Sep 17 00:00:00 2001 From: Khwezi Mngoma Date: Wed, 3 Jun 2026 00:47:54 +0200 Subject: [PATCH] Used shared components Built loopbackip check override based on environment --- MidrandBooksApi/ApiVersionTargetAttribute.cs | 7 -- MidrandBooksApi/EndpointTags.cs | 7 -- MidrandBooksApi/IEndpoint.cs | 6 -- MidrandBooksApi/MidrandBooksApi.csproj | 5 +- .../OpenApiBearerSecuritySchemeTransformer.cs | 16 ---- .../Endpoints/ConfirmationEndpoint.cs | 12 ++- MidrandBooksApi/Program.cs | 3 - MidrandBooksApi/Setup.cs | 90 ------------------- MidrandBooksApi/http/app.http | 8 -- 9 files changed, 11 insertions(+), 143 deletions(-) delete mode 100644 MidrandBooksApi/ApiVersionTargetAttribute.cs delete mode 100644 MidrandBooksApi/EndpointTags.cs delete mode 100644 MidrandBooksApi/IEndpoint.cs delete mode 100644 MidrandBooksApi/OpenApiBearerSecuritySchemeTransformer.cs delete mode 100644 MidrandBooksApi/Setup.cs delete mode 100644 MidrandBooksApi/http/app.http diff --git a/MidrandBooksApi/ApiVersionTargetAttribute.cs b/MidrandBooksApi/ApiVersionTargetAttribute.cs deleted file mode 100644 index d02fb28..0000000 --- a/MidrandBooksApi/ApiVersionTargetAttribute.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace MidrandBooksApi; - -[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] -public sealed class ApiVersionTargetAttribute(int majorVersion) : Attribute -{ - public int MajorVersion { get; } = majorVersion; -} diff --git a/MidrandBooksApi/EndpointTags.cs b/MidrandBooksApi/EndpointTags.cs deleted file mode 100644 index af64953..0000000 --- a/MidrandBooksApi/EndpointTags.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace MidrandBooksApi; - -public static class EndpointTags -{ - public const string Books = nameof(Books); - public const string Payments = nameof(Payments); -} diff --git a/MidrandBooksApi/IEndpoint.cs b/MidrandBooksApi/IEndpoint.cs deleted file mode 100644 index e2a2c05..0000000 --- a/MidrandBooksApi/IEndpoint.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace MidrandBooksApi; - -public interface IEndpoint -{ - void Map(IEndpointRouteBuilder builder); -} diff --git a/MidrandBooksApi/MidrandBooksApi.csproj b/MidrandBooksApi/MidrandBooksApi.csproj index b6be5dd..db53633 100644 --- a/MidrandBooksApi/MidrandBooksApi.csproj +++ b/MidrandBooksApi/MidrandBooksApi.csproj @@ -34,6 +34,7 @@ + @@ -53,13 +54,13 @@ - + - + diff --git a/MidrandBooksApi/OpenApiBearerSecuritySchemeTransformer.cs b/MidrandBooksApi/OpenApiBearerSecuritySchemeTransformer.cs deleted file mode 100644 index de99e93..0000000 --- a/MidrandBooksApi/OpenApiBearerSecuritySchemeTransformer.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace MidrandBooksApi; - -public sealed class OpenApiBearerSecuritySchemeTransformer : IOpenApiDocumentTransformer -{ - public async Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken) - { - var bearerScheme = new OpenApiSecurityScheme - { - Type = SecuritySchemeType.Http, - Scheme = "bearer", - Description = "JWT Authorization header using the Bearer scheme. Example: \"Bearer {token}\"" - }; - - document.AddComponent("Bearer", bearerScheme); - } -} diff --git a/MidrandBooksApi/Payments/Endpoints/ConfirmationEndpoint.cs b/MidrandBooksApi/Payments/Endpoints/ConfirmationEndpoint.cs index 5417e65..c83540f 100644 --- a/MidrandBooksApi/Payments/Endpoints/ConfirmationEndpoint.cs +++ b/MidrandBooksApi/Payments/Endpoints/ConfirmationEndpoint.cs @@ -1,7 +1,11 @@ -using LiteCharms.Features.MidrandBooks.Payments; +using LiteCharms.Features.Abstractions; +using LiteCharms.Features.Api; +using LiteCharms.Features.Extensions; +using LiteCharms.Features.MidrandBooks.Payments; using LiteCharms.Features.MidrandBooks.Payments.Events; using LiteCharms.Features.MidrandBooks.Payments.Models; using LiteCharms.Features.Quartz.Abstractions; +using static LiteCharms.Features.Extensions.Api; namespace MidrandBooksApi.Payments.Endpoints; @@ -22,7 +26,7 @@ public sealed class ConfirmationEndpoint : IEndpoint string? remoteIp = request.HttpContext.Connection.RemoteIpAddress?.ToString(); - var ipValidation = await payfastService.ValidateReferrerIpAsync(remoteIp!, cancellationToken); + var ipValidation = await payfastService.ValidateReferrerIpAsync(remoteIp!, !hostEnvironment.IsProduction(), cancellationToken); if (ipValidation.IsFailed || !ipValidation.Value) return Results.Unauthorized(); @@ -54,7 +58,7 @@ public sealed class ConfirmationEndpoint : IEndpoint return Results.Unauthorized(); var notification = PayfastPaymentConfirmationReceivedEvent.Create(payload, payload.MerchantPaymentId!, - performBackgroundChecks: false); // Set to false because comprehensive checks are completed inline above + allowLoopback: !hostEnvironment.IsProduction(), performBackgroundChecks: false); // Set to false because comprehensive checks are completed inline above await jobOrchestrator.SendAsync(notification, cancellationToken); @@ -68,7 +72,7 @@ public sealed class ConfirmationEndpoint : IEndpoint .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status401Unauthorized) - .WithTags(EndpointTags.Payments); + .WithTags(Api.Payments); } private static PayfastWebhookPayload ParseForm(IFormCollection formCollection, string incomingSignature) => new() diff --git a/MidrandBooksApi/Program.cs b/MidrandBooksApi/Program.cs index 2e801d0..5df7539 100644 --- a/MidrandBooksApi/Program.cs +++ b/MidrandBooksApi/Program.cs @@ -1,9 +1,6 @@ -using Asp.Versioning.Builder; -using k8s.Models; using LiteCharms.Features.Extensions; using LiteCharms.Features.Mediator; using LiteCharms.Features.MidrandBooks.Extensions; -using MidrandBooksApi; using static LiteCharms.Features.Extensions.Quartz; var builder = WebApplication.CreateBuilder(args); diff --git a/MidrandBooksApi/Setup.cs b/MidrandBooksApi/Setup.cs deleted file mode 100644 index a34a8a7..0000000 --- a/MidrandBooksApi/Setup.cs +++ /dev/null @@ -1,90 +0,0 @@ -namespace MidrandBooksApi; - -public static class Setup -{ - public static IApplicationBuilder MapEndpoints(this WebApplication app, Dictionary versionGroups) - { - var endpoints = app.Services.GetRequiredService>(); - - foreach (var endpoint in endpoints) - { - var versionAttributes = endpoint.GetType().GetCustomAttributes().ToList(); - - if (versionAttributes.Count != 0) - { - foreach (var attr in versionAttributes) - if (versionGroups.TryGetValue(attr.MajorVersion, out var targetGroup)) - endpoint.Map(targetGroup); - } - else - endpoint.Map(app); - } - - return app; - } - - public static IServiceCollection AddEndpoints(this IServiceCollection services, Assembly assembly) - { - ServiceDescriptor[] discriptors = [.. assembly.DefinedTypes - .Where(t => t is { IsInterface: false, IsAbstract: false }) - .Where(t => t.IsAssignableTo(typeof(IEndpoint))) - .Select(t => ServiceDescriptor.Transient(typeof(IEndpoint), t))]; - - services.TryAddEnumerable(discriptors); - - return services; - } - - public static string ToEndpointName(this Type target, string? annotation = "") => - $"{target.Name.Replace("Endpoint", string.Empty)}{annotation}".ToLower(); - - public static IServiceCollection AddApiServices(this IServiceCollection services, IConfiguration configuration) - { - services.AddHttpClient(); - - services.AddApiVersioning(options => - { - options.DefaultApiVersion = new ApiVersion(1); - options.ReportApiVersions = true; - options.AssumeDefaultVersionWhenUnspecified = true; - options.ApiVersionReader = ApiVersionReader.Combine(new UrlSegmentApiVersionReader(), - new QueryStringApiVersionReader("version"), - new QueryStringApiVersionReader("version"), - new MediaTypeApiVersionReader("version")); - }) - .AddApiExplorer(options => - { - options.GroupNameFormat = "'v'VVV"; - options.SubstituteApiVersionInUrl = true; - }); - - var urls = configuration["ASPNETCORE_URLS"] ?? configuration["Urls"]; - var healthUrl = "http://localhost:8080/health"; - - if (!string.IsNullOrWhiteSpace(urls)) - { - string firstUrl = urls.Split(';').FirstOrDefault(s => s.Contains("http://"))! - .Replace("*", "localhost").Replace("+", "localhost"); - - healthUrl = $"{firstUrl.TrimEnd('/')}/health"; - } - - services.AddHealthChecksUI(setup => - { - setup.SetNotifyUnHealthyOneTimeUntilChange(); - setup.AddHealthCheckEndpoint("primary, heal", healthUrl); - setup.SetHeaderText("Midrand Books"); - }) - .AddInMemoryStorage(); - - services.AddOutputCache(options => - { - options.AddBasePolicy(builder => builder.Cache()); - options.DefaultExpirationTimeSpan = TimeSpan.FromSeconds(10); - }); - - services.AddOpenApi(options => options.AddDocumentTransformer()); - - return services; - } -} diff --git a/MidrandBooksApi/http/app.http b/MidrandBooksApi/http/app.http deleted file mode 100644 index 7297a16..0000000 --- a/MidrandBooksApi/http/app.http +++ /dev/null @@ -1,8 +0,0 @@ -## Payfast Payment Confirmation -# This endpoint is used by Payfast to confirm the payment status of a transaction. -# It receives a POST request with the payment details and updates the order status accordingly. - -POST {{baseUrl}}/v1/payments/payfast/confirm -Content-Type: application/x-www-form-urlencoded - -amount={{amount}}&item_name={{item_name}}&m_payment_id={{paymentId}}&signature={{signature}}