Skip to content

Commit f2dc471

Browse files
authored
[Backend API] Implement endpoint for view event details (aliencube#318)
1 parent c7991e7 commit f2dc471

File tree

5 files changed

+150
-18
lines changed

5 files changed

+150
-18
lines changed

src/AzureOpenAIProxy.ApiApp/Endpoints/AdminEventEndpoints.cs

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using Azure;
2+
13
using AzureOpenAIProxy.ApiApp.Models;
24
using AzureOpenAIProxy.ApiApp.Services;
35

@@ -107,12 +109,35 @@ public static RouteHandlerBuilder AddGetAdminEvent(this WebApplication app)
107109
{
108110
// Todo: Issue #19 https://github.com/aliencube/azure-openai-sdk-proxy/issues/19
109111
// Need authorization by admin
110-
var builder = app.MapGet(AdminEndpointUrls.AdminEventDetails, (
111-
[FromRoute] string eventId) =>
112+
var builder = app.MapGet(AdminEndpointUrls.AdminEventDetails, async (
113+
[FromRoute] Guid eventId,
114+
IAdminEventService service,
115+
ILoggerFactory loggerFactory) =>
112116
{
113-
// Todo: Issue #208 https://github.com/aliencube/azure-openai-sdk-proxy/issues/208
114-
return Results.Ok();
115-
// Todo: Issue #208
117+
var logger = loggerFactory.CreateLogger(nameof(AdminEventEndpoints));
118+
logger.LogInformation($"Received request to fetch details for event with ID: {eventId}");
119+
120+
try
121+
{
122+
var details = await service.GetEvent(eventId);
123+
return Results.Ok(details);
124+
}
125+
catch(RequestFailedException ex)
126+
{
127+
if(ex.Status == 404)
128+
{
129+
logger.LogError($"Failed to get event details of {eventId}");
130+
return Results.NotFound();
131+
}
132+
133+
logger.LogError(ex, $"Error occurred while fetching event details of {eventId} with status {ex.Status}");
134+
return Results.Problem(ex.Message, statusCode: StatusCodes.Status500InternalServerError);
135+
}
136+
catch(Exception ex)
137+
{
138+
logger.LogError(ex, $"Error occurred while fetching event details of {eventId}");
139+
return Results.Problem(ex.Message, statusCode: StatusCodes.Status500InternalServerError);
140+
}
116141
})
117142
.Produces<AdminEventDetails>(statusCode: StatusCodes.Status200OK, contentType: "application/json")
118143
.Produces(statusCode: StatusCodes.Status401Unauthorized)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace AzureOpenAIProxy.ApiApp;
2+
3+
/// <summary>
4+
/// This represents the partition keys for azure table storage
5+
/// </summary>
6+
public class PartitionKeys
7+
{
8+
/// <summary>
9+
/// Partition key for event details
10+
/// </summary>
11+
public const string EventDetails = "event-details";
12+
13+
/// <summary>
14+
/// Partition key for resource details
15+
/// </summary>
16+
public const string ResourceDetails = "resource-details";
17+
}

src/AzureOpenAIProxy.ApiApp/Repositories/AdminEventRepository.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,30 @@ public async Task<List<AdminEventDetails>> GetEvents()
6363
/// <inheritdoc />
6464
public async Task<AdminEventDetails> GetEvent(Guid eventId)
6565
{
66-
throw new NotImplementedException();
66+
TableClient tableClient = await GetTableClientAsync();
67+
68+
var eventDetail = await tableClient.GetEntityAsync<AdminEventDetails>(
69+
rowKey: eventId.ToString(),
70+
partitionKey: PartitionKeys.EventDetails
71+
).ConfigureAwait(false);
72+
73+
return eventDetail.Value;
6774
}
6875

6976
/// <inheritdoc />
7077
public async Task<AdminEventDetails> UpdateEvent(Guid eventId, AdminEventDetails eventDetails)
7178
{
7279
throw new NotImplementedException();
7380
}
81+
82+
private async Task<TableClient> GetTableClientAsync()
83+
{
84+
TableClient tableClient = _tableServiceClient.GetTableClient(_storageAccountSettings.TableStorage.TableName);
85+
86+
await tableClient.CreateIfNotExistsAsync().ConfigureAwait(false);
87+
88+
return tableClient;
89+
}
7490
}
7591

7692
/// <summary>

test/AzureOpenAIProxy.ApiApp.Tests/Repositories/AdminEventRepositoryTests.cs

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
using Azure.Data.Tables;
1+
using Azure;
2+
using Azure.Data.Tables;
23

34
using AzureOpenAIProxy.ApiApp.Configurations;
45
using AzureOpenAIProxy.ApiApp.Models;
56
using AzureOpenAIProxy.ApiApp.Repositories;
67

7-
using Castle.Core.Configuration;
8-
98
using FluentAssertions;
109

1110
using Microsoft.Extensions.DependencyInjection;
1211

1312
using NSubstitute;
13+
using NSubstitute.ExceptionExtensions;
1414

1515
namespace AzureOpenAIProxy.ApiApp.Tests.Repositories;
1616

@@ -88,20 +88,59 @@ public void Given_Instance_When_GetEvents_Invoked_Then_It_Should_Throw_Exception
8888
func.Should().ThrowAsync<NotImplementedException>();
8989
}
9090

91-
[Fact]
92-
public void Given_Instance_When_GetEvent_Invoked_Then_It_Should_Throw_Exception()
91+
[Theory]
92+
[InlineData(404)]
93+
[InlineData(500)]
94+
public async Task Given_Failure_In_Get_Entity_When_GetEvent_Invoked_Then_It_Should_Throw_Exception(int statusCode)
9395
{
9496
// Arrange
9597
var settings = Substitute.For<StorageAccountSettings>();
9698
var tableServiceClient = Substitute.For<TableServiceClient>();
9799
var eventId = Guid.NewGuid();
98100
var repository = new AdminEventRepository(tableServiceClient, settings);
99101

102+
var exception = new RequestFailedException(statusCode, "Request Error", default, default);
103+
104+
var tableClient = Substitute.For<TableClient>();
105+
tableServiceClient.GetTableClient(Arg.Any<string>()).Returns(tableClient);
106+
tableClient.GetEntityAsync<AdminEventDetails>(Arg.Any<string>(), Arg.Any<string>())
107+
.ThrowsAsync(exception);
108+
100109
// Act
101-
Func<Task> func = async () => await repository.GetEvent(eventId);
110+
Func<Task> func = () => repository.GetEvent(eventId);
102111

103112
// Assert
104-
func.Should().ThrowAsync<NotImplementedException>();
113+
var assertion = await func.Should().ThrowAsync<RequestFailedException>();
114+
assertion.Which.Status.Should().Be(statusCode);
115+
}
116+
117+
[Theory]
118+
[InlineData("c355cc28-d847-4637-aad9-2f03d39aa51f", "event-details")]
119+
public async Task Given_Exist_EventId_When_GetEvent_Invoked_Then_It_Should_Return_AdminEventDetails(string eventId, string partitionKey)
120+
{
121+
// Arrange
122+
var settings = Substitute.For<StorageAccountSettings>();
123+
var tableServiceClient = Substitute.For<TableServiceClient>();
124+
var repository = new AdminEventRepository(tableServiceClient, settings);
125+
126+
var eventDetails = new AdminEventDetails
127+
{
128+
RowKey = eventId,
129+
PartitionKey = partitionKey
130+
};
131+
132+
var response = Response.FromValue(eventDetails, Substitute.For<Response>());
133+
134+
var tableClient = Substitute.For<TableClient>();
135+
tableServiceClient.GetTableClient(Arg.Any<string>()).Returns(tableClient);
136+
tableClient.GetEntityAsync<AdminEventDetails>(partitionKey, eventId)
137+
.Returns(Task.FromResult(response));
138+
139+
// Act
140+
var result = await repository.GetEvent(Guid.Parse(eventId));
141+
142+
// Assert
143+
result.Should().BeEquivalentTo(eventDetails);
105144
}
106145

107146
[Fact]

test/AzureOpenAIProxy.ApiApp.Tests/Services/AdminEventServiceTests.cs

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using AzureOpenAIProxy.ApiApp.Models;
1+
using Azure;
2+
3+
using AzureOpenAIProxy.ApiApp.Models;
24
using AzureOpenAIProxy.ApiApp.Repositories;
35
using AzureOpenAIProxy.ApiApp.Services;
46

@@ -7,6 +9,7 @@
79
using Microsoft.Extensions.DependencyInjection;
810

911
using NSubstitute;
12+
using NSubstitute.ExceptionExtensions;
1013

1114
namespace AzureOpenAIProxy.ApiApp.Tests.Services;
1215

@@ -54,19 +57,51 @@ public void Given_Instance_When_GetEvents_Invoked_Then_It_Should_Throw_Exception
5457
func.Should().ThrowAsync<NotImplementedException>();
5558
}
5659

