From d4394d98865793ec4007821c12e75cd90c8d0e3b Mon Sep 17 00:00:00 2001 From: Justin Yoo Date: Sun, 1 Sep 2024 12:04:42 +0900 Subject: [PATCH 1/6] Add AdminEventService layer --- .../Endpoints/AdminEventEndpoints.cs | 31 ++++++- .../Models/EventDetails.cs | 5 -- .../Services/AdminEventService.cs | 67 +++++++++++++++ .../Services/AdminEventServiceTests.cs | 81 +++++++++++++++++++ .../AppHostProgramTests.cs | 2 +- 5 files changed, 177 insertions(+), 9 deletions(-) create mode 100644 src/AzureOpenAIProxy.ApiApp/Services/AdminEventService.cs create mode 100644 test/AzureOpenAIProxy.ApiApp.Tests/Services/AdminEventServiceTests.cs diff --git a/src/AzureOpenAIProxy.ApiApp/Endpoints/AdminEventEndpoints.cs b/src/AzureOpenAIProxy.ApiApp/Endpoints/AdminEventEndpoints.cs index 0cde354b..beb5371f 100644 --- a/src/AzureOpenAIProxy.ApiApp/Endpoints/AdminEventEndpoints.cs +++ b/src/AzureOpenAIProxy.ApiApp/Endpoints/AdminEventEndpoints.cs @@ -1,4 +1,5 @@ using AzureOpenAIProxy.ApiApp.Models; +using AzureOpenAIProxy.ApiApp.Services; using Microsoft.AspNetCore.Mvc; @@ -18,14 +19,38 @@ public static RouteHandlerBuilder AddNewAdminEvent(this WebApplication app) { var builder = app.MapPost(AdminEndpointUrls.AdminEvents, async ( [FromBody] AdminEventDetails payload, - HttpRequest request) => + IAdminEventService service, + ILoggerFactory loggerFactory) => { + var logger = loggerFactory.CreateLogger(nameof(AdminEventEndpoints)); + logger.LogInformation("Received a new event request"); + + if (payload is null) + { + logger.LogError("No payload found"); + + return Results.BadRequest("Payload is null"); + } + + //try + //{ + // var result = await service.CreateEvent(payload); + + // logger.LogInformation("Created a new event"); + + // return Results.Ok(result); + //} + //catch (Exception ex) + //{ + // logger.LogError(ex, "Failed to create a new event"); + + // return Results.Problem(ex.Message, statusCode: StatusCodes.Status500InternalServerError); + //} + return await Task.FromResult(Results.Ok()); }) - // TODO: Check both request/response payloads .Accepts(contentType: "application/json") .Produces(statusCode: StatusCodes.Status200OK, contentType: "application/json") - // TODO: Check both request/response payloads .Produces(statusCode: StatusCodes.Status400BadRequest) .Produces(statusCode: StatusCodes.Status401Unauthorized) .Produces(statusCode: StatusCodes.Status500InternalServerError, contentType: "text/plain") diff --git a/src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs b/src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs index 5c33fafb..ca3e9c55 100644 --- a/src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs +++ b/src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs @@ -1,8 +1,3 @@ -using System.ComponentModel.DataAnnotations; -using System.Text.Json.Serialization; - -using AzureOpenAIProxy.ApiApp.Models; - /// /// This represents the event's detailed data for response by EventEndpoint. /// diff --git a/src/AzureOpenAIProxy.ApiApp/Services/AdminEventService.cs b/src/AzureOpenAIProxy.ApiApp/Services/AdminEventService.cs new file mode 100644 index 00000000..3cad1438 --- /dev/null +++ b/src/AzureOpenAIProxy.ApiApp/Services/AdminEventService.cs @@ -0,0 +1,67 @@ +using AzureOpenAIProxy.ApiApp.Models; + +namespace AzureOpenAIProxy.ApiApp.Services; + +/// +/// This provides interfaces to class. +/// +public interface IAdminEventService +{ + /// + /// Creates a new event. + /// + /// Event payload. + /// Returns the event payload created. + Task CreateEvent(AdminEventDetails eventDetails); + + /// + /// Gets the list of events. + /// + /// Returns the list of events. + Task> GetEvents(); + + /// + /// Gets the event details. + /// + /// Event ID. + /// Returns the event details. + Task GetEvent(Guid eventId); + + /// + /// Updates the event details. + /// + /// Event ID. + /// Event details to update. + /// Returns the updated event details. + Task UpdateEvent(Guid eventId, AdminEventDetails eventDetails); +} + +/// +/// This represents the service entity for admin event. +/// +public class AdminEventService : IAdminEventService +{ + /// + public async Task CreateEvent(AdminEventDetails eventDetails) + { + throw new NotImplementedException(); + } + + /// + public async Task> GetEvents() + { + throw new NotImplementedException(); + } + + /// + public async Task GetEvent(Guid eventId) + { + throw new NotImplementedException(); + } + + /// + public async Task UpdateEvent(Guid eventId, AdminEventDetails eventDetails) + { + throw new NotImplementedException(); + } +} diff --git a/test/AzureOpenAIProxy.ApiApp.Tests/Services/AdminEventServiceTests.cs b/test/AzureOpenAIProxy.ApiApp.Tests/Services/AdminEventServiceTests.cs new file mode 100644 index 00000000..81827423 --- /dev/null +++ b/test/AzureOpenAIProxy.ApiApp.Tests/Services/AdminEventServiceTests.cs @@ -0,0 +1,81 @@ +using AzureOpenAIProxy.ApiApp.Models; +using AzureOpenAIProxy.ApiApp.Services; + +using FluentAssertions; + +namespace AzureOpenAIProxy.ApiApp.Tests.Services; + +public class AdminEventServiceTests +{ + [Fact] + public void Given_Instance_When_CreateEvent_Invoked_Then_It_Should_Throw_Exception() + { + // Arrange + var eventDetails = new AdminEventDetails() + { + DateStart = DateTimeOffset.UtcNow, + DateEnd = DateTimeOffset.UtcNow, + TimeZone = "UTC", + IsActive = true, + OrganizerName = "Organiser", + OrganizerEmail = "organiser@email.com", + }; + var service = new AdminEventService(); + + // Act + Func func = async () => await service.CreateEvent(eventDetails); + + // Assert + func.Should().ThrowAsync(); + } + + [Fact] + public void Given_Instance_When_GetEvents_Invoked_Then_It_Should_Throw_Exception() + { + // Arrange + var service = new AdminEventService(); + + // Act + Func func = async () => await service.GetEvents(); + + // Assert + func.Should().ThrowAsync(); + } + + [Fact] + public void Given_Instance_When_GetEvent_Invoked_Then_It_Should_Throw_Exception() + { + // Arrange + var eventId = Guid.NewGuid(); + var service = new AdminEventService(); + + // Act + Func func = async () => await service.GetEvent(eventId); + + // Assert + func.Should().ThrowAsync(); + } + + [Fact] + public void Given_Instance_When_UpdateEvent_Invoked_Then_It_Should_Throw_Exception() + { + // Arrange + var eventId = Guid.NewGuid(); + var eventDetails = new AdminEventDetails() + { + DateStart = DateTimeOffset.UtcNow, + DateEnd = DateTimeOffset.UtcNow, + TimeZone = "UTC", + IsActive = true, + OrganizerName = "Organiser", + OrganizerEmail = "organiser@email.com", + }; + var service = new AdminEventService(); + + // Act + Func func = async () => await service.UpdateEvent(eventId, eventDetails); + + // Assert + func.Should().ThrowAsync(); + } +} diff --git a/test/AzureOpenAIProxy.AppHost.Tests/AppHostProgramTests.cs b/test/AzureOpenAIProxy.AppHost.Tests/AppHostProgramTests.cs index 22adb326..4af8e386 100644 --- a/test/AzureOpenAIProxy.AppHost.Tests/AppHostProgramTests.cs +++ b/test/AzureOpenAIProxy.AppHost.Tests/AppHostProgramTests.cs @@ -4,7 +4,7 @@ using FluentAssertions; -namespace AzureOpenAIProxy.Tests; +namespace AzureOpenAIProxy.AppHost.Tests; public class AppHostProgramTests(AspireAppHostFixture host) : IClassFixture { From 1441776162a03b43e03e44dc8099e4529fc35bd5 Mon Sep 17 00:00:00 2001 From: Justin Yoo Date: Sun, 1 Sep 2024 12:10:05 +0900 Subject: [PATCH 2/6] Update tests --- src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs | 2 +- .../Services/AdminEventServiceTests.cs | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs b/src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs index ca3e9c55..2307da3e 100644 --- a/src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs +++ b/src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs @@ -6,7 +6,7 @@ public class EventDetails /// /// Gets or sets the event id. /// - public required string? EventId { get; set; } + public required Guid? EventId { get; set; } /// /// Gets or sets the event title name. diff --git a/test/AzureOpenAIProxy.ApiApp.Tests/Services/AdminEventServiceTests.cs b/test/AzureOpenAIProxy.ApiApp.Tests/Services/AdminEventServiceTests.cs index 81827423..2135bb5e 100644 --- a/test/AzureOpenAIProxy.ApiApp.Tests/Services/AdminEventServiceTests.cs +++ b/test/AzureOpenAIProxy.ApiApp.Tests/Services/AdminEventServiceTests.cs @@ -13,12 +13,18 @@ public void Given_Instance_When_CreateEvent_Invoked_Then_It_Should_Throw_Excepti // Arrange var eventDetails = new AdminEventDetails() { + EventId = Guid.NewGuid(), + Title = "Event", + Summary = "Event summary", + Description = "Event description", DateStart = DateTimeOffset.UtcNow, DateEnd = DateTimeOffset.UtcNow, TimeZone = "UTC", IsActive = true, OrganizerName = "Organiser", OrganizerEmail = "organiser@email.com", + MaxTokenCap = 100, + DailyRequestCap = 1000, }; var service = new AdminEventService(); @@ -63,12 +69,18 @@ public void Given_Instance_When_UpdateEvent_Invoked_Then_It_Should_Throw_Excepti var eventId = Guid.NewGuid(); var eventDetails = new AdminEventDetails() { + EventId = Guid.NewGuid(), + Title = "Event", + Summary = "Event summary", + Description = "Event description", DateStart = DateTimeOffset.UtcNow, DateEnd = DateTimeOffset.UtcNow, TimeZone = "UTC", IsActive = true, OrganizerName = "Organiser", OrganizerEmail = "organiser@email.com", + MaxTokenCap = 100, + DailyRequestCap = 1000, }; var service = new AdminEventService(); From afc9cc5d42e0491ed1cbae53e45237eb87162fef Mon Sep 17 00:00:00 2001 From: Justin Yoo Date: Sun, 1 Sep 2024 12:24:07 +0900 Subject: [PATCH 3/6] Add AdminEventService to IoC --- .../Models/AdminEventDetails.cs | 12 ++++++------ .../Models/EventDetails.cs | 10 +++++----- src/AzureOpenAIProxy.ApiApp/Program.cs | 4 ++++ .../Services/AdminEventService.cs | 18 ++++++++++++++++++ 4 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/AzureOpenAIProxy.ApiApp/Models/AdminEventDetails.cs b/src/AzureOpenAIProxy.ApiApp/Models/AdminEventDetails.cs index 4f587217..cc75331c 100644 --- a/src/AzureOpenAIProxy.ApiApp/Models/AdminEventDetails.cs +++ b/src/AzureOpenAIProxy.ApiApp/Models/AdminEventDetails.cs @@ -13,32 +13,32 @@ public class AdminEventDetails : EventDetails /// /// Gets or sets the event start date. /// - public required DateTimeOffset? DateStart { get; set; } + public DateTimeOffset DateStart { get; set; } /// /// Gets or sets the event end date. /// - public required DateTimeOffset? DateEnd { get; set; } + public DateTimeOffset DateEnd { get; set; } /// /// Gets or sets the event start to end date timezone. /// - public required string? TimeZone { get; set; } + public string TimeZone { get; set; } = string.Empty; /// /// Gets or sets the event active status. /// - public required bool? IsActive { get; set; } + public bool IsActive { get; set; } /// /// Gets or sets the event organizer name. /// - public required string? OrganizerName { get; set; } + public string OrganizerName { get; set; } = string.Empty; /// /// Gets or sets the event organizer email. /// - public required string? OrganizerEmail { get; set; } + public string OrganizerEmail { get; set; } = string.Empty; /// /// Gets or sets the event coorganizer name. diff --git a/src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs b/src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs index 2307da3e..803b6e0b 100644 --- a/src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs +++ b/src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs @@ -6,25 +6,25 @@ public class EventDetails /// /// Gets or sets the event id. /// - public required Guid? EventId { get; set; } + public Guid EventId { get; set; } /// /// Gets or sets the event title name. /// - public required string? Title { get; set; } + public string Title { get; set; } = string.Empty; /// /// Gets or sets the event summary. /// - public required string? Summary { get; set; } + public string Summary { get; set; } = string.Empty; /// /// Gets or sets the Azure OpenAI Service request max token capacity. /// - public required int? MaxTokenCap { get; set; } + public int MaxTokenCap { get; set; } /// /// Gets or sets the Azure OpenAI Service daily request capacity. /// - public required int? DailyRequestCap { get; set; } + public int DailyRequestCap { get; set; } } \ No newline at end of file diff --git a/src/AzureOpenAIProxy.ApiApp/Program.cs b/src/AzureOpenAIProxy.ApiApp/Program.cs index 5ce6fbe0..1ef3a28c 100644 --- a/src/AzureOpenAIProxy.ApiApp/Program.cs +++ b/src/AzureOpenAIProxy.ApiApp/Program.cs @@ -1,5 +1,6 @@ using AzureOpenAIProxy.ApiApp.Endpoints; using AzureOpenAIProxy.ApiApp.Extensions; +using AzureOpenAIProxy.ApiApp.Services; var builder = WebApplication.CreateBuilder(args); @@ -14,6 +15,9 @@ // Add OpenAPI service builder.Services.AddOpenApiService(); +// Add admin services +builder.Services.AddAdminEventService(); + var app = builder.Build(); app.MapDefaultEndpoints(); diff --git a/src/AzureOpenAIProxy.ApiApp/Services/AdminEventService.cs b/src/AzureOpenAIProxy.ApiApp/Services/AdminEventService.cs index 3cad1438..d3e0533f 100644 --- a/src/AzureOpenAIProxy.ApiApp/Services/AdminEventService.cs +++ b/src/AzureOpenAIProxy.ApiApp/Services/AdminEventService.cs @@ -65,3 +65,21 @@ public async Task UpdateEvent(Guid eventId, AdminEventDetails throw new NotImplementedException(); } } + +/// +/// This represents the extension class for +/// +public static class AdminEventServiceExtensions +{ + /// + /// Adds the instance to the service collection. + /// + /// instance. + /// Returns instance. + public static IServiceCollection AddAdminEventService(this IServiceCollection services) + { + services.AddScoped(); + + return services; + } +} From 4c4ad4ca113fda24aded9d20d78745f6dea57507 Mon Sep 17 00:00:00 2001 From: Justin Yoo Date: Sun, 1 Sep 2024 12:34:52 +0900 Subject: [PATCH 4/6] Update event payload --- .../Models/AdminEventDetails.cs | 10 +++++++++- src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs | 7 +++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/AzureOpenAIProxy.ApiApp/Models/AdminEventDetails.cs b/src/AzureOpenAIProxy.ApiApp/Models/AdminEventDetails.cs index cc75331c..a3ccdf25 100644 --- a/src/AzureOpenAIProxy.ApiApp/Models/AdminEventDetails.cs +++ b/src/AzureOpenAIProxy.ApiApp/Models/AdminEventDetails.cs @@ -1,4 +1,6 @@ -namespace AzureOpenAIProxy.ApiApp.Models; +using System.Text.Json.Serialization; + +namespace AzureOpenAIProxy.ApiApp.Models; /// /// This represent the event detail data for response by admin event endpoint. @@ -13,31 +15,37 @@ public class AdminEventDetails : EventDetails /// /// Gets or sets the event start date. /// + [JsonRequired] public DateTimeOffset DateStart { get; set; } /// /// Gets or sets the event end date. /// + [JsonRequired] public DateTimeOffset DateEnd { get; set; } /// /// Gets or sets the event start to end date timezone. /// + [JsonRequired] public string TimeZone { get; set; } = string.Empty; /// /// Gets or sets the event active status. /// + [JsonRequired] public bool IsActive { get; set; } /// /// Gets or sets the event organizer name. /// + [JsonRequired] public string OrganizerName { get; set; } = string.Empty; /// /// Gets or sets the event organizer email. /// + [JsonRequired] public string OrganizerEmail { get; set; } = string.Empty; /// diff --git a/src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs b/src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs index 803b6e0b..ae3594f2 100644 --- a/src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs +++ b/src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs @@ -1,3 +1,5 @@ +using System.Text.Json.Serialization; + /// /// This represents the event's detailed data for response by EventEndpoint. /// @@ -6,25 +8,30 @@ public class EventDetails /// /// Gets or sets the event id. /// + [JsonRequired] public Guid EventId { get; set; } /// /// Gets or sets the event title name. /// + [JsonRequired] public string Title { get; set; } = string.Empty; /// /// Gets or sets the event summary. /// + [JsonRequired] public string Summary { get; set; } = string.Empty; /// /// Gets or sets the Azure OpenAI Service request max token capacity. /// + [JsonRequired] public int MaxTokenCap { get; set; } /// /// Gets or sets the Azure OpenAI Service daily request capacity. /// + [JsonRequired] public int DailyRequestCap { get; set; } } \ No newline at end of file From d1ce469ec0639920327852d8017937da9cb45d72 Mon Sep 17 00:00:00 2001 From: Justin Yoo Date: Sun, 1 Sep 2024 12:41:13 +0900 Subject: [PATCH 5/6] Add more test --- .../Services/AdminEventServiceTests.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/AzureOpenAIProxy.ApiApp.Tests/Services/AdminEventServiceTests.cs b/test/AzureOpenAIProxy.ApiApp.Tests/Services/AdminEventServiceTests.cs index 2135bb5e..8079e2c8 100644 --- a/test/AzureOpenAIProxy.ApiApp.Tests/Services/AdminEventServiceTests.cs +++ b/test/AzureOpenAIProxy.ApiApp.Tests/Services/AdminEventServiceTests.cs @@ -3,10 +3,25 @@ using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; + namespace AzureOpenAIProxy.ApiApp.Tests.Services; public class AdminEventServiceTests { + [Fact] + public void Given_ServiceCollection_When_AddAdminEventService_Invoked_Then_It_Should_Contain_AdminEventService() + { + // Arrange + var services = new ServiceCollection(); + + // Act + services.AddAdminEventService(); + + // Assert + services.SingleOrDefault(p => p.ServiceType == typeof(IAdminEventService)).Should().NotBeNull(); + } + [Fact] public void Given_Instance_When_CreateEvent_Invoked_Then_It_Should_Throw_Exception() { From c7c23ff252bd3ddca46aa606879eb12d614d5e81 Mon Sep 17 00:00:00 2001 From: Justin Yoo Date: Sun, 1 Sep 2024 12:49:34 +0900 Subject: [PATCH 6/6] Remove unnecessary initialisers --- .../Services/AdminEventServiceTests.cs | 32 ++----------------- 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/test/AzureOpenAIProxy.ApiApp.Tests/Services/AdminEventServiceTests.cs b/test/AzureOpenAIProxy.ApiApp.Tests/Services/AdminEventServiceTests.cs index 8079e2c8..e459e4d0 100644 --- a/test/AzureOpenAIProxy.ApiApp.Tests/Services/AdminEventServiceTests.cs +++ b/test/AzureOpenAIProxy.ApiApp.Tests/Services/AdminEventServiceTests.cs @@ -26,21 +26,7 @@ public void Given_ServiceCollection_When_AddAdminEventService_Invoked_Then_It_Sh public void Given_Instance_When_CreateEvent_Invoked_Then_It_Should_Throw_Exception() { // Arrange - var eventDetails = new AdminEventDetails() - { - EventId = Guid.NewGuid(), - Title = "Event", - Summary = "Event summary", - Description = "Event description", - DateStart = DateTimeOffset.UtcNow, - DateEnd = DateTimeOffset.UtcNow, - TimeZone = "UTC", - IsActive = true, - OrganizerName = "Organiser", - OrganizerEmail = "organiser@email.com", - MaxTokenCap = 100, - DailyRequestCap = 1000, - }; + var eventDetails = new AdminEventDetails(); var service = new AdminEventService(); // Act @@ -82,21 +68,7 @@ public void Given_Instance_When_UpdateEvent_Invoked_Then_It_Should_Throw_Excepti { // Arrange var eventId = Guid.NewGuid(); - var eventDetails = new AdminEventDetails() - { - EventId = Guid.NewGuid(), - Title = "Event", - Summary = "Event summary", - Description = "Event description", - DateStart = DateTimeOffset.UtcNow, - DateEnd = DateTimeOffset.UtcNow, - TimeZone = "UTC", - IsActive = true, - OrganizerName = "Organiser", - OrganizerEmail = "organiser@email.com", - MaxTokenCap = 100, - DailyRequestCap = 1000, - }; + var eventDetails = new AdminEventDetails(); var service = new AdminEventService(); // Act