Skip to content

Commit 0acc671

Browse files
committed
fix: replace env defaults for stored procedures
1 parent d50e607 commit 0acc671

3 files changed

Lines changed: 180 additions & 3 deletions

File tree

src/Config/Converters/EntitySourceConverterFactory.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public EntitySourceConverter(DeserializationVariableReplacementSettings? replace
6565
List<ParameterMetadata> paramList = [];
6666
foreach (JsonProperty prop in parametersElement.EnumerateObject())
6767
{
68-
string? defaultValue = GetClrValue(prop.Value)?.ToString();
68+
string? defaultValue = GetClrValue(prop.Value, _replacementSettings)?.ToString();
6969
paramList.Add(new ParameterMetadata
7070
{
7171
Name = prop.Name,
@@ -112,18 +112,25 @@ public EntitySourceConverter(DeserializationVariableReplacementSettings? replace
112112
}
113113
}
114114

115-
private static object GetClrValue(JsonElement element)
115+
private static object GetClrValue(JsonElement element, DeserializationVariableReplacementSettings? replacementSettings)
116116
{
117117
return element.ValueKind switch
118118
{
119-
JsonValueKind.String => element.GetString() ?? string.Empty,
119+
JsonValueKind.String => DeserializeStringValue(element, replacementSettings) ?? string.Empty,
120120
JsonValueKind.Number => GetNumberValue(element),
121121
JsonValueKind.True => true,
122122
JsonValueKind.False => false,
123123
_ => element.ToString()
124124
};
125125
}
126126

127+
private static string? DeserializeStringValue(JsonElement element, DeserializationVariableReplacementSettings? replacementSettings)
128+
{
129+
Utf8JsonReader reader = new(JsonSerializer.SerializeToUtf8Bytes(element.GetString()));
130+
reader.Read();
131+
return reader.DeserializeString(replacementSettings);
132+
}
133+
127134
/// <summary>
128135
/// Attempts to get the correct numeric value from the <see cref="JsonElement"/>.
129136
/// If all possible numeric values are exhausted, the raw text is returned.

src/Service.Tests/UnitTests/RuntimeConfigLoaderJsonDeserializerTests.cs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,62 @@ public void TestConfigParsingWithEnvVarReplacement(bool replaceEnvVar, string da
165165
ClearEnvironmentVariablesFromDictionary(_environmentFileContentDict);
166166
}
167167

168+
[DataTestMethod]
169+
[DataRow(false, "@env('year-end')", DisplayName = "Do not replace stored procedure parameter default environment variable.")]
170+
[DataRow(true, "2024-06-30", DisplayName = "Replace stored procedure parameter default environment variable.")]
171+
public void TestStoredProcedureParameterDefaultEnvVarReplacement(bool replaceEnvVar, string expectedDefaultValue)
172+
{
173+
const string envVarName = "year-end";
174+
Environment.SetEnvironmentVariable(envVarName, "2024-06-30");
175+
176+
try
177+
{
178+
string configJson = @"
179+
{
180+
""data-source"": {
181+
""database-type"": ""mssql"",
182+
""connection-string"": ""Server=tcp:127.0.0.1,1433;Persist Security Info=False;Trusted_Connection=True;TrustServerCertificate=True;MultipleActiveResultSets=False;Connection Timeout=5;""
183+
},
184+
""entities"": {
185+
""GetRecordsByDate"": {
186+
""source"": {
187+
""object"": ""get_records_by_date"",
188+
""type"": ""stored-procedure"",
189+
""parameters"": {
190+
""YearEndDate"": ""@env('year-end')""
191+
}
192+
},
193+
""permissions"": [
194+
{
195+
""role"": ""anonymous"",
196+
""actions"": [
197+
{
198+
""action"": ""execute""
199+
}
200+
]
201+
}
202+
]
203+
}
204+
}
205+
}";
206+
207+
bool isParsingSuccessful = RuntimeConfigLoader.TryParseConfig(
208+
configJson,
209+
out RuntimeConfig runtimeConfig,
210+
replacementSettings: new DeserializationVariableReplacementSettings(
211+
azureKeyVaultOptions: null,
212+
doReplaceEnvVar: replaceEnvVar,
213+
doReplaceAkvVar: false));
214+
215+
Assert.IsTrue(isParsingSuccessful);
216+
Assert.AreEqual(expectedDefaultValue, runtimeConfig.Entities["GetRecordsByDate"].Source.Parameters[0].Default);
217+
}
218+
finally
219+
{
220+
Environment.SetEnvironmentVariable(envVarName, null);
221+
}
222+
}
223+
168224
/// <summary>
169225
/// Test the parsing of DataSource section in runtime config for cosmosdb_nosql
170226
/// where under cosmosDb options has database as null, container is not provided, and schema is an empty string.
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Data;
7+
using Azure.DataApiBuilder.Auth;
8+
using Azure.DataApiBuilder.Config;
9+
using Azure.DataApiBuilder.Config.DatabasePrimitives;
10+
using Azure.DataApiBuilder.Config.ObjectModel;
11+
using Azure.DataApiBuilder.Core.Models;
12+
using Azure.DataApiBuilder.Core.Resolvers;
13+
using Azure.DataApiBuilder.Core.Services;
14+
using Microsoft.VisualStudio.TestTools.UnitTesting;
15+
using Moq;
16+
17+
namespace Azure.DataApiBuilder.Service.Tests.UnitTests
18+
{
19+
[TestClass]
20+
public class SqlExecuteStructureUnitTests
21+
{
22+
[TestMethod]
23+
public void ConfigDefaultDateTimeParameterIsResolvedAsDateTime()
24+
{
25+
const string entityName = "GetRecordsByDate";
26+
const string parameterName = "YearEndDate";
27+
const string envVarName = "year-end";
28+
DateTime expectedDate = new(year: 2024, month: 6, day: 30, hour: 0, minute: 0, second: 0, kind: DateTimeKind.Utc);
29+
30+
Environment.SetEnvironmentVariable(envVarName, "2024-06-30");
31+
try
32+
{
33+
Assert.IsTrue(RuntimeConfigLoader.TryParseConfig(
34+
GetStoredProcedureConfigWithEnvDefault(),
35+
out RuntimeConfig runtimeConfig,
36+
replacementSettings: new DeserializationVariableReplacementSettings(
37+
azureKeyVaultOptions: null,
38+
doReplaceEnvVar: true,
39+
doReplaceAkvVar: false)));
40+
41+
StoredProcedureDefinition storedProcedureDefinition = new();
42+
storedProcedureDefinition.Parameters.Add(
43+
parameterName,
44+
new ParameterDefinition
45+
{
46+
SystemType = typeof(DateTime),
47+
DbType = DbType.DateTime,
48+
HasConfigDefault = true,
49+
ConfigDefaultValue = runtimeConfig.Entities[entityName].Source.Parameters[0].Default
50+
});
51+
52+
DatabaseStoredProcedure storedProcedure = new(schemaName: "dbo", tableName: "get_records_by_date")
53+
{
54+
SourceType = EntitySourceType.StoredProcedure,
55+
StoredProcedureDefinition = storedProcedureDefinition
56+
};
57+
58+
Mock<ISqlMetadataProvider> metadataProvider = new();
59+
metadataProvider.SetupGet(x => x.EntityToDatabaseObject).Returns(new Dictionary<string, DatabaseObject>
60+
{
61+
{ entityName, storedProcedure }
62+
});
63+
metadataProvider.Setup(x => x.GetStoredProcedureDefinition(entityName)).Returns(storedProcedureDefinition);
64+
metadataProvider.Setup(x => x.GetSourceDefinition(entityName)).Returns(storedProcedureDefinition);
65+
66+
SqlExecuteStructure executeStructure = new(
67+
entityName,
68+
metadataProvider.Object,
69+
Mock.Of<IAuthorizationResolver>(),
70+
null,
71+
new Dictionary<string, object?>());
72+
73+
string dbConnectionParameterName = (string)executeStructure.ProcedureParameters[parameterName];
74+
Assert.AreEqual(expectedDate, executeStructure.Parameters[dbConnectionParameterName].Value);
75+
}
76+
finally
77+
{
78+
Environment.SetEnvironmentVariable(envVarName, null);
79+
}
80+
}
81+
82+
private static string GetStoredProcedureConfigWithEnvDefault()
83+
{
84+
return @"
85+
{
86+
""data-source"": {
87+
""database-type"": ""mssql"",
88+
""connection-string"": ""Server=tcp:127.0.0.1,1433;Persist Security Info=False;Trusted_Connection=True;TrustServerCertificate=True;MultipleActiveResultSets=False;Connection Timeout=5;""
89+
},
90+
""entities"": {
91+
""GetRecordsByDate"": {
92+
""source"": {
93+
""object"": ""get_records_by_date"",
94+
""type"": ""stored-procedure"",
95+
""parameters"": {
96+
""YearEndDate"": ""@env('year-end')""
97+
}
98+
},
99+
""permissions"": [
100+
{
101+
""role"": ""anonymous"",
102+
""actions"": [
103+
{
104+
""action"": ""execute""
105+
}
106+
]
107+
}
108+
]
109+
}
110+
}
111+
}";
112+
}
113+
}
114+
}

0 commit comments

Comments
 (0)