diff --git a/src/AzureOpenAIProxy.ApiApp/Endpoints/EndpointUrls.cs b/src/AzureOpenAIProxy.ApiApp/Endpoints/EndpointUrls.cs
index a5c6d5d2..b69068eb 100644
--- a/src/AzureOpenAIProxy.ApiApp/Endpoints/EndpointUrls.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Endpoints/EndpointUrls.cs
@@ -1,4 +1,4 @@
-namespace AzureOpenAIProxy.ApiApp.Endpoints;
+namespace AzureOpenAIProxy.ApiApp.Endpoints;
///
/// This represents the collection of the endpoint URLs.
@@ -14,4 +14,9 @@ public static class EndpointUrls
/// Declares the chat completions endpoint.
///
public const string ChatCompletions = "/openai/deployments/{deploymentName}/chat/completions";
-}
\ No newline at end of file
+
+ ///
+ /// Declares the event endpoint.
+ ///
+ public const string Events = "/events";
+}
diff --git a/src/AzureOpenAIProxy.ApiApp/Endpoints/EventEndpoint.cs b/src/AzureOpenAIProxy.ApiApp/Endpoints/EventEndpoint.cs
new file mode 100644
index 00000000..a82efb31
--- /dev/null
+++ b/src/AzureOpenAIProxy.ApiApp/Endpoints/EventEndpoint.cs
@@ -0,0 +1,39 @@
+using System.Text.Json;
+
+using AzureOpenAIProxy.ApiApp.Models;
+
+namespace AzureOpenAIProxy.ApiApp.Endpoints;
+
+///
+/// This represents the endpoint entity for events that the logged user joined.
+///
+public static class EventEndpoint
+{
+ ///
+ /// Adds the event endpoint.
+ ///
+ /// instance.
+ /// Returns instance.
+ public static RouteHandlerBuilder AddEventList(this WebApplication app)
+ {
+ var builder = app.MapGet(EndpointUrls.Events, () =>
+ {
+ // TODO: Issue #179 https://github.com/aliencube/azure-openai-sdk-proxy/issues/179
+ return Results.Ok();
+ })
+ .Produces>(statusCode: StatusCodes.Status200OK, contentType: "application/json")
+ .Produces(statusCode: StatusCodes.Status401Unauthorized)
+ .Produces(statusCode: StatusCodes.Status500InternalServerError, contentType: "text/plain")
+ .WithTags("events")
+ .WithName("GetEvents")
+ .WithOpenApi(operation =>
+ {
+ operation.Description = "Gets all events' details that the user joined.";
+ operation.Summary = "This endpoint gets all events' details that the user joined.";
+
+ return operation;
+ });
+
+ return builder;
+ }
+}
\ No newline at end of file
diff --git a/src/AzureOpenAIProxy.ApiApp/Extensions/ServiceCollectionExtensions.cs b/src/AzureOpenAIProxy.ApiApp/Extensions/ServiceCollectionExtensions.cs
index d47f5097..c5ad44fe 100644
--- a/src/AzureOpenAIProxy.ApiApp/Extensions/ServiceCollectionExtensions.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Extensions/ServiceCollectionExtensions.cs
@@ -1,4 +1,4 @@
-using Azure.Identity;
+using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
using AzureOpenAIProxy.ApiApp.Builders;
@@ -132,4 +132,4 @@ public static IServiceCollection AddOpenApiService(this IServiceCollection servi
return services;
}
-}
+}
\ No newline at end of file
diff --git a/src/AzureOpenAIProxy.ApiApp/Filters/OpenApiTagFilter.cs b/src/AzureOpenAIProxy.ApiApp/Filters/OpenApiTagFilter.cs
index 196c87fb..9ed10fa3 100644
--- a/src/AzureOpenAIProxy.ApiApp/Filters/OpenApiTagFilter.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Filters/OpenApiTagFilter.cs
@@ -16,7 +16,8 @@ public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
[
new OpenApiTag { Name = "weather", Description = "Weather forecast operations" },
new OpenApiTag { Name = "openai", Description = "Azure OpenAI operations" },
- new OpenApiTag { Name = "admin", Description = "Admin for organizing events" }
+ new OpenApiTag { Name = "admin", Description = "Admin for organizing events" },
+ new OpenApiTag { Name = "events", Description = "User events" }
];
}
}
diff --git a/src/AzureOpenAIProxy.ApiApp/Models/AdminEventDetails.cs b/src/AzureOpenAIProxy.ApiApp/Models/AdminEventDetails.cs
index 19a20eed..4f587217 100644
--- a/src/AzureOpenAIProxy.ApiApp/Models/AdminEventDetails.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Models/AdminEventDetails.cs
@@ -3,30 +3,15 @@
///
/// This represent the event detail data for response by admin event endpoint.
///
-public class AdminEventDetails
+public class AdminEventDetails : EventDetails
{
///
- /// Gets or sets the event id.
- ///
- public required string? EventId { get; set; }
-
- ///
- /// Gets or sets the event title name.
- ///
- public required string? Title { get; set; }
-
- ///
- /// Gets or sets the event summary.
- ///
- public required string? Summary { get; set; }
-
- ///
- /// Gets or sets the event description.
+ /// Gets or sets the event description.
///
public string? Description { get; set; }
///
- /// Gets or sets the event start date.
+ /// Gets or sets the event start date.
///
public required DateTimeOffset? DateStart { get; set; }
@@ -46,7 +31,7 @@ public class AdminEventDetails
public required bool? IsActive { get; set; }
///
- /// Gets or sets the event organizer name.
+ /// Gets or sets the event organizer name.
///
public required string? OrganizerName { get; set; }
@@ -64,14 +49,4 @@ public class AdminEventDetails
/// Gets or sets the event coorganizer email.
///
public string? CoorganizerEmail { get; set; }
-
- ///
- /// Gets or sets the Azure OpenAI Service request max token capacity.
- ///
- public required int? MaxTokenCap { get; set; }
-
- ///
- /// Gets or sets the Azure OpenAI Service daily request capacity.
- ///
- public required int? DailyRequestCap { get; set; }
}
\ No newline at end of file
diff --git a/src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs b/src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs
new file mode 100644
index 00000000..5c33fafb
--- /dev/null
+++ b/src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs
@@ -0,0 +1,35 @@
+using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
+
+using AzureOpenAIProxy.ApiApp.Models;
+
+///
+/// This represents the event's detailed data for response by EventEndpoint.
+///
+public class EventDetails
+{
+ ///
+ /// Gets or sets the event id.
+ ///
+ public required string? EventId { get; set; }
+
+ ///
+ /// Gets or sets the event title name.
+ ///
+ public required string? Title { get; set; }
+
+ ///
+ /// Gets or sets the event summary.
+ ///
+ public required string? Summary { get; set; }
+
+ ///
+ /// Gets or sets the Azure OpenAI Service request max token capacity.
+ ///
+ public required int? MaxTokenCap { get; set; }
+
+ ///
+ /// Gets or sets the Azure OpenAI Service daily request capacity.
+ ///
+ public required 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 07457d08..832c6dd6 100644
--- a/src/AzureOpenAIProxy.ApiApp/Program.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Program.cs
@@ -39,6 +39,9 @@
app.AddWeatherForecast();
app.AddChatCompletions();
+// Event Endpoints
+app.AddEventList();
+
// Admin Endpoints
app.AddAdminEvents();
app.AddAdminEventList();
diff --git a/test/AzureOpenAIProxy.AppHost.Tests/ApiApp/Endpoints/GetEventsOpenApiTests.cs b/test/AzureOpenAIProxy.AppHost.Tests/ApiApp/Endpoints/GetEventsOpenApiTests.cs
new file mode 100644
index 00000000..8aad2350
--- /dev/null
+++ b/test/AzureOpenAIProxy.AppHost.Tests/ApiApp/Endpoints/GetEventsOpenApiTests.cs
@@ -0,0 +1,126 @@
+using System.Text.Json;
+
+using AzureOpenAIProxy.AppHost.Tests.Fixtures;
+
+using FluentAssertions;
+
+using IdentityModel.Client;
+
+namespace AzureOpenAIProxy.AppHost.Tests.ApiApp.Endpoints;
+
+public class GetEventsOpenApiTests(AspireAppHostFixture host) : IClassFixture
+{
+ [Fact]
+ public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_Path()
+ {
+ // Arrange
+ using var httpClient = host.App!.CreateHttpClient("apiapp");
+
+ // Act
+ var json = await httpClient.GetStringAsync("/swagger/v1.0.0/swagger.json");
+ var apiDocument = JsonSerializer.Deserialize(json);
+
+ // Assert
+ var result = apiDocument!.RootElement.GetProperty("paths")
+ .TryGetProperty("/events", out var property) ? property : default;
+ result.ValueKind.Should().Be(JsonValueKind.Object);
+ }
+
+ [Fact]
+ public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_Verb()
+ {
+ // Arrange
+ using var httpClient = host.App!.CreateHttpClient("apiapp");
+
+ // Act
+ var json = await httpClient.GetStringAsync("/swagger/v1.0.0/swagger.json");
+ var apiDocument = JsonSerializer.Deserialize(json);
+
+ // Assert
+ var result = apiDocument!.RootElement.GetProperty("paths")
+ .GetProperty("/events")
+ .TryGetProperty("get", out var property) ? property : default;
+ result.ValueKind.Should().Be(JsonValueKind.Object);
+ }
+
+ [Theory]
+ [InlineData("events")]
+ public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_Tags(string tag)
+ {
+ // Arrange
+ using var httpClient = host.App!.CreateHttpClient("apiapp");
+
+ // Act
+ var json = await httpClient.GetStringAsync("/swagger/v1.0.0/swagger.json");
+ var apiDocument = JsonSerializer.Deserialize(json);
+
+ // Assert
+ var result = apiDocument!.RootElement.GetProperty("paths")
+ .GetProperty("/events")
+ .GetProperty("get")
+ .TryGetProperty("tags", out var property) ? property : default;
+ result.ValueKind.Should().Be(JsonValueKind.Array);
+ result.EnumerateArray().Select(p => p.GetString()).Should().Contain(tag);
+ }
+
+ [Theory]
+ [InlineData("summary")]
+ [InlineData("operationId")]
+ public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_Value(string attribute)
+ {
+ // Arrange
+ using var httpClient = host.App!.CreateHttpClient("apiapp");
+
+ // Act
+ var json = await httpClient.GetStringAsync("/swagger/v1.0.0/swagger.json");
+ var apiDocument = JsonSerializer.Deserialize(json);
+
+ // Assert
+ var result = apiDocument!.RootElement.GetProperty("paths")
+ .GetProperty("/events")
+ .GetProperty("get")
+ .TryGetProperty(attribute, out var property) ? property : default;
+ result.ValueKind.Should().Be(JsonValueKind.String);
+ }
+
+ [Theory]
+ [InlineData("responses")]
+ public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_Object(string attribute)
+ {
+ // Arrange
+ using var httpClient = host.App!.CreateHttpClient("apiapp");
+
+ // Act
+ var json = await httpClient.GetStringAsync("/swagger/v1.0.0/swagger.json");
+ var apiDocument = JsonSerializer.Deserialize(json);
+
+ // Assert
+ var result = apiDocument!.RootElement.GetProperty("paths")
+ .GetProperty("/events")
+ .GetProperty("get")
+ .TryGetProperty(attribute, out var property) ? property : default;
+ result.ValueKind.Should().Be(JsonValueKind.Object);
+ }
+
+ [Theory]
+ [InlineData("200")]
+ [InlineData("401")]
+ [InlineData("500")]
+ public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_Response(string attribute)
+ {
+ // Arrange
+ using var httpClient = host.App!.CreateHttpClient("apiapp");
+
+ // Act
+ var json = await httpClient.GetStringAsync("/swagger/v1.0.0/swagger.json");
+ var apiDocument = JsonSerializer.Deserialize(json);
+
+ // Assert
+ var result = apiDocument!.RootElement.GetProperty("paths")
+ .GetProperty("/events")
+ .GetProperty("get")
+ .GetProperty("responses")
+ .TryGetProperty(attribute, out var property) ? property : default;
+ result.ValueKind.Should().Be(JsonValueKind.Object);
+ }
+}
\ No newline at end of file