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

[Admin] Table Storage Connection Setting #299

Merged
Merged
Show file tree
Hide file tree
Changes from 21 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
1 change: 1 addition & 0 deletions src/AzureOpenAIProxy.ApiApp/AzureOpenAIProxy.ApiApp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

<ItemGroup>
<PackageReference Include="Azure.AI.OpenAI" Version="$(AzureOpenAIVersion)" />
<PackageReference Include="Azure.Data.Tables" Version="12.9.0" />
<PackageReference Include="Azure.Identity" Version="1.12.0" />
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.6.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="$(AspNetCoreVersion)" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class KeyVaultSettings
public string? VaultUri { get; set; }

/// <summary>
/// Gets or sets the secret name.
/// Gets or sets the secret names.
/// </summary>
public string? SecretName { get; set; }
public Dictionary<string, string> SecretNames { get; set; } = [];
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public static IOpenAISettingsBuilder WithKeyVault(this IOpenAISettingsBuilder bu
var client = sp.GetService<SecretClient>()
?? throw new InvalidOperationException($"{nameof(SecretClient)} service is not registered.");

var value = client.GetSecret(settings.SecretName!).Value.Value;
var value = client.GetSecret(settings.SecretNames[KeyVaultSecretNames.OpenAI]!).Value.Value;

var instances = JsonSerializer.Deserialize<List<OpenAIInstanceSettings>>(value);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Azure.Identity;
using Azure;
using Azure.Data.Tables;
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;

using AzureOpenAIProxy.ApiApp.Builders;
Expand Down Expand Up @@ -35,9 +37,9 @@ public static IServiceCollection AddKeyVaultService(this IServiceCollection serv
throw new InvalidOperationException($"{nameof(KeyVaultSettings.VaultUri)} is not defined.");
}

if (string.IsNullOrWhiteSpace(settings.SecretName) == true)
if (string.IsNullOrWhiteSpace(settings.SecretNames[KeyVaultSecretNames.OpenAI]) == true)
{
throw new InvalidOperationException($"{nameof(KeyVaultSettings.SecretName)} is not defined.");
throw new InvalidOperationException($"{nameof(KeyVaultSettings.SecretNames)}.{KeyVaultSecretNames.OpenAI} is not defined.");
}

var client = new SecretClient(new Uri(settings.VaultUri), new DefaultAzureCredential());
Expand Down Expand Up @@ -134,4 +136,31 @@ public static IServiceCollection AddOpenApiService(this IServiceCollection servi

return services;
}

/// <summary>
/// Adds the TableServiceClient to the services collection.
/// </summary>
/// <param name="services"><see cref="IServiceCollection"/> instance.</param>
/// <returns>Returns <see cref="IServiceCollection"/> instance.</returns>
public static IServiceCollection AddTableStorageService(this IServiceCollection services)
{
services.AddSingleton<TableServiceClient>(sp => {
var configuration = sp.GetService<IConfiguration>()
?? throw new InvalidOperationException($"{nameof(IConfiguration)} service is not registerd.");

var settings = configuration.GetSection(AzureSettings.Name).GetSection(KeyVaultSettings.Name).Get<KeyVaultSettings>()
?? throw new InvalidOperationException($"{nameof(KeyVaultSettings)} could not be retrieved from the configuration.");

var clientSecret = sp.GetService<SecretClient>()
?? throw new InvalidOperationException($"{nameof(SecretClient)} service is not registered.");

var storageKeyVault = clientSecret.GetSecret(settings.SecretNames[KeyVaultSecretNames.Storage]!);

var client = new TableServiceClient(storageKeyVault.Value.Value);

return client;
});

return services;
}
}
justinyoo marked this conversation as resolved.
Show resolved Hide resolved
18 changes: 18 additions & 0 deletions src/AzureOpenAIProxy.ApiApp/KeyVaultSecretNames.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace AzureOpenAIProxy.ApiApp;

/// <summary>
/// This represents the keyvault secret names in appsettings.json
/// </summary>
public static class KeyVaultSecretNames
{
/// <summary>
/// Keyvault secret name for OpenAI instance settings
/// </summary>
public const string OpenAI = "OpenAI";

/// <summary>
/// Keyvault secret name for table storage connection string
/// </summary>
public const string Storage = "Storage";

}
3 changes: 3 additions & 0 deletions src/AzureOpenAIProxy.ApiApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
// Add OpenAPI service
builder.Services.AddOpenApiService();

// Add TableStorageClient
builder.Services.AddTableStorageService();

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

Expand Down
5 changes: 4 additions & 1 deletion src/AzureOpenAIProxy.ApiApp/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@
},
"KeyVault": {
"VaultUri": "https://{{key-vault-name}}.vault.azure.net/",
"SecretName": "azure-openai-instances"
"SecretNames": {
"OpenAI": "azure-openai-instances",
"Storage": "storage-connection-string"
}
}
},

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public void Given_NullOrEmpty_VaultUri_When_Invoked_AddKeyVaultService_Then_It_S
var dict = new Dictionary<string, string>()
{
{ "Azure:KeyVault:VaultUri", vaultUri! },
{ "Azure:KeyVault:SecretName", secretName },
{ "Azure:KeyVault:SecretNames:OpenAI", secretName },
};
#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();
Expand All @@ -108,16 +108,16 @@ public void Given_NullOrEmpty_VaultUri_When_Invoked_AddKeyVaultService_Then_It_S
}

[Theory]
[InlineData("http://localhost", default(string))]
[InlineData("http://localhost", "")]
public void Given_NullOrEmpty_SecretName_When_Invoked_AddKeyVaultService_Then_It_Should_Throw_Exception(string vaultUri, string? secretName)
[InlineData("http://localhost", default(string), typeof(KeyNotFoundException))]
[InlineData("http://localhost", "", typeof(InvalidOperationException))]
public void Given_NullOrEmpty_SecretName_When_Invoked_AddKeyVaultService_Then_It_Should_Throw_Exception(string vaultUri, string? secretName, Type exceptionType)
{
// Arrange
var services = new ServiceCollection();
var dict = new Dictionary<string, string>()
{
{ "Azure:KeyVault:VaultUri", vaultUri },
{ "Azure:KeyVault:SecretName", secretName! },
{ "Azure:KeyVault:SecretNames:OpenAI", secretName! },
justinyoo marked this conversation as resolved.
Show resolved Hide resolved
};
#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();
Expand All @@ -129,7 +129,7 @@ public void Given_NullOrEmpty_SecretName_When_Invoked_AddKeyVaultService_Then_It
Action action = () => services.BuildServiceProvider().GetService<SecretClient>();

// Assert
action.Should().Throw<InvalidOperationException>();
action.Should().Throw<Exception>().Which.Should().BeOfType(exceptionType);
}

[Theory]
Expand All @@ -141,7 +141,7 @@ public void Given_Invalid_VaultUri_When_Invoked_AddKeyVaultService_Then_It_Shoul
var dict = new Dictionary<string, string>()
{
{ "Azure:KeyVault:VaultUri", vaultUri },
{ "Azure:KeyVault:SecretName", secretName },
{ "Azure:KeyVault:SecretNames:OpenAI", secretName },
};
#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();
Expand All @@ -165,7 +165,7 @@ public void Given_AppSettings_When_Invoked_AddKeyVaultService_Then_It_Should_Ret
var dict = new Dictionary<string, string>()
{
{ "Azure:KeyVault:VaultUri", vaultUri },
{ "Azure:KeyVault:SecretName", secretName },
{ "Azure:KeyVault:SecretNames:OpenAI", secretName },
};
#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();
Expand All @@ -190,7 +190,7 @@ public void Given_AppSettings_When_Invoked_AddKeyVaultService_Then_It_Should_Ret
var dict = new Dictionary<string, string>()
{
{ "Azure:KeyVault:VaultUri", vaultUri },
{ "Azure:KeyVault:SecretName", secretName },
{ "Azure:KeyVault:SecretNames:OpenAI", secretName },
};
#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();
Expand Down
Loading