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 list event details #331

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ public static IServiceCollection AddTableStorageService(this IServiceCollection
{
services.AddSingleton<TableServiceClient>(sp => {
var configuration = sp.GetService<IConfiguration>()
?? throw new InvalidOperationException($"{nameof(IConfiguration)} service is not registerd.");
?? throw new InvalidOperationException($"{nameof(IConfiguration)} service is not registered.");

var settings = configuration.GetSection(AzureSettings.Name).GetSection(KeyVaultSettings.Name).Get<KeyVaultSettings>()
?? throw new InvalidOperationException($"{nameof(KeyVaultSettings)} could not be retrieved from the configuration.");
Expand Down
45 changes: 41 additions & 4 deletions src/AzureOpenAIProxy.ApiApp/Repositories/AdminEventRepository.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using Azure.Data.Tables;
using System.Collections.Specialized;
using System.Text.Json;

using Azure.Data.Tables;

using AzureOpenAIProxy.ApiApp.Configurations;
using AzureOpenAIProxy.ApiApp.Extensions;
Expand Down Expand Up @@ -38,6 +41,13 @@ public interface IAdminEventRepository
/// <param name="eventDetails">Event details instance.</param>
/// <returns>Returns the updated record of the event details.</returns>
Task<AdminEventDetails> UpdateEvent(Guid eventId, AdminEventDetails eventDetails);

/// <summary>
/// Deletes the event details.
/// </summary>
/// <param name="eventDetails">Event details instance.</param>
/// <returns>Removed EventID of event details instance.</returns>
Task<Guid> DeleteEvent(AdminEventDetails eventDetails);
}
Comment on lines +45 to +50
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이벤트를 삭제할 일이 없습니다.


/// <summary>
Expand All @@ -51,13 +61,25 @@ public class AdminEventRepository(TableServiceClient tableServiceClient, Storage
/// <inheritdoc />
public async Task<AdminEventDetails> CreateEvent(AdminEventDetails eventDetails)
{
throw new NotImplementedException();
var tableClient = _tableServiceClient.GetTableClient(_storageAccountSettings.TableStorage.TableName);

await tableClient.AddEntityAsync<AdminEventDetails>(eventDetails);

return eventDetails;
}
Comment on lines +64 to +68
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 태스크는 이벤트 리스트 가져오는 거라 이 메소드는 굳이 구현하실 필요가 없습니다.


/// <inheritdoc />
public async Task<List<AdminEventDetails>> GetEvents()
{
throw new NotImplementedException();
var tableClient = await GetTableClientAsync();
var eventDetailsList = new List<AdminEventDetails>();

await foreach (var entity in tableClient.QueryAsync<AdminEventDetails>())
{
eventDetailsList.Add(entity);
}

Comment on lines +77 to +80
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테이블에 이벤트 디테일만 들어있는 게 아니라서, 이렇게 하면 에러 날 거예요. 적어도 파티션 키를 넣어줘야 할 겁니다.

return eventDetailsList;
}

/// <inheritdoc />
Expand All @@ -76,7 +98,22 @@ public async Task<AdminEventDetails> GetEvent(Guid eventId)
/// <inheritdoc />
public async Task<AdminEventDetails> UpdateEvent(Guid eventId, AdminEventDetails eventDetails)
{
throw new NotImplementedException();
var tableClient = await GetTableClientAsync();

eventDetails.EventId = eventId;

await tableClient.UpdateEntityAsync<AdminEventDetails>(eventDetails, eventDetails.ETag, TableUpdateMode.Replace);

return eventDetails;
}
Comment on lines +101 to +107
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

마찬가지로 이 태스크의 스코프를 벗어난 구현입니다.


public async Task<Guid> DeleteEvent(AdminEventDetails eventDetails)
{
var tableClient = await GetTableClientAsync();

await tableClient.DeleteEntityAsync(eventDetails.PartitionKey, eventDetails.RowKey);

return eventDetails.EventId;
}
Comment on lines +110 to +116
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

필요 없습니다.


private async Task<TableClient> GetTableClientAsync()
Expand Down
15 changes: 15 additions & 0 deletions src/AzureOpenAIProxy.ApiApp/Services/AdminEventService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ public interface IAdminEventService
/// <param name="eventDetails">Event details to update.</param>
/// <returns>Returns the updated event details.</returns>
Task<AdminEventDetails> UpdateEvent(Guid eventId, AdminEventDetails eventDetails);

/// <summary>
/// Deletes the event details.
/// </summary>
/// <param name="eventDetails">Event details to update.</param>
/// <returns>Removed EventID of event details instance.</returns>
Task<Guid> DeleteEvent(AdminEventDetails eventDetails);
Comment on lines +38 to +44
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

필요 없습니다.

}

/// <summary>
Expand Down Expand Up @@ -75,6 +82,14 @@ public async Task<AdminEventDetails> UpdateEvent(Guid eventId, AdminEventDetails

return result;
}

/// <inheritdoc />
public async Task<Guid> DeleteEvent(AdminEventDetails eventDetails)
{
var result = await this._repository.DeleteEvent(eventDetails).ConfigureAwait(false);

return result;
}
Comment on lines +86 to +92
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

필요 없습니다

}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,19 @@

public class AdminEventRepositoryTests
{
private readonly StorageAccountSettings mockSettings;
private readonly TableServiceClient mockTableServiceClient;
private readonly TableClient mockTableClient;

public AdminEventRepositoryTests()
{
mockSettings = Substitute.For<StorageAccountSettings>();
mockTableServiceClient = Substitute.For<TableServiceClient>();
mockTableClient = Substitute.For<TableClient>();

mockTableServiceClient.GetTableClient(Arg.Any<string>()).Returns(mockTableClient);
}
Comment on lines +19 to +30
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트 코드는 이렇게 작성하면 안될 거예요.


[Fact]
public void Given_ServiceCollection_When_AddAdminEventRepository_Invoked_Then_It_Should_Contain_AdminEventRepository()
{
Expand All @@ -33,11 +46,10 @@
public void Given_Null_TableServiceClient_When_Creating_AdminEventRepository_Then_It_Should_Throw_Exception()
{
// Arrange
var settings = Substitute.For<StorageAccountSettings>();
var tableServiceClient = default(TableServiceClient);

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

Check warning on line 52 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)'.

Check warning on line 52 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 @@ -48,44 +60,39 @@
{
// Arrange
var settings = default(StorageAccountSettings);
var tableServiceClient = Substitute.For<TableServiceClient>();

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

Check warning on line 65 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)'.

Check warning on line 65 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>();
}

[Fact]
public void Given_Instance_When_CreateEvent_Invoked_Then_It_Should_Throw_Exception()
public async Task Given_Instance_When_CreateEvent_Invoked_Then_It_Should_Return_Created_Event()
{
// Arrange
var settings = Substitute.For<StorageAccountSettings>();
var tableServiceClient = Substitute.For<TableServiceClient>();
var eventDetails = new AdminEventDetails();
var repository = new AdminEventRepository(tableServiceClient, settings);
var repository = new AdminEventRepository(mockTableServiceClient, mockSettings);

// Act
Func<Task> func = async () => await repository.CreateEvent(eventDetails);
await repository.CreateEvent(eventDetails);

// Assert
func.Should().ThrowAsync<NotImplementedException>();
await mockTableClient.Received(1).AddEntityAsync(eventDetails, default);
}

[Fact]
public void Given_Instance_When_GetEvents_Invoked_Then_It_Should_Throw_Exception()
public async Task Given_Instance_When_GetEvents_Invoked_Then_It_Should_Invoke_QueryAsync_Method()
{
// Arrange
var settings = Substitute.For<StorageAccountSettings>();
var tableServiceClient = Substitute.For<TableServiceClient>();
var repository = new AdminEventRepository(tableServiceClient, settings);
var repository = new AdminEventRepository(mockTableServiceClient, mockSettings);

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

// Assert
func.Should().ThrowAsync<NotImplementedException>();
mockTableClient.Received(1).QueryAsync<AdminEventDetails>();
}

[Theory]
Expand All @@ -94,16 +101,12 @@
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 repository = new AdminEventRepository(mockTableServiceClient, mockSettings);

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>())
mockTableClient.GetEntityAsync<AdminEventDetails>(Arg.Any<string>(), Arg.Any<string>())
.ThrowsAsync(exception);

// Act
Expand All @@ -119,9 +122,7 @@
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 repository = new AdminEventRepository(mockTableServiceClient, mockSettings);

var eventDetails = new AdminEventDetails
{
Expand All @@ -131,9 +132,7 @@

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)
mockTableClient.GetEntityAsync<AdminEventDetails>(partitionKey, eventId)
.Returns(Task.FromResult(response));

// Act
Expand All @@ -144,19 +143,39 @@
}

[Fact]
public void Given_Instance_When_UpdateEvent_Invoked_Then_It_Should_Throw_Exception()
public async Task Given_Instance_When_UpdateEvent_Invoked_Then_It_Should_Invoke_UpdateEntityAsync_Method()
{
// Arrange
var settings = Substitute.For<StorageAccountSettings>();
var tableServiceClient = Substitute.For<TableServiceClient>();
var eventId = Guid.NewGuid();
var eventDetails = new AdminEventDetails();
var repository = new AdminEventRepository(tableServiceClient, settings);
var repository = new AdminEventRepository(mockTableServiceClient, mockSettings);

// Act
await repository.UpdateEvent(eventId, eventDetails);

// Assert
await mockTableClient.Received(1)
.UpdateEntityAsync<AdminEventDetails>(Arg.Any<AdminEventDetails>(),
Arg.Any<Azure.ETag>(),
TableUpdateMode.Replace);
}

[Fact]
public async Task Given_Instance_When_DeleteEvent_Invoked_Then_It_Should_Invoke_DeleteEntityAsync_Method()
{
// Arrange
var eventDetails = new AdminEventDetails();
var repository = new AdminEventRepository(mockTableServiceClient, mockSettings);
var eventId = Guid.NewGuid();

eventDetails.EventId = eventId;

// Act
Func<Task> func = async () => await repository.UpdateEvent(eventId, eventDetails);
Guid deletedEventId = await repository.DeleteEvent(eventDetails);

// Assert
func.Should().ThrowAsync<NotImplementedException>();
deletedEventId.Should().Be(eventId);
await mockTableClient.Received(1)
.DeleteEntityAsync(Arg.Any<string>(), Arg.Any<string>());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ namespace AzureOpenAIProxy.ApiApp.Tests.Services;

public class AdminEventServiceTests
{
private readonly IAdminEventRepository mockRepository;

public AdminEventServiceTests()
{
mockRepository = Substitute.For<IAdminEventRepository>();
}

[Fact]
public void Given_ServiceCollection_When_AddAdminEventService_Invoked_Then_It_Should_Contain_AdminEventService()
{
Expand All @@ -33,8 +40,7 @@ public void Given_Instance_When_CreateEvent_Invoked_Then_It_Should_Throw_Excepti
{
// Arrange
var eventDetails = new AdminEventDetails();
var repository = Substitute.For<IAdminEventRepository>();
var service = new AdminEventService(repository);
var service = new AdminEventService(mockRepository);

// Act
Func<Task> func = async () => await service.CreateEvent(eventDetails);
Expand All @@ -47,8 +53,7 @@ public void Given_Instance_When_CreateEvent_Invoked_Then_It_Should_Throw_Excepti
public void Given_Instance_When_GetEvents_Invoked_Then_It_Should_Throw_Exception()
{
// Arrange
var repository = Substitute.For<IAdminEventRepository>();
var service = new AdminEventService(repository);
var service = new AdminEventService(mockRepository);

// Act
Func<Task> func = async () => await service.GetEvents();
Expand All @@ -65,12 +70,11 @@ public async Task Given_Failure_In_Get_Entity_When_GetEvent_Invoked_Then_It_Shou
{
// Arrange
var eventId = Guid.NewGuid();
var repository = Substitute.For<IAdminEventRepository>();
var service = new AdminEventService(repository);
var service = new AdminEventService(mockRepository);

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

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

// Act
Func<Task> func = () => service.GetEvent(eventId);
Expand All @@ -85,8 +89,7 @@ public async Task Given_Failure_In_Get_Entity_When_GetEvent_Invoked_Then_It_Shou
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 service = new AdminEventService(mockRepository);

var eventDetails = new AdminEventDetails
{
Expand All @@ -95,7 +98,7 @@ public async Task Given_Exist_EventId_When_GetEvent_Invoked_Then_It_Should_Retur

var guid = Guid.Parse(eventId);

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

// Act
var result = await service.GetEvent(guid);
Expand All @@ -105,18 +108,37 @@ public async Task Given_Exist_EventId_When_GetEvent_Invoked_Then_It_Should_Retur
}

[Fact]
public void Given_Instance_When_UpdateEvent_Invoked_Then_It_Should_Throw_Exception()
public async Task Given_Instance_When_UpdateEvent_Invoked_Then_It_Should_Return_Updated_Event_Details()
{
// Arrange
var eventId = Guid.NewGuid();
var eventDetails = new AdminEventDetails();
var repository = Substitute.For<IAdminEventRepository>();
var service = new AdminEventService(repository);
var service = new AdminEventService(mockRepository);

mockRepository.UpdateEvent(eventId, eventDetails).Returns(eventDetails);

// Act
Func<Task> func = async () => await service.UpdateEvent(eventId, eventDetails);
var updatedEventDetails = await service.UpdateEvent(eventId, eventDetails);

// Assert
func.Should().ThrowAsync<NotImplementedException>();
updatedEventDetails.Should().BeEquivalentTo(eventDetails);
}

[Fact]
public async Task Given_Instance_When_DeleteEvent_Invoked_Then_It_Should_Return_Deleted_Event_Id()
{
// Arrange
var eventId = Guid.NewGuid();
var eventDetails = new AdminEventDetails();
var service = new AdminEventService(mockRepository);

eventDetails.EventId = eventId;
mockRepository.DeleteEvent(eventDetails).Returns(eventDetails.EventId);

// Act
var deletedEventId = await service.DeleteEvent(eventDetails);

// Assert
deletedEventId.Should().Be(eventId);
}
}
Loading