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 new resource details #308 #332

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
17 changes: 15 additions & 2 deletions src/AzureOpenAIProxy.ApiApp/Endpoints/AdminResourceEndpoints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public static RouteHandlerBuilder AddNewAdminResource(this WebApplication app)
{
var builder = app.MapPost(AdminEndpointUrls.AdminResources, async (
[FromBody] AdminResourceDetails payload,
IAdminEventService service,
IAdminResourceService service,
ILoggerFactory loggerFactory) =>
{
var logger = loggerFactory.CreateLogger(nameof(AdminResourceEndpoints));
Expand All @@ -32,7 +32,20 @@ public static RouteHandlerBuilder AddNewAdminResource(this WebApplication app)
return Results.BadRequest("Payload is null");
}

return await Task.FromResult(Results.Ok());
try
{
var result = await service.CreateResource(payload);

logger.LogInformation("Created a new resource");

return Results.Ok(result);
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to create a new resource");

return Results.Problem(ex.Message, statusCode: StatusCodes.Status500InternalServerError);
}
})
.Accepts<AdminResourceDetails>(contentType: "application/json")
.Produces<AdminResourceDetails>(statusCode: StatusCodes.Status200OK, contentType: "application/json")
Expand Down
2 changes: 2 additions & 0 deletions src/AzureOpenAIProxy.ApiApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@

// Add admin services
builder.Services.AddAdminEventService();
builder.Services.AddAdminResourceService();

// Add admin repositories
builder.Services.AddAdminEventRepository();
builder.Services.AddAdminResourceRepository();

var app = builder.Build();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using Azure.Data.Tables;

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

namespace AzureOpenAIProxy.ApiApp.Repositories;

/// <summary>
/// This provides interfaces to the <see cref="AdminResourceRepository"/> class.
/// </summary>
public interface IAdminResourceRepository
{
/// <summary>
/// Creates a new record of resource details.
/// </summary>
/// <param name="resourceDetails">Resource details instance.</param>
/// <returns>Returns the resource details instance created.</returns>
Task<AdminResourceDetails> CreateResource(AdminResourceDetails resourceDetails);
}

/// <summary>
/// This represents the repository entity for the admin resource.
/// </summary>
public class AdminResourceRepository(TableServiceClient tableServiceClient, StorageAccountSettings storageAccountSettings) : IAdminResourceRepository
{
private readonly TableServiceClient _tableServiceClient = tableServiceClient ?? throw new ArgumentNullException(nameof(tableServiceClient));
private readonly StorageAccountSettings _storageAccountSettings = storageAccountSettings ?? throw new ArgumentNullException(nameof(storageAccountSettings));

/// <inheritdoc />
public async Task<AdminResourceDetails> CreateResource(AdminResourceDetails resourceDetails)
{
TableClient tableClient = await GetTableClientAsync();

await tableClient.AddEntityAsync(resourceDetails).ConfigureAwait(false);

return resourceDetails;
}

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

await tableClient.CreateIfNotExistsAsync().ConfigureAwait(false);

return tableClient;
}
}

/// <summary>
/// This represents the extension class for <see cref="IServiceCollection"/>
/// </summary>
public static class AdminResourceRepositoryExtensions
{
/// <summary>
/// Adds the <see cref="AdminResourceRepository"/> instance to the service collection.
/// </summary>
/// <param name="services"><see cref="IServiceCollection"/> instance.</param>
/// <returns>Returns <see cref="IServiceCollection"/> instance.</returns>
public static IServiceCollection AddAdminResourceRepository(this IServiceCollection services)
{
services.AddScoped<IAdminResourceRepository, AdminResourceRepository>();

return services;
}
}
52 changes: 52 additions & 0 deletions src/AzureOpenAIProxy.ApiApp/Services/AdminResourceService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using AzureOpenAIProxy.ApiApp.Models;
using AzureOpenAIProxy.ApiApp.Repositories;

namespace AzureOpenAIProxy.ApiApp.Services;

/// <summary>
/// This provides interfaces to the <see cref="AdminResourceService"/> class.
/// </summary>
public interface IAdminResourceService
{
/// <summary>
/// Creates a new resource.
/// </summary>
/// <param name="resourceDetails">Resource payload.</param>
/// <returns>Returns the resource payload created.</returns>
Task<AdminResourceDetails> CreateResource(AdminResourceDetails resourceDetails);
}

/// <summary>
/// This represents the service entity for admin resource.
/// </summary>
public class AdminResourceService(IAdminResourceRepository repository) : IAdminResourceService
{
private readonly IAdminResourceRepository _repository = repository ?? throw new ArgumentNullException(nameof(repository));

/// <inheritdoc />
public async Task<AdminResourceDetails> CreateResource(AdminResourceDetails resourceDetails)
{
resourceDetails.PartitionKey = PartitionKeys.ResourceDetails;
resourceDetails.RowKey = resourceDetails.ResourceId.ToString();

var result = await _repository.CreateResource(resourceDetails).ConfigureAwait(false);
return result;
}
}

/// <summary>
/// This represents the extension class for <see cref="IServiceCollection"/>.
/// </summary>
public static class AdminResourceServiceExtensions
{
/// <summary>
/// Adds the <see cref="AdminResourceService"/> instance to the service collection.
/// </summary>
/// <param name="services"><see cref="IServiceCollection"/> instance.</param>
/// <returns>Returns <see cref="IServiceCollection"/> instance.</returns>
public static IServiceCollection AddAdminResourceService(this IServiceCollection services)
{
services.AddScoped<IAdminResourceService, AdminResourceService>();
return services;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@
},
"KeyVault": {
"VaultUri": "https://{{key-vault-name}}.vault.azure.net/",
"SecretName": "azure-openai-instances"
"SecretNames": {
"OpenAI": "azure-openai-instances",
"Storage": "storage-connection-string"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Azure;
using Azure.Data.Tables;

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

using FluentAssertions;

using Microsoft.Extensions.DependencyInjection;

using NSubstitute;
using NSubstitute.ExceptionExtensions;

namespace AzureOpenAIProxy.ApiApp.Tests.Repositories;

public class AdminResourceRepositoryTests
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Azure;

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

using FluentAssertions;

using Microsoft.Extensions.DependencyInjection;

using NSubstitute;
using NSubstitute.ExceptionExtensions;

namespace AzureOpenAIProxy.ApiApp.Tests.Services;

public class AdminResourceServiceTests
{
}
Loading