Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backend API] Implement endpoint for view event details #318

35 changes: 30 additions & 5 deletions src/AzureOpenAIProxy.ApiApp/Endpoints/AdminEventEndpoints.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Azure;

using AzureOpenAIProxy.ApiApp.Models;
using AzureOpenAIProxy.ApiApp.Services;

Expand Down Expand Up @@ -107,12 +109,35 @@ public static RouteHandlerBuilder AddGetAdminEvent(this WebApplication app)
{
// Todo: Issue #19 https://github.com/aliencube/azure-openai-sdk-proxy/issues/19
// Need authorization by admin
var builder = app.MapGet(AdminEndpointUrls.AdminEventDetails, (
[FromRoute] string eventId) =>
var builder = app.MapGet(AdminEndpointUrls.AdminEventDetails, async (
[FromRoute] Guid eventId,
IAdminEventService service,
ILoggerFactory loggerFactory) =>
{
// Todo: Issue #208 https://github.com/aliencube/azure-openai-sdk-proxy/issues/208
return Results.Ok();
// Todo: Issue #208
var logger = loggerFactory.CreateLogger(nameof(AdminEventEndpoints));
logger.LogInformation($"Received request to fetch details for event with ID: {eventId}");

try
{
var details = await service.GetEvent(eventId);
return details is null? Results.NotFound(): Results.Ok(details);
justinyoo marked this conversation as resolved.
Show resolved Hide resolved
}
catch(RequestFailedException ex)
{
if(ex.Status == 404)
{
logger.LogError($"Failed to get event details of {eventId}");
return Results.NotFound();
}

logger.LogError(ex, $"Error occurred while fetching event details of {eventId} with status {ex.Status}");
return Results.Problem(ex.Message, statusCode: StatusCodes.Status500InternalServerError);
}
catch(Exception ex)
{
logger.LogError(ex, $"Error occurred while fetching event details of {eventId}");
return Results.Problem(ex.Message, statusCode: StatusCodes.Status500InternalServerError);
}
})
.Produces<AdminEventDetails>(statusCode: StatusCodes.Status200OK, contentType: "application/json")
.Produces(statusCode: StatusCodes.Status401Unauthorized)
Expand Down
18 changes: 17 additions & 1 deletion src/AzureOpenAIProxy.ApiApp/Repositories/AdminEventRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@
private readonly StorageAccountSettings _storageAccountSettings = storageAccountSettings ?? throw new ArgumentNullException(nameof(storageAccountSettings));

/// <inheritdoc />
public async Task<AdminEventDetails> CreateEvent(AdminEventDetails eventDetails)

Check warning on line 52 in src/AzureOpenAIProxy.ApiApp/Repositories/AdminEventRepository.cs

View workflow job for this annotation

GitHub Actions / build-test

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 52 in src/AzureOpenAIProxy.ApiApp/Repositories/AdminEventRepository.cs

View workflow job for this annotation

GitHub Actions / build-test

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
{
throw new NotImplementedException();
}

/// <inheritdoc />
public async Task<List<AdminEventDetails>> GetEvents()

Check warning on line 58 in src/AzureOpenAIProxy.ApiApp/Repositories/AdminEventRepository.cs

View workflow job for this annotation

GitHub Actions / build-test

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 58 in src/AzureOpenAIProxy.ApiApp/Repositories/AdminEventRepository.cs

View workflow job for this annotation

GitHub Actions / build-test

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
{
throw new NotImplementedException();
}
Expand All @@ -63,14 +63,30 @@
/// <inheritdoc />
public async Task<AdminEventDetails> GetEvent(Guid eventId)
{
throw new NotImplementedException();
TableClient tableClient = await GetTableClientAsync();

var eventDetail = await tableClient.GetEntityAsync<AdminEventDetails>(
rowKey: eventId.ToString(),
partitionKey: "event-details"
justinyoo marked this conversation as resolved.
Show resolved Hide resolved
).ConfigureAwait(false);

return eventDetail.Value;
}

/// <inheritdoc />
public async Task<AdminEventDetails> UpdateEvent(Guid eventId, AdminEventDetails eventDetails)

Check warning on line 77 in src/AzureOpenAIProxy.ApiApp/Repositories/AdminEventRepository.cs

View workflow job for this annotation

GitHub Actions / build-test

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 77 in src/AzureOpenAIProxy.ApiApp/Repositories/AdminEventRepository.cs

View workflow job for this annotation

GitHub Actions / build-test

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
{
throw new NotImplementedException();
}

private async Task<TableClient> GetTableClientAsync()
{
TableClient tableClient = _tableServiceClient.GetTableClient(_storageAccountSettings.TableStorage.TableName);

await tableClient.CreateIfNotExistsAsync();
justinyoo marked this conversation as resolved.
Show resolved Hide resolved

return tableClient;
}
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
using Azure.Data.Tables;
using Azure;
using Azure.Data.Tables;

using AzureOpenAIProxy.ApiApp.Configurations;
using AzureOpenAIProxy.ApiApp.Models;
using AzureOpenAIProxy.ApiApp.Repositories;

using Castle.Core.Configuration;

using FluentAssertions;

using Microsoft.Extensions.DependencyInjection;

using NSubstitute;
using NSubstitute.ExceptionExtensions;

namespace AzureOpenAIProxy.ApiApp.Tests.Repositories;

Expand All @@ -37,7 +37,7 @@
var tableServiceClient = default(TableServiceClient);

// Act
Action action = () => new AdminEventRepository(tableServiceClient, settings);

Check warning on line 40 in test/AzureOpenAIProxy.ApiApp.Tests/Repositories/AdminEventRepositoryTests.cs

View workflow job for this annotation

GitHub Actions / build-test

Possible null reference argument for parameter 'tableServiceClient' in 'AdminEventRepository.AdminEventRepository(TableServiceClient tableServiceClient, StorageAccountSettings storageAccountSettings)'.

// Assert
action.Should().Throw<ArgumentNullException>();
Expand All @@ -51,7 +51,7 @@
var tableServiceClient = Substitute.For<TableServiceClient>();

// Act
Action action = () => new AdminEventRepository(tableServiceClient, settings);

Check warning on line 54 in test/AzureOpenAIProxy.ApiApp.Tests/Repositories/AdminEventRepositoryTests.cs

View workflow job for this annotation

GitHub Actions / build-test

Possible null reference argument for parameter 'storageAccountSettings' in 'AdminEventRepository.AdminEventRepository(TableServiceClient tableServiceClient, StorageAccountSettings storageAccountSettings)'.

// Assert
action.Should().Throw<ArgumentNullException>();
Expand Down Expand Up @@ -88,20 +88,59 @@
func.Should().ThrowAsync<NotImplementedException>();
}

[Fact]
public void Given_Instance_When_GetEvent_Invoked_Then_It_Should_Throw_Exception()
[Theory]
[InlineData(404)]
[InlineData(500)]
public async Task Given_Failure_In_Get_Entity_When_GetEvent_Invoked_Then_It_Should_Throw_Exception(int statusCode)
{
// Arrange
var settings = Substitute.For<StorageAccountSettings>();
var tableServiceClient = Substitute.For<TableServiceClient>();
var eventId = Guid.NewGuid();
var repository = new AdminEventRepository(tableServiceClient, settings);

var exception = new RequestFailedException(statusCode, "Request Error", default, default);

var tableClient = Substitute.For<TableClient>();
tableServiceClient.GetTableClient(Arg.Any<string>()).Returns(tableClient);
tableClient.GetEntityAsync<AdminEventDetails>(Arg.Any<string>(), Arg.Any<string>())
.ThrowsAsync(exception);

// Act
Func<Task> func = async () => await repository.GetEvent(eventId);
Func<Task> func = () => repository.GetEvent(eventId);

// Assert
func.Should().ThrowAsync<NotImplementedException>();
var assertion = await func.Should().ThrowAsync<RequestFailedException>();
assertion.Which.Status.Should().Be(statusCode);
}

[Theory]
[InlineData("c355cc28-d847-4637-aad9-2f03d39aa51f", "event-details")]
public async Task Given_Exist_EventId_When_GetEvent_Invoked_Then_It_Should_Return_AdminEventDetails(string eventId, string partitionKey)
{
// Arrange
var settings = Substitute.For<StorageAccountSettings>();
var tableServiceClient = Substitute.For<TableServiceClient>();
var repository = new AdminEventRepository(tableServiceClient, settings);

var eventDetails = new AdminEventDetails
{
RowKey = eventId,
PartitionKey = partitionKey
};

var response = Response.FromValue(eventDetails, Substitute.For<Response>());

var tableClient = Substitute.For<TableClient>();
tableServiceClient.GetTableClient(Arg.Any<string>()).Returns(tableClient);
tableClient.GetEntityAsync<AdminEventDetails>(partitionKey, eventId)
.Returns(Task.FromResult(response));

// Act
var result = await repository.GetEvent(Guid.Parse(eventId));

// Assert
result.Should().BeEquivalentTo(eventDetails);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
using AzureOpenAIProxy.ApiApp.Models;
using Azure;

using AzureOpenAIProxy.ApiApp.Models;
using AzureOpenAIProxy.ApiApp.Repositories;
using AzureOpenAIProxy.ApiApp.Services;

using FluentAssertions;

using Google.Protobuf.WellKnownTypes;

justinyoo marked this conversation as resolved.
Show resolved Hide resolved
using Microsoft.Extensions.DependencyInjection;

using NSubstitute;
using NSubstitute.ExceptionExtensions;

namespace AzureOpenAIProxy.ApiApp.Tests.Services;

Expand Down Expand Up @@ -54,19 +59,51 @@ public void Given_Instance_When_GetEvents_Invoked_Then_It_Should_Throw_Exception
func.Should().ThrowAsync<NotImplementedException>();
}

[Fact]
public void Given_Instance_When_GetEvent_Invoked_Then_It_Should_Throw_Exception()

[Theory]
[InlineData(404)]
[InlineData(500)]
public async Task Given_Failure_In_Get_Entity_When_GetEvent_Invoked_Then_It_Should_Throw_Exception(int statusCode)
{
// Arrange
var eventId = Guid.NewGuid();
var repository = Substitute.For<IAdminEventRepository>();
var service = new AdminEventService(repository);

var exception = new RequestFailedException(statusCode, "Request Failed", default, default);

repository.GetEvent(Arg.Any<Guid>()).ThrowsAsync(exception);

// Act
Func<Task> func = async () => await service.GetEvent(eventId);
Func<Task> func = () => service.GetEvent(eventId);

// Assert
func.Should().ThrowAsync<NotImplementedException>();
var assertion = await func.Should().ThrowAsync<RequestFailedException>();
assertion.Which.Status.Should().Be(statusCode);
}

[Theory]
[InlineData("c355cc28-d847-4637-aad9-2f03d39aa51f")]
public async Task Given_Exist_EventId_When_GetEvent_Invoked_Then_It_Should_Return_AdminEventDetails(string eventId)
{
// Arrange
var repository = Substitute.For<IAdminEventRepository>();
var service = new AdminEventService(repository);

var eventDetails = new AdminEventDetails
{
RowKey = eventId
};

var guid = Guid.Parse(eventId);

repository.GetEvent(guid).Returns(Task.FromResult(eventDetails));

// Act
var result = await service.GetEvent(guid);

// Assert
result.Should().BeEquivalentTo(eventDetails);
}

[Fact]
Expand Down
Loading