Skip to content

Commit a3bdde8

Browse files
authored
Include empty entries when building InvocationRequest payload for OOP workers (#8642)
* Adding empty entries when preparing the RpcInvocation payload sent to OOP workers based on a new worker capability we are introducing "IncludeEmptyEntriesInMessagePayload" * Removing capability check * Updated release notesRevert "Removing capability check" This reverts commit 52303be4
1 parent b77d766 commit a3bdde8

File tree

4 files changed

+61
-5
lines changed

4 files changed

+61
-5
lines changed

release_notes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
- Updated Java Worker Version to [2.4.0](https://github.com/Azure/azure-functions-java-worker/releases/tag/2.4.0)
1010
- Update PowerShell Worker 7.0 to 4.0.2255 [Release Note](https://github.com/Azure/azure-functions-powershell-worker/releases/tag/v4.0.2255)
1111
- Update PowerShell Worker 7.2 to 4.0.2258 [Release Note](https://github.com/Azure/azure-functions-powershell-worker/releases/tag/v4.0.2258)
12+
- Adding support to conditionally include empty entries from trigger payload when sending to OOP workers. ([#8499](https://github.com/Azure/azure-functions-host/issues/8499))
1213

1314
**Release sprint:** Sprint 128
1415
[ [bugs](https://github.com/Azure/azure-functions-host/issues?q=is%3Aissue+milestone%3A%22Functions+Sprint+128%22+label%3Abug+is%3Aclosed) | [features](https://github.com/Azure/azure-functions-host/issues?q=is%3Aissue+milestone%3A%22Functions+Sprint+128%22+label%3Afeature+is%3Aclosed) ]

src/WebJobs.Script.Grpc/MessageExtensions/GrpcMessageConversionExtensions.cs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ public static ValueTask<TypedData> ToRpc(this object value, ILogger logger, Grpc
5454
string str => new TypedData() { String = str },
5555
double dbl => new TypedData() { Double = dbl },
5656
byte[][] arrBytes when IsTypedDataCollectionSupported(capabilities) => arrBytes.ToRpcByteArray(),
57-
string[] arrStr when IsTypedDataCollectionSupported(capabilities) => arrStr.ToRpcStringArray(),
57+
string[] arrStr when IsTypedDataCollectionSupported(capabilities) => arrStr.ToRpcStringArray(
58+
ShouldIncludeEmptyEntriesInMessagePayload(capabilities)),
5859
double[] arrDouble when IsTypedDataCollectionSupported(capabilities) => arrDouble.ToRpcDoubleArray(),
5960
long[] arrLong when IsTypedDataCollectionSupported(capabilities) => arrLong.ToRpcLongArray(),
6061
_ => value.ToRpcDefault(),
@@ -203,7 +204,7 @@ private static async Task PopulateBodyAndRawBody(HttpRequest request, RpcHttp ht
203204
rawBodyString = await request.ReadAsStringAsync();
204205
try
205206
{
206-
// REVIEW: We are json deserializing this to a JObject only to serialze
207+
// REVIEW: We are json deserializing this to a JObject only to serialize
207208
// it back to string below. Why?
208209
body = JsonConvert.DeserializeObject(rawBodyString);
209210
}
@@ -273,16 +274,25 @@ internal static TypedData ToRpcByteArray(this byte[][] arrBytes)
273274
return typedData;
274275
}
275276

276-
internal static TypedData ToRpcStringArray(this string[] arrString)
277+
internal static TypedData ToRpcStringArray(this string[] arrString, bool includeEmptyEntries)
277278
{
278279
TypedData typedData = new TypedData();
279280
CollectionString collectionString = new CollectionString();
280281
foreach (string element in arrString)
281282
{
282-
if (!string.IsNullOrEmpty(element))
283+
// Don't add null entries.("Add" method below will throw)
284+
if (element is null)
283285
{
284-
collectionString.String.Add(element);
286+
continue;
285287
}
288+
289+
// Empty string entries are okay to add based on includeEmptyEntries param value.
290+
if (element == string.Empty && !includeEmptyEntries)
291+
{
292+
continue;
293+
}
294+
295+
collectionString.String.Add(element);
286296
}
287297
typedData.CollectionString = collectionString;
288298

@@ -315,6 +325,11 @@ internal static TypedData ToRpcLongArray(this long[] arrLong)
315325
return typedData;
316326
}
317327

328+
private static bool ShouldIncludeEmptyEntriesInMessagePayload(GrpcCapabilities capabilities)
329+
{
330+
return !string.IsNullOrWhiteSpace(capabilities.GetCapabilityState(RpcWorkerConstants.IncludeEmptyEntriesInMessagePayload));
331+
}
332+
318333
private static bool IsRawBodyBytesRequested(GrpcCapabilities capabilities)
319334
{
320335
return !string.IsNullOrEmpty(capabilities.GetCapabilityState(RpcWorkerConstants.RawHttpBodyBytes));

src/WebJobs.Script/Workers/Rpc/RpcWorkerConstants.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ public static class RpcWorkerConstants
5555
public const string HandlesWorkerTerminateMessage = "HandlesWorkerTerminateMessage";
5656
public const string HandlesInvocationCancelMessage = "HandlesInvocationCancelMessage";
5757

58+
/// <summary>
59+
/// Indicates whether empty entries in the trigger message should be included when sending RpcInvocation data to OOP workers.
60+
/// </summary>
61+
public const string IncludeEmptyEntriesInMessagePayload = "IncludeEmptyEntriesInMessagePayload";
62+
5863
// Host Capabilities
5964
public const string V2Compatable = "V2Compatable";
6065

test/WebJobs.Script.Tests/Workers/Rpc/GrpcMessageConversionExtensionsTests.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,41 @@ public async Task ToRpc_Collection_String_Without_Capabilities_Value()
485485
Assert.Equal(typedData.Json, returned_typedata.Json);
486486
}
487487

488+
[Fact]
489+
public async Task ToRpc_Collection_String_IgnoreEmptyEntries_When_Capability_Is_Not_Present()
490+
{
491+
var logger = MockNullLoggerFactory.CreateLogger();
492+
var capabilities = new GrpcCapabilities(logger);
493+
capabilities.UpdateCapabilities(new MapField<string, string>
494+
{
495+
{ RpcWorkerConstants.TypedDataCollection, bool.TrueString },
496+
});
497+
498+
string[] arrString = { "element1", string.Empty, "element_2" };
499+
TypedData actual = await arrString.ToRpc(logger, capabilities);
500+
501+
var expected = new RepeatedField<string> { "element1", "element_2" };
502+
Assert.Equal(expected, actual.CollectionString.String);
503+
}
504+
505+
[Fact]
506+
public async Task ToRpc_Collection_String_IncludeEmptyEntries_When_Capability_Is_Present()
507+
{
508+
var logger = MockNullLoggerFactory.CreateLogger();
509+
var capabilities = new GrpcCapabilities(logger);
510+
capabilities.UpdateCapabilities(new MapField<string, string>
511+
{
512+
{ RpcWorkerConstants.TypedDataCollection, bool.TrueString },
513+
{ RpcWorkerConstants.IncludeEmptyEntriesInMessagePayload, bool.TrueString }
514+
});
515+
516+
string[] arrString = { "element1", string.Empty, "element_2", null };
517+
TypedData actual = await arrString.ToRpc(logger, capabilities);
518+
519+
var expected = new RepeatedField<string> { "element1", string.Empty, "element_2" }; // null entry should be still skipped
520+
Assert.Equal(expected, actual.CollectionString.String);
521+
}
522+
488523
[Fact]
489524
public async Task ToRpc_Collection_Long_With_Capabilities_Value()
490525
{

0 commit comments

Comments
 (0)