Skip to content

Commit ef63b6b

Browse files
authored
Fix for ISO string modification during function metadata binding parsing (#10735)
1 parent 6323cca commit ef63b6b

File tree

3 files changed

+49
-13
lines changed

3 files changed

+49
-13
lines changed

release_notes.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,6 @@
1111
- [Added net9 prelaunch app.](https://github.com/Azure/azure-functions-dotnet-worker/pull/2898)
1212
- Update the `DefaultHttpProxyService` to better handle client disconnect scenarios (#10688)
1313
- Replaced `InvalidOperationException` with `HttpForwardingException` when there is a ForwarderError
14+
- Fix modification of ISO strings during the parsing of function metadata bindings.
15+
- This fixes the listener errors related to the CosmosDB trigger's `StartFromTime` parameter. (#10735)
1416
- Updated `WebJobs.Script` to target .NET 8 (instead of .NET Standard 2.1)

src/WebJobs.Script/Host/WorkerFunctionMetadataProvider.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using Microsoft.Azure.WebJobs.Script.Workers.Rpc;
1414
using Microsoft.Extensions.Logging;
1515
using Microsoft.Extensions.Options;
16+
using Newtonsoft.Json;
1617
using Newtonsoft.Json.Linq;
1718

1819
namespace Microsoft.Azure.WebJobs.Script
@@ -26,6 +27,7 @@ internal class WorkerFunctionMetadataProvider : IWorkerFunctionMetadataProvider
2627
private readonly IEnvironment _environment;
2728
private readonly IWebHostRpcWorkerChannelManager _channelManager;
2829
private readonly IScriptHostManager _scriptHostManager;
30+
private readonly JsonSerializerSettings _dateTimeSerializerSettings;
2931
private string _workerRuntime;
3032
private ImmutableArray<FunctionMetadata> _functions;
3133

@@ -42,6 +44,7 @@ public WorkerFunctionMetadataProvider(
4244
_channelManager = webHostRpcWorkerChannelManager;
4345
_scriptHostManager = scriptHostManager;
4446
_workerRuntime = _environment.GetEnvironmentVariable(EnvironmentSettingNames.FunctionWorkerRuntime);
47+
_dateTimeSerializerSettings = new JsonSerializerSettings { DateParseHandling = DateParseHandling.None };
4548
}
4649

4750
public ImmutableDictionary<string, ImmutableArray<string>> FunctionErrors
@@ -146,7 +149,7 @@ public async Task<FunctionMetadataResult> GetFunctionMetadataAsync(IEnumerable<R
146149
return new FunctionMetadataResult(useDefaultMetadataIndexing: false, _functions);
147150
}
148151

149-
internal static void ValidateFunctionAppFormat(string scriptPath, ILogger logger, IEnvironment environment, IFileSystem fileSystem = null)
152+
internal void ValidateFunctionAppFormat(string scriptPath, ILogger logger, IEnvironment environment, IFileSystem fileSystem = null)
150153
{
151154
fileSystem = fileSystem ?? FileUtility.Instance;
152155
bool mixedApp = false;
@@ -244,13 +247,14 @@ internal IEnumerable<FunctionMetadata> ValidateMetadata(IEnumerable<RawFunctionM
244247
return validatedMetadata;
245248
}
246249

247-
internal static FunctionMetadata ValidateBindings(IEnumerable<string> rawBindings, FunctionMetadata function)
250+
internal FunctionMetadata ValidateBindings(IEnumerable<string> rawBindings, FunctionMetadata function)
248251
{
249252
HashSet<string> bindingNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
250253

251254
foreach (string binding in rawBindings)
252255
{
253-
var functionBinding = BindingMetadata.Create(JObject.Parse(binding));
256+
var deserializedObj = JsonConvert.DeserializeObject<JObject>(binding, _dateTimeSerializerSettings);
257+
var functionBinding = BindingMetadata.Create(deserializedObj);
254258

255259
Utility.ValidateBinding(functionBinding);
256260

test/WebJobs.Script.Tests/WorkerFunctionMetadataProviderTests.cs

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,34 @@
88
using System.Linq;
99
using System.Threading.Tasks;
1010
using Microsoft.Azure.WebJobs.Script.Description;
11-
using Microsoft.Azure.WebJobs.Script.Grpc.Messages;
12-
using Microsoft.Azure.WebJobs.Script.Workers;
1311
using Microsoft.Azure.WebJobs.Script.Workers.Rpc;
1412
using Microsoft.Extensions.Logging;
13+
using Microsoft.Extensions.Options;
1514
using Moq;
16-
using NuGet.ContentModel;
1715
using Xunit;
1816

1917
namespace Microsoft.Azure.WebJobs.Script.Tests
2018
{
2119
public class WorkerFunctionMetadataProviderTests
2220
{
21+
private readonly WorkerFunctionMetadataProvider _workerFunctionMetadataProvider;
22+
23+
public WorkerFunctionMetadataProviderTests()
24+
{
25+
var mockScriptOptions = new Mock<IOptionsMonitor<ScriptApplicationHostOptions>>();
26+
var mockLogger = new Mock<ILogger<WorkerFunctionMetadataProvider>>();
27+
var mockEnvironment = new Mock<IEnvironment>();
28+
var mockChannelManager = new Mock<IWebHostRpcWorkerChannelManager>();
29+
var mockScriptHostManager = new Mock<IScriptHostManager>();
30+
31+
_workerFunctionMetadataProvider = new WorkerFunctionMetadataProvider(
32+
mockScriptOptions.Object,
33+
mockLogger.Object,
34+
mockEnvironment.Object,
35+
mockChannelManager.Object,
36+
mockScriptHostManager.Object);
37+
}
38+
2339
[Fact]
2440
public void ValidateBindings_NoBindings_Throws()
2541
{
@@ -28,7 +44,7 @@ public void ValidateBindings_NoBindings_Throws()
2844

2945
var ex = Assert.Throws<FormatException>(() =>
3046
{
31-
WorkerFunctionMetadataProvider.ValidateBindings(rawBindings, functionMetadata);
47+
_workerFunctionMetadataProvider.ValidateBindings(rawBindings, functionMetadata);
3248
});
3349

3450
Assert.Equal("At least one binding must be declared.", ex.Message);
@@ -45,7 +61,7 @@ public void ValidateBindings_DuplicateBindingNames_Throws()
4561

4662
var ex = Assert.Throws<InvalidOperationException>(() =>
4763
{
48-
WorkerFunctionMetadataProvider.ValidateBindings(rawBindings, functionMetadata);
64+
_workerFunctionMetadataProvider.ValidateBindings(rawBindings, functionMetadata);
4965
});
5066

5167
Assert.Equal("Multiple bindings with name 'dupe' discovered. Binding names must be unique.", ex.Message);
@@ -60,7 +76,7 @@ public void ValidateBindings_NoTriggerBinding_Throws()
6076

6177
var ex = Assert.Throws<InvalidOperationException>(() =>
6278
{
63-
WorkerFunctionMetadataProvider.ValidateBindings(rawBindings, functionMetadata);
79+
_workerFunctionMetadataProvider.ValidateBindings(rawBindings, functionMetadata);
6480
});
6581

6682
Assert.Equal("No trigger binding specified. A function must have a trigger input binding.", ex.Message);
@@ -81,7 +97,7 @@ public void ValidateBindings_InvalidName_Throws(string bindingName)
8197

8298
var ex = Assert.Throws<ArgumentException>(() =>
8399
{
84-
WorkerFunctionMetadataProvider.ValidateBindings(rawBindings, functionMetadata);
100+
_workerFunctionMetadataProvider.ValidateBindings(rawBindings, functionMetadata);
85101
});
86102

87103
Assert.Equal($"The binding name {bindingName} is invalid. Please assign a valid name to the binding.", ex.Message);
@@ -108,7 +124,7 @@ public void ValidateBindings_ValidName_DoesNotThrow(string bindingName)
108124

109125
try
110126
{
111-
WorkerFunctionMetadataProvider.ValidateBindings(rawBindings, functionMetadata);
127+
_workerFunctionMetadataProvider.ValidateBindings(rawBindings, functionMetadata);
112128
}
113129
catch (ArgumentException)
114130
{
@@ -123,7 +139,7 @@ public void ValidateFunctionAppFormat_InputMixedApp()
123139
logger.ClearLogMessages();
124140
string scriptPath = Path.Combine(Environment.CurrentDirectory, @"..", "..", "..", "..", "sample", "node");
125141
var environment = SystemEnvironment.Instance;
126-
WorkerFunctionMetadataProvider.ValidateFunctionAppFormat(scriptPath, logger, environment);
142+
_workerFunctionMetadataProvider.ValidateFunctionAppFormat(scriptPath, logger, environment);
127143
var traces = logger.GetLogMessages();
128144
var functionLoadLogs = traces.Where(m => m.FormattedMessage.Contains("Detected mixed function app. Some functions may not be indexed"));
129145
Assert.True(functionLoadLogs.Any());
@@ -139,7 +155,7 @@ public void ValidateBindings_OutputNameWithoutDirection_Throws()
139155

140156
var ex = Assert.Throws<ArgumentException>(() =>
141157
{
142-
WorkerFunctionMetadataProvider.ValidateBindings(rawBindings, functionMetadata);
158+
_workerFunctionMetadataProvider.ValidateBindings(rawBindings, functionMetadata);
143159
});
144160

145161
Assert.Equal($"{ScriptConstants.SystemReturnParameterBindingName} bindings must specify a direction of 'out'.", ex.Message);
@@ -180,5 +196,19 @@ public async void ValidateFunctionMetadata_Logging()
180196
Assert.Equal("Reading functions metadata (Worker)", traces[1].FormattedMessage);
181197
// The third log is Host is running without any initialized channels, restarting the JobHost. This is not relevant to this test.
182198
}
199+
200+
[Fact]
201+
public void ValidateFunctionMetadata_IsoStringNotAltered()
202+
{
203+
FunctionMetadata functionMetadata = new FunctionMetadata();
204+
List<string> rawBindings = new List<string>();
205+
var isoString = "2025-02-10T22:45:33Z";
206+
rawBindings.Add("{\"type\": \"cosmosDBTrigger\",\"name\": \"cosmosTrigger\",\"direction\": \"in\",\"databaseName\":\"databaseName\"," +
207+
"\"containerName\":\"containerNameFoo\",\"leaseContainerName\":\"leaseContanerFoo\",\"createLeaseContainerIfNotExists\":true," +
208+
"\"connection\":\"CosmosConnection\",\"startFromTime\":\"" + isoString + "\",\"dataType\":\"String\"}");
209+
210+
var function = _workerFunctionMetadataProvider.ValidateBindings(rawBindings, functionMetadata);
211+
Assert.Equal(isoString, function.Bindings.FirstOrDefault().Raw["startFromTime"].ToString());
212+
}
183213
}
184214
}

0 commit comments

Comments
 (0)