Skip to content

Commit 656ad20

Browse files
committed
Merge main branch into Usr/sogh/mcpconfigentities.v3
2 parents bf781c6 + 3554118 commit 656ad20

37 files changed

+835
-357
lines changed

src/Cli.Tests/EndToEndTests.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,11 @@ public void TestInitializingRestAndGraphQLGlobalSettings()
116116
string[] args = { "init", "-c", TEST_RUNTIME_CONFIG_FILE, "--connection-string", SAMPLE_TEST_CONN_STRING, "--database-type", "mssql", "--rest.path", "/rest-api", "--rest.enabled", "false", "--graphql.path", "/graphql-api" };
117117
Program.Execute(args, _cliLogger!, _fileSystem!, _runtimeConfigLoader!);
118118

119+
DeserializationVariableReplacementSettings replacementSettings = new(azureKeyVaultOptions: null, doReplaceEnvVar: true, doReplaceAkvVar: true);
119120
Assert.IsTrue(_runtimeConfigLoader!.TryLoadConfig(
120121
TEST_RUNTIME_CONFIG_FILE,
121122
out RuntimeConfig? runtimeConfig,
122-
replaceEnvVar: true));
123+
replacementSettings: replacementSettings));
123124

124125
SqlConnectionStringBuilder builder = new(runtimeConfig.DataSource.ConnectionString);
125126
Assert.AreEqual(ProductInfo.GetDataApiBuilderUserAgent(), builder.ApplicationName);
@@ -195,10 +196,11 @@ public void TestEnablingMultipleCreateOperation(CliBool isMultipleCreateEnabled,
195196

196197
Program.Execute(args.ToArray(), _cliLogger!, _fileSystem!, _runtimeConfigLoader!);
197198

199+
DeserializationVariableReplacementSettings replacementSettings = new(azureKeyVaultOptions: null, doReplaceEnvVar: true, doReplaceAkvVar: true);
198200
Assert.IsTrue(_runtimeConfigLoader!.TryLoadConfig(
199201
TEST_RUNTIME_CONFIG_FILE,
200202
out RuntimeConfig? runtimeConfig,
201-
replaceEnvVar: true));
203+
replacementSettings: replacementSettings));
202204

203205
Assert.IsNotNull(runtimeConfig);
204206
Assert.AreEqual(expectedDbType, runtimeConfig.DataSource.DatabaseType);

