Refactor EventGrid extension converter, tests, and samples (#1695, #1716, #1727, #1723)

This commit is contained in:
Lilian Kasem 2023-07-13 12:27:09 -07:00 коммит произвёл Lilian Kasem
Родитель f10d01c4a2
Коммит 67b642a42d
16 изменённых файлов: 446 добавлений и 417 удалений

Просмотреть файл

@ -7,11 +7,11 @@ using Microsoft.Azure.Functions.Worker.Extensions.EventGrid.TypeConverters;
namespace Microsoft.Azure.Functions.Worker
{
[AllowConverterFallback(true)]
[InputConverter(typeof(EventGridCloudEventConverter))]
[InputConverter(typeof(EventGridEventConverter))]
[InputConverter(typeof(EventGridBinaryDataConverter))]
[InputConverter(typeof(EventGridStringArrayConverter))]
[ConverterFallbackBehavior(ConverterFallbackBehavior.Default)]
public sealed class EventGridTriggerAttribute : TriggerBindingAttribute
{
private bool _isBatched = false;

Просмотреть файл

@ -3,76 +3,42 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Azure.Functions.Worker.Converters;
namespace Microsoft.Azure.Functions.Worker.Extensions.EventGrid.TypeConverters
{
/// <summary>
/// Converter to bind to BinaryData or BinaryData[] parameter.
/// Converter to bind to <see cref="BinaryData" /> or <see cref="BinaryData[]" /> type parameters.
/// </summary>
[SupportedConverterType(typeof(BinaryData))]
[SupportedConverterType(typeof(BinaryData[]))]
internal class EventGridBinaryDataConverter : IInputConverter
[SupportedTargetType(typeof(BinaryData))]
[SupportedTargetType(typeof(BinaryData[]))]
internal class EventGridBinaryDataConverter : EventGridConverterBase
{
public ValueTask<ConversionResult> ConvertAsync(ConverterContext context)
protected override ConversionResult ConvertCore(Type targetType, string json)
{
try
ConversionResult result = targetType switch
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
Type t when t == typeof(BinaryData) => ConversionResult.Success(BinaryData.FromString(json)),
Type t when t == typeof(BinaryData[]) => ConversionResult.Success(ConvertToBinaryDataArray(json)),
_ => ConversionResult.Failed(new InvalidOperationException($"'{targetType.Name}' is not supported by this converter."))
};
return result;
}
if (context.Source is not string contextSource)
private BinaryData[] ConvertToBinaryDataArray(string json)
{
return new(ConversionResult.Failed(new InvalidOperationException("Context source must be a non-null string. Current type of context source is " + context?.Source?.GetType())));
var data = JsonSerializer.Deserialize<List<object>>(json);
var result = data.Select(item => BinaryData.FromString(item.ToString())).ToArray();
if (result is null)
{
throw new Exception("Unable to convert to BinaryData[].");
}
var targetType = context.TargetType;
switch (targetType)
{
case Type t when t == typeof(BinaryData):
return new(ConversionResult.Success((BinaryData.FromString(contextSource))));
case Type t when t == typeof(BinaryData[]):
return new(ConversionResult.Success(ConvertToBinaryDataArray(contextSource)));
}
}
catch (JsonException ex)
{
string msg = String.Format(CultureInfo.CurrentCulture,
@"Binding parameters to complex objects uses JSON serialization.
1. Bind the parameter type as 'string' instead to get the raw values and avoid JSON deserialization, or
2. Change the event payload to be valid json.");
return new(ConversionResult.Failed(new InvalidOperationException(msg, ex)));
}
catch (Exception ex)
{
return new(ConversionResult.Failed(ex));
}
return new(ConversionResult.Unhandled());
}
private BinaryData?[]? ConvertToBinaryDataArray(string contextSource)
{
var jsonData = JsonSerializer.Deserialize(contextSource, typeof(List<object>)) as List<object>;
List<BinaryData?> binaryDataList = new List<BinaryData?>();
if (jsonData is not null)
{
foreach (var item in jsonData)
{
var binaryData = item == null? null: BinaryData.FromString(item.ToString());
binaryDataList.Add(binaryData);
}
}
return binaryDataList.ToArray();
return result;
}
}
}

Просмотреть файл

@ -2,32 +2,29 @@
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
using System.Text.Json;
using Azure.Messaging;
using Microsoft.Azure.Functions.Worker.Converters;
namespace Microsoft.Azure.Functions.Worker.Extensions.EventGrid.TypeConverters
{
/// <summary>
/// Converter to bind to CloudEvent or CloudEvent[] parameter.
/// Converter to bind to <see cref="CloudEvent" /> or <see cref="CloudEvent[]" /> type parameters.
/// </summary>
[SupportedConverterType(typeof(CloudEvent))]
[SupportedConverterType(typeof(CloudEvent[]))]
internal class EventGridCloudEventConverter: IInputConverter
[SupportedTargetType(typeof(CloudEvent))]
[SupportedTargetType(typeof(CloudEvent[]))]
internal class EventGridCloudEventConverter: EventGridConverterBase
{
public ValueTask<ConversionResult> ConvertAsync(ConverterContext context)
protected override ConversionResult ConvertCore(Type targetType, string json)
{
if (context is null)
var cloudEvent = JsonSerializer.Deserialize(json, targetType);
if (cloudEvent is null)
{
return new(ConversionResult.Failed(new ArgumentNullException(nameof(context))));
return ConversionResult.Failed(new Exception("Unable to convert to CloudEvent."));
}
if (context.TargetType != typeof(CloudEvent) && context.TargetType != typeof(CloudEvent[]))
{
return new(ConversionResult.Unhandled());
}
return EventGridHelper.DeserializeToTargetType(context);
return ConversionResult.Success(cloudEvent);
}
}
}

Просмотреть файл

@ -0,0 +1,50 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
using System.Globalization;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Azure.Functions.Worker.Converters;
namespace Microsoft.Azure.Functions.Worker
{
internal abstract class EventGridConverterBase : IInputConverter
{
public EventGridConverterBase() { }
public ValueTask<ConversionResult> ConvertAsync(ConverterContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
try
{
if (context.Source is not string json)
{
throw new InvalidOperationException("Context source must be a non-null string");
}
var result = ConvertCore(context.TargetType, json);
return new ValueTask<ConversionResult>(result);
}
catch (JsonException ex)
{
string msg = String.Format(CultureInfo.CurrentCulture,
@"Binding parameters to complex objects uses JSON serialization.
1. Bind the parameter type as 'string' instead to get the raw values and avoid JSON deserialization, or
2. Change the queue payload to be valid json.");
return new ValueTask<ConversionResult>(ConversionResult.Failed(new InvalidOperationException(msg, ex)));
}
catch (Exception ex)
{
return new ValueTask<ConversionResult>(ConversionResult.Failed(ex));
}
}
protected abstract ConversionResult ConvertCore(Type targetType, string json);
}
}

Просмотреть файл

@ -2,32 +2,29 @@
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
using System.Text.Json;
using Azure.Messaging.EventGrid;
using Microsoft.Azure.Functions.Worker.Converters;
namespace Microsoft.Azure.Functions.Worker.Extensions.EventGrid.TypeConverters
{
/// <summary>
/// Converter to bind to EventGridEvent or EventGridEvent[] parameter.
/// Converter to bind to <see cref="EventGridEvent" /> or <see cref="EventGridEvent[]" /> type parameters.
/// </summary>
[SupportedConverterType(typeof(EventGridEvent))]
[SupportedConverterType(typeof(EventGridEvent[]))]
internal class EventGridEventConverter : IInputConverter
[SupportedTargetType(typeof(EventGridEvent))]
[SupportedTargetType(typeof(EventGridEvent[]))]
internal class EventGridEventConverter : EventGridConverterBase
{
public ValueTask<ConversionResult> ConvertAsync(ConverterContext context)
protected override ConversionResult ConvertCore(Type targetType, string json)
{
if (context is null)
var eventGridEvent = JsonSerializer.Deserialize(json, targetType);
if (eventGridEvent is null)
{
return new(ConversionResult.Failed(new ArgumentNullException(nameof(context))));
return ConversionResult.Failed(new Exception("Unable to convert to EventGridEvent."));
}
if (context?.TargetType != typeof(EventGridEvent) && context?.TargetType != typeof(EventGridEvent[]))
{
return new(ConversionResult.Unhandled());
}
return EventGridHelper.DeserializeToTargetType(context);
return ConversionResult.Success(eventGridEvent);
}
}
}

Просмотреть файл

@ -1,42 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
using System.Globalization;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Azure.Functions.Worker.Converters;
namespace Microsoft.Azure.Functions.Worker.Extensions.EventGrid.TypeConverters
{
internal static class EventGridHelper
{
internal static ValueTask<ConversionResult> DeserializeToTargetType(ConverterContext context)
{
try
{
if (context.Source is not string contextSource)
{
return new(ConversionResult.Failed(new InvalidOperationException("Context source must be a non-null string. Current type of context source is " + context?.Source?.GetType())));
}
var targetType = context.TargetType;
var item = JsonSerializer.Deserialize(contextSource, targetType);
return new(ConversionResult.Success(item));
}
catch (JsonException ex)
{
string msg = String.Format(CultureInfo.CurrentCulture,
@"Binding parameters to complex objects uses JSON serialization.
1. Bind the parameter type as 'string' instead to get the raw values and avoid JSON deserialization, or
2. Change the event payload to be valid json.");
return new(ConversionResult.Failed(new InvalidOperationException(msg, ex)));
}
catch (Exception ex)
{
return new(ConversionResult.Failed(ex));
}
}
}
}

Просмотреть файл

@ -3,62 +3,29 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Azure.Functions.Worker.Converters;
namespace Microsoft.Azure.Functions.Worker.Extensions.EventGrid.TypeConverters
{
/// <summary>
/// Converter to bind to string[] parameter.
/// Converter to bind to <see cref="string[]" /> type parameters.
/// </summary>
[SupportedConverterType(typeof(string[]))]
internal class EventGridStringArrayConverter : IInputConverter
[SupportedTargetType(typeof(string[]))]
internal class EventGridStringArrayConverter : EventGridConverterBase
{
public ValueTask<ConversionResult> ConvertAsync(ConverterContext context)
protected override ConversionResult ConvertCore(Type targetType, string json)
{
try
var jsonData = JsonSerializer.Deserialize<List<object>>(json);
var result = jsonData.Select(d => d.ToString()).ToArray();
if (result is null)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
return ConversionResult.Failed(new Exception("Unable to convert to string[]."));
}
if (context.TargetType != typeof(string[]))
{
return new(ConversionResult.Unhandled());
}
if (context.Source is not string contextSource)
{
return new(ConversionResult.Failed(new InvalidOperationException("Context source must be a non-null string. Current type of context source is " + context?.Source?.GetType())));
}
var jsonData = JsonSerializer.Deserialize(contextSource, typeof(List<object>)) as List<object>;
List<string?> stringList = new List<string?>();
if (jsonData is not null)
{
return new(ConversionResult.Success(jsonData.Select(d => d?.ToString()).ToArray()));
}
}
catch (JsonException ex)
{
string msg = String.Format(CultureInfo.CurrentCulture,
@"Binding parameters to complex objects uses JSON serialization.
1. Bind the parameter type as 'string' instead to get the raw values and avoid JSON deserialization, or
2. Change the event payload to be valid json.");
return new(ConversionResult.Failed(new InvalidOperationException(msg, ex)));
}
catch (Exception ex)
{
return new(ConversionResult.Failed(ex));
}
return new(ConversionResult.Unhandled());
return ConversionResult.Success(result);
}
}
}

Просмотреть файл

@ -0,0 +1,48 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
// Default URL for triggering event grid function in the local environment.
// http://localhost:7071/runtime/webhooks/EventGrid?functionName={functionname}
using Azure.Messaging;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
namespace SampleApp
{
/// <summary>
/// Samples demonstrating binding to the <see cref="CloudEvent"/> type.
/// </summary>
public class CloudEventSamples
{
private readonly ILogger<CloudEventSamples> _logger;
public CloudEventSamples(ILogger<CloudEventSamples> logger)
{
_logger = logger;
}
/// <summary>
/// This function demonstrates binding to a single <see cref="CloudEvent"/>.
/// </summary>
[Function(nameof(CloudEventFunction))]
public void CloudEventFunction([EventGridTrigger] CloudEvent cloudEvent)
{
_logger.LogInformation("Event type: {type}, Event subject: {subject}", cloudEvent.Type, cloudEvent.Subject);
}
/// <summary>
/// This function demonstrates binding to an array of <see cref="CloudEvent"/>.
/// Note that when doing so, you must also set the <see cref="EventGridTriggerAttribute.IsBatched"/> property
/// to <value>true</value>.
/// </summary>
[Function(nameof(CloudEventBatchFunction))]
public void CloudEventBatchFunction([EventGridTrigger(IsBatched = true)] CloudEvent[] cloudEvents)
{
foreach (var cloudEvent in cloudEvents)
{
_logger.LogInformation("Event type: {type}, Event subject: {subject}", cloudEvent.Type, cloudEvent.Subject);
}
}
}
}

Просмотреть файл

@ -0,0 +1,48 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
// Default URL for triggering event grid function in the local environment.
// http://localhost:7071/runtime/webhooks/EventGrid?functionName={functionname}
using Azure.Messaging.EventGrid;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
namespace SampleApp
{
/// <summary>
/// Samples demonstrating binding to the <see cref="EventGridEvent"/> type.
/// </summary>
public class EventGridEventSamples
{
private readonly ILogger<EventGridEventSamples> _logger;
public EventGridEventSamples(ILogger<EventGridEventSamples> logger)
{
_logger = logger;
}
/// <summary>
/// This function demonstrates binding to a single <see cref="EventGridEvent"/>.
/// </summary>
[Function(nameof(EventGridEventFunction))]
public void EventGridEventFunction([EventGridTrigger] EventGridEvent eventGridEvent)
{
_logger.LogInformation("Event received: {event}", eventGridEvent.Data.ToString());
}
/// <summary>
/// This function demonstrates binding to an array of <see cref="EventGridEvent"/>.
/// Note that when doing so, you must also set the <see cref="EventGridTriggerAttribute.IsBatched"/> property
/// to <value>true</value>.
/// </summary>
[Function(nameof(EventGridEventBatchFunction))]
public void EventGridEventBatchFunction([EventGridTrigger(IsBatched = true)] EventGridEvent[] eventGridEvents)
{
foreach (var eventGridEvent in eventGridEvents)
{
_logger.LogInformation("Event received: {event}", eventGridEvent.Data.ToString());
}
}
}
}

Просмотреть файл

@ -1,105 +0,0 @@
// Default URL for triggering event grid function in the local environment.
// http://localhost:7071/runtime/webhooks/EventGrid?functionName={functionname}
using System;
using Azure.Messaging;
using Azure.Messaging.EventGrid;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
namespace WorkerBindingSamples.EventGrid
{
public class EventGridTriggerBindingSamples
{
private readonly ILogger _logger;
public EventGridTriggerBindingSamples(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<EventGridTriggerBindingSamples>();
}
[Function("MyEventFunction")]
public void MyEventFunction([EventGridTrigger] MyEvent input)
{
_logger.LogInformation(input.Data?.ToString());
}
[Function("CloudEventFunction")]
public void CloudEventFunction([EventGridTrigger] CloudEvent input)
{
_logger.LogInformation("Event type: {type}, Event subject: {subject}", input.Type, input.Subject);
}
[Function("MultipleCloudEventFunction")]
public void MultipleCloudEventFunction([EventGridTrigger(IsBatched = true)] CloudEvent[] input)
{
for (var i = 0; i < input.Length; i++)
{
var cloudEvent = input[i];
_logger.LogInformation("Event type: {type}, Event subject: {subject}", cloudEvent.Type, cloudEvent.Subject);
}
}
[Function("EventGridEvent")]
public void EventGridEvent([EventGridTrigger] EventGridEvent input)
{
_logger.LogInformation("Event received: {event}", input.Data.ToString());
}
[Function("EventGridEventArray")]
public void EventGridEventArray([EventGridTrigger(IsBatched = true)] EventGridEvent[] input)
{
for (var i = 0; i < input.Length; i++)
{
var eventGridEvent = input[i];
_logger.LogInformation("Event received: {event}", eventGridEvent.Data.ToString());
}
}
[Function("BinaryDataEvent")]
public void BinaryDataEvent([EventGridTrigger] BinaryData input)
{
_logger.LogInformation("Event received: {event}", input.ToString());
}
[Function("BinaryDataArrayEvent")]
public void BinaryDataArrayEvent([EventGridTrigger(IsBatched = true)] BinaryData[] input)
{
for (var i = 0; i < input.Length; i++)
{
var binaryDataEvent = input[i];
_logger.LogInformation("Event received: {event}", binaryDataEvent.ToString());
}
}
[Function("StringArrayEvent")]
public void StringArrayEvent([EventGridTrigger(IsBatched = true)] string[] input)
{
for (var i = 0; i < input.Length; i++)
{
var stringEventGrid = input[i];
_logger.LogInformation("Event received: {event}", stringEventGrid);
}
}
[Function("StringEvent")]
public void StringEvent([EventGridTrigger] string input)
{
_logger.LogInformation("Event received: {event}", input);
}
}
public class MyEvent
{
public string? Id { get; set; }
public string? Topic { get; set; }
public string? Subject { get; set; }
public string? EventType { get; set; }
public DateTime EventTime { get; set; }
public object? Data { get; set; }
}
}

Просмотреть файл

@ -314,5 +314,77 @@
}
}
]
},
{
"name": "CloudEventFunction",
"scriptFile": "WorkerBindingSamples.dll",
"entryPoint": "SampleApp.CloudEventSamples.CloudEventFunction",
"language": "dotnet-isolated",
"properties": {
"IsCodeless": false
},
"bindings": [
{
"name": "cloudEvent",
"direction": "In",
"type": "eventGridTrigger",
"cardinality": "One",
"properties": {}
}
]
},
{
"name": "CloudEventBatchFunction",
"scriptFile": "WorkerBindingSamples.dll",
"entryPoint": "SampleApp.CloudEventSamples.CloudEventBatchFunction",
"language": "dotnet-isolated",
"properties": {
"IsCodeless": false
},
"bindings": [
{
"name": "cloudEvents",
"direction": "In",
"type": "eventGridTrigger",
"cardinality": "Many",
"properties": {}
}
]
},
{
"name": "EventGridEventFunction",
"scriptFile": "WorkerBindingSamples.dll",
"entryPoint": "SampleApp.EventGridEventSamples.EventGridEventFunction",
"language": "dotnet-isolated",
"properties": {
"IsCodeless": false
},
"bindings": [
{
"name": "eventGridEvent",
"direction": "In",
"type": "eventGridTrigger",
"cardinality": "One",
"properties": {}
}
]
},
{
"name": "EventGridEventBatchFunction",
"scriptFile": "WorkerBindingSamples.dll",
"entryPoint": "SampleApp.EventGridEventSamples.EventGridEventBatchFunction",
"language": "dotnet-isolated",
"properties": {
"IsCodeless": false
},
"bindings": [
{
"name": "eventGridEvents",
"direction": "In",
"type": "eventGridTrigger",
"cardinality": "Many",
"properties": {}
}
]
}
]