57-
[Fact]
58-
public void Given_Instance_When_GetEvent_Invoked_Then_It_Should_Throw_Exception()
60+
61+
[Theory]
62+
[InlineData(404)]
63+
[InlineData(500)]
64+
public async Task Given_Failure_In_Get_Entity_When_GetEvent_Invoked_Then_It_Should_Throw_Exception(int statusCode)
5965
{
6066
// Arrange
6167
var eventId = Guid.NewGuid();
6268
var repository = Substitute.For<IAdminEventRepository>();
6369
var service = new AdminEventService(repository);
6470

71+
var exception = new RequestFailedException(statusCode, "Request Failed", default, default);
72+
73+
repository.GetEvent(Arg.Any<Guid>()).ThrowsAsync(exception);
74+
6575
// Act
66-
Func<Task> func = async () => await service.GetEvent(eventId);
76+
Func<Task> func = () => service.GetEvent(eventId);
6777

6878
// Assert
69-
func.Should().ThrowAsync<NotImplementedException>();
79+
var assertion = await func.Should().ThrowAsync<RequestFailedException>();
80+
assertion.Which.Status.Should().Be(statusCode);
81+
}
82+
83+
[Theory]
84+
[InlineData("c355cc28-d847-4637-aad9-2f03d39aa51f")]
85+
public async Task Given_Exist_EventId_When_GetEvent_Invoked_Then_It_Should_Return_AdminEventDetails(string eventId)
86+
{
87+
// Arrange
88+
var repository = Substitute.For<IAdminEventRepository>();
89+
var service = new AdminEventService(repository);
90+
91+
var eventDetails = new AdminEventDetails
92+
{
93+
RowKey = eventId
94+
};
95+
96+
var guid = Guid.Parse(eventId);
97+
98+
repository.GetEvent(guid).Returns(Task.FromResult(eventDetails));
99+
100+
// Act
101+
var result = await service.GetEvent(guid);
102+
103+
// Assert
104+
result.Should().BeEquivalentTo(eventDetails);
70105
}
71106

72107
[Fact]

0 commit comments

Comments
 (0)