src/Cli.Tests/EnvironmentTests.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,13 @@ public class EnvironmentTests
1919
[TestInitialize]
2020
public void TestInitialize()
2121
{
22-
StringJsonConverterFactory converterFactory = new(EnvironmentVariableReplacementFailureMode.Throw);
22+
DeserializationVariableReplacementSettings replacementSettings = new(
23+
azureKeyVaultOptions: null,
24+
doReplaceEnvVar: true,
25+
doReplaceAkvVar: false,
26+
envFailureMode: EnvironmentVariableReplacementFailureMode.Throw);
27+
28+
StringJsonConverterFactory converterFactory = new(replacementSettings);
2329
_options = new()
2430
{
2531
PropertyNameCaseInsensitive = true

src/Cli/ConfigGenerator.cs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2657,9 +2657,10 @@ private static bool TryUpdateConfiguredAzureKeyVaultOptions(
26572657
// Azure Key Vault Endpoint
26582658
if (options.AzureKeyVaultEndpoint is not null)
26592659
{
2660+
// Ensure endpoint flag is marked user provided so converter writes it.
26602661
updatedAzureKeyVaultOptions = updatedAzureKeyVaultOptions is not null
2661-
? updatedAzureKeyVaultOptions with { Endpoint = options.AzureKeyVaultEndpoint }
2662-
: new AzureKeyVaultOptions { Endpoint = options.AzureKeyVaultEndpoint };
2662+
? updatedAzureKeyVaultOptions with { Endpoint = options.AzureKeyVaultEndpoint, UserProvidedEndpoint = true }
2663+
: new AzureKeyVaultOptions(endpoint: options.AzureKeyVaultEndpoint);
26632664
_logger.LogInformation("Updated RuntimeConfig with azure-key-vault.endpoint as '{endpoint}'", options.AzureKeyVaultEndpoint);
26642665
}
26652666

@@ -2668,7 +2669,7 @@ private static bool TryUpdateConfiguredAzureKeyVaultOptions(
26682669
{
26692670
updatedRetryPolicyOptions = updatedRetryPolicyOptions is not null
26702671
? updatedRetryPolicyOptions with { Mode = options.AzureKeyVaultRetryPolicyMode.Value, UserProvidedMode = true }
2671-
: new AKVRetryPolicyOptions { Mode = options.AzureKeyVaultRetryPolicyMode.Value, UserProvidedMode = true };
2672+
: new AKVRetryPolicyOptions(mode: options.AzureKeyVaultRetryPolicyMode.Value);
26722673
_logger.LogInformation("Updated RuntimeConfig with azure-key-vault.retry-policy.mode as '{mode}'", options.AzureKeyVaultRetryPolicyMode.Value);
26732674
}
26742675

@@ -2683,7 +2684,7 @@ private static bool TryUpdateConfiguredAzureKeyVaultOptions(
26832684

26842685
updatedRetryPolicyOptions = updatedRetryPolicyOptions is not null
26852686
? updatedRetryPolicyOptions with { MaxCount = options.AzureKeyVaultRetryPolicyMaxCount.Value, UserProvidedMaxCount = true }
2686-
: new AKVRetryPolicyOptions { MaxCount = options.AzureKeyVaultRetryPolicyMaxCount.Value, UserProvidedMaxCount = true };
2687+
: new AKVRetryPolicyOptions(maxCount: options.AzureKeyVaultRetryPolicyMaxCount.Value);
26872688
_logger.LogInformation("Updated RuntimeConfig with azure-key-vault.retry-policy.max-count as '{maxCount}'", options.AzureKeyVaultRetryPolicyMaxCount.Value);
26882689
}
26892690

@@ -2698,7 +2699,7 @@ private static bool TryUpdateConfiguredAzureKeyVaultOptions(
26982699

26992700
updatedRetryPolicyOptions = updatedRetryPolicyOptions is not null
27002701
? updatedRetryPolicyOptions with { DelaySeconds = options.AzureKeyVaultRetryPolicyDelaySeconds.Value, UserProvidedDelaySeconds = true }
2701-
: new AKVRetryPolicyOptions { DelaySeconds = options.AzureKeyVaultRetryPolicyDelaySeconds.Value, UserProvidedDelaySeconds = true };
2702+
: new AKVRetryPolicyOptions(delaySeconds: options.AzureKeyVaultRetryPolicyDelaySeconds.Value);
27022703
_logger.LogInformation("Updated RuntimeConfig with azure-key-vault.retry-policy.delay-seconds as '{delaySeconds}'", options.AzureKeyVaultRetryPolicyDelaySeconds.Value);
27032704
}
27042705

@@ -2713,7 +2714,7 @@ private static bool TryUpdateConfiguredAzureKeyVaultOptions(
27132714

27142715
updatedRetryPolicyOptions = updatedRetryPolicyOptions is not null
27152716
? updatedRetryPolicyOptions with { MaxDelaySeconds = options.AzureKeyVaultRetryPolicyMaxDelaySeconds.Value, UserProvidedMaxDelaySeconds = true }
2716-
: new AKVRetryPolicyOptions { MaxDelaySeconds = options.AzureKeyVaultRetryPolicyMaxDelaySeconds.Value, UserProvidedMaxDelaySeconds = true };
2717+
: new AKVRetryPolicyOptions(maxDelaySeconds: options.AzureKeyVaultRetryPolicyMaxDelaySeconds.Value);
27172718
_logger.LogInformation("Updated RuntimeConfig with azure-key-vault.retry-policy.max-delay-seconds as '{maxDelaySeconds}'", options.AzureKeyVaultRetryPolicyMaxDelaySeconds.Value);
27182719
}
27192720

@@ -2728,16 +2729,17 @@ private static bool TryUpdateConfiguredAzureKeyVaultOptions(
27282729

27292730
updatedRetryPolicyOptions = updatedRetryPolicyOptions is not null
27302731
? updatedRetryPolicyOptions with { NetworkTimeoutSeconds = options.AzureKeyVaultRetryPolicyNetworkTimeoutSeconds.Value, UserProvidedNetworkTimeoutSeconds = true }
2731-
: new AKVRetryPolicyOptions { NetworkTimeoutSeconds = options.AzureKeyVaultRetryPolicyNetworkTimeoutSeconds.Value, UserProvidedNetworkTimeoutSeconds = true };
2732+
: new AKVRetryPolicyOptions(networkTimeoutSeconds: options.AzureKeyVaultRetryPolicyNetworkTimeoutSeconds.Value);
27322733
_logger.LogInformation("Updated RuntimeConfig with azure-key-vault.retry-policy.network-timeout-seconds as '{networkTimeoutSeconds}'", options.AzureKeyVaultRetryPolicyNetworkTimeoutSeconds.Value);
27332734
}
27342735

2735-
// Update Azure Key Vault options with retry policy if retry policy was modified
2736+
// Update Azure Key Vault options with retry policy if modified
27362737
if (updatedRetryPolicyOptions is not null)
27372738
{
2739+
// Ensure outer AKV object marks retry policy as user provided so it serializes.
27382740
updatedAzureKeyVaultOptions = updatedAzureKeyVaultOptions is not null
2739-
? updatedAzureKeyVaultOptions with { RetryPolicy = updatedRetryPolicyOptions }
2740-
: new AzureKeyVaultOptions { RetryPolicy = updatedRetryPolicyOptions };
2741+
? updatedAzureKeyVaultOptions with { RetryPolicy = updatedRetryPolicyOptions, UserProvidedRetryPolicy = true }
2742+
: new AzureKeyVaultOptions(retryPolicy: updatedRetryPolicyOptions);
27412743
}
27422744

27432745
// Update runtime config if Azure Key Vault options were modified

src/Cli/Exporter.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ public static bool Export(ExportOptions options, ILogger logger, FileSystemRunti
4444
}
4545

4646
// Load the runtime configuration from the file
47-
if (!loader.TryLoadConfig(runtimeConfigFile, out RuntimeConfig? runtimeConfig, replaceEnvVar: true))
47+
DeserializationVariableReplacementSettings replacementSettings = new(azureKeyVaultOptions: null, doReplaceEnvVar: true, doReplaceAkvVar: true);
48+
if (!loader.TryLoadConfig(runtimeConfigFile, out RuntimeConfig? runtimeConfig, replacementSettings: replacementSettings))
4849
{
4950
logger.LogError("Failed to read the config file: {0}.", runtimeConfigFile);
5051
return false;

src/Config/Azure.DataApiBuilder.Config.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
<ItemGroup>
1717
<PackageReference Include="Azure.Identity" />
18+
<PackageReference Include="Azure.Security.KeyVault.Secrets" />
1819
<PackageReference Include="Microsoft.AspNetCore.Authorization" />
1920
<PackageReference Include="Microsoft.IdentityModel.Protocols" />
2021
<PackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" />

src/Config/Converters/AKVRetryPolicyOptionsConverterFactory.cs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ namespace Azure.DataApiBuilder.Config.Converters;
1212
/// </summary>
1313
internal class AKVRetryPolicyOptionsConverterFactory : JsonConverterFactory
1414
{
15-
// Determines whether to replace environment variable with its
16-
// value or not while deserializing.
17-
private bool _replaceEnvVar;
15+
// Settings for variable replacement during deserialization.
16+
// Currently allows for Azure Key Vault (via @akv('secret-name')) and Environment Variable replacement.
17+
private readonly DeserializationVariableReplacementSettings? _replacementSettings;
1818

1919
/// <inheritdoc/>
2020
public override bool CanConvert(Type typeToConvert)
@@ -25,34 +25,34 @@ public override bool CanConvert(Type typeToConvert)
2525
/// <inheritdoc/>
2626
public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options)
2727
{
28-
return new AKVRetryPolicyOptionsConverter(_replaceEnvVar);
28+
return new AKVRetryPolicyOptionsConverter(_replacementSettings);
2929
}
3030

31-
/// <param name="replaceEnvVar">Whether to replace environment variable with its
32-
/// value or not while deserializing.</param>
33-
internal AKVRetryPolicyOptionsConverterFactory(bool replaceEnvVar)
31+
/// <param name="replacementSettings">Settings for variable replacement during deserialization.
32+
/// If null, no variable replacement will be performed.</param>
33+
internal AKVRetryPolicyOptionsConverterFactory(DeserializationVariableReplacementSettings? replacementSettings = null)
3434
{
35-
_replaceEnvVar = replaceEnvVar;
35+
_replacementSettings = replacementSettings;
3636
}
3737

3838
private class AKVRetryPolicyOptionsConverter : JsonConverter<AKVRetryPolicyOptions>
3939
{
40-
// Determines whether to replace environment variable with its
41-
// value or not while deserializing.
42-
private bool _replaceEnvVar;
40+
// Settings for variable replacement during deserialization.
41+
// Currently allows for Azure Key Vault (via @akv('<secret>')) and Environment Variable replacement.
42+
private readonly DeserializationVariableReplacementSettings? _replacementSettings;
4343

44-
/// <param name="replaceEnvVar">Whether to replace environment variable with its
45-
/// value or not while deserializing.</param>
46-
public AKVRetryPolicyOptionsConverter(bool replaceEnvVar)
44+
/// <param name="replacementSettings">Settings for variable replacement during deserialization.
45+
/// If null, no variable replacement will be performed.</param>
46+
public AKVRetryPolicyOptionsConverter(DeserializationVariableReplacementSettings? replacementSettings)
4747
{
48-
_replaceEnvVar = replaceEnvVar;
48+
_replacementSettings = replacementSettings;
4949
}
5050

5151
/// <summary>
5252
/// Defines how DAB reads AKV Retry Policy options and defines which values are
5353
/// used to instantiate those options.
5454
/// </summary>
55-
/// <exception cref="JsonException">Thrown when improperly formatted cache options are provided.</exception>
55+
/// <exception cref="JsonException">Thrown when improperly formatted retry policy options are provided.</exception>
5656
public override AKVRetryPolicyOptions? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
5757
{
5858
if (reader.TokenType is JsonTokenType.StartObject)
@@ -82,7 +82,7 @@ public AKVRetryPolicyOptionsConverter(bool replaceEnvVar)
8282
}
8383
else
8484
{
85-
mode = EnumExtensions.Deserialize<AKVRetryPolicyMode>(reader.DeserializeString(_replaceEnvVar)!);
85+
mode = EnumExtensions.Deserialize<AKVRetryPolicyMode>(reader.DeserializeString(_replacementSettings)!);
8686
}
8787

8888
break;
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using System.Text.Json;
5+
using System.Text.Json.Serialization;
6+
using Azure.DataApiBuilder.Config.ObjectModel;
7+
8+
namespace Azure.DataApiBuilder.Config.Converters;
9+
10+
/// <summary>
11+
/// Converter factory for AzureKeyVaultOptions that can optionally perform variable replacement.
12+
/// </summary>
13+
internal class AzureKeyVaultOptionsConverterFactory : JsonConverterFactory
14+
{
15+
// Determines whether to replace environment variable with its
16+
// value or not while deserializing.
17+
private readonly DeserializationVariableReplacementSettings? _replacementSettings;
18+
19+
/// <param name="replacementSettings">How to handle variable replacement during deserialization.</param>
20+
internal AzureKeyVaultOptionsConverterFactory(DeserializationVariableReplacementSettings? replacementSettings = null)
21+
{
22+
_replacementSettings = replacementSettings;
23+
}
24+
25+
/// <inheritdoc/>
26+
public override bool CanConvert(Type typeToConvert)
27+
{
28+
return typeToConvert.IsAssignableTo(typeof(AzureKeyVaultOptions));
29+
}
30+
31+
/// <inheritdoc/>
32+
public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options)
33+
{
34+
return new AzureKeyVaultOptionsConverter(_replacementSettings);
35+
}
36+
37+
private class AzureKeyVaultOptionsConverter : JsonConverter<AzureKeyVaultOptions>
38+
{
39+
// Determines whether to replace environment variable with its
40+
// value or not while deserializing.
41+
private readonly DeserializationVariableReplacementSettings? _replacementSettings;
42+
43+
/// <param name="replaceEnvVar">Whether to replace environment variable with its
44+
/// value or not while deserializing.</param>
45+
public AzureKeyVaultOptionsConverter(DeserializationVariableReplacementSettings? replacementSettings)
46+
{
47+
_replacementSettings = replacementSettings;
48+
}
49+
50+
/// <summary>
51+
/// Reads AzureKeyVaultOptions with optional variable replacement.
52+
/// </summary>
53+
public override AzureKeyVaultOptions? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
54+
{
55+
if (reader.TokenType is JsonTokenType.Null)
56+
{
57+
return null;
58+
}
59+
60+
if (reader.TokenType is JsonTokenType.StartObject)
61+
{
62+
string? endpoint = null;
63+
AKVRetryPolicyOptions? retryPolicy = null;
64+
65+
while (reader.Read())
66+
{
67+
if (reader.TokenType is JsonTokenType.EndObject)
68+
{
69+
return new AzureKeyVaultOptions(endpoint, retryPolicy);
70+
}
71+
72+
string? property = reader.GetString();
73+
reader.Read();
74+
75+
switch (property)
76+
{
77+
case "endpoint":
78+
if (reader.TokenType is JsonTokenType.String)
79+
{
80+
endpoint = reader.DeserializeString(_replacementSettings);
81+
}
82+
83+
break;
84+
85+
case "retry-policy":
86+
if (reader.TokenType is JsonTokenType.StartObject)
87+
{
88+
// Uses the AKVRetryPolicyOptionsConverter to read the retry-policy object.
89+
retryPolicy = JsonSerializer.Deserialize<AKVRetryPolicyOptions>(ref reader, options);
90+
}
91+
92+
break;
93+
94+
default:
95+
throw new JsonException($"Unexpected property {property}");
96+
}
97+
}
98+
}
99+
100+
throw new JsonException("Invalid AzureKeyVaultOptions format");
101+
}
102+
103+
/// <summary>
104+
/// When writing the AzureKeyVaultOptions back to a JSON file, only write the properties
105+
/// if they are user provided. This avoids polluting the written JSON file with properties
106+
/// the user most likely omitted when writing the original DAB runtime config file.
107+
/// This Write operation is only used when a RuntimeConfig object is serialized to JSON.
108+
/// </summary>
109+
public override void Write(Utf8JsonWriter writer, AzureKeyVaultOptions value, JsonSerializerOptions options)
110+
{
111+
writer.WriteStartObject();
112+
113+
if (value?.UserProvidedEndpoint is true)
114+
{
115+
writer.WritePropertyName("endpoint");
116+
JsonSerializer.Serialize(writer, value.Endpoint, options);
117+
}
118+
119+
if (value?.UserProvidedRetryPolicy is true)
120+
{
121+
writer.WritePropertyName("retry-policy");
122+
JsonSerializer.Serialize(writer, value.RetryPolicy, options);
123+
}
124+
125+
writer.WriteEndObject();
126+
}
127+
}
128+
}

0 commit comments

Comments
 (0)