Просмотреть файл

@ -20,19 +20,41 @@ namespace Microsoft.Azure.Functions.Worker.Extensions.Tests.EventGrid
}
[Fact]
public async Task ConvertAsync_SourceAsObject_ReturnsUnhandled()
public async Task ConvertAsync_Source_IsNotAString_ReturnsFailed()
{
var context = new TestConverterContext(typeof(BinaryData), new object());
var conversionResult = await _eventGridConverter.ConvertAsync(context);
Assert.Equal(ConversionStatus.Failed, conversionResult.Status);
Assert.Equal("Context source must be a non-null string", conversionResult.Error.Message);
}
[Fact]
public async Task ConvertAsync_UnsupportedTargetType_ReturnsFailed()
{
var context = new TestConverterContext(typeof(string), "");
var conversionResult = await _eventGridConverter.ConvertAsync(context);
Assert.Equal(ConversionStatus.Failed, conversionResult.Status);
}
[Fact]
public async Task ConvertAsync_Returns_Success()
public async Task ConvertAsync_InvalidJson_ThrowsJsonException_ReturnsFailed()
{
var context = new TestConverterContext(typeof(BinaryData), "{\"specversion\":\"1.0\",\"id\":\"2947780a-356b-c5a5-feb4-f5261fb2f155\",\"type\":\"test\",\"source\":\"moo\",\"subject\":\"lol test\",\"time\":\"2020-09-14T10:00:00Z\",\"data\":{\"artist\":\"wooo\",\"song\":\"some song\"}}");
var context = new TestConverterContext(typeof(BinaryData[]), @"{""invalid"" :json""}");
var conversionResult = await _eventGridConverter.ConvertAsync(context);
Assert.Equal(ConversionStatus.Failed, conversionResult.Status);
Assert.Contains("Binding parameters to complex objects uses JSON serialization", conversionResult.Error.Message);
}
[Fact]
public async Task ConvertAsync_SingleBinaryData_ReturnsSuccess()
{
var context = new TestConverterContext(typeof(BinaryData), EventGridTestHelper.GetEventGridJsonData());
var conversionResult = await _eventGridConverter.ConvertAsync(context);
@ -41,28 +63,9 @@ namespace Microsoft.Azure.Functions.Worker.Extensions.Tests.EventGrid
}
[Fact]
public async Task ConvertAsync_Returns_Unhandled_For_Unsupported_Type()
public async Task ConvertAsync_BinaryDataArray_ReturnsSuccess()
{
var context = new TestConverterContext(typeof(string), "{\"specversion\":\"1.0\",\"id\":\"2947780a-356b-c5a5-feb4-f5261fb2f155\",\"type\":\"test\",\"source\":\"moo\",\"subject\":\"lol test\",\"time\":\"2020-09-14T10:00:00Z\",\"data\":{\"artist\":\"wooo\",\"song\":\"some song\"}}");
var conversionResult = await _eventGridConverter.ConvertAsync(context);
Assert.Equal(ConversionStatus.Unhandled, conversionResult.Status);
}
[Fact]
public async Task ConvertAsync_SourceAsObject_BinaryDataCollectible_ReturnsUnhandled()
{
var context = new TestConverterContext(typeof(BinaryData[]), new object());
var conversionResult = await _eventGridConverter.ConvertAsync(context);
Assert.Equal(ConversionStatus.Failed, conversionResult.Status);
}
[Fact]
public async Task ConvertAsync_BinaryDataCollectible_Returns_Success()
{
var context = new TestConverterContext(typeof(BinaryData[]), "[{\"specversion\":\"1.0\",\"id\":\"b85d631a-101e-005a-02f2-cee7aa06f148\",\"type\":\"zohan.music.request\",\"source\":\"https://zohan.dev/music/\",\"subject\":\"zohan/music/requests/4322\",\"time\":\"2020-09-14T10:00:00Z\",\"data\":{\"artist\":\"Gerardo\",\"song\":\"Rico Suave\"}},{\"specversion\":\"1.0\",\"id\":\"2947780a-356b-c5a5-feb4-f5261fb2f155\",\"type\":\"test\",\"source\":\"moo\",\"subject\":\"life is very lit\",\"time\":\"2020-09-14T10:00:00Z\",\"data\":{\"artist\":\"wooo\",\"song\":\"life is lit\"}}]");
var context = new TestConverterContext(typeof(BinaryData[]), EventGridTestHelper.GetEventGridJsonDataArray());
var conversionResult = await _eventGridConverter.ConvertAsync(context);
@ -72,19 +75,9 @@ namespace Microsoft.Azure.Functions.Worker.Extensions.Tests.EventGrid
}
[Fact]
public async Task ConvertAsync_BinaryDataCollectible_Returns_Unhandled_For_Unsupported_Type()
public async Task ConvertAsync_BinaryDataArray_SingleElement_ReturnsSuccess()
{
var context = new TestConverterContext(typeof(string), "[{\"specversion\":\"1.0\",\"id\":\"b85d631a-101e-005a-02f2-cee7aa06f148\",\"type\":\"zohan.music.request\",\"source\":\"https://zohan.dev/music/\",\"subject\":\"zohan/music/requests/4322\",\"time\":\"2020-09-14T10:00:00Z\",\"data\":{\"artist\":\"Gerardo\",\"song\":\"Rico Suave\"}},{\"specversion\":\"1.0\",\"id\":\"2947780a-356b-c5a5-feb4-f5261fb2f155\",\"type\":\"test\",\"source\":\"moo\",\"subject\":\"life is very lit\",\"time\":\"2020-09-14T10:00:00Z\",\"data\":{\"artist\":\"wooo\",\"song\":\"life is lit\"}}]");
var conversionResult = await _eventGridConverter.ConvertAsync(context);
Assert.Equal(ConversionStatus.Unhandled, conversionResult.Status);
}
[Fact]
public async Task ConvertAsync_SingleElement_Returns_Success()
{
var context = new TestConverterContext(typeof(BinaryData[]), "[{\"specversion\":\"1.0\",\"id\":\"2947780a-356b-c5a5-feb4-f5261fb2f155\",\"type\":\"test\",\"source\":\"moo\",\"subject\":\"lol test\",\"time\":\"2020-09-14T10:00:00Z\",\"data\":{\"artist\":\"wooo\",\"song\":\"some song\"}}]");
var context = new TestConverterContext(typeof(BinaryData[]), $"[{EventGridTestHelper.GetEventGridJsonData()}]");
var conversionResult = await _eventGridConverter.ConvertAsync(context);

Просмотреть файл

@ -1,10 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
using Azure.Messaging;
using Azure.Messaging.EventGrid;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Converters;
using Microsoft.Azure.Functions.Worker.Extensions.EventGrid.TypeConverters;
@ -21,24 +19,45 @@ namespace Microsoft.Azure.Functions.Worker.Extensions.Tests.EventGrid
public EventGridCloudEventConverterTests()
{
var host = new HostBuilder().ConfigureFunctionsWorkerDefaults((WorkerOptions options) => { }).Build();
_eventGridConverter = new EventGridCloudEventConverter();
}
[Fact]
public async Task ConvertAsync_SourceAsObject_ReturnsFailed()
public async Task ConvertAsync_Source_IsNotAString_ReturnsFailed()
{
var context = new TestConverterContext(typeof(CloudEvent), new object());
var conversionResult = await _eventGridConverter.ConvertAsync(context);
Assert.Equal(ConversionStatus.Failed, conversionResult.Status);
Assert.Equal("Context source must be a non-null string", conversionResult.Error.Message);
}
[Fact]
public async Task ConvertAsync_UnsupportedTargetType_ReturnsFailed()
{
var context = new TestConverterContext(typeof(string), "");
var conversionResult = await _eventGridConverter.ConvertAsync(context);
Assert.Equal(ConversionStatus.Failed, conversionResult.Status);
}
[Fact]
public async Task ConvertAsync_Returns_Success()
public async Task ConvertAsync_InvalidJson_ThrowsJsonException_ReturnsFailed()
{
var context = new TestConverterContext(typeof(CloudEvent), "{\"specversion\":\"1.0\",\"id\":\"2947780a-356b-c5a5-feb4-f5261fb2f155\",\"type\":\"test\",\"source\":\"moo\",\"subject\":\"lol test\",\"time\":\"2020-09-14T10:00:00Z\",\"data\":{\"artist\":\"wooo\",\"song\":\"some song\"}}");
var context = new TestConverterContext(typeof(CloudEvent[]), @"{""invalid"" :json""}");
var conversionResult = await _eventGridConverter.ConvertAsync(context);
Assert.Equal(ConversionStatus.Failed, conversionResult.Status);
Assert.Contains("Binding parameters to complex objects uses JSON serialization", conversionResult.Error.Message);
}
[Fact]
public async Task ConvertAsync_SingleCloudEvent_ReturnsSuccess()
{
var context = new TestConverterContext(typeof(CloudEvent), EventGridTestHelper.GetEventGridJsonData());
var conversionResult = await _eventGridConverter.ConvertAsync(context);
@ -46,30 +65,11 @@ namespace Microsoft.Azure.Functions.Worker.Extensions.Tests.EventGrid
Assert.True(conversionResult.Value is CloudEvent);
}
[Fact]
public async Task ConvertAsync_Returns_Unhandled_For_Unsupported_Type()
{
var context = new TestConverterContext(typeof(string), "{\"specversion\":\"1.0\",\"id\":\"2947780a-356b-c5a5-feb4-f5261fb2f155\",\"type\":\"test\",\"source\":\"moo\",\"subject\":\"lol test\",\"time\":\"2020-09-14T10:00:00Z\",\"data\":{\"artist\":\"wooo\",\"song\":\"some song\"}}");
var conversionResult = await _eventGridConverter.ConvertAsync(context);
Assert.Equal(ConversionStatus.Unhandled, conversionResult.Status);
}
[Fact]
public async Task ConvertAsync_SourceAsObject_CloudEventCollectible_ReturnsFailed()
public async Task ConvertAsync_CloudEventArray_ReturnsSuccess()
{
var context = new TestConverterContext(typeof(CloudEvent[]), new object());
var conversionResult = await _eventGridConverter.ConvertAsync(context);
Assert.Equal(ConversionStatus.Failed, conversionResult.Status);
}
[Fact]
public async Task ConvertAsync_CloudEventCollectible_Returns_Success()
{
var context = new TestConverterContext(typeof(CloudEvent[]), "[{\"specversion\":\"1.0\",\"id\":\"b85d631a-101e-005a-02f2-cee7aa06f148\",\"type\":\"zohan.music.request\",\"source\":\"https://zohan.dev/music/\",\"subject\":\"zohan/music/requests/4322\",\"time\":\"2020-09-14T10:00:00Z\",\"data\":{\"artist\":\"Gerardo\",\"song\":\"Rico Suave\"}},{\"specversion\":\"1.0\",\"id\":\"2947780a-356b-c5a5-feb4-f5261fb2f155\",\"type\":\"test\",\"source\":\"moo\",\"subject\":\"life is very lit\",\"time\":\"2020-09-14T10:00:00Z\",\"data\":{\"artist\":\"wooo\",\"song\":\"life is lit\"}}]");
var context = new TestConverterContext(typeof(CloudEvent[]), EventGridTestHelper.GetEventGridJsonDataArray());
var conversionResult = await _eventGridConverter.ConvertAsync(context);
@ -79,19 +79,9 @@ namespace Microsoft.Azure.Functions.Worker.Extensions.Tests.EventGrid
}
[Fact]
public async Task ConvertAsync_CloudEventCollectible_Returns_Unhandled_For_Unsupported_Type()
public async Task ConvertAsync_CloudEventArray_SingleElement_ReturnsSuccess()
{
var context = new TestConverterContext(typeof(string), "[{\"specversion\":\"1.0\",\"id\":\"b85d631a-101e-005a-02f2-cee7aa06f148\",\"type\":\"zohan.music.request\",\"source\":\"https://zohan.dev/music/\",\"subject\":\"zohan/music/requests/4322\",\"time\":\"2020-09-14T10:00:00Z\",\"data\":{\"artist\":\"Gerardo\",\"song\":\"Rico Suave\"}},{\"specversion\":\"1.0\",\"id\":\"2947780a-356b-c5a5-feb4-f5261fb2f155\",\"type\":\"test\",\"source\":\"moo\",\"subject\":\"life is very lit\",\"time\":\"2020-09-14T10:00:00Z\",\"data\":{\"artist\":\"wooo\",\"song\":\"life is lit\"}}]");
var conversionResult = await _eventGridConverter.ConvertAsync(context);
Assert.Equal(ConversionStatus.Unhandled, conversionResult.Status);
}
[Fact]
public async Task ConvertAsync_SingleElement_Returns_Success()
{
var context = new TestConverterContext(typeof(CloudEvent[]), "[{\"specversion\":\"1.0\",\"id\":\"2947780a-356b-c5a5-feb4-f5261fb2f155\",\"type\":\"test\",\"source\":\"moo\",\"subject\":\"lol test\",\"time\":\"2020-09-14T10:00:00Z\",\"data\":{\"artist\":\"wooo\",\"song\":\"some song\"}}]");
var context = new TestConverterContext(typeof(CloudEvent[]), $"[{EventGridTestHelper.GetEventGridJsonData()}]");
var conversionResult = await _eventGridConverter.ConvertAsync(context);

Просмотреть файл

@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
using Azure.Messaging.EventGrid;
using Microsoft.Azure.Functions.Worker;
@ -25,19 +24,52 @@ namespace Microsoft.Azure.Functions.Worker.Extensions.Tests.EventGrid
}
[Fact]
public async Task ConvertAsync_SourceAsObject_ReturnsUnhandled()
public async Task ConvertAsync_Source_IsNotAString_ReturnsUnhandled()
{
var context = new TestConverterContext(typeof(EventGridEvent), new object());
var conversionResult = await _eventGridConverter.ConvertAsync(context);
Assert.Equal(ConversionStatus.Failed, conversionResult.Status);
Assert.Equal("Context source must be a non-null string", conversionResult.Error.Message);
}
[Fact]
public async Task ConvertAsync_UnsupportedTargetType_ReturnsFailed()
{
var context = new TestConverterContext(typeof(string), "");
var conversionResult = await _eventGridConverter.ConvertAsync(context);
Assert.Equal(ConversionStatus.Failed, conversionResult.Status);
}
[Fact]
public async Task ConvertAsync_Returns_Success()
public async Task ConvertAsync_InvalidJson_ThrowsJsonException_ReturnsFailed()
{
var context = new TestConverterContext(typeof(EventGridEvent), "{\"id\":\"'1\",\"topic\":\"hello\",\"subject\":\"yoursubject\",\"eventType\":\"yourEventType\",\"eventTime\":\"2018-01-23T17:02:19.6069787Z\",\"data\":\"test\",\"dataVersion\":\"test\"}");
var context = new TestConverterContext(typeof(EventGridEvent), @"{""invalid"" :json""}");
var conversionResult = await _eventGridConverter.ConvertAsync(context);
Assert.Equal(ConversionStatus.Failed, conversionResult.Status);
Assert.Contains("Binding parameters to complex objects uses JSON serialization", conversionResult.Error.Message);
}
[Fact]
public async Task ConvertAsync_InvalidData_Throws_ReturnsFailed()
{
var context = new TestConverterContext(typeof(EventGridEvent), EventGridTestHelper.GetEventGridJsonData());
var conversionResult = await _eventGridConverter.ConvertAsync(context);
Assert.Equal(ConversionStatus.Failed, conversionResult.Status);
Assert.Equal("Value cannot be null. (Parameter 'EventType')", conversionResult.Error.Message);
}
[Fact]
public async Task ConvertAsync_SingleEventGridEvent_ReturnsSuccess()
{
var context = new TestConverterContext(typeof(EventGridEvent), EventGridTestHelper.GetEventGridEventJsonData());
var conversionResult = await _eventGridConverter.ConvertAsync(context);
@ -46,39 +78,9 @@ namespace Microsoft.Azure.Functions.Worker.Extensions.Tests.EventGrid
}
[Fact]
public async Task ConvertAsync_Returns_Unhandled_For_Unsupported_Type()
public async Task ConvertAsync_EventGridArray_ReturnsSuccess()
{
var context = new TestConverterContext(typeof(string), "{\"id\":\"'1\",\"topic\":\"hello\",\"subject\":\"yoursubject\",\"eventType\":\"yourEventType\",\"eventTime\":\"2018-01-23T17:02:19.6069787Z\",\"data\":\"test\",\"dataVersion\":\"test\"}");
var conversionResult = await _eventGridConverter.ConvertAsync(context);
Assert.Equal(ConversionStatus.Unhandled, conversionResult.Status);
}
[Fact]
public async Task ConvertAsync_Returns_Failed_Bad_Source()
{
var context = new TestConverterContext(typeof(EventGridEvent), "{\"specversion\":\"1.0\",\"id\":\"2947780a-356b-c5a5-feb4-f5261fb2f155\",\"type\":\"test\",\"source\":\"moo\",\"subject\":\"lol test\",\"time\":\"2020-09-14T10:00:00Z\",\"data\":{\"artist\":\"wooo\",\"song\":\"some song\"}}");
var conversionResult = await _eventGridConverter.ConvertAsync(context);
Assert.Equal(ConversionStatus.Failed, conversionResult.Status);
}
[Fact]
public async Task ConvertAsync__EventGridCollectible_SourceAsObject_ReturnsUnhandled()
{
var context = new TestConverterContext(typeof(EventGridEvent[]), new object());
var conversionResult = await _eventGridConverter.ConvertAsync(context);
Assert.Equal(ConversionStatus.Failed, conversionResult.Status);
}
[Fact]
public async Task ConvertAsync__EventGridCollectible_Returns_Success()
{
var context = new TestConverterContext(typeof(EventGridEvent[]), "[{\"id\":\"'1\",\"topic\":\"hello\",\"subject\":\"yoursubject\",\"eventType\":\"yourEventType\",\"eventTime\":\"2018-01-23T17:02:19.6069787Z\",\"data\":\"test\",\"dataVersion\":\"test\"},{\"id\":\"'1\",\"topic\":\"hello\",\"subject\":\"yoursubject\",\"eventType\":\"yourEventType\",\"eventTime\":\"2018-01-23T17:02:19.6069787Z\",\"data\":\"lmao\",\"dataVersion\":\"test\"}]");
var context = new TestConverterContext(typeof(EventGridEvent[]), EventGridTestHelper.GetEventGridEventJsonDataArray());
var conversionResult = await _eventGridConverter.ConvertAsync(context);
@ -86,25 +88,5 @@ namespace Microsoft.Azure.Functions.Worker.Extensions.Tests.EventGrid
Assert.True(conversionResult.Value is EventGridEvent[]);
Assert.Equal(2, ((EventGridEvent[])conversionResult.Value).Length);
}
[Fact]
public async Task ConvertAsync_EventGridCollectible_Returns_Unhandled_For_Unsupported_Type()
{
var context = new TestConverterContext(typeof(string), "{\"id\":\"'1\",\"topic\":\"hello\",\"subject\":\"yoursubject\",\"eventType\":\"yourEventType\",\"eventTime\":\"2018-01-23T17:02:19.6069787Z\",\"data\":\"test\",\"dataVersion\":\"test\"}");
var conversionResult = await _eventGridConverter.ConvertAsync(context);
Assert.Equal(ConversionStatus.Unhandled, conversionResult.Status);
}
[Fact]
public async Task ConvertAsync__EventGridCollectible_Returns_Failed_Bad_Source()
{
var context = new TestConverterContext(typeof(EventGridEvent[]), "{\"specversion\":\"1.0\",\"id\":\"2947780a-356b-c5a5-feb4-f5261fb2f155\",\"type\":\"test\",\"source\":\"moo\",\"subject\":\"lol test\",\"time\":\"2020-09-14T10:00:00Z\",\"data\":{\"artist\":\"wooo\",\"song\":\"some song\"}}");
var conversionResult = await _eventGridConverter.ConvertAsync(context);
Assert.Equal(ConversionStatus.Failed, conversionResult.Status);
}
}
}

