Skip to content

Commit 999dfd7

Browse files
authored
[Admin] Table Storage Connection Setting (#299)
1 parent 068b0e5 commit 999dfd7

File tree

8 files changed

+74
-16
lines changed

8 files changed

+74
-16
lines changed

src/AzureOpenAIProxy.ApiApp/AzureOpenAIProxy.ApiApp.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
<ItemGroup>
99
<PackageReference Include="Azure.AI.OpenAI" Version="$(AzureOpenAIVersion)" />
10+
<PackageReference Include="Azure.Data.Tables" Version="12.9.0" />
1011
<PackageReference Include="Azure.Identity" Version="1.12.0" />
1112
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.6.0" />
1213
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="$(AspNetCoreVersion)" />

src/AzureOpenAIProxy.ApiApp/Configurations/KeyVaultSettings.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public class KeyVaultSettings
1616
public string? VaultUri { get; set; }
1717

1818
/// <summary>
19-
/// Gets or sets the secret name.
19+
/// Gets or sets the secret names.
2020
/// </summary>
21-
public string? SecretName { get; set; }
21+
public Dictionary<string, string> SecretNames { get; set; } = [];
2222
}

src/AzureOpenAIProxy.ApiApp/Extensions/OpenAISettingsBuilderExtensions.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public static IOpenAISettingsBuilder WithKeyVault(this IOpenAISettingsBuilder bu
4848
var client = sp.GetService<SecretClient>()
4949
?? throw new InvalidOperationException($"{nameof(SecretClient)} service is not registered.");
5050

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

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

src/AzureOpenAIProxy.ApiApp/Extensions/ServiceCollectionExtensions.cs

+36-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Azure.Identity;
1+
using Azure.Data.Tables;
2+
using Azure.Identity;
23
using Azure.Security.KeyVault.Secrets;
34

45
using AzureOpenAIProxy.ApiApp.Builders;
@@ -35,9 +36,9 @@ public static IServiceCollection AddKeyVaultService(this IServiceCollection serv
3536
throw new InvalidOperationException($"{nameof(KeyVaultSettings.VaultUri)} is not defined.");
3637
}
3738

38-
if (string.IsNullOrWhiteSpace(settings.SecretName) == true)
39+
if (string.IsNullOrWhiteSpace(settings.SecretNames[KeyVaultSecretNames.OpenAI]) == true)
3940
{
40-
throw new InvalidOperationException($"{nameof(KeyVaultSettings.SecretName)} is not defined.");
41+
throw new InvalidOperationException($"{nameof(KeyVaultSettings.SecretNames)}.{KeyVaultSecretNames.OpenAI} is not defined.");
4142
}
4243

4344
var client = new SecretClient(new Uri(settings.VaultUri), new DefaultAzureCredential());
@@ -134,4 +135,36 @@ public static IServiceCollection AddOpenApiService(this IServiceCollection servi
134135

135136
return services;
136137
}
138+
139+
/// <summary>
140+
/// Adds the TableServiceClient to the services collection.
141+
/// </summary>
142+
/// <param name="services"><see cref="IServiceCollection"/> instance.</param>
143+
/// <returns>Returns <see cref="IServiceCollection"/> instance.</returns>
144+
public static IServiceCollection AddTableStorageService(this IServiceCollection services)
145+
{
146+
services.AddSingleton<TableServiceClient>(sp => {
147+
var configuration = sp.GetService<IConfiguration>()
148+
?? throw new InvalidOperationException($"{nameof(IConfiguration)} service is not registerd.");
149+
150+
var settings = configuration.GetSection(AzureSettings.Name).GetSection(KeyVaultSettings.Name).Get<KeyVaultSettings>()
151+
?? throw new InvalidOperationException($"{nameof(KeyVaultSettings)} could not be retrieved from the configuration.");
152+
153+
var clientSecret = sp.GetService<SecretClient>()
154+
?? throw new InvalidOperationException($"{nameof(SecretClient)} service is not registered.");
155+
156+
if (string.IsNullOrWhiteSpace(settings.SecretNames[KeyVaultSecretNames.Storage]) == true)
157+
{
158+
throw new InvalidOperationException($"{nameof(KeyVaultSettings.SecretNames)}.{KeyVaultSecretNames.Storage} is not defined.");
159+
}
160+
161+
var storageKeyVault = clientSecret.GetSecret(settings.SecretNames[KeyVaultSecretNames.Storage]!);
162+
163+
var client = new TableServiceClient(storageKeyVault.Value.Value);
164+
165+
return client;
166+
});
167+
168+
return services;
169+
}
137170
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
namespace AzureOpenAIProxy.ApiApp;
2+
3+
/// <summary>
4+
/// This represents the keyvault secret names in appsettings.json
5+
/// </summary>
6+
public static class KeyVaultSecretNames
7+
{
8+
/// <summary>
9+
/// Keyvault secret name for OpenAI instance settings
10+
/// </summary>
11+
public const string OpenAI = "OpenAI";
12+
13+
/// <summary>
14+
/// Keyvault secret name for table storage connection string
15+
/// </summary>
16+
public const string Storage = "Storage";
17+
18+
}

src/AzureOpenAIProxy.ApiApp/Program.cs

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
// Add OpenAPI service
1717
builder.Services.AddOpenApiService();
1818

19+
// Add TableStorageClient
20+
builder.Services.AddTableStorageService();
21+
1922
// Add admin services
2023
builder.Services.AddAdminEventService();
2124

src/AzureOpenAIProxy.ApiApp/appsettings.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@
2525
},
2626
"KeyVault": {
2727
"VaultUri": "https://{{key-vault-name}}.vault.azure.net/",
28-
"SecretName": "azure-openai-instances"
28+
"SecretNames": {
29+
"OpenAI": "azure-openai-instances",
30+
"Storage": "storage-connection-string"
31+
}
2932
}
3033
},
3134

