diff --git a/src/AzureOpenAIProxy.ApiApp/Endpoints/PlaygroundEndpoints.cs b/src/AzureOpenAIProxy.ApiApp/Endpoints/PlaygroundEndpoints.cs
index 91c01b8f..5a55fe77 100644
--- a/src/AzureOpenAIProxy.ApiApp/Endpoints/PlaygroundEndpoints.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Endpoints/PlaygroundEndpoints.cs
@@ -1,3 +1,7 @@
+using Azure;
+
+using AzureOpenAIProxy.ApiApp.Services;
+
using Microsoft.AspNetCore.Mvc;
namespace AzureOpenAIProxy.ApiApp.Endpoints;
@@ -14,10 +18,24 @@ public static class PlaygroundEndpoints
/// Returns instance.
public static RouteHandlerBuilder AddListEvents(this WebApplication app)
{
- var builder = app.MapGet(PlaygroundEndpointUrls.Events, () =>
+ // ASSUMPTION: User has already logged in
+ var builder = app.MapGet(PlaygroundEndpointUrls.Events, async (
+ IPlaygroundService service,
+ ILoggerFactory loggerFactory) =>
{
- // TODO: Issue #179 https://github.com/aliencube/azure-openai-sdk-proxy/issues/179
- return Results.Ok();
+ var logger = loggerFactory.CreateLogger(nameof(AdminEventEndpoints));
+ logger.LogInformation("Received request to fetch events list");
+
+ try
+ {
+ var eventDetailsList = await service.GetEvents();
+ return Results.Ok(eventDetailsList);
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, $"Error occurred while fetching events list");
+ return Results.Problem(ex.Message, statusCode: StatusCodes.Status500InternalServerError);
+ }
})
.Produces>(statusCode: StatusCodes.Status200OK, contentType: "application/json")
.Produces(statusCode: StatusCodes.Status401Unauthorized)
diff --git a/src/AzureOpenAIProxy.ApiApp/Program.cs b/src/AzureOpenAIProxy.ApiApp/Program.cs
index f35b83ee..969e9caa 100644
--- a/src/AzureOpenAIProxy.ApiApp/Program.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Program.cs
@@ -28,6 +28,12 @@
// Add admin repositories
builder.Services.AddAdminEventRepository();
+// Add playground services
+builder.Services.AddPlaygroundService();
+
+// Add playground repositories
+builder.Services.AddEventRepository();
+
var app = builder.Build();
app.MapDefaultEndpoints();
diff --git a/src/AzureOpenAIProxy.ApiApp/Repositories/EventRepository.cs b/src/AzureOpenAIProxy.ApiApp/Repositories/EventRepository.cs
new file mode 100644
index 00000000..3cbf4e72
--- /dev/null
+++ b/src/AzureOpenAIProxy.ApiApp/Repositories/EventRepository.cs
@@ -0,0 +1,71 @@
+using Azure.Data.Tables;
+
+using AzureOpenAIProxy.ApiApp.Configurations;
+
+namespace AzureOpenAIProxy.ApiApp.Repositories;
+
+///
+/// This provides interfaces to the class.
+///
+public interface IEventRepository
+{
+ ///
+ /// Gets the list of events.
+ ///
+ /// Returns the list of events.
+ Task> GetEvents();
+}
+
+public class EventRepository(TableServiceClient tableServiceClient, StorageAccountSettings storageAccountSettings) : IEventRepository
+{
+ private readonly TableServiceClient _tableServiceClient = tableServiceClient ?? throw new ArgumentNullException(nameof(tableServiceClient));
+ private readonly StorageAccountSettings _storageAccountSettings = storageAccountSettings ?? throw new ArgumentNullException(nameof(storageAccountSettings));
+
+ ///
+ ///
+ /// The results are sorted based on the following criteria:
+ /// Lexical order of event titles.
+ ///
+ public async Task> GetEvents()
+ {
+ TableClient tableClient = await GetTableClientAsync();
+
+ List events = [];
+
+ await foreach(EventDetails eventDetails in tableClient.QueryAsync(e => e.PartitionKey.Equals(PartitionKeys.EventDetails)).ConfigureAwait(false))
+ {
+ events.Add(eventDetails);
+ }
+
+ events.Sort((e1, e2) => e1.Title.CompareTo(e2.Title));
+
+ return events;
+ }
+
+ private async Task GetTableClientAsync()
+ {
+ TableClient tableClient = _tableServiceClient.GetTableClient(_storageAccountSettings.TableStorage.TableName);
+
+ await tableClient.CreateIfNotExistsAsync().ConfigureAwait(false);
+
+ return tableClient;
+ }
+}
+
+///
+/// This represents the extension class for
+///
+public static class EventRepositoryExtensions
+{
+ ///
+ /// Adds the instance to the service collection.
+ ///
+ /// instance.
+ /// Returns instance.
+ public static IServiceCollection AddEventRepository(this IServiceCollection services)
+ {
+ services.AddScoped();
+
+ return services;
+ }
+}
\ No newline at end of file
diff --git a/src/AzureOpenAIProxy.ApiApp/Services/PlaygroundService.cs b/src/AzureOpenAIProxy.ApiApp/Services/PlaygroundService.cs
new file mode 100644
index 00000000..5d9ef331
--- /dev/null
+++ b/src/AzureOpenAIProxy.ApiApp/Services/PlaygroundService.cs
@@ -0,0 +1,57 @@
+using AzureOpenAIProxy.ApiApp.Repositories;
+
+namespace AzureOpenAIProxy.ApiApp.Services;
+
+///
+/// This provides interfaces to class.
+///
+public interface IPlaygroundService
+{
+ ///
+ /// Get the list of deployment model.
+ ///
+ /// Returns the list of deployment models.
+ Task> GetDeploymentModels(string eventId);
+
+ ///
+ /// Get the list of events.
+ ///
+ /// Returns the list of events.
+ Task> GetEvents();
+}
+
+public class PlaygroundService(IEventRepository eventRepository) : IPlaygroundService
+{
+ private readonly IEventRepository _eventRepository = eventRepository ?? throw new ArgumentNullException(nameof(eventRepository));
+ ///
+ public async Task> GetDeploymentModels(string eventId)
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ public async Task> GetEvents()
+ {
+ var result = await _eventRepository.GetEvents().ConfigureAwait(false);
+
+ return result;
+ }
+}
+
+///
+/// This represents the extension class for
+///
+public static class PlaygroundServiceExtensions
+{
+ ///
+ /// Adds the instance to the service collection.
+ ///
+ /// instance.
+ /// Returns instance.
+ public static IServiceCollection AddPlaygroundService(this IServiceCollection services)
+ {
+ services.AddScoped();
+
+ return services;
+ }
+}
\ No newline at end of file
diff --git a/test/AzureOpenAIProxy.ApiApp.Tests/Repositories/EventRepositoryTests.cs b/test/AzureOpenAIProxy.ApiApp.Tests/Repositories/EventRepositoryTests.cs
new file mode 100644
index 00000000..b98a0b34
--- /dev/null
+++ b/test/AzureOpenAIProxy.ApiApp.Tests/Repositories/EventRepositoryTests.cs
@@ -0,0 +1,120 @@
+using System.Linq.Expressions;
+
+using Azure;
+using Azure.Data.Tables;
+
+using AzureOpenAIProxy.ApiApp.Configurations;
+using AzureOpenAIProxy.ApiApp.Repositories;
+
+using FluentAssertions;
+
+using Microsoft.Extensions.DependencyInjection;
+
+using NSubstitute;
+using NSubstitute.ExceptionExtensions;
+
+namespace AzureOpenAIProxy.ApiApp.Tests.Repositories;
+
+public class EventRepositoryTests
+{
+ [Fact]
+ public void Given_ServiceCollection_When_AddEventRepository_Invoked_Then_It_Should_Contain_EventRepository()
+ {
+ // Arrange
+ var services = new ServiceCollection();
+
+ // Act
+ services.AddEventRepository();
+
+ // Assert
+ services.SingleOrDefault(p => p.ServiceType == typeof(IEventRepository)).Should().NotBeNull();
+ }
+
+ [Fact]
+ public void Given_Null_TableServiceClient_When_Creating_EventRepository_Then_It_Should_Throw_Exception()
+ {
+ // Arrange
+ var settings = Substitute.For();
+ var tableServiceClient = default(TableServiceClient);
+
+ // Act
+ Action action = () => new EventRepository(tableServiceClient, settings);
+
+ // Assert
+ action.Should().Throw();
+ }
+
+ [Fact]
+ public void Given_Null_StorageAccountSettings_When_Creating_EventRepository_Then_It_Should_Throw_Exception()
+ {
+ // Arrange
+ var settings = default(StorageAccountSettings);
+ var tableServiceClient = Substitute.For();
+
+ // Act
+ Action action = () => new EventRepository(tableServiceClient, settings);
+
+ // Assert
+ action.Should().Throw();
+ }
+
+ [Fact]
+ public async Task Given_Failure_In_Get_Entities_When_GetEvents_Invoked_Then_It_Should_Throw_Exception()
+ {
+ // Arrange
+ var settings = Substitute.For();
+ var tableServiceClient = Substitute.For();
+ var repository = new EventRepository(tableServiceClient, settings);
+
+ var tableClient = Substitute.For();
+ tableServiceClient.GetTableClient(Arg.Any()).Returns(tableClient);
+ tableClient.QueryAsync(Arg.Any>>()).Throws(new Exception("error occurred"));
+
+ // Act
+ Func func = repository.GetEvents;
+
+ // Assert
+ var assertion = await func.Should().ThrowAsync();
+ }
+
+ [Fact]
+ public async Task Given_Exist_Events_When_GetEvents_Invoked_Then_It_Should_Return_EventDetails_List()
+ {
+ // Arrange
+ Random rand = new();
+ int listSize = rand.Next(1, 20);
+ Guid eventId = new();
+
+ var settings = Substitute.For();
+ var tableServiceClient = Substitute.For();
+ var repository = new EventRepository(tableServiceClient, settings);
+
+ var eventDetails = new EventDetails
+ {
+ RowKey = eventId.ToString(),
+ PartitionKey = PartitionKeys.EventDetails
+ };
+
+ List events = [];
+
+ for(int i = 0; i < listSize; ++i)
+ {
+ events.Add(eventDetails);
+ }
+
+ var pages = Page.FromValues(events, default, Substitute.For());
+ var asyncPages = AsyncPageable.FromPages([pages]);
+
+ var tableClient = Substitute.For();
+ tableServiceClient.GetTableClient(Arg.Any()).Returns(tableClient);
+ tableClient.QueryAsync(Arg.Any>>()).Returns(asyncPages);
+
+ // Act
+ var result = await repository.GetEvents();
+
+ // Assert
+ result.Count.Should().Be(listSize);
+ result.First().Should().BeEquivalentTo(eventDetails);
+ }
+
+}
\ No newline at end of file
diff --git a/test/AzureOpenAIProxy.ApiApp.Tests/Services/PlaygroundServiceTests.cs b/test/AzureOpenAIProxy.ApiApp.Tests/Services/PlaygroundServiceTests.cs
new file mode 100644
index 00000000..38e93879
--- /dev/null
+++ b/test/AzureOpenAIProxy.ApiApp.Tests/Services/PlaygroundServiceTests.cs
@@ -0,0 +1,93 @@
+using Azure;
+
+using AzureOpenAIProxy.ApiApp.Repositories;
+using AzureOpenAIProxy.ApiApp.Services;
+
+using FluentAssertions;
+
+using Microsoft.Extensions.DependencyInjection;
+
+using NSubstitute;
+using NSubstitute.ExceptionExtensions;
+
+namespace AzureOpenAIProxy.ApiApp.Tests.Services;
+
+public class PlaygroundServiceTests
+{
+ [Fact]
+ public void Given_ServiceCollection_When_AddPlaygroundService_Invoked_Then_It_Should_Contain_PlaygroundService()
+ {
+ // Arrange
+ var services = new ServiceCollection();
+
+ // Act
+ services.AddPlaygroundService();
+
+ // Assert
+ services.SingleOrDefault(p => p.ServiceType == typeof(IPlaygroundService)).Should().NotBeNull();
+ }
+
+ [Fact]
+ public void Given_Instance_When_GetDeploymentModels_Invoked_Then_It_Should_Throw_Exception()
+ {
+ // Arrange
+ string eventId = "test-id";
+ var repository = Substitute.For();
+ var service = new PlaygroundService(repository);
+
+ // Act
+ Func func = async () => await service.GetDeploymentModels(eventId);
+
+ // Assert
+ func.Should().ThrowAsync();
+ }
+
+ [Fact]
+ public async Task Given_Failure_In_Get_Entities_When_GetEvents_Invoked_Then_It_Should_Throw_Exception()
+ {
+ // Arrange
+ var eventId = Guid.NewGuid();
+ var repository = Substitute.For();
+ var service = new PlaygroundService(repository);
+
+ repository.GetEvents().ThrowsAsync(new Exception("Error occurred"));
+
+ // Act
+ Func func = service.GetEvents;
+
+ // Assert
+ var assertion = await func.Should().ThrowAsync();
+ }
+
+ [Fact]
+ public async Task Given_Exist_Events_When_GetEvents_Invoked_Then_It_Should_Return_EventDetails_List()
+ {
+ // Arrange
+ Random rand = new();
+ int listSize = rand.Next(1, 20);
+ Guid eventId = new();
+ var repository = Substitute.For();
+ var service = new PlaygroundService(repository);
+
+ var eventDetails = new EventDetails
+ {
+ RowKey = eventId.ToString()
+ };
+
+ List events = [];
+ for(int i = 0; i < listSize; ++i)
+ {
+ events.Add(eventDetails);
+ }
+
+ repository.GetEvents().Returns(events);
+
+ // Act
+ var result = await service.GetEvents();
+
+ // Assert
+ result.Count.Should().Be(listSize);
+ result.First().Should().BeEquivalentTo(eventDetails);
+ }
+
+}
\ No newline at end of file