Просмотреть файл

@ -19,19 +19,41 @@ namespace Microsoft.Azure.Functions.Worker.Extensions.Tests.EventGrid
}
[Fact]
public async Task ConvertAsync_SourceAsObject_ReturnsUnhandled()
public async Task ConvertAsync_Source_IsNotAString_ReturnsUnhandled()
{
var context = new TestConverterContext(typeof(string[]), new object());
var conversionResult = await _eventGridConverter.ConvertAsync(context);
Assert.Equal(ConversionStatus.Failed, conversionResult.Status);
Assert.Equal("Context source must be a non-null string", conversionResult.Error.Message);
}
[Fact]
public async Task ConvertAsync_UnsupportedTargetType_ReturnsFailed()
{
var context = new TestConverterContext(typeof(byte[]), "");
var conversionResult = await _eventGridConverter.ConvertAsync(context);
Assert.Equal(ConversionStatus.Failed, conversionResult.Status);
}
[Fact]
public async Task ConvertAsync_Returns_Success()
public async Task ConvertAsync_InvalidJson_ThrowsJsonException_ReturnsFailed()
{
var context = new TestConverterContext(typeof(string[]), "[{\"specversion\":\"1.0\",\"id\":\"b85d631a-101e-005a-02f2-cee7aa06f148\",\"type\":\"zohan.music.request\",\"source\":\"https://zohan.dev/music/\",\"subject\":\"zohan/music/requests/4322\",\"time\":\"2020-09-14T10:00:00Z\",\"data\":{\"artist\":\"Gerardo\",\"song\":\"Rico Suave\"}},{\"specversion\":\"1.0\",\"id\":\"2947780a-356b-c5a5-feb4-f5261fb2f155\",\"type\":\"test\",\"source\":\"moo\",\"subject\":\"life is very lit\",\"time\":\"2020-09-14T10:00:00Z\",\"data\":{\"artist\":\"wooo\",\"song\":\"life is lit\"}}]");
var context = new TestConverterContext(typeof(string[]), @"{""invalid"" :json""}");
var conversionResult = await _eventGridConverter.ConvertAsync(context);
Assert.Equal(ConversionStatus.Failed, conversionResult.Status);
Assert.Contains("Binding parameters to complex objects uses JSON serialization", conversionResult.Error.Message);
}
[Fact]
public async Task ConvertAsync_StringArray_ReturnsSuccess()
{
var context = new TestConverterContext(typeof(string[]), EventGridTestHelper.GetEventGridJsonDataArray());
var conversionResult = await _eventGridConverter.ConvertAsync(context);
@ -41,19 +63,9 @@ namespace Microsoft.Azure.Functions.Worker.Extensions.Tests.EventGrid
}
[Fact]
public async Task ConvertAsync_Returns_Unhandled_For_Unsupported_Type()
public async Task ConvertAsync_StringArray_SingleElement_Returns_Success()
{
var context = new TestConverterContext(typeof(string), "[{\"specversion\":\"1.0\",\"id\":\"b85d631a-101e-005a-02f2-cee7aa06f148\",\"type\":\"zohan.music.request\",\"source\":\"https://zohan.dev/music/\",\"subject\":\"zohan/music/requests/4322\",\"time\":\"2020-09-14T10:00:00Z\",\"data\":{\"artist\":\"Gerardo\",\"song\":\"Rico Suave\"}},{\"specversion\":\"1.0\",\"id\":\"2947780a-356b-c5a5-feb4-f5261fb2f155\",\"type\":\"test\",\"source\":\"moo\",\"subject\":\"life is very lit\",\"time\":\"2020-09-14T10:00:00Z\",\"data\":{\"artist\":\"wooo\",\"song\":\"life is lit\"}}]");
var conversionResult = await _eventGridConverter.ConvertAsync(context);
Assert.Equal(ConversionStatus.Unhandled, conversionResult.Status);
}
[Fact]
public async Task ConvertAsync_SingleElement_Returns_Success()
{
var context = new TestConverterContext(typeof(string[]), "[{\"specversion\":\"1.0\",\"id\":\"2947780a-356b-c5a5-feb4-f5261fb2f155\",\"type\":\"test\",\"source\":\"moo\",\"subject\":\"lol test\",\"time\":\"2020-09-14T10:00:00Z\",\"data\":{\"artist\":\"wooo\",\"song\":\"some song\"}}]");
var context = new TestConverterContext(typeof(string[]), $"[{EventGridTestHelper.GetEventGridJsonData()}]");
var conversionResult = await _eventGridConverter.ConvertAsync(context);

Просмотреть файл

@ -0,0 +1,54 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
using Google.Protobuf;
using Microsoft.Azure.Functions.Worker.Grpc.Messages;
namespace Microsoft.Azure.Functions.Worker.Extensions.Tests
{
internal static class EventGridTestHelper
{
public static string GetEventGridJsonData(string id = "2947780a-356b-c5a5-feb4-f5261fb2f155", string song = "Vampire")
{
return $@"{{
""specversion"" : ""1.0"",
""id"" : ""{id}"",
""type"" : ""UnitTestData"",
""source"" : ""UnitTest"",
""subject"" : ""Song"",
""time"" : ""2020-09-14T10:00:00Z"",
""data"" : {{ ""artist"":""Olivia Rodrigo"",""song"":""{song}"" }}
}}";
}
public static string GetEventGridJsonDataArray()
{
return $@"[
{GetEventGridJsonData("2947780a-356b-c5a5-feb4-f5261fb2f155", "Driver's License")},
{GetEventGridJsonData("b85d631a-101e-005a-02f2-cee7aa06f148", "Deja Vu")}
]";
}
public static string GetEventGridEventJsonData(string id = "2947780a-356b-c5a5-feb4-f5261fb2f155", string song = "Vampire")
{
return $@"{{
""id"" : ""{id}"",
""topic"" : ""UnitTestData"",
""subject"" : ""Song"",
""eventType"" : ""MyEvent"",
""eventTime"" : ""2020-09-14T10:00:00Z"",
""data"" : {{ ""artist"":""Olivia Rodrigo"",""song"":""{song}"" }},
""dataVersion"" : ""1.0""
}}";
}
public static string GetEventGridEventJsonDataArray()
{
return $@"[
{GetEventGridEventJsonData("2947780a-356b-c5a5-feb4-f5261fb2f155", "Driver's License")},
{GetEventGridEventJsonData("b85d631a-101e-005a-02f2-cee7aa06f148", "Deja Vu")}
]";
}
}
}