From d7aab48fcc3134941858d65da3f0f71651533e36 Mon Sep 17 00:00:00 2001
From: Lucy Oh <56earls@gmail.com>
Date: Sat, 31 Aug 2024 16:34:12 +0900
Subject: [PATCH 01/13] [Playground] Component - Tab for system message and
parameters (UI only) #225 (#244)
---
.../Components/Pages/Home.razor | 2 +-
.../Components/Pages/Playground.razor | 4 +-
.../Components/UI/ConfigTabComponent.razor | 20 ++++++
.../Components/_Imports.razor | 1 +
src/AzureOpenAIProxy.PlaygroundApp/Program.cs | 2 +-
.../UI/ConfigTabComponentTest.cs | 71 +++++++++++++++++++
6 files changed, 97 insertions(+), 3 deletions(-)
create mode 100644 src/AzureOpenAIProxy.PlaygroundApp/Components/UI/ConfigTabComponent.razor
create mode 100644 test/AzureOpenAIProxy.PlaygroundApp.Tests/UI/ConfigTabComponentTest.cs
diff --git a/src/AzureOpenAIProxy.PlaygroundApp/Components/Pages/Home.razor b/src/AzureOpenAIProxy.PlaygroundApp/Components/Pages/Home.razor
index d05c2a66..7c8ab364 100644
--- a/src/AzureOpenAIProxy.PlaygroundApp/Components/Pages/Home.razor
+++ b/src/AzureOpenAIProxy.PlaygroundApp/Components/Pages/Home.razor
@@ -7,4 +7,4 @@
Welcome to your new app.
-
\ No newline at end of file
+
diff --git a/src/AzureOpenAIProxy.PlaygroundApp/Components/Pages/Playground.razor b/src/AzureOpenAIProxy.PlaygroundApp/Components/Pages/Playground.razor
index 5e52f38a..08f49d57 100644
--- a/src/AzureOpenAIProxy.PlaygroundApp/Components/Pages/Playground.razor
+++ b/src/AzureOpenAIProxy.PlaygroundApp/Components/Pages/Playground.razor
@@ -2,4 +2,6 @@
Playground Page
-
playground page!
\ No newline at end of file
+
playground page!
+
+
\ No newline at end of file
diff --git a/src/AzureOpenAIProxy.PlaygroundApp/Components/UI/ConfigTabComponent.razor b/src/AzureOpenAIProxy.PlaygroundApp/Components/UI/ConfigTabComponent.razor
new file mode 100644
index 00000000..33d9589a
--- /dev/null
+++ b/src/AzureOpenAIProxy.PlaygroundApp/Components/UI/ConfigTabComponent.razor
@@ -0,0 +1,20 @@
+
+
+ This is "System message" tab.
+
+
+ This is "Parameters" tab.
+
+
+
+
[TEST] Active tab changed to: @SelectedTab?.Id
+
+@code {
+ FluentTab? SelectedTab;
+
+ private async Task ChangeTab(FluentTab tab)
+ {
+ SelectedTab = tab;
+ await Task.CompletedTask;
+ }
+}
\ No newline at end of file
diff --git a/src/AzureOpenAIProxy.PlaygroundApp/Components/_Imports.razor b/src/AzureOpenAIProxy.PlaygroundApp/Components/_Imports.razor
index e8aaa070..beab74d2 100644
--- a/src/AzureOpenAIProxy.PlaygroundApp/Components/_Imports.razor
+++ b/src/AzureOpenAIProxy.PlaygroundApp/Components/_Imports.razor
@@ -14,3 +14,4 @@
@using AzureOpenAIProxy.PlaygroundApp
@using AzureOpenAIProxy.PlaygroundApp.Components
+@using AzureOpenAIProxy.PlaygroundApp.Components.UI
\ No newline at end of file
diff --git a/src/AzureOpenAIProxy.PlaygroundApp/Program.cs b/src/AzureOpenAIProxy.PlaygroundApp/Program.cs
index a61639f7..7db92b99 100644
--- a/src/AzureOpenAIProxy.PlaygroundApp/Program.cs
+++ b/src/AzureOpenAIProxy.PlaygroundApp/Program.cs
@@ -36,4 +36,4 @@
app.MapRazorComponents()
.AddInteractiveServerRenderMode();
-await app.RunAsync();
+await app.RunAsync();
\ No newline at end of file
diff --git a/test/AzureOpenAIProxy.PlaygroundApp.Tests/UI/ConfigTabComponentTest.cs b/test/AzureOpenAIProxy.PlaygroundApp.Tests/UI/ConfigTabComponentTest.cs
new file mode 100644
index 00000000..fbb98d36
--- /dev/null
+++ b/test/AzureOpenAIProxy.PlaygroundApp.Tests/UI/ConfigTabComponentTest.cs
@@ -0,0 +1,71 @@
+using Microsoft.Playwright;
+using Microsoft.Playwright.NUnit;
+
+namespace AzureOpenAIProxy.PlaygroundApp.Tests.UI;
+
+[Parallelizable(ParallelScope.Self)]
+[TestFixture]
+[Property("Category", "Integration")]
+public class ConfigTabComponentTest : PageTest
+{
+ public override BrowserNewContextOptions ContextOptions() => new() { IgnoreHTTPSErrors = true, };
+
+ [SetUp]
+ public async Task SetUp()
+ {
+ await Page.GotoAsync("https://localhost:5001/playground/");
+ await Page.WaitForLoadStateAsync(LoadState.NetworkIdle);
+ }
+
+ [Test]
+ public async Task Given_ConfigTab_When_Endpoint_Invoked_Then_ConfigTab_Should_Be_Displayed()
+ {
+ // Act
+ var configTab = Page.Locator("fluent-tabs#config-tab");
+
+ // Assert
+ await Expect(configTab).ToBeVisibleAsync();
+ }
+
+ [Test]
+ public async Task Given_ConfigTab_When_Endpoint_Invoked_Then_Id_Should_Be_System_Message_Tab()
+ {
+ // Act
+ var sysMsgPanel = Page.Locator("fluent-tab-panel#system-message-tab-panel");
+ var parameterPanel = Page.Locator("fluent-tab-panel#parameters-tab-panel");
+
+ // Assert
+ await Expect(sysMsgPanel).ToBeVisibleAsync();
+ await Expect(parameterPanel).ToBeHiddenAsync();
+ }
+
+ [Test]
+ [TestCase(
+ "fluent-tab#parameters-tab",
+ "fluent-tab-panel#parameters-tab-panel",
+ "fluent-tab-panel#system-message-tab-panel"
+ )]
+ [TestCase(
+ "fluent-tab#system-message-tab",
+ "fluent-tab-panel#system-message-tab-panel",
+ "fluent-tab-panel#parameters-tab-panel"
+ )]
+ public async Task Given_ConfigTab_When_Changed_Then_Tab_Should_Be_Updated(
+ string selectedTabSelector,
+ string selectedPanelSelector,
+ string hiddenPanelSelector
+ )
+ {
+ // Arrange
+ var selectedTab = Page.Locator(selectedTabSelector);
+ var selectedPanel = Page.Locator(selectedPanelSelector);
+ var hiddenPanel = Page.Locator(hiddenPanelSelector);
+
+ // Act
+ await selectedTab.ClickAsync();
+
+ // Assert
+ await Expect(selectedPanel).ToBeVisibleAsync();
+ await Expect(hiddenPanel).ToBeHiddenAsync();
+ }
+}
\ No newline at end of file
From 947d240da4062cebf6ab08d8fad4ef63f333389c Mon Sep 17 00:00:00 2001
From: sikutisa <32262904+sikutisa@users.noreply.github.com>
Date: Sat, 31 Aug 2024 17:32:43 +0900
Subject: [PATCH 02/13] [Backend API]Refactor OpenAPI doc version to
appsettings.json (#274)
---
.github/workflows/azure-dev-build-only.yml | 3 +-
.github/workflows/azure-dev.yml | 3 +-
.../Configurations/OpenApiSettings.cs | 17 +++
src/AzureOpenAIProxy.ApiApp/Constants.cs | 5 -
.../ApplicationBuilderExtensions.cs | 4 +-
.../Extensions/OpenApiSettingsExtensions.cs | 37 ++++++
.../Extensions/ServiceCollectionExtensions.cs | 10 +-
.../OpenApiSettingsExtensionsTests.cs | 121 ++++++++++++++++++
8 files changed, 186 insertions(+), 14 deletions(-)
create mode 100644 src/AzureOpenAIProxy.ApiApp/Configurations/OpenApiSettings.cs
create mode 100644 src/AzureOpenAIProxy.ApiApp/Extensions/OpenApiSettingsExtensions.cs
create mode 100644 test/AzureOpenAIProxy.ApiApp.Tests/Extensions/OpenApiSettingsExtensionsTests.cs
diff --git a/.github/workflows/azure-dev-build-only.yml b/.github/workflows/azure-dev-build-only.yml
index c42de024..ad48cba8 100644
--- a/.github/workflows/azure-dev-build-only.yml
+++ b/.github/workflows/azure-dev-build-only.yml
@@ -83,8 +83,7 @@ jobs:
- name: Create openapi.json
shell: pwsh
run: |
- $fileContent = Get-Content './src/AzureOpenAIProxy.ApiApp/Constants.cs'
- $API_VERSION = [regex]::Match($fileContent, 'public const string Version = "([^"]+)"').Groups[1].Value
+ $API_VERSION = $(Get-Content ./src/AzureOpenAIProxy.ApiApp/appsettings.json | ConvertFrom-Json).OpenApi.DocVersion
Invoke-WebRequest -Uri "https://localhost:7001/swagger/$API_VERSION/swagger.json" -OutFile "openapi.json"
diff --git a/.github/workflows/azure-dev.yml b/.github/workflows/azure-dev.yml
index 7500eda5..7f7b121b 100644
--- a/.github/workflows/azure-dev.yml
+++ b/.github/workflows/azure-dev.yml
@@ -97,8 +97,7 @@ jobs:
- name: Create openapi.json
shell: pwsh
run: |
- $fileContent = Get-Content './src/AzureOpenAIProxy.ApiApp/Constants.cs'
- $API_VERSION = [regex]::Match($fileContent, 'public const string Version = "([^"]+)"').Groups[1].Value
+ $API_VERSION = $(Get-Content ./src/AzureOpenAIProxy.ApiApp/appsettings.json | ConvertFrom-Json).OpenApi.DocVersion
Invoke-WebRequest -Uri "https://localhost:7001/swagger/$API_VERSION/swagger.json" -OutFile "openapi.json"
diff --git a/src/AzureOpenAIProxy.ApiApp/Configurations/OpenApiSettings.cs b/src/AzureOpenAIProxy.ApiApp/Configurations/OpenApiSettings.cs
new file mode 100644
index 00000000..36ae9ecd
--- /dev/null
+++ b/src/AzureOpenAIProxy.ApiApp/Configurations/OpenApiSettings.cs
@@ -0,0 +1,17 @@
+namespace AzureOpenAIProxy.ApiApp.Configurations;
+
+///
+/// This represents the settings entity for Open API.
+///
+public class OpenApiSettings
+{
+ ///
+ /// Gets the name of the configuration settings.
+ ///
+ public const string Name = "OpenApi";
+
+ ///
+ /// Gets or sets the Open API Doc version.
+ ///
+ public string? DocVersion { get; set; } = string.Empty;
+}
\ No newline at end of file
diff --git a/src/AzureOpenAIProxy.ApiApp/Constants.cs b/src/AzureOpenAIProxy.ApiApp/Constants.cs
index a133b166..ab6f5722 100644
--- a/src/AzureOpenAIProxy.ApiApp/Constants.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Constants.cs
@@ -5,11 +5,6 @@
///
public static class Constants
{
- ///
- /// Declares the current version of the API.
- ///
- public const string Version = "v1.0.0";
-
///
/// Declares the title of the OpenAPI doc.
///
diff --git a/src/AzureOpenAIProxy.ApiApp/Extensions/ApplicationBuilderExtensions.cs b/src/AzureOpenAIProxy.ApiApp/Extensions/ApplicationBuilderExtensions.cs
index 390767c1..b24e4943 100644
--- a/src/AzureOpenAIProxy.ApiApp/Extensions/ApplicationBuilderExtensions.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Extensions/ApplicationBuilderExtensions.cs
@@ -18,6 +18,8 @@ public static IApplicationBuilder UseSwaggerUI(this WebApplication app, string b
return app;
}
+ var settings = app.Services.GetOpenApiSettings();
+
app.UseSwagger(options =>
{
//options.RouteTemplate = $"swagger/{Constants.Version}/swagger.json";
@@ -32,7 +34,7 @@ public static IApplicationBuilder UseSwaggerUI(this WebApplication app, string b
app.UseSwaggerUI(options =>
{
- options.SwaggerEndpoint($"{Constants.Version}/swagger.json", Constants.Title);
+ options.SwaggerEndpoint($"{settings.DocVersion}/swagger.json", Constants.Title);
});
return app;
diff --git a/src/AzureOpenAIProxy.ApiApp/Extensions/OpenApiSettingsExtensions.cs b/src/AzureOpenAIProxy.ApiApp/Extensions/OpenApiSettingsExtensions.cs
new file mode 100644
index 00000000..ef8a7877
--- /dev/null
+++ b/src/AzureOpenAIProxy.ApiApp/Extensions/OpenApiSettingsExtensions.cs
@@ -0,0 +1,37 @@
+using AzureOpenAIProxy.ApiApp.Configurations;
+
+namespace AzureOpenAIProxy.ApiApp.Extensions;
+
+///
+/// This represents the extension entity for the class.
+///
+public static class OpenApiSettingsExtensions
+{
+ ///
+ /// Gets the OpenApi configuration settings by reading appsettings.json.
+ ///
+ /// instance.
+ /// Returns instance.
+ public static OpenApiSettings GetOpenApiSettings(this IServiceProvider serviceProvider)
+ {
+ var configuration = serviceProvider.GetService()
+ ?? throw new InvalidOperationException($"{nameof(IConfiguration)} service is not registered.");
+
+ var settings = configuration.GetSection(OpenApiSettings.Name).Get()
+ ?? throw new InvalidOperationException($"{nameof(OpenApiSettings)} could not be retrieved from the configuration.");
+
+ return settings;
+ }
+
+ ///
+ /// Gets the OpenApi configuration settings by reading appsettings.json.
+ ///
+ /// instance.
+ /// Returns instance.
+ public static OpenApiSettings GetOpenApiSettings(this IServiceCollection services)
+ {
+ var serviceProvider = services.BuildServiceProvider();
+
+ return serviceProvider.GetOpenApiSettings();
+ }
+}
\ No newline at end of file
diff --git a/src/AzureOpenAIProxy.ApiApp/Extensions/ServiceCollectionExtensions.cs b/src/AzureOpenAIProxy.ApiApp/Extensions/ServiceCollectionExtensions.cs
index c5ad44fe..ae2971bb 100644
--- a/src/AzureOpenAIProxy.ApiApp/Extensions/ServiceCollectionExtensions.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Extensions/ServiceCollectionExtensions.cs
@@ -1,4 +1,4 @@
-using Azure.Identity;
+using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
using AzureOpenAIProxy.ApiApp.Builders;
@@ -88,13 +88,15 @@ public static IServiceCollection AddOpenAIService(this IServiceCollection servic
/// Returns instance.
public static IServiceCollection AddOpenApiService(this IServiceCollection services)
{
+ var settings = services.GetOpenApiSettings();
+
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
services.AddEndpointsApiExplorer();
services.AddSwaggerGen(options =>
{
var info = new OpenApiInfo()
{
- Version = Constants.Version,
+ Version = settings.DocVersion,
Title = Constants.Title,
Description = "Providing a proxy service to Azure OpenAI API",
Contact = new OpenApiContact()
@@ -104,7 +106,7 @@ public static IServiceCollection AddOpenApiService(this IServiceCollection servi
Url = new Uri("https://aka.ms/aoai-proxy.net")
},
};
- options.SwaggerDoc(Constants.Version, info);
+ options.SwaggerDoc(settings.DocVersion, info);
options.AddSecurityDefinition(
"apiKey",
@@ -132,4 +134,4 @@ public static IServiceCollection AddOpenApiService(this IServiceCollection servi
return services;
}
-}
\ No newline at end of file
+}
diff --git a/test/AzureOpenAIProxy.ApiApp.Tests/Extensions/OpenApiSettingsExtensionsTests.cs b/test/AzureOpenAIProxy.ApiApp.Tests/Extensions/OpenApiSettingsExtensionsTests.cs
new file mode 100644
index 00000000..4cf01827
--- /dev/null
+++ b/test/AzureOpenAIProxy.ApiApp.Tests/Extensions/OpenApiSettingsExtensionsTests.cs
@@ -0,0 +1,121 @@
+using AzureOpenAIProxy.ApiApp.Extensions;
+
+using FluentAssertions;
+
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+using NSubstitute;
+
+namespace AzureOpenAIProxy.ApiApp.Tests.Extensions;
+
+public class OpenApiSettingsExtensionsTests
+{
+ [Fact]
+ public void Given_Null_OpenApiSettings_When_Added_ToServiceProvider_Then_It_Should_Throw_Exception()
+ {
+ // Arrange
+ var config = default(IConfiguration);
+
+ var sp = Substitute.For();
+ ServiceProviderServiceExtensions.GetService(sp).Returns(config);
+
+ // Act
+ Action action = () => sp.GetOpenApiSettings();
+
+ // Assert
+ action.Should().Throw();
+ }
+
+ [Fact]
+ public void Given_Empty_OpenApiSettings_When_Added_ToServiceProvider_Then_It_Should_Throw_Exception()
+ {
+ // Arrange
+ var dict = new Dictionary()
+ {
+ { "OpenApi", "" }
+ };
+#pragma warning disable CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.
+ var config = new ConfigurationBuilder().AddInMemoryCollection(dict).Build();
+#pragma warning restore CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.
+
+ var sp = Substitute.For();
+ ServiceProviderServiceExtensions.GetService(sp).Returns(config);
+
+ // Act
+ Action action = () => sp.GetOpenApiSettings();
+
+ // Assert
+ action.Should().Throw();
+ }
+
+ [Fact]
+ public void Given_Empty_OpenApiSettings_When_Added_ToServiceCollection_Then_It_Should_Throw_Exception()
+ {
+ // Arrange
+ var dict = new Dictionary()
+ {
+ { "OpenApi", "" }
+ };
+#pragma warning disable CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.
+ var config = new ConfigurationBuilder().AddInMemoryCollection(dict).Build();
+#pragma warning restore CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.
+
+ var sc = new ServiceCollection();
+ sc.AddSingleton(config);
+
+ // Act
+ Action action = () => sc.GetOpenApiSettings();
+
+ // Assert
+ action.Should().Throw();
+ }
+
+ [Theory]
+ [InlineData("")]
+ [InlineData("v1.0.0")]
+ public void Given_OpenApiSettings_When_Added_ToServiceProvider_Then_It_Should_Return_DocVersion(string docVersion)
+ {
+ // Arrange
+ var dict = new Dictionary()
+ {
+ { "OpenApi:DocVersion", docVersion }
+ };
+#pragma warning disable CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.
+ var config = new ConfigurationBuilder().AddInMemoryCollection(dict).Build();
+#pragma warning restore CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.
+
+ var sp = Substitute.For();
+ ServiceProviderServiceExtensions.GetService(sp).Returns(config);
+
+ // Act
+ var result = sp.GetOpenApiSettings();
+
+ // Assert
+ result.DocVersion.Should().Be(docVersion);
+ }
+
+ [Theory]
+ [InlineData("")]
+ [InlineData("v1.0.0")]
+ public void Given_OpenApiSettings_When_Added_ToServiceCollection_Then_It_Should_Return_DocVersion(string docVersion)
+ {
+ // Arrange
+ var dict = new Dictionary()
+ {
+ { "OpenApi:DocVersion", docVersion }
+ };
+#pragma warning disable CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.
+ var config = new ConfigurationBuilder().AddInMemoryCollection(dict).Build();
+#pragma warning restore CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.
+
+ var sc = new ServiceCollection();
+ sc.AddSingleton(config);
+
+ // Act
+ var result = sc.GetOpenApiSettings();
+
+ // Assert
+ result.DocVersion.Should().Be(docVersion);
+ }
+}
\ No newline at end of file
From 0d15c4a2c4a29d2dc1e7607376fe464b60c9eec9 Mon Sep 17 00:00:00 2001
From: Justin Yoo
Date: Sat, 31 Aug 2024 18:28:34 +0900
Subject: [PATCH 03/13] Update directory structure and file naming (#282)
---
.../Endpoints/AdminEndpointUrls.cs | 11 +-
.../Endpoints/AdminEventEndpoints.cs | 76 +++++-----
.../Endpoints/PlaygroundEndpointUrls.cs | 15 ++
...ventEndpoint.cs => PlaygroundEndpoints.cs} | 10 +-
...int.cs => ProxyChatCompletionsEndpoint.cs} | 6 +-
.../{EndpointUrls.cs => ProxyEndpointUrls.cs} | 9 +-
.../Endpoints/WeatherForecastEndpoint.cs | 2 +-
.../Extensions/HttpRequestExtensions.cs | 2 +-
src/AzureOpenAIProxy.ApiApp/Program.cs | 16 +-
.../PlaygroundPageTests.cs} | 38 ++---
.../PlaygroundPageTests.cs} | 140 +++++++++---------
11 files changed, 170 insertions(+), 155 deletions(-)
create mode 100644 src/AzureOpenAIProxy.ApiApp/Endpoints/PlaygroundEndpointUrls.cs
rename src/AzureOpenAIProxy.ApiApp/Endpoints/{EventEndpoint.cs => PlaygroundEndpoints.cs} (83%)
rename src/AzureOpenAIProxy.ApiApp/Endpoints/{ChatCompletionsEndpoint.cs => ProxyChatCompletionsEndpoint.cs} (92%)
rename src/AzureOpenAIProxy.ApiApp/Endpoints/{EndpointUrls.cs => ProxyEndpointUrls.cs} (66%)
rename test/AzureOpenAIProxy.AppHost.Tests/PlaygroundApp/{Components/Pages/PlaygroundPageTest.cs => Pages/PlaygroundPageTests.cs} (75%)
rename test/AzureOpenAIProxy.PlaygroundApp.Tests/{UI/ConfigTabComponentTest.cs => Pages/PlaygroundPageTests.cs} (86%)
diff --git a/src/AzureOpenAIProxy.ApiApp/Endpoints/AdminEndpointUrls.cs b/src/AzureOpenAIProxy.ApiApp/Endpoints/AdminEndpointUrls.cs
index 82f89d60..0fdba526 100644
--- a/src/AzureOpenAIProxy.ApiApp/Endpoints/AdminEndpointUrls.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Endpoints/AdminEndpointUrls.cs
@@ -1,18 +1,25 @@
namespace AzureOpenAIProxy.ApiApp.Endpoints;
+///
+/// This represents the collection of the admin endpoint URLs.
+///
public static class AdminEndpointUrls
{
///
/// Declares the admin event details endpoint.
///
+ ///
+ /// - GET method for an event details
+ /// - PUT method for update an event details
+ ///
public const string AdminEventDetails = "/admin/events/{eventId}";
///
/// Declares the admin event list endpoint.
///
///
- /// - Get method for listing all events
- /// - Post method for new event creation
+ /// - GET method for listing all events
+ /// - POST method for new event creation
///
public const string AdminEvents = "/admin/events";
}
\ No newline at end of file
diff --git a/src/AzureOpenAIProxy.ApiApp/Endpoints/AdminEventEndpoints.cs b/src/AzureOpenAIProxy.ApiApp/Endpoints/AdminEventEndpoints.cs
index c319e491..0cde354b 100644
--- a/src/AzureOpenAIProxy.ApiApp/Endpoints/AdminEventEndpoints.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Endpoints/AdminEventEndpoints.cs
@@ -5,35 +5,36 @@
namespace AzureOpenAIProxy.ApiApp.Endpoints;
///
-/// This represents the endpoint entity for get event details by admin
+/// This represents the endpoint entity for event details by admin
///
public static class AdminEventEndpoints
{
///
- /// Adds the get event details by admin endpoint
+ /// Adds the admin event endpoint
///
/// instance.
/// Returns instance.
- public static RouteHandlerBuilder AddAdminEvents(this WebApplication app)
+ public static RouteHandlerBuilder AddNewAdminEvent(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.MapPost(AdminEndpointUrls.AdminEvents, async (
+ [FromBody] AdminEventDetails payload,
+ HttpRequest request) =>
{
- // Todo: Issue #208 https://github.com/aliencube/azure-openai-sdk-proxy/issues/208
- return Results.Ok();
- // Todo: Issue #208
+ return await Task.FromResult(Results.Ok());
})
+ // TODO: Check both request/response payloads
+ .Accepts(contentType: "application/json")
.Produces(statusCode: StatusCodes.Status200OK, contentType: "application/json")
+ // TODO: Check both request/response payloads
+ .Produces(statusCode: StatusCodes.Status400BadRequest)
.Produces(statusCode: StatusCodes.Status401Unauthorized)
.Produces(statusCode: StatusCodes.Status500InternalServerError, contentType: "text/plain")
.WithTags("admin")
- .WithName("GetAdminEventDetails")
+ .WithName("CreateAdminEvent")
.WithOpenApi(operation =>
{
- operation.Summary = "Gets event details from the given event ID";
- operation.Description = "This endpoint gets the event details from the given event ID.";
+ operation.Summary = "Create admin event";
+ operation.Description = "Create admin event";
return operation;
});
@@ -46,7 +47,7 @@ public static RouteHandlerBuilder AddAdminEvents(this WebApplication app)
///
/// instance.
/// Returns instance.
- public static RouteHandlerBuilder AddAdminEventList(this WebApplication app)
+ public static RouteHandlerBuilder AddListAdminEvents(this WebApplication app)
{
// Todo: Issue #19 https://github.com/aliencube/azure-openai-sdk-proxy/issues/19
// Need authorization by admin
@@ -73,65 +74,64 @@ public static RouteHandlerBuilder AddAdminEventList(this WebApplication app)
}
///
- /// Adds the update event details by admin endpoint
+ /// Adds the get event details by admin endpoint
///
/// instance.
/// Returns instance.
- public static RouteHandlerBuilder AddUpdateAdminEvents(this WebApplication app)
+ 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.MapPut(AdminEndpointUrls.AdminEventDetails, (
- [FromRoute] string eventId,
- [FromBody] AdminEventDetails payload) =>
+ var builder = app.MapGet(AdminEndpointUrls.AdminEventDetails, (
+ [FromRoute] string eventId) =>
{
- // Todo: Issue #203 https://github.com/aliencube/azure-openai-sdk-proxy/issues/203
+ // Todo: Issue #208 https://github.com/aliencube/azure-openai-sdk-proxy/issues/208
return Results.Ok();
+ // Todo: Issue #208
})
- .Accepts(contentType: "application/json")
.Produces(statusCode: StatusCodes.Status200OK, contentType: "application/json")
.Produces(statusCode: StatusCodes.Status401Unauthorized)
- .Produces(statusCode: StatusCodes.Status404NotFound)
.Produces(statusCode: StatusCodes.Status500InternalServerError, contentType: "text/plain")
.WithTags("admin")
- .WithName("UpdateAdminEventDetails")
+ .WithName("GetAdminEvent")
.WithOpenApi(operation =>
{
- operation.Summary = "Updates event details from the given event ID";
- operation.Description = "This endpoint updates the event details from the given event ID.";
+ operation.Summary = "Gets event details from the given event ID";
+ operation.Description = "This endpoint gets the event details from the given event ID.";
return operation;
});
return builder;
}
-
+
///
- /// Adds the admin event endpoint
+ /// Adds the update event details by admin endpoint
///
/// instance.
/// Returns instance.
- public static RouteHandlerBuilder CreateAdminEvent(this WebApplication app)
+ public static RouteHandlerBuilder AddUpdateAdminEvent(this WebApplication app)
{
- var builder = app.MapPost(AdminEndpointUrls.AdminEvents, async (
- [FromBody] AdminEventDetails payload,
- HttpRequest request) =>
+ // Todo: Issue #19 https://github.com/aliencube/azure-openai-sdk-proxy/issues/19
+ // Need authorization by admin
+ var builder = app.MapPut(AdminEndpointUrls.AdminEventDetails, (
+ [FromRoute] string eventId,
+ [FromBody] AdminEventDetails payload) =>
{
- return await Task.FromResult(Results.Ok());
+ // Todo: Issue #203 https://github.com/aliencube/azure-openai-sdk-proxy/issues/203
+ return Results.Ok();
})
- // TODO: Check both request/response payloads
.Accepts(contentType: "application/json")
.Produces(statusCode: StatusCodes.Status200OK, contentType: "application/json")
- // TODO: Check both request/response payloads
- .Produces(statusCode: StatusCodes.Status400BadRequest)
.Produces(statusCode: StatusCodes.Status401Unauthorized)
+ .Produces(statusCode: StatusCodes.Status404NotFound)
.Produces(statusCode: StatusCodes.Status500InternalServerError, contentType: "text/plain")
.WithTags("admin")
- .WithName("CreateAdminEvent")
+ .WithName("UpdateAdminEvent")
.WithOpenApi(operation =>
{
- operation.Summary = "Create admin event";
- operation.Description = "Create admin event";
+ operation.Summary = "Updates event details from the given event ID";
+ operation.Description = "This endpoint updates the event details from the given event ID.";
return operation;
});
diff --git a/src/AzureOpenAIProxy.ApiApp/Endpoints/PlaygroundEndpointUrls.cs b/src/AzureOpenAIProxy.ApiApp/Endpoints/PlaygroundEndpointUrls.cs
new file mode 100644
index 00000000..b8ccbd2b
--- /dev/null
+++ b/src/AzureOpenAIProxy.ApiApp/Endpoints/PlaygroundEndpointUrls.cs
@@ -0,0 +1,15 @@
+namespace AzureOpenAIProxy.ApiApp.Endpoints;
+
+///
+/// This represents the collection of the playground endpoint URLs.
+///
+public static class PlaygroundEndpointUrls
+{
+ ///
+ /// Declares the event endpoint.
+ ///
+ ///
+ /// - GET method for listing all events
+ ///
+ public const string Events = "/events";
+}
diff --git a/src/AzureOpenAIProxy.ApiApp/Endpoints/EventEndpoint.cs b/src/AzureOpenAIProxy.ApiApp/Endpoints/PlaygroundEndpoints.cs
similarity index 83%
rename from src/AzureOpenAIProxy.ApiApp/Endpoints/EventEndpoint.cs
rename to src/AzureOpenAIProxy.ApiApp/Endpoints/PlaygroundEndpoints.cs
index a82efb31..c21d6029 100644
--- a/src/AzureOpenAIProxy.ApiApp/Endpoints/EventEndpoint.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Endpoints/PlaygroundEndpoints.cs
@@ -1,22 +1,18 @@
-using System.Text.Json;
-
-using AzureOpenAIProxy.ApiApp.Models;
-
namespace AzureOpenAIProxy.ApiApp.Endpoints;
///
/// This represents the endpoint entity for events that the logged user joined.
///
-public static class EventEndpoint
+public static class PlaygroundEndpoints
{
///
/// Adds the event endpoint.
///
/// instance.
/// Returns instance.
- public static RouteHandlerBuilder AddEventList(this WebApplication app)
+ public static RouteHandlerBuilder AddListEvents(this WebApplication app)
{
- var builder = app.MapGet(EndpointUrls.Events, () =>
+ var builder = app.MapGet(PlaygroundEndpointUrls.Events, () =>
{
// TODO: Issue #179 https://github.com/aliencube/azure-openai-sdk-proxy/issues/179
return Results.Ok();
diff --git a/src/AzureOpenAIProxy.ApiApp/Endpoints/ChatCompletionsEndpoint.cs b/src/AzureOpenAIProxy.ApiApp/Endpoints/ProxyChatCompletionsEndpoint.cs
similarity index 92%
rename from src/AzureOpenAIProxy.ApiApp/Endpoints/ChatCompletionsEndpoint.cs
rename to src/AzureOpenAIProxy.ApiApp/Endpoints/ProxyChatCompletionsEndpoint.cs
index 72d7572e..a758150f 100644
--- a/src/AzureOpenAIProxy.ApiApp/Endpoints/ChatCompletionsEndpoint.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Endpoints/ProxyChatCompletionsEndpoint.cs
@@ -12,7 +12,7 @@ namespace AzureOpenAIProxy.ApiApp.Endpoints;
///
/// This represents the endpoint entity for chat completions.
///
-public static class ChatCompletionsEndpoint
+public static class ProxyChatCompletionsEndpoint
{
///
/// Adds the chat completion endpoint.
@@ -21,7 +21,7 @@ public static class ChatCompletionsEndpoint
/// Returns instance.
public static RouteHandlerBuilder AddChatCompletions(this WebApplication app)
{
- var builder = app.MapPost(EndpointUrls.ChatCompletions, async (
+ var builder = app.MapPost(ProxyEndpointUrls.ChatCompletions, async (
[OpenApiParameterIgnore][FromHeader(Name = "api-key")] string apiKey,
[FromRoute] string deploymentName,
[FromQuery(Name = "api-version")] string apiVersion,
@@ -30,7 +30,7 @@ public static RouteHandlerBuilder AddChatCompletions(this WebApplication app)
IOpenAIService openai,
ILoggerFactory loggerFactory) =>
{
- var logger = loggerFactory.CreateLogger(nameof(ChatCompletionsEndpoint));
+ var logger = loggerFactory.CreateLogger(nameof(ProxyChatCompletionsEndpoint));
logger.LogInformation("Received a chat completion request");
request.Body.Position = 0;
diff --git a/src/AzureOpenAIProxy.ApiApp/Endpoints/EndpointUrls.cs b/src/AzureOpenAIProxy.ApiApp/Endpoints/ProxyEndpointUrls.cs
similarity index 66%
rename from src/AzureOpenAIProxy.ApiApp/Endpoints/EndpointUrls.cs
rename to src/AzureOpenAIProxy.ApiApp/Endpoints/ProxyEndpointUrls.cs
index b69068eb..0808fc42 100644
--- a/src/AzureOpenAIProxy.ApiApp/Endpoints/EndpointUrls.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Endpoints/ProxyEndpointUrls.cs
@@ -1,9 +1,9 @@
namespace AzureOpenAIProxy.ApiApp.Endpoints;
///
-/// This represents the collection of the endpoint URLs.
+/// This represents the collection of the proxy endpoint URLs.
///
-public static class EndpointUrls
+public static class ProxyEndpointUrls
{
///
/// Declares the weather forecast endpoint.
@@ -14,9 +14,4 @@ public static class EndpointUrls
/// Declares the chat completions endpoint.
///
public const string ChatCompletions = "/openai/deployments/{deploymentName}/chat/completions";
-
- ///
- /// Declares the event endpoint.
- ///
- public const string Events = "/events";
}
diff --git a/src/AzureOpenAIProxy.ApiApp/Endpoints/WeatherForecastEndpoint.cs b/src/AzureOpenAIProxy.ApiApp/Endpoints/WeatherForecastEndpoint.cs
index 8656c921..7e87fed0 100644
--- a/src/AzureOpenAIProxy.ApiApp/Endpoints/WeatherForecastEndpoint.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Endpoints/WeatherForecastEndpoint.cs
@@ -19,7 +19,7 @@ public static class WeatherForecastEndpoint
/// Returns instance.
public static RouteHandlerBuilder AddWeatherForecast(this WebApplication app)
{
- var builder = app.MapGet(EndpointUrls.WeatherForecast, () =>
+ var builder = app.MapGet(ProxyEndpointUrls.WeatherForecast, () =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
diff --git a/src/AzureOpenAIProxy.ApiApp/Extensions/HttpRequestExtensions.cs b/src/AzureOpenAIProxy.ApiApp/Extensions/HttpRequestExtensions.cs
index d3cfa52b..951f6841 100644
--- a/src/AzureOpenAIProxy.ApiApp/Extensions/HttpRequestExtensions.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Extensions/HttpRequestExtensions.cs
@@ -10,7 +10,7 @@ public static class HttpRequestExtensions
/// Gets the base URL.
///
/// instance.
- ///
+ /// Returns the base URL from .
public static string? BaseUrl(this HttpRequest req)
{
if (req == null) return null;
diff --git a/src/AzureOpenAIProxy.ApiApp/Program.cs b/src/AzureOpenAIProxy.ApiApp/Program.cs
index e64fbddf..5ce6fbe0 100644
--- a/src/AzureOpenAIProxy.ApiApp/Program.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Program.cs
@@ -37,15 +37,17 @@
app.UseHttpsRedirection();
app.AddWeatherForecast();
+
+// Proxy endpoints
app.AddChatCompletions();
-// Event Endpoints
-app.AddEventList();
+// Playground endpoints
+app.AddListEvents();
-// Admin Endpoints
-app.AddAdminEvents();
-app.AddAdminEventList();
-app.AddUpdateAdminEvents();
-app.CreateAdminEvent();
+// Admin endpoints
+app.AddNewAdminEvent();
+app.AddListAdminEvents();
+app.AddGetAdminEvent();
+app.AddUpdateAdminEvent();
await app.RunAsync();
diff --git a/test/AzureOpenAIProxy.AppHost.Tests/PlaygroundApp/Components/Pages/PlaygroundPageTest.cs b/test/AzureOpenAIProxy.AppHost.Tests/PlaygroundApp/Pages/PlaygroundPageTests.cs
similarity index 75%
rename from test/AzureOpenAIProxy.AppHost.Tests/PlaygroundApp/Components/Pages/PlaygroundPageTest.cs
rename to test/AzureOpenAIProxy.AppHost.Tests/PlaygroundApp/Pages/PlaygroundPageTests.cs
index 16b905bf..05b1b953 100644
--- a/test/AzureOpenAIProxy.AppHost.Tests/PlaygroundApp/Components/Pages/PlaygroundPageTest.cs
+++ b/test/AzureOpenAIProxy.AppHost.Tests/PlaygroundApp/Pages/PlaygroundPageTests.cs
@@ -1,20 +1,20 @@
-using AzureOpenAIProxy.AppHost.Tests.Fixtures;
-
-namespace AzureOpenAIProxy.AppHost.Tests.PlaygroundApp.Components.Pages;
-
-public class PlaygroundPageTest(AspireAppHostFixture host) : IClassFixture
-{
- [Fact]
- public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_OK()
- {
- // Arrange
- var httpClient = host.App!.CreateHttpClient("playgroundapp");
- await host.ResourceNotificationService.WaitForResourceAsync("playgroundapp", KnownResourceStates.Running).WaitAsync(TimeSpan.FromSeconds(30));
-
- // Act
- var response = await httpClient.GetAsync("/playground");
-
- // Assert
- response.EnsureSuccessStatusCode(); // Status Code 200-299
- }
+using AzureOpenAIProxy.AppHost.Tests.Fixtures;
+
+namespace AzureOpenAIProxy.AppHost.Tests.PlaygroundApp.Pages;
+
+public class PlaygroundPageTests(AspireAppHostFixture host) : IClassFixture
+{
+ [Fact]
+ public async Task Given_Resource_When_Invoked_Endpoint_Then_It_Should_Return_OK()
+ {
+ // Arrange
+ var httpClient = host.App!.CreateHttpClient("playgroundapp");
+ await host.ResourceNotificationService.WaitForResourceAsync("playgroundapp", KnownResourceStates.Running).WaitAsync(TimeSpan.FromSeconds(30));
+
+ // Act
+ var response = await httpClient.GetAsync("/playground");
+
+ // Assert
+ response.EnsureSuccessStatusCode(); // Status Code 200-299
+ }
}
\ No newline at end of file
diff --git a/test/AzureOpenAIProxy.PlaygroundApp.Tests/UI/ConfigTabComponentTest.cs b/test/AzureOpenAIProxy.PlaygroundApp.Tests/Pages/PlaygroundPageTests.cs
similarity index 86%
rename from test/AzureOpenAIProxy.PlaygroundApp.Tests/UI/ConfigTabComponentTest.cs
rename to test/AzureOpenAIProxy.PlaygroundApp.Tests/Pages/PlaygroundPageTests.cs
index fbb98d36..7b012f98 100644
--- a/test/AzureOpenAIProxy.PlaygroundApp.Tests/UI/ConfigTabComponentTest.cs
+++ b/test/AzureOpenAIProxy.PlaygroundApp.Tests/Pages/PlaygroundPageTests.cs
@@ -1,71 +1,71 @@
-using Microsoft.Playwright;
-using Microsoft.Playwright.NUnit;
-
-namespace AzureOpenAIProxy.PlaygroundApp.Tests.UI;
-
-[Parallelizable(ParallelScope.Self)]
-[TestFixture]
-[Property("Category", "Integration")]
-public class ConfigTabComponentTest : PageTest
-{
- public override BrowserNewContextOptions ContextOptions() => new() { IgnoreHTTPSErrors = true, };
-
- [SetUp]
- public async Task SetUp()
- {
- await Page.GotoAsync("https://localhost:5001/playground/");
- await Page.WaitForLoadStateAsync(LoadState.NetworkIdle);
- }
-
- [Test]
- public async Task Given_ConfigTab_When_Endpoint_Invoked_Then_ConfigTab_Should_Be_Displayed()
- {
- // Act
- var configTab = Page.Locator("fluent-tabs#config-tab");
-
- // Assert
- await Expect(configTab).ToBeVisibleAsync();
- }
-
- [Test]
- public async Task Given_ConfigTab_When_Endpoint_Invoked_Then_Id_Should_Be_System_Message_Tab()
- {
- // Act
- var sysMsgPanel = Page.Locator("fluent-tab-panel#system-message-tab-panel");
- var parameterPanel = Page.Locator("fluent-tab-panel#parameters-tab-panel");
-
- // Assert
- await Expect(sysMsgPanel).ToBeVisibleAsync();
- await Expect(parameterPanel).ToBeHiddenAsync();
- }
-
- [Test]
- [TestCase(
- "fluent-tab#parameters-tab",
- "fluent-tab-panel#parameters-tab-panel",
- "fluent-tab-panel#system-message-tab-panel"
- )]
- [TestCase(
- "fluent-tab#system-message-tab",
- "fluent-tab-panel#system-message-tab-panel",
- "fluent-tab-panel#parameters-tab-panel"
- )]
- public async Task Given_ConfigTab_When_Changed_Then_Tab_Should_Be_Updated(
- string selectedTabSelector,
- string selectedPanelSelector,
- string hiddenPanelSelector
- )
- {
- // Arrange
- var selectedTab = Page.Locator(selectedTabSelector);
- var selectedPanel = Page.Locator(selectedPanelSelector);
- var hiddenPanel = Page.Locator(hiddenPanelSelector);
-
- // Act
- await selectedTab.ClickAsync();
-
- // Assert
- await Expect(selectedPanel).ToBeVisibleAsync();
- await Expect(hiddenPanel).ToBeHiddenAsync();
- }
+using Microsoft.Playwright;
+using Microsoft.Playwright.NUnit;
+
+namespace AzureOpenAIProxy.PlaygroundApp.Tests.Pages;
+
+[Parallelizable(ParallelScope.Self)]
+[TestFixture]
+[Property("Category", "Integration")]
+public class PlaygroundPageTests : PageTest
+{
+ public override BrowserNewContextOptions ContextOptions() => new() { IgnoreHTTPSErrors = true, };
+
+ [SetUp]
+ public async Task SetUp()
+ {
+ await Page.GotoAsync("https://localhost:5001/playground/");
+ await Page.WaitForLoadStateAsync(LoadState.NetworkIdle);
+ }
+
+ [Test]
+ public async Task Given_ConfigTab_When_Endpoint_Invoked_Then_ConfigTab_Should_Be_Displayed()
+ {
+ // Act
+ var configTab = Page.Locator("fluent-tabs#config-tab");
+
+ // Assert
+ await Expect(configTab).ToBeVisibleAsync();
+ }
+
+ [Test]
+ public async Task Given_ConfigTab_When_Endpoint_Invoked_Then_Id_Should_Be_System_Message_Tab()
+ {
+ // Act
+ var systemMessagePanel = Page.Locator("fluent-tab-panel#system-message-tab-panel");
+ var parameterPanel = Page.Locator("fluent-tab-panel#parameters-tab-panel");
+
+ // Assert
+ await Expect(systemMessagePanel).ToBeVisibleAsync();
+ await Expect(parameterPanel).ToBeHiddenAsync();
+ }
+
+ [Test]
+ [TestCase(
+ "fluent-tab#parameters-tab",
+ "fluent-tab-panel#parameters-tab-panel",
+ "fluent-tab-panel#system-message-tab-panel"
+ )]
+ [TestCase(
+ "fluent-tab#system-message-tab",
+ "fluent-tab-panel#system-message-tab-panel",
+ "fluent-tab-panel#parameters-tab-panel"
+ )]
+ public async Task Given_ConfigTab_When_Changed_Then_Tab_Should_Be_Updated(
+ string selectedTabSelector,
+ string selectedPanelSelector,
+ string hiddenPanelSelector
+ )
+ {
+ // Arrange
+ var selectedTab = Page.Locator(selectedTabSelector);
+ var selectedPanel = Page.Locator(selectedPanelSelector);
+ var hiddenPanel = Page.Locator(hiddenPanelSelector);
+
+ // Act
+ await selectedTab.ClickAsync();
+
+ // Assert
+ await Expect(selectedPanel).ToBeVisibleAsync();
+ await Expect(hiddenPanel).ToBeHiddenAsync();
+ }
}
\ No newline at end of file
From 25e963097b3f8dababd208259b8e66d96c66b09f Mon Sep 17 00:00:00 2001
From: o-ii <148066227+o-ii@users.noreply.github.com>
Date: Sat, 31 Aug 2024 22:21:51 +0900
Subject: [PATCH 04/13] [Playground] Component - button to display a given
input #171 (#236)
---
.../Components/Layout/MainLayout.razor | 4 +-
.../Components/Pages/Tests.razor | 14 ++++++
.../Components/UI/DebugButtonComponent.razor | 22 +++++++++
.../Components/UI/DebugTargetComponent.razor | 15 ++++++
.../Pages/TestsPageTests.cs | 47 +++++++++++++++++++
5 files changed, 100 insertions(+), 2 deletions(-)
create mode 100644 src/AzureOpenAIProxy.PlaygroundApp/Components/Pages/Tests.razor
create mode 100644 src/AzureOpenAIProxy.PlaygroundApp/Components/UI/DebugButtonComponent.razor
create mode 100644 src/AzureOpenAIProxy.PlaygroundApp/Components/UI/DebugTargetComponent.razor
create mode 100644 test/AzureOpenAIProxy.PlaygroundApp.Tests/Pages/TestsPageTests.cs
diff --git a/src/AzureOpenAIProxy.PlaygroundApp/Components/Layout/MainLayout.razor b/src/AzureOpenAIProxy.PlaygroundApp/Components/Layout/MainLayout.razor
index 1bad0f45..56bf7fcf 100644
--- a/src/AzureOpenAIProxy.PlaygroundApp/Components/Layout/MainLayout.razor
+++ b/src/AzureOpenAIProxy.PlaygroundApp/Components/Layout/MainLayout.razor
@@ -21,7 +21,7 @@
🗙
-
+
-
+
\ No newline at end of file
diff --git a/src/AzureOpenAIProxy.PlaygroundApp/Components/Pages/Tests.razor b/src/AzureOpenAIProxy.PlaygroundApp/Components/Pages/Tests.razor
new file mode 100644
index 00000000..0929588c
--- /dev/null
+++ b/src/AzureOpenAIProxy.PlaygroundApp/Components/Pages/Tests.razor
@@ -0,0 +1,14 @@
+@page "/tests"
+@rendermode InteractiveServer
+
+
+
+
+@code {
+ private object? currentValue;
+
+ private void SetInput(int newValue)
+ {
+ currentValue = newValue;
+ }
+}
\ No newline at end of file
diff --git a/src/AzureOpenAIProxy.PlaygroundApp/Components/UI/DebugButtonComponent.razor b/src/AzureOpenAIProxy.PlaygroundApp/Components/UI/DebugButtonComponent.razor
new file mode 100644
index 00000000..93abbb75
--- /dev/null
+++ b/src/AzureOpenAIProxy.PlaygroundApp/Components/UI/DebugButtonComponent.razor
@@ -0,0 +1,22 @@
+@inject IToastService ToastService
+
+
+Debug
+
+@code {
+ [Parameter]
+ public object? Input { get; set; }
+
+ private async Task ShowToast()
+ {
+ if (Input is null)
+ {
+ ToastService.ShowToast(ToastIntent.Warning, "Input is null.");
+ await Task.CompletedTask;
+ return;
+ }
+
+ ToastService.ShowToast(ToastIntent.Success, $"{Input} (Type: {Input.GetType()})");
+ await Task.CompletedTask;
+ }
+}
\ No newline at end of file
diff --git a/src/AzureOpenAIProxy.PlaygroundApp/Components/UI/DebugTargetComponent.razor b/src/AzureOpenAIProxy.PlaygroundApp/Components/UI/DebugTargetComponent.razor
new file mode 100644
index 00000000..4f2c30bb
--- /dev/null
+++ b/src/AzureOpenAIProxy.PlaygroundApp/Components/UI/DebugTargetComponent.razor
@@ -0,0 +1,15 @@
+
+ 123
+ 456
+ 789
+
+
+@code {
+ [Parameter]
+ public EventCallback OnValueChanged { get; set; }
+
+ private async Task SetValue(int value)
+ {
+ await OnValueChanged.InvokeAsync(value);
+ }
+}
\ No newline at end of file
diff --git a/test/AzureOpenAIProxy.PlaygroundApp.Tests/Pages/TestsPageTests.cs b/test/AzureOpenAIProxy.PlaygroundApp.Tests/Pages/TestsPageTests.cs
new file mode 100644
index 00000000..f0b6f8c6
--- /dev/null
+++ b/test/AzureOpenAIProxy.PlaygroundApp.Tests/Pages/TestsPageTests.cs
@@ -0,0 +1,47 @@
+using Microsoft.Playwright;
+using Microsoft.Playwright.NUnit;
+
+namespace AzureOpenAIProxy.PlaygroundApp.Tests.Pages;
+
+[Parallelizable(ParallelScope.Self)]
+[TestFixture]
+[Property("Category", "Integration")]
+public class TestsPageTests : PageTest
+{
+ public override BrowserNewContextOptions ContextOptions() => new()
+ {
+ IgnoreHTTPSErrors = true,
+ };
+
+ [SetUp]
+ public async Task Setup()
+ {
+ // Arrange
+ await Page.GotoAsync("https://localhost:5001/tests");
+ await Page.WaitForLoadStateAsync(LoadState.NetworkIdle);
+ }
+
+ [Test]
+ public async Task Given_No_Input_When_DebugButton_Clicked_Then_Toast_Should_Show_NullMessage()
+ {
+ // Act
+ await Page.GetByRole(AriaRole.Button, new() { Name = "Debug" }).ClickAsync();
+
+ // Assert
+ await Expect(Page.Locator(".fluent-toast-title")).ToHaveTextAsync("Input is null.");
+ }
+
+ [Test]
+ [TestCase(123, typeof(int))]
+ [TestCase(456, typeof(int))]
+ [TestCase(789, typeof(int))]
+ public async Task Given_Input_When_DebugButton_Clicked_Then_Toast_Should_Show_Input(int inputValue, Type inputType)
+ {
+ // Act
+ await Page.GetByRole(AriaRole.Radio, new() { Name = $"{inputValue}" }).ClickAsync();
+ await Page.GetByRole(AriaRole.Button, new() { Name = "Debug" }).ClickAsync();
+
+ // Assert
+ await Expect(Page.Locator(".fluent-toast-title")).ToHaveTextAsync($"{inputValue} (Type: {inputType})");
+ }
+}
\ No newline at end of file
From bbef6f10e01831177bb0bfa448109f21700be851 Mon Sep 17 00:00:00 2001
From: Justin Yoo
Date: Sun, 1 Sep 2024 12:53:06 +0900
Subject: [PATCH 05/13] [Admin] Add admin event service layer (#286)
---
.../Endpoints/AdminEventEndpoints.cs | 31 ++++++-
.../Models/AdminEventDetails.cs | 22 +++--
.../Models/EventDetails.cs | 18 ++--
src/AzureOpenAIProxy.ApiApp/Program.cs | 4 +
.../Services/AdminEventService.cs | 85 +++++++++++++++++++
.../Services/AdminEventServiceTests.cs | 80 +++++++++++++++++
.../AppHostProgramTests.cs | 2 +-
7 files changed, 223 insertions(+), 19 deletions(-)
create mode 100644 src/AzureOpenAIProxy.ApiApp/Services/AdminEventService.cs
create mode 100644 test/AzureOpenAIProxy.ApiApp.Tests/Services/AdminEventServiceTests.cs
diff --git a/src/AzureOpenAIProxy.ApiApp/Endpoints/AdminEventEndpoints.cs b/src/AzureOpenAIProxy.ApiApp/Endpoints/AdminEventEndpoints.cs
index 0cde354b..beb5371f 100644
--- a/src/AzureOpenAIProxy.ApiApp/Endpoints/AdminEventEndpoints.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Endpoints/AdminEventEndpoints.cs
@@ -1,4 +1,5 @@
using AzureOpenAIProxy.ApiApp.Models;
+using AzureOpenAIProxy.ApiApp.Services;
using Microsoft.AspNetCore.Mvc;
@@ -18,14 +19,38 @@ public static RouteHandlerBuilder AddNewAdminEvent(this WebApplication app)
{
var builder = app.MapPost(AdminEndpointUrls.AdminEvents, async (
[FromBody] AdminEventDetails payload,
- HttpRequest request) =>
+ IAdminEventService service,
+ ILoggerFactory loggerFactory) =>
{
+ var logger = loggerFactory.CreateLogger(nameof(AdminEventEndpoints));
+ logger.LogInformation("Received a new event request");
+
+ if (payload is null)
+ {
+ logger.LogError("No payload found");
+
+ return Results.BadRequest("Payload is null");
+ }
+
+ //try
+ //{
+ // var result = await service.CreateEvent(payload);
+
+ // logger.LogInformation("Created a new event");
+
+ // return Results.Ok(result);
+ //}
+ //catch (Exception ex)
+ //{
+ // logger.LogError(ex, "Failed to create a new event");
+
+ // return Results.Problem(ex.Message, statusCode: StatusCodes.Status500InternalServerError);
+ //}
+
return await Task.FromResult(Results.Ok());
})
- // TODO: Check both request/response payloads
.Accepts(contentType: "application/json")
.Produces(statusCode: StatusCodes.Status200OK, contentType: "application/json")
- // TODO: Check both request/response payloads
.Produces(statusCode: StatusCodes.Status400BadRequest)
.Produces(statusCode: StatusCodes.Status401Unauthorized)
.Produces(statusCode: StatusCodes.Status500InternalServerError, contentType: "text/plain")
diff --git a/src/AzureOpenAIProxy.ApiApp/Models/AdminEventDetails.cs b/src/AzureOpenAIProxy.ApiApp/Models/AdminEventDetails.cs
index 4f587217..a3ccdf25 100644
--- a/src/AzureOpenAIProxy.ApiApp/Models/AdminEventDetails.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Models/AdminEventDetails.cs
@@ -1,4 +1,6 @@
-namespace AzureOpenAIProxy.ApiApp.Models;
+using System.Text.Json.Serialization;
+
+namespace AzureOpenAIProxy.ApiApp.Models;
///
/// This represent the event detail data for response by admin event endpoint.
@@ -13,32 +15,38 @@ public class AdminEventDetails : EventDetails
///
/// Gets or sets the event start date.
///
- public required DateTimeOffset? DateStart { get; set; }
+ [JsonRequired]
+ public DateTimeOffset DateStart { get; set; }
///
/// Gets or sets the event end date.
///
- public required DateTimeOffset? DateEnd { get; set; }
+ [JsonRequired]
+ public DateTimeOffset DateEnd { get; set; }
///
/// Gets or sets the event start to end date timezone.
///
- public required string? TimeZone { get; set; }
+ [JsonRequired]
+ public string TimeZone { get; set; } = string.Empty;
///
/// Gets or sets the event active status.
///
- public required bool? IsActive { get; set; }
+ [JsonRequired]
+ public bool IsActive { get; set; }
///
/// Gets or sets the event organizer name.
///
- public required string? OrganizerName { get; set; }
+ [JsonRequired]
+ public string OrganizerName { get; set; } = string.Empty;
///
/// Gets or sets the event organizer email.
///
- public required string? OrganizerEmail { get; set; }
+ [JsonRequired]
+ public string OrganizerEmail { get; set; } = string.Empty;
///
/// Gets or sets the event coorganizer name.
diff --git a/src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs b/src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs
index 5c33fafb..ae3594f2 100644
--- a/src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Models/EventDetails.cs
@@ -1,8 +1,5 @@
-using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;
-using AzureOpenAIProxy.ApiApp.Models;
-
///
/// This represents the event's detailed data for response by EventEndpoint.
///
@@ -11,25 +8,30 @@ public class EventDetails
///
/// Gets or sets the event id.
///
- public required string? EventId { get; set; }
+ [JsonRequired]
+ public Guid EventId { get; set; }
///
/// Gets or sets the event title name.
///
- public required string? Title { get; set; }
+ [JsonRequired]
+ public string Title { get; set; } = string.Empty;
///
/// Gets or sets the event summary.
///
- public required string? Summary { get; set; }
+ [JsonRequired]
+ public string Summary { get; set; } = string.Empty;
///
/// Gets or sets the Azure OpenAI Service request max token capacity.
///
- public required int? MaxTokenCap { get; set; }
+ [JsonRequired]
+ public int MaxTokenCap { get; set; }
///
/// Gets or sets the Azure OpenAI Service daily request capacity.
///
- public required int? DailyRequestCap { get; set; }
+ [JsonRequired]
+ public int DailyRequestCap { get; set; }
}
\ No newline at end of file
diff --git a/src/AzureOpenAIProxy.ApiApp/Program.cs b/src/AzureOpenAIProxy.ApiApp/Program.cs
index 5ce6fbe0..1ef3a28c 100644
--- a/src/AzureOpenAIProxy.ApiApp/Program.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Program.cs
@@ -1,5 +1,6 @@
using AzureOpenAIProxy.ApiApp.Endpoints;
using AzureOpenAIProxy.ApiApp.Extensions;
+using AzureOpenAIProxy.ApiApp.Services;
var builder = WebApplication.CreateBuilder(args);
@@ -14,6 +15,9 @@
// Add OpenAPI service
builder.Services.AddOpenApiService();
+// Add admin services
+builder.Services.AddAdminEventService();
+
var app = builder.Build();
app.MapDefaultEndpoints();
diff --git a/src/AzureOpenAIProxy.ApiApp/Services/AdminEventService.cs b/src/AzureOpenAIProxy.ApiApp/Services/AdminEventService.cs
new file mode 100644
index 00000000..d3e0533f
--- /dev/null
+++ b/src/AzureOpenAIProxy.ApiApp/Services/AdminEventService.cs
@@ -0,0 +1,85 @@
+using AzureOpenAIProxy.ApiApp.Models;
+
+namespace AzureOpenAIProxy.ApiApp.Services;
+
+///
+/// This provides interfaces to class.
+///
+public interface IAdminEventService
+{
+ ///
+ /// Creates a new event.
+ ///
+ /// Event payload.
+ /// Returns the event payload created.
+ Task CreateEvent(AdminEventDetails eventDetails);
+
+ ///
+ /// Gets the list of events.
+ ///
+ /// Returns the list of events.
+ Task> GetEvents();
+
+ ///
+ /// Gets the event details.
+ ///
+ /// Event ID.
+ /// Returns the event details.
+ Task GetEvent(Guid eventId);
+
+ ///
+ /// Updates the event details.
+ ///
+ /// Event ID.
+ /// Event details to update.
+ /// Returns the updated event details.
+ Task UpdateEvent(Guid eventId, AdminEventDetails eventDetails);
+}
+
+///
+/// This represents the service entity for admin event.
+///
+public class AdminEventService : IAdminEventService
+{
+ ///
+ public async Task CreateEvent(AdminEventDetails eventDetails)
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ public async Task> GetEvents()
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ public async Task GetEvent(Guid eventId)
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ public async Task UpdateEvent(Guid eventId, AdminEventDetails eventDetails)
+ {
+ throw new NotImplementedException();
+ }
+}
+
+///
+/// This represents the extension class for
+///
+public static class AdminEventServiceExtensions
+{
+ ///
+ /// Adds the instance to the service collection.
+ ///
+ /// instance.
+ /// Returns instance.
+ public static IServiceCollection AddAdminEventService(this IServiceCollection services)
+ {
+ services.AddScoped();
+
+ return services;
+ }
+}
diff --git a/test/AzureOpenAIProxy.ApiApp.Tests/Services/AdminEventServiceTests.cs b/test/AzureOpenAIProxy.ApiApp.Tests/Services/AdminEventServiceTests.cs
new file mode 100644
index 00000000..e459e4d0
--- /dev/null
+++ b/test/AzureOpenAIProxy.ApiApp.Tests/Services/AdminEventServiceTests.cs
@@ -0,0 +1,80 @@
+using AzureOpenAIProxy.ApiApp.Models;
+using AzureOpenAIProxy.ApiApp.Services;
+
+using FluentAssertions;
+
+using Microsoft.Extensions.DependencyInjection;
+
+namespace AzureOpenAIProxy.ApiApp.Tests.Services;
+
+public class AdminEventServiceTests
+{
+ [Fact]
+ public void Given_ServiceCollection_When_AddAdminEventService_Invoked_Then_It_Should_Contain_AdminEventService()
+ {
+ // Arrange
+ var services = new ServiceCollection();
+
+ // Act
+ services.AddAdminEventService();
+
+ // Assert
+ services.SingleOrDefault(p => p.ServiceType == typeof(IAdminEventService)).Should().NotBeNull();
+ }
+
+ [Fact]
+ public void Given_Instance_When_CreateEvent_Invoked_Then_It_Should_Throw_Exception()
+ {
+ // Arrange
+ var eventDetails = new AdminEventDetails();
+ var service = new AdminEventService();
+
+ // Act
+ Func func = async () => await service.CreateEvent(eventDetails);
+
+ // Assert
+ func.Should().ThrowAsync();
+ }
+
+ [Fact]
+ public void Given_Instance_When_GetEvents_Invoked_Then_It_Should_Throw_Exception()
+ {
+ // Arrange
+ var service = new AdminEventService();
+
+ // Act
+ Func func = async () => await service.GetEvents();
+
+ // Assert
+ func.Should().ThrowAsync();
+ }
+
+ [Fact]
+ public void Given_Instance_When_GetEvent_Invoked_Then_It_Should_Throw_Exception()
+ {
+ // Arrange
+ var eventId = Guid.NewGuid();
+ var service = new AdminEventService();
+
+ // Act
+ Func func = async () => await service.GetEvent(eventId);
+
+ // Assert
+ func.Should().ThrowAsync();
+ }
+
+ [Fact]
+ public void Given_Instance_When_UpdateEvent_Invoked_Then_It_Should_Throw_Exception()
+ {
+ // Arrange
+ var eventId = Guid.NewGuid();
+ var eventDetails = new AdminEventDetails();
+ var service = new AdminEventService();
+
+ // Act
+ Func func = async () => await service.UpdateEvent(eventId, eventDetails);
+
+ // Assert
+ func.Should().ThrowAsync();
+ }
+}
diff --git a/test/AzureOpenAIProxy.AppHost.Tests/AppHostProgramTests.cs b/test/AzureOpenAIProxy.AppHost.Tests/AppHostProgramTests.cs
index 22adb326..4af8e386 100644
--- a/test/AzureOpenAIProxy.AppHost.Tests/AppHostProgramTests.cs
+++ b/test/AzureOpenAIProxy.AppHost.Tests/AppHostProgramTests.cs
@@ -4,7 +4,7 @@
using FluentAssertions;
-namespace AzureOpenAIProxy.Tests;
+namespace AzureOpenAIProxy.AppHost.Tests;
public class AppHostProgramTests(AspireAppHostFixture host) : IClassFixture
{
From 6aef2d5768ad13ae55545a78d59b677abfd6ad00 Mon Sep 17 00:00:00 2001
From: Justin Yoo
Date: Sun, 1 Sep 2024 13:53:24 +0900
Subject: [PATCH 06/13] [Admin] Add repository layer for admin events (#287)
---
src/AzureOpenAIProxy.ApiApp/Program.cs | 4 +
.../Repositories/AdminEventRepository.cs | 85 +++++++++++++++++++
.../Services/AdminEventService.cs | 21 +++--
.../Repositories/AdminEventRepositoryTests.cs | 80 +++++++++++++++++
.../Services/AdminEventServiceTests.cs | 15 +++-
5 files changed, 196 insertions(+), 9 deletions(-)
create mode 100644 src/AzureOpenAIProxy.ApiApp/Repositories/AdminEventRepository.cs
create mode 100644 test/AzureOpenAIProxy.ApiApp.Tests/Repositories/AdminEventRepositoryTests.cs
diff --git a/src/AzureOpenAIProxy.ApiApp/Program.cs b/src/AzureOpenAIProxy.ApiApp/Program.cs
index 1ef3a28c..4c5a0e55 100644
--- a/src/AzureOpenAIProxy.ApiApp/Program.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Program.cs
@@ -1,5 +1,6 @@
using AzureOpenAIProxy.ApiApp.Endpoints;
using AzureOpenAIProxy.ApiApp.Extensions;
+using AzureOpenAIProxy.ApiApp.Repositories;
using AzureOpenAIProxy.ApiApp.Services;
var builder = WebApplication.CreateBuilder(args);
@@ -18,6 +19,9 @@
// Add admin services
builder.Services.AddAdminEventService();
+// Add admin repositories
+builder.Services.AddAdminEventRepository();
+
var app = builder.Build();
app.MapDefaultEndpoints();
diff --git a/src/AzureOpenAIProxy.ApiApp/Repositories/AdminEventRepository.cs b/src/AzureOpenAIProxy.ApiApp/Repositories/AdminEventRepository.cs
new file mode 100644
index 00000000..552b6416
--- /dev/null
+++ b/src/AzureOpenAIProxy.ApiApp/Repositories/AdminEventRepository.cs
@@ -0,0 +1,85 @@
+using AzureOpenAIProxy.ApiApp.Models;
+
+namespace AzureOpenAIProxy.ApiApp.Repositories;
+
+///
+/// This provides interfaces to the class.
+///
+public interface IAdminEventRepository
+{
+ ///
+ /// Creates a new record of event details.
+ ///
+ /// Event details instance.
+ /// Returns the event details instance created.
+ Task CreateEvent(AdminEventDetails eventDetails);
+
+ ///
+ /// Gets the list of events.
+ ///
+ /// Returns the list of events.
+ Task> GetEvents();
+
+ ///
+ /// Gets the event details.
+ ///
+ /// Event ID.
+ /// Returns the event details record.
+ Task GetEvent(Guid eventId);
+
+ ///
+ /// Updates the event details.
+ ///
+ /// Event ID.
+ /// Event details instance.
+ /// Returns the updated record of the event details.
+ Task UpdateEvent(Guid eventId, AdminEventDetails eventDetails);
+}
+
+///
+/// This represents the repository entity for the admin event.
+///
+public class AdminEventRepository : IAdminEventRepository
+{
+ ///
+ public async Task CreateEvent(AdminEventDetails eventDetails)
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ public async Task> GetEvents()
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ public async Task GetEvent(Guid eventId)
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ public async Task UpdateEvent(Guid eventId, AdminEventDetails eventDetails)
+ {
+ throw new NotImplementedException();
+ }
+}
+
+///
+/// This represents the extension class for
+///
+public static class AdminEventRepositoryExtensions
+{
+ ///
+ /// Adds the instance to the service collection.
+ ///
+ /// instance.
+ /// Returns instance.
+ public static IServiceCollection AddAdminEventRepository(this IServiceCollection services)
+ {
+ services.AddScoped();
+
+ return services;
+ }
+}
diff --git a/src/AzureOpenAIProxy.ApiApp/Services/AdminEventService.cs b/src/AzureOpenAIProxy.ApiApp/Services/AdminEventService.cs
index d3e0533f..09f21797 100644
--- a/src/AzureOpenAIProxy.ApiApp/Services/AdminEventService.cs
+++ b/src/AzureOpenAIProxy.ApiApp/Services/AdminEventService.cs
@@ -1,4 +1,5 @@
using AzureOpenAIProxy.ApiApp.Models;
+using AzureOpenAIProxy.ApiApp.Repositories;
namespace AzureOpenAIProxy.ApiApp.Services;
@@ -39,30 +40,40 @@ public interface IAdminEventService
///
/// This represents the service entity for admin event.
///
-public class AdminEventService : IAdminEventService
+public class AdminEventService(IAdminEventRepository repository) : IAdminEventService
{
+ private readonly IAdminEventRepository _repository = repository ?? throw new ArgumentNullException(nameof(repository));
+
///
public async Task CreateEvent(AdminEventDetails eventDetails)
{
- throw new NotImplementedException();
+ var result = await this._repository.CreateEvent(eventDetails).ConfigureAwait(false);
+
+ return result;
}
///
public async Task> GetEvents()
{
- throw new NotImplementedException();
+ var result = await this._repository.GetEvents().ConfigureAwait(false);
+
+ return result;
}
///
public async Task GetEvent(Guid eventId)
{
- throw new NotImplementedException();
+ var result = await this._repository.GetEvent(eventId).ConfigureAwait(false);
+
+ return result;
}
///
public async Task UpdateEvent(Guid eventId, AdminEventDetails eventDetails)
{
- throw new NotImplementedException();
+ var result = await this._repository.UpdateEvent(eventId, eventDetails).ConfigureAwait(false);
+
+ return result;
}
}
diff --git a/test/AzureOpenAIProxy.ApiApp.Tests/Repositories/AdminEventRepositoryTests.cs b/test/AzureOpenAIProxy.ApiApp.Tests/Repositories/AdminEventRepositoryTests.cs
new file mode 100644
index 00000000..0e00c5d4
--- /dev/null
+++ b/test/AzureOpenAIProxy.ApiApp.Tests/Repositories/AdminEventRepositoryTests.cs
@@ -0,0 +1,80 @@
+using AzureOpenAIProxy.ApiApp.Models;
+using AzureOpenAIProxy.ApiApp.Repositories;
+
+using FluentAssertions;
+
+using Microsoft.Extensions.DependencyInjection;
+
+namespace AzureOpenAIProxy.ApiApp.Tests.Repositories;
+
+public class AdminEventRepositoryTests
+{
+ [Fact]
+ public void Given_ServiceCollection_When_AddAdminEventRepository_Invoked_Then_It_Should_Contain_AdminEventRepository()
+ {
+ // Arrange
+ var services = new ServiceCollection();
+
+ // Act
+ services.AddAdminEventRepository();
+
+ // Assert
+ services.SingleOrDefault(p => p.ServiceType == typeof(IAdminEventRepository)).Should().NotBeNull();
+ }
+
+ [Fact]
+ public void Given_Instance_When_CreateEvent_Invoked_Then_It_Should_Throw_Exception()
+ {
+ // Arrange
+ var eventDetails = new AdminEventDetails();
+ var repository = new AdminEventRepository();
+
+ // Act
+ Func func = async () => await repository.CreateEvent(eventDetails);
+
+ // Assert
+ func.Should().ThrowAsync();
+ }
+
+ [Fact]
+ public void Given_Instance_When_GetEvents_Invoked_Then_It_Should_Throw_Exception()
+ {
+ // Arrange
+ var repository = new AdminEventRepository();
+
+ // Act
+ Func func = async () => await repository.GetEvents();
+
+ // Assert
+ func.Should().ThrowAsync();
+ }
+
+ [Fact]
+ public void Given_Instance_When_GetEvent_Invoked_Then_It_Should_Throw_Exception()
+ {
+ // Arrange
+ var eventId = Guid.NewGuid();
+ var repository = new AdminEventRepository();
+
+ // Act
+ Func func = async () => await repository.GetEvent(eventId);
+
+ // Assert
+ func.Should().ThrowAsync();
+ }
+
+ [Fact]
+ public void Given_Instance_When_UpdateEvent_Invoked_Then_It_Should_Throw_Exception()
+ {
+ // Arrange
+ var eventId = Guid.NewGuid();
+ var eventDetails = new AdminEventDetails();
+ var repository = new AdminEventRepository();
+
+ // Act
+ Func func = async () => await repository.UpdateEvent(eventId, eventDetails);
+
+ // Assert
+ func.Should().ThrowAsync();
+ }
+}
diff --git a/test/AzureOpenAIProxy.ApiApp.Tests/Services/AdminEventServiceTests.cs b/test/AzureOpenAIProxy.ApiApp.Tests/Services/AdminEventServiceTests.cs
index e459e4d0..cf56f8e2 100644
--- a/test/AzureOpenAIProxy.ApiApp.Tests/Services/AdminEventServiceTests.cs
+++ b/test/AzureOpenAIProxy.ApiApp.Tests/Services/AdminEventServiceTests.cs
@@ -1,10 +1,13 @@
using AzureOpenAIProxy.ApiApp.Models;
+using AzureOpenAIProxy.ApiApp.Repositories;
using AzureOpenAIProxy.ApiApp.Services;
using FluentAssertions;
using Microsoft.Extensions.DependencyInjection;
+using NSubstitute;
+
namespace AzureOpenAIProxy.ApiApp.Tests.Services;
public class AdminEventServiceTests
@@ -27,7 +30,8 @@ public void Given_Instance_When_CreateEvent_Invoked_Then_It_Should_Throw_Excepti
{
// Arrange
var eventDetails = new AdminEventDetails();
- var service = new AdminEventService();
+ var repository = Substitute.For();
+ var service = new AdminEventService(repository);
// Act
Func func = async () => await service.CreateEvent(eventDetails);
@@ -40,7 +44,8 @@ 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 service = new AdminEventService();
+ var repository = Substitute.For();
+ var service = new AdminEventService(repository);
// Act
Func func = async () => await service.GetEvents();
@@ -54,7 +59,8 @@ public void Given_Instance_When_GetEvent_Invoked_Then_It_Should_Throw_Exception(
{
// Arrange
var eventId = Guid.NewGuid();
- var service = new AdminEventService();
+ var repository = Substitute.For();
+ var service = new AdminEventService(repository);
// Act
Func func = async () => await service.GetEvent(eventId);
@@ -69,7 +75,8 @@ public void Given_Instance_When_UpdateEvent_Invoked_Then_It_Should_Throw_Excepti
// Arrange
var eventId = Guid.NewGuid();
var eventDetails = new AdminEventDetails();
- var service = new AdminEventService();
+ var repository = Substitute.For();
+ var service = new AdminEventService(repository);
// Act
Func func = async () => await service.UpdateEvent(eventId, eventDetails);
From 43ed06156995472310f945d7d0ce48386a9ac5fb Mon Sep 17 00:00:00 2001
From: Justin Yoo
Date: Sun, 1 Sep 2024 15:42:12 +0900
Subject: [PATCH 07/13] [Playground] playground page layout (#291)
---
.../Components/App.razor | 4 +--
.../Components/Layout/MainLayout.razor.css | 30 +++++++++----------
.../Components/Layout/NavMenu.razor.css | 14 ++++-----
.../Components/Pages/Playground.razor | 15 ++++++++--
.../wwwroot/app.css | 2 +-
.../Pages/PlaygroundPageTests.cs | 30 +++++++++++++++++++
6 files changed, 68 insertions(+), 27 deletions(-)
diff --git a/src/AzureOpenAIProxy.PlaygroundApp/Components/App.razor b/src/AzureOpenAIProxy.PlaygroundApp/Components/App.razor
index 6b73ab6b..4d95987f 100644
--- a/src/AzureOpenAIProxy.PlaygroundApp/Components/App.razor
+++ b/src/AzureOpenAIProxy.PlaygroundApp/Components/App.razor
@@ -5,10 +5,10 @@
-
+ @* *@
+
-
diff --git a/src/AzureOpenAIProxy.PlaygroundApp/Components/Layout/MainLayout.razor.css b/src/AzureOpenAIProxy.PlaygroundApp/Components/Layout/MainLayout.razor.css
index 038baf17..a6731151 100644
--- a/src/AzureOpenAIProxy.PlaygroundApp/Components/Layout/MainLayout.razor.css
+++ b/src/AzureOpenAIProxy.PlaygroundApp/Components/Layout/MainLayout.razor.css
@@ -27,23 +27,23 @@ main {
text-decoration: none;
}
- .top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
- text-decoration: underline;
- }
+ .top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
+ text-decoration: underline;
+ }
- .top-row ::deep a:first-child {
- overflow: hidden;
- text-overflow: ellipsis;
- }
+ .top-row ::deep a:first-child {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
@media (max-width: 640.98px) {
.top-row {
justify-content: space-between;
}
- .top-row ::deep a, .top-row ::deep .btn-link {
- margin-left: 0;
- }
+ .top-row ::deep a, .top-row ::deep .btn-link {
+ margin-left: 0;
+ }
}
@media (min-width: 641px) {
@@ -64,11 +64,11 @@ main {
z-index: 1;
}
- .top-row.auth ::deep a:first-child {
- flex: 1;
- text-align: right;
- width: 0;
- }
+ .top-row.auth ::deep a:first-child {
+ flex: 1;
+ text-align: right;
+ width: 0;
+ }
.top-row, article {
padding-left: 2rem !important;
diff --git a/src/AzureOpenAIProxy.PlaygroundApp/Components/Layout/NavMenu.razor.css b/src/AzureOpenAIProxy.PlaygroundApp/Components/Layout/NavMenu.razor.css
index 4e15395e..e8902497 100644
--- a/src/AzureOpenAIProxy.PlaygroundApp/Components/Layout/NavMenu.razor.css
+++ b/src/AzureOpenAIProxy.PlaygroundApp/Components/Layout/NavMenu.razor.css
@@ -1,4 +1,4 @@
-.navbar-toggler {
+/*.navbar-toggler {
appearance: none;
cursor: pointer;
width: 3.5rem;
@@ -94,12 +94,12 @@
display: none;
}
- .nav-scrollable {
- /* Never collapse the sidebar for wide screens */
- display: block;
+ .nav-scrollable {*/
+/* Never collapse the sidebar for wide screens */
+/*display: block;*/
- /* Allow sidebar to scroll for tall menus */
- height: calc(100vh - 3.5rem);
+/* Allow sidebar to scroll for tall menus */
+/*height: calc(100vh - 3.5rem);
overflow-y: auto;
}
-}
+}*/
diff --git a/src/AzureOpenAIProxy.PlaygroundApp/Components/Pages/Playground.razor b/src/AzureOpenAIProxy.PlaygroundApp/Components/Pages/Playground.razor
index 08f49d57..06fe0b5d 100644
--- a/src/AzureOpenAIProxy.PlaygroundApp/Components/Pages/Playground.razor
+++ b/src/AzureOpenAIProxy.PlaygroundApp/Components/Pages/Playground.razor
@@ -1,7 +1,18 @@
@page "/playground"
+@rendermode InteractiveServer
Playground Page
-