test/AzureOpenAIProxy.ApiApp.Tests/Extensions/ServiceCollectionExtensionsTests.cs

+9-9
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public void Given_NullOrEmpty_VaultUri_When_Invoked_AddKeyVaultService_Then_It_S
9292
var dict = new Dictionary<string, string>()
9393
{
9494
{ "Azure:KeyVault:VaultUri", vaultUri! },
95-
{ "Azure:KeyVault:SecretName", secretName },
95+
{ "Azure:KeyVault:SecretNames:OpenAI", secretName },
9696
};
9797
#pragma warning disable CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.
9898
var config = new ConfigurationBuilder().AddInMemoryCollection(dict).Build();
@@ -108,16 +108,16 @@ public void Given_NullOrEmpty_VaultUri_When_Invoked_AddKeyVaultService_Then_It_S
108108
}
109109

110110
[Theory]
111-
[InlineData("http://localhost", default(string))]
112-
[InlineData("http://localhost", "")]
113-
public void Given_NullOrEmpty_SecretName_When_Invoked_AddKeyVaultService_Then_It_Should_Throw_Exception(string vaultUri, string? secretName)
111+
[InlineData("http://localhost", default(string), typeof(KeyNotFoundException))]
112+
[InlineData("http://localhost", "", typeof(InvalidOperationException))]
113+
public void Given_NullOrEmpty_SecretName_When_Invoked_AddKeyVaultService_Then_It_Should_Throw_Exception(string vaultUri, string? secretName, Type exceptionType)
114114
{
115115
// Arrange
116116
var services = new ServiceCollection();
117117
var dict = new Dictionary<string, string>()
118118
{
119119
{ "Azure:KeyVault:VaultUri", vaultUri },
120-
{ "Azure:KeyVault:SecretName", secretName! },
120+
{ "Azure:KeyVault:SecretNames:OpenAI", secretName! },
121121
};
122122
#pragma warning disable CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.
123123
var config = new ConfigurationBuilder().AddInMemoryCollection(dict).Build();
@@ -129,7 +129,7 @@ public void Given_NullOrEmpty_SecretName_When_Invoked_AddKeyVaultService_Then_It
129129
Action action = () => services.BuildServiceProvider().GetService<SecretClient>();
130130

131131
// Assert
132-
action.Should().Throw<InvalidOperationException>();
132+
action.Should().Throw<Exception>().Which.Should().BeOfType(exceptionType);
133133
}
134134

135135
[Theory]
@@ -141,7 +141,7 @@ public void Given_Invalid_VaultUri_When_Invoked_AddKeyVaultService_Then_It_Shoul
141141
var dict = new Dictionary<string, string>()
142142
{
143143
{ "Azure:KeyVault:VaultUri", vaultUri },
144-
{ "Azure:KeyVault:SecretName", secretName },
144+
{ "Azure:KeyVault:SecretNames:OpenAI", secretName },
145145
};
146146
#pragma warning disable CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.
147147
var config = new ConfigurationBuilder().AddInMemoryCollection(dict).Build();
@@ -165,7 +165,7 @@ public void Given_AppSettings_When_Invoked_AddKeyVaultService_Then_It_Should_Ret
165165
var dict = new Dictionary<string, string>()
166166
{
167167
{ "Azure:KeyVault:VaultUri", vaultUri },
168-
{ "Azure:KeyVault:SecretName", secretName },
168+
{ "Azure:KeyVault:SecretNames:OpenAI", secretName },
169169
};
170170
#pragma warning disable CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.
171171
var config = new ConfigurationBuilder().AddInMemoryCollection(dict).Build();
@@ -190,7 +190,7 @@ public void Given_AppSettings_When_Invoked_AddKeyVaultService_Then_It_Should_Ret
190190
var dict = new Dictionary<string, string>()
191191
{
192192
{ "Azure:KeyVault:VaultUri", vaultUri },
193-
{ "Azure:KeyVault:SecretName", secretName },
193+
{ "Azure:KeyVault:SecretNames:OpenAI", secretName },
194194
};
195195
#pragma warning disable CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.
196196
var config = new ConfigurationBuilder().AddInMemoryCollection(dict).Build();

0 commit comments

Comments
 (0)