diff --git a/src/AzureOpenAIProxy.ApiApp/Models/AdminResourceDetails.cs b/src/AzureOpenAIProxy.ApiApp/Models/AdminResourceDetails.cs
index b610f077..7cb8e1c5 100644
--- a/src/AzureOpenAIProxy.ApiApp/Models/AdminResourceDetails.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Models/AdminResourceDetails.cs
@@ -1,6 +1,9 @@
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
+using Azure;
+using Azure.Data.Tables;
+
using AzureOpenAIProxy.ApiApp.Converters;
namespace AzureOpenAIProxy.ApiApp.Models;
@@ -8,7 +11,7 @@ namespace AzureOpenAIProxy.ApiApp.Models;
///
/// This represent the entity for the resource details for admin.
///
-public class AdminResourceDetails
+public class AdminResourceDetails : ITableEntity
{
///
/// Gets or sets the event id.
@@ -57,6 +60,22 @@ public class AdminResourceDetails
///
[JsonRequired]
public bool IsActive { get; set; }
+
+ ///
+ [JsonIgnore]
+ public string PartitionKey { get; set; } = string.Empty;
+
+ ///
+ [JsonIgnore]
+ public string RowKey { get; set; } = string.Empty;
+
+ ///
+ [JsonIgnore]
+ public DateTimeOffset? Timestamp { get; set; }
+
+ ///
+ [JsonIgnore]
+ public ETag ETag { get; set; }
}
///
diff --git a/src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs b/src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs
index 459ff655..d70c3952 100644
--- a/src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs
@@ -1,9 +1,12 @@
using System.Text.Json.Serialization;
+using Azure;
+using Azure.Data.Tables;
+
///
/// This represent the entity for the event details for users.
///
-public class EventDetails
+public class EventDetails : ITableEntity
{
///
/// Gets or sets the event id.
@@ -34,4 +37,20 @@ public class EventDetails
///
[JsonRequired]
public int DailyRequestCap { get; set; }
+
+ ///
+ [JsonIgnore]
+ public string PartitionKey { get; set; } = string.Empty;
+
+ ///
+ [JsonIgnore]
+ public string RowKey { get; set; } = string.Empty;
+
+ ///
+ [JsonIgnore]
+ public DateTimeOffset? Timestamp { get; set; }
+
+ ///
+ [JsonIgnore]
+ public ETag ETag { get; set; }
}
\ No newline at end of file
diff --git a/test/AzureOpenAIProxy.ApiApp.Tests/Models/AdminEventDetailsTests.cs b/test/AzureOpenAIProxy.ApiApp.Tests/Models/AdminEventDetailsTests.cs
new file mode 100644
index 00000000..3ab3ad7f
--- /dev/null
+++ b/test/AzureOpenAIProxy.ApiApp.Tests/Models/AdminEventDetailsTests.cs
@@ -0,0 +1,87 @@
+using System.Text.Json;
+
+using AzureOpenAIProxy.ApiApp.Models;
+
+using FluentAssertions;
+
+namespace AzureOpenAIProxy.ApiApp.Tests.Models;
+
+public class AdminEventDetailsTests
+{
+ private static readonly AdminEventDetails examplePayload = new()
+ {
+ EventId = Guid.Parse("67f410a3-c5e4-4326-a3ad-5812b9adfc06"),
+ Title = "Test Title",
+ Summary = "Test Summary",
+ Description = "Test Description",
+ DateStart = DateTimeOffset.Parse("2024-12-01T12:34:56+00:00"),
+ DateEnd = DateTimeOffset.Parse("2024-12-02T12:34:56+00:00"),
+ TimeZone = "Asia/Seoul",
+ IsActive = true,
+ OrganizerName = "Test Organizer",
+ OrganizerEmail = "organiser@testemail.com",
+ CoorganizerName = "Test Coorganizer",
+ CoorganizerEmail = "coorganiser@testemail.com",
+ MaxTokenCap = 1000,
+ DailyRequestCap = 4000,
+ };
+
+ private static readonly string exampleJson = """
+ {
+ "eventId": "67f410a3-c5e4-4326-a3ad-5812b9adfc06",
+ "title": "Test Title",
+ "summary": "Test Summary",
+ "description": "Test Description",
+ "dateStart": "2024-12-01T12:34:56+00:00",
+ "dateEnd": "2024-12-02T12:34:56+00:00",
+ "timeZone": "Asia/Seoul",
+ "isActive": true,
+ "organizerName": "Test Organizer",
+ "organizerEmail": "organiser@testemail.com",
+ "coorganizerName": "Test Coorganizer",
+ "coorganizerEmail": "coorganiser@testemail.com",
+ "maxTokenCap": 1000,
+ "dailyRequestCap": 4000
+ }
+ """;
+
+ private static readonly JsonSerializerOptions options = new()
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
+ WriteIndented = true
+ };
+
+ [Fact]
+ public void Given_ExamplePayload_When_Serialized_Then_It_Should_Match_Json()
+ {
+ // Act
+ var serialised = JsonSerializer.Serialize(examplePayload, options);
+
+ // Assert
+ serialised.Should().ContainAll(
+ "\"eventId\":", "\"67f410a3-c5e4-4326-a3ad-5812b9adfc06\"",
+ "\"title\":", "\"Test Title\"",
+ "\"summary\":", "\"Test Summary\"",
+ "\"description\":", "\"Test Description\"",
+ "\"dateStart\":", "\"2024-12-01T12:34:56+00:00\"",
+ "\"dateEnd\":", "\"2024-12-02T12:34:56+00:00\"",
+ "\"timeZone\":", "\"Asia/Seoul\"",
+ "\"isActive\":", "true",
+ "\"organizerName\":", "\"Test Organizer\"",
+ "\"organizerEmail\":", "\"organiser@testemail.com\"",
+ "\"coorganizerName\":", "\"Test Coorganizer\"",
+ "\"coorganizerEmail\":", "\"coorganiser@testemail.com\"",
+ "\"maxTokenCap\":", "1000",
+ "\"dailyRequestCap\":", "4000");
+ }
+
+ [Fact]
+ public void Given_ExampleJson_When_Deserialized_Then_It_Should_Match_Object()
+ {
+ // Arrange & Act
+ var deserialised = JsonSerializer.Deserialize(exampleJson, options);
+
+ // Assert
+ deserialised.Should().BeEquivalentTo(examplePayload);
+ }
+}
\ No newline at end of file
diff --git a/test/AzureOpenAIProxy.ApiApp.Tests/Models/EventDetailsTests.cs b/test/AzureOpenAIProxy.ApiApp.Tests/Models/EventDetailsTests.cs
new file mode 100644
index 00000000..f5e987d0
--- /dev/null
+++ b/test/AzureOpenAIProxy.ApiApp.Tests/Models/EventDetailsTests.cs
@@ -0,0 +1,58 @@
+using System.Text.Json;
+
+using FluentAssertions;
+
+namespace AzureOpenAIProxy.ApiApp.Tests.Models;
+
+public class EventDetailsTests
+{
+ private static readonly EventDetails examplePayload = new()
+ {
+ EventId = Guid.Parse("67f410a3-c5e4-4326-a3ad-5812b9adfc06"),
+ Title = "Test Title",
+ Summary = "Test Summary",
+ MaxTokenCap = 1000,
+ DailyRequestCap = 4000,
+ };
+
+ private static readonly string exampleJson = """
+ {
+ "eventId": "67f410a3-c5e4-4326-a3ad-5812b9adfc06",
+ "title": "Test Title",
+ "summary": "Test Summary",
+ "maxTokenCap": 1000,
+ "dailyRequestCap": 4000
+ }
+ """;
+
+ private static readonly JsonSerializerOptions options = new()
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
+ WriteIndented = true
+ };
+
+ [Fact]
+ public void Given_ExamplePayload_When_Serialized_Then_It_Should_Match_Json()
+ {
+ // Act
+ var serialised = JsonSerializer.Serialize(examplePayload, options);
+
+ // Assert
+ serialised.Should().ContainAll(
+ "\"eventId\":", "\"67f410a3-c5e4-4326-a3ad-5812b9adfc06\"",
+ "\"title\":", "\"Test Title\"",
+ "\"summary\":", "\"Test Summary\"",
+ "\"maxTokenCap\":", "1000",
+ "\"dailyRequestCap\":", "4000");
+ }
+
+ [Fact]
+ public void Given_ExampleJson_When_Deserialized_Then_It_Should_Match_Object()
+ {
+ // Arrange & Act
+ var deserialised = JsonSerializer.Deserialize(exampleJson, options);
+
+ // Assert
+ deserialised.Should().BeEquivalentTo(examplePayload);
+ }
+}
\ No newline at end of file
diff --git a/test/AzureOpenAIProxy.AppHost.Tests/ApiApp/Endpoints/AdminCreateEventsOpenApiTests.cs b/test/AzureOpenAIProxy.AppHost.Tests/ApiApp/Endpoints/AdminCreateEventsOpenApiTests.cs
index 79e566f4..466600c8 100644
--- a/test/AzureOpenAIProxy.AppHost.Tests/ApiApp/Endpoints/AdminCreateEventsOpenApiTests.cs
+++ b/test/AzureOpenAIProxy.AppHost.Tests/ApiApp/Endpoints/AdminCreateEventsOpenApiTests.cs
@@ -4,6 +4,8 @@
using FluentAssertions;
+using IdentityModel.Client;
+
namespace AzureOpenAIProxy.AppHost.Tests.ApiApp.Endpoints;
public class AdminCreateEventsOpenApiTests(AspireAppHostFixture host) : IClassFixture
@@ -165,4 +167,106 @@ public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_Mod
.TryGetProperty("AdminEventDetails", out var property) ? property : default;
result.ValueKind.Should().Be(JsonValueKind.Object);
}
+
+ [Theory]
+ [InlineData("eventId", true)]
+ [InlineData("title", true)]
+ [InlineData("summary", true)]
+ [InlineData("description", false)]
+ [InlineData("dateStart", true)]
+ [InlineData("dateEnd", true)]
+ [InlineData("timeZone", true)]
+ [InlineData("isActive", true)]
+ [InlineData("organizerName", true)]
+ [InlineData("organizerEmail", true)]
+ [InlineData("coorganizerName", false)]
+ [InlineData("coorganizerEmail", false)]
+ [InlineData("maxTokenCap", true)]
+ [InlineData("dailyRequestCap", true)]
+ public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_Required(string attribute, bool isRequired)
+ {
+ // Arrange
+ using var httpClient = host.App!.CreateHttpClient("apiapp");
+ await host.ResourceNotificationService.WaitForResourceAsync("apiapp", KnownResourceStates.Running).WaitAsync(TimeSpan.FromSeconds(30));
+
+ // Act
+ var json = await httpClient.GetStringAsync("/swagger/v1.0.0/swagger.json");
+ var openapi = JsonSerializer.Deserialize(json);
+
+ // Assert
+ var result = openapi!.RootElement.GetProperty("components")
+ .GetProperty("schemas")
+ .GetProperty("AdminEventDetails")
+ .TryGetStringArray("required")
+ .ToList();
+ result.Contains(attribute).Should().Be(isRequired);
+ }
+
+ [Theory]
+ [InlineData("eventId")]
+ [InlineData("title")]
+ [InlineData("summary")]
+ [InlineData("description")]
+ [InlineData("dateStart")]
+ [InlineData("dateEnd")]
+ [InlineData("timeZone")]
+ [InlineData("isActive")]
+ [InlineData("organizerName")]
+ [InlineData("organizerEmail")]
+ [InlineData("coorganizerName")]
+ [InlineData("coorganizerEmail")]
+ [InlineData("maxTokenCap")]
+ [InlineData("dailyRequestCap")]
+ public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_Property(string attribute)
+ {
+ // Arrange
+ using var httpClient = host.App!.CreateHttpClient("apiapp");
+ await host.ResourceNotificationService.WaitForResourceAsync("apiapp", KnownResourceStates.Running).WaitAsync(TimeSpan.FromSeconds(30));
+
+ // Act
+ var json = await httpClient.GetStringAsync("/swagger/v1.0.0/swagger.json");
+ var openapi = JsonSerializer.Deserialize(json);
+
+ // Assert
+ var result = openapi!.RootElement.GetProperty("components")
+ .GetProperty("schemas")
+ .GetProperty("AdminEventDetails")
+ .GetProperty("properties")
+ .TryGetProperty(attribute, out var property) ? property : default;
+ result.ValueKind.Should().Be(JsonValueKind.Object);
+ }
+
+ [Theory]
+ [InlineData("eventId", "string")]
+ [InlineData("title", "string")]
+ [InlineData("summary", "string")]
+ [InlineData("description", "string")]
+ [InlineData("dateStart", "string")]
+ [InlineData("dateEnd", "string")]
+ [InlineData("timeZone", "string")]
+ [InlineData("isActive", "boolean")]
+ [InlineData("organizerName", "string")]
+ [InlineData("organizerEmail", "string")]
+ [InlineData("coorganizerName", "string")]
+ [InlineData("coorganizerEmail", "string")]
+ [InlineData("maxTokenCap", "integer")]
+ [InlineData("dailyRequestCap", "integer")]
+ public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_Type(string attribute, string type)
+ {
+ // Arrange
+ using var httpClient = host.App!.CreateHttpClient("apiapp");
+ await host.ResourceNotificationService.WaitForResourceAsync("apiapp", KnownResourceStates.Running).WaitAsync(TimeSpan.FromSeconds(30));
+
+ // Act
+ var json = await httpClient.GetStringAsync("/swagger/v1.0.0/swagger.json");
+ var openapi = JsonSerializer.Deserialize(json);
+
+ // Assert
+ var result = openapi!.RootElement.GetProperty("components")
+ .GetProperty("schemas")
+ .GetProperty("AdminEventDetails")
+ .GetProperty("properties")
+ .GetProperty(attribute);
+ result.TryGetString("type").Should().Be(type);
+ }
}
diff --git a/test/AzureOpenAIProxy.AppHost.Tests/ApiApp/Endpoints/AdminGetEventDetailsOpenApiTests.cs b/test/AzureOpenAIProxy.AppHost.Tests/ApiApp/Endpoints/AdminGetEventDetailsOpenApiTests.cs
index dbb0e846..c2d84bfb 100644
--- a/test/AzureOpenAIProxy.AppHost.Tests/ApiApp/Endpoints/AdminGetEventDetailsOpenApiTests.cs
+++ b/test/AzureOpenAIProxy.AppHost.Tests/ApiApp/Endpoints/AdminGetEventDetailsOpenApiTests.cs
@@ -4,8 +4,6 @@
using FluentAssertions;
-using IdentityModel.Client;
-
namespace AzureOpenAIProxy.AppHost.Tests.ApiApp.Endpoints;
public class AdminGetEventDetailsOpenApiTests(AspireAppHostFixture host) : IClassFixture
@@ -154,6 +152,7 @@ public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_Obj
[Theory]
[InlineData("200")]
[InlineData("401")]
+ [InlineData("404")]
[InlineData("500")]
public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_Response(string attribute)
{
@@ -173,141 +172,4 @@ public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_Res
.TryGetProperty(attribute, out var property) ? property : default;
result.ValueKind.Should().Be(JsonValueKind.Object);
}
-
- [Fact]
- public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_Schemas()
- {
- // Arrange
- using var httpClient = host.App!.CreateHttpClient("apiapp");
- await host.ResourceNotificationService.WaitForResourceAsync("apiapp", KnownResourceStates.Running).WaitAsync(TimeSpan.FromSeconds(30));
-
- // Act
- var json = await httpClient.GetStringAsync("/swagger/v1.0.0/swagger.json");
- var openapi = JsonSerializer.Deserialize(json);
-
- // Assert
- var result = openapi!.RootElement.GetProperty("components")
- .TryGetProperty("schemas", out var property) ? property : default;
- result.ValueKind.Should().Be(JsonValueKind.Object);
- }
-
- [Fact]
- public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_Model()
- {
- // Arrange
- using var httpClient = host.App!.CreateHttpClient("apiapp");
- await host.ResourceNotificationService.WaitForResourceAsync("apiapp", KnownResourceStates.Running).WaitAsync(TimeSpan.FromSeconds(30));
-
- // Act
- var json = await httpClient.GetStringAsync("/swagger/v1.0.0/swagger.json");
- var openapi = JsonSerializer.Deserialize(json);
-
- // Assert
- var result = openapi!.RootElement.GetProperty("components")
- .GetProperty("schemas")
- .TryGetProperty("AdminEventDetails", out var property) ? property : default;
- result.ValueKind.Should().Be(JsonValueKind.Object);
- }
-
- [Theory]
- [InlineData("eventId", true)]
- [InlineData("title", true)]
- [InlineData("summary", true)]
- [InlineData("description", false)]
- [InlineData("dateStart", true)]
- [InlineData("dateEnd", true)]
- [InlineData("timeZone", true)]
- [InlineData("isActive", true)]
- [InlineData("organizerName", true)]
- [InlineData("organizerEmail", true)]
- [InlineData("coorganizerName", false)]
- [InlineData("coorganizerEmail", false)]
- [InlineData("maxTokenCap", true)]
- [InlineData("dailyRequestCap", true)]
- public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_Required(string attribute, bool isRequired)
- {
- // Arrange
- using var httpClient = host.App!.CreateHttpClient("apiapp");
- await host.ResourceNotificationService.WaitForResourceAsync("apiapp", KnownResourceStates.Running).WaitAsync(TimeSpan.FromSeconds(30));
-
- // Act
- var json = await httpClient.GetStringAsync("/swagger/v1.0.0/swagger.json");
- var openapi = JsonSerializer.Deserialize(json);
-
- // Assert
- var result = openapi!.RootElement.GetProperty("components")
- .GetProperty("schemas")
- .GetProperty("AdminEventDetails")
- .TryGetStringArray("required")
- .ToList();
- result.Contains(attribute).Should().Be(isRequired);
- }
-
- [Theory]
- [InlineData("eventId")]
- [InlineData("title")]
- [InlineData("summary")]
- [InlineData("description")]
- [InlineData("dateStart")]
- [InlineData("dateEnd")]
- [InlineData("timeZone")]
- [InlineData("isActive")]
- [InlineData("organizerName")]
- [InlineData("organizerEmail")]
- [InlineData("coorganizerName")]
- [InlineData("coorganizerEmail")]
- [InlineData("maxTokenCap")]
- [InlineData("dailyRequestCap")]
- public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_Property(string attribute)
- {
- // Arrange
- using var httpClient = host.App!.CreateHttpClient("apiapp");
- await host.ResourceNotificationService.WaitForResourceAsync("apiapp", KnownResourceStates.Running).WaitAsync(TimeSpan.FromSeconds(30));
-
- // Act
- var json = await httpClient.GetStringAsync("/swagger/v1.0.0/swagger.json");
- var openapi = JsonSerializer.Deserialize(json);
-
- // Assert
- var result = openapi!.RootElement.GetProperty("components")
- .GetProperty("schemas")
- .GetProperty("AdminEventDetails")
- .GetProperty("properties")
- .TryGetProperty(attribute, out var property) ? property : default;
- result.ValueKind.Should().Be(JsonValueKind.Object);
- }
-
- [Theory]
- [InlineData("eventId", "string")]
- [InlineData("title", "string")]
- [InlineData("summary", "string")]
- [InlineData("description", "string")]
- [InlineData("dateStart", "string")]
- [InlineData("dateEnd", "string")]
- [InlineData("timeZone", "string")]
- [InlineData("isActive", "boolean")]
- [InlineData("organizerName", "string")]
- [InlineData("organizerEmail", "string")]
- [InlineData("coorganizerName", "string")]
- [InlineData("coorganizerEmail", "string")]
- [InlineData("maxTokenCap", "integer")]
- [InlineData("dailyRequestCap", "integer")]
- public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_Type(string attribute, string type)
- {
- // Arrange
- using var httpClient = host.App!.CreateHttpClient("apiapp");
- await host.ResourceNotificationService.WaitForResourceAsync("apiapp", KnownResourceStates.Running).WaitAsync(TimeSpan.FromSeconds(30));
-
- // Act
- var json = await httpClient.GetStringAsync("/swagger/v1.0.0/swagger.json");
- var openapi = JsonSerializer.Deserialize(json);
-
- // Assert
- var result = openapi!.RootElement.GetProperty("components")
- .GetProperty("schemas")
- .GetProperty("AdminEventDetails")
- .GetProperty("properties")
- .GetProperty(attribute);
- result.TryGetString("type").Should().Be(type);
- }
}