remove EventGridFaultyEvent, remove publisher, add unit test project
This commit is contained in:
Родитель
4eb98649de
Коммит
77ad9271bd
|
@ -1,15 +1,15 @@
|
||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 15
|
# Visual Studio 15
|
||||||
VisualStudioVersion = 15.0.26430.6
|
VisualStudioVersion = 15.0.26730.3
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D0455F87-5E51-4AFD-ACF1-2F50A352F3D5}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D0455F87-5E51-4AFD-ACF1-2F50A352F3D5}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventGridExtension", "src\EventGridExtension\EventGridExtension.csproj", "{D04DD2A7-EE6B-44C2-B615-DC380D8D2569}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventGridExtension", "src\EventGridExtension\EventGridExtension.csproj", "{D04DD2A7-EE6B-44C2-B615-DC380D8D2569}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{B40A0AB6-1971-4DE9-954A-DD92AF1694CC}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{B40A0AB6-1971-4DE9-954A-DD92AF1694CC}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventGridBinding", "test\localTests\EventGridBinding.csproj", "{828D7696-5D52-4AEB-8973-4382E9760094}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Extension.tests", "test\Extension.tests\Extension.tests.csproj", "{694416E6-094C-4F52-8B0F-D6B8197E5660}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
@ -21,16 +21,19 @@ Global
|
||||||
{D04DD2A7-EE6B-44C2-B615-DC380D8D2569}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{D04DD2A7-EE6B-44C2-B615-DC380D8D2569}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{D04DD2A7-EE6B-44C2-B615-DC380D8D2569}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{D04DD2A7-EE6B-44C2-B615-DC380D8D2569}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{D04DD2A7-EE6B-44C2-B615-DC380D8D2569}.Release|Any CPU.Build.0 = Release|Any CPU
|
{D04DD2A7-EE6B-44C2-B615-DC380D8D2569}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{828D7696-5D52-4AEB-8973-4382E9760094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{694416E6-094C-4F52-8B0F-D6B8197E5660}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{828D7696-5D52-4AEB-8973-4382E9760094}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{694416E6-094C-4F52-8B0F-D6B8197E5660}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{828D7696-5D52-4AEB-8973-4382E9760094}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{694416E6-094C-4F52-8B0F-D6B8197E5660}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{828D7696-5D52-4AEB-8973-4382E9760094}.Release|Any CPU.Build.0 = Release|Any CPU
|
{694416E6-094C-4F52-8B0F-D6B8197E5660}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
{D04DD2A7-EE6B-44C2-B615-DC380D8D2569} = {D0455F87-5E51-4AFD-ACF1-2F50A352F3D5}
|
{D04DD2A7-EE6B-44C2-B615-DC380D8D2569} = {D0455F87-5E51-4AFD-ACF1-2F50A352F3D5}
|
||||||
{828D7696-5D52-4AEB-8973-4382E9760094} = {B40A0AB6-1971-4DE9-954A-DD92AF1694CC}
|
{694416E6-094C-4F52-8B0F-D6B8197E5660} = {B40A0AB6-1971-4DE9-954A-DD92AF1694CC}
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {7059D7BF-670B-4F26-9A7A-E73F95BEBF57}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
|
@ -36,7 +36,9 @@ build_script:
|
||||||
dotnet build -v q
|
dotnet build -v q
|
||||||
|
|
||||||
dotnet pack src\EventGridExtension\EventGridExtension.csproj -o ..\..\buildoutput --no-build --version-suffix "-$env:APPVEYOR_BUILD_NUMBER"
|
dotnet pack src\EventGridExtension\EventGridExtension.csproj -o ..\..\buildoutput --no-build --version-suffix "-$env:APPVEYOR_BUILD_NUMBER"
|
||||||
|
test_script:
|
||||||
|
- ps: >-
|
||||||
|
dotnet test .\test\Extension.tests\ -v q --no-build
|
||||||
artifacts:
|
artifacts:
|
||||||
- path: buildoutput\*.nupkg
|
- path: buildoutput\*.nupkg
|
||||||
name: Binaries
|
name: Binaries
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
using Newtonsoft.Json;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Extensions.EventGrid
|
|
||||||
{
|
|
||||||
public class DefaultPublisher : IPublisher
|
|
||||||
{
|
|
||||||
public const string Name = "DefaultPublisher";
|
|
||||||
|
|
||||||
public string PublisherName
|
|
||||||
{
|
|
||||||
get { return Name; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<IDisposable> Recycles
|
|
||||||
{
|
|
||||||
get { return null; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public Dictionary<string, Type> ExtractBindingContract(Type t)
|
|
||||||
{
|
|
||||||
if (t == typeof(EventGridEvent) || t == typeof(string))
|
|
||||||
{
|
|
||||||
var contract = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
// for javascript, 1st attempt is to return JSON string of EventGridEvent
|
|
||||||
contract.Add("EventGridTrigger", t);
|
|
||||||
return contract;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<Dictionary<string, object>> ExtractBindingData(EventGridEvent e, Type t)
|
|
||||||
{
|
|
||||||
var bindingData = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
if (t == typeof(EventGridEvent))
|
|
||||||
{
|
|
||||||
bindingData.Add("EventGridTrigger", e);
|
|
||||||
}
|
|
||||||
else if (t == typeof(string))
|
|
||||||
{
|
|
||||||
bindingData.Add("EventGridTrigger", JsonConvert.SerializeObject(e, Formatting.Indented));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.FromResult<Dictionary<string, object>>(bindingData);
|
|
||||||
}
|
|
||||||
|
|
||||||
public object GetArgument(Dictionary<string, object> bindingData)
|
|
||||||
{
|
|
||||||
return bindingData["EventGridTrigger"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -11,8 +11,8 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="$(TargetFramework)=='net46'">
|
<ItemGroup Condition="$(TargetFramework)=='net46'">
|
||||||
<PackageReference Include="Microsoft.Azure.WebJobs" Version="2.1.0-beta1" />
|
<PackageReference Include="Microsoft.Azure.WebJobs" Version="2.1.0-beta1-10976" />
|
||||||
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions" Version="2.1.0-beta1" />
|
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions" Version="2.1.0-beta1-10489" />
|
||||||
<Reference Include="System.Web" />
|
<Reference Include="System.Web" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using Microsoft.Azure.WebJobs.Host.Config;
|
using Microsoft.Azure.WebJobs.Host;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Config;
|
||||||
using Microsoft.Azure.WebJobs.Host.Executors;
|
using Microsoft.Azure.WebJobs.Host.Executors;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System;
|
using System;
|
||||||
|
@ -15,12 +16,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.EventGrid
|
||||||
public class EventGridExtensionConfig : IExtensionConfigProvider,
|
public class EventGridExtensionConfig : IExtensionConfigProvider,
|
||||||
IAsyncConverter<HttpRequestMessage, HttpResponseMessage>
|
IAsyncConverter<HttpRequestMessage, HttpResponseMessage>
|
||||||
{
|
{
|
||||||
private bool _isTest = false;
|
private TraceWriter _tracer = null;
|
||||||
public bool IsTest
|
|
||||||
{
|
|
||||||
get { return _isTest; }
|
|
||||||
set { _isTest = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Initialize(ExtensionConfigContext context)
|
public void Initialize(ExtensionConfigContext context)
|
||||||
{
|
{
|
||||||
|
@ -28,6 +24,11 @@ namespace Microsoft.Azure.WebJobs.Extensions.EventGrid
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("context");
|
throw new ArgumentNullException("context");
|
||||||
}
|
}
|
||||||
|
else if (context.Trace == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("context.Trace");
|
||||||
|
}
|
||||||
|
_tracer = context.Trace;
|
||||||
|
|
||||||
Uri url = context.GetWebhookHandler();
|
Uri url = context.GetWebhookHandler();
|
||||||
|
|
||||||
|
@ -50,6 +51,9 @@ namespace Microsoft.Azure.WebJobs.Extensions.EventGrid
|
||||||
|
|
||||||
private async Task<HttpResponseMessage> ProcessAsync(HttpRequestMessage req)
|
private async Task<HttpResponseMessage> ProcessAsync(HttpRequestMessage req)
|
||||||
{
|
{
|
||||||
|
// webjobs.script uses req.GetQueryNameValuePairs();
|
||||||
|
// which requires webapi.core...but this does not work for .netframework2.0
|
||||||
|
// TODO change this once webjobs.script is migrated
|
||||||
var functionName = HttpUtility.ParseQueryString(req.RequestUri.Query)["functionName"];
|
var functionName = HttpUtility.ParseQueryString(req.RequestUri.Query)["functionName"];
|
||||||
if (String.IsNullOrEmpty(functionName) || !_listeners.ContainsKey(functionName))
|
if (String.IsNullOrEmpty(functionName) || !_listeners.ContainsKey(functionName))
|
||||||
{
|
{
|
||||||
|
@ -67,20 +71,13 @@ namespace Microsoft.Azure.WebJobs.Extensions.EventGrid
|
||||||
{
|
{
|
||||||
string jsonArray = await req.Content.ReadAsStringAsync();
|
string jsonArray = await req.Content.ReadAsStringAsync();
|
||||||
SubscriptionValidationEvent validationEvent = null;
|
SubscriptionValidationEvent validationEvent = null;
|
||||||
try
|
|
||||||
{
|
|
||||||
List<EventGridEvent> events = JsonConvert.DeserializeObject<List<EventGridEvent>>(jsonArray);
|
List<EventGridEvent> events = JsonConvert.DeserializeObject<List<EventGridEvent>>(jsonArray);
|
||||||
validationEvent = events[0].Data.ToObject<SubscriptionValidationEvent>();
|
validationEvent = events[0].Data.ToObject<SubscriptionValidationEvent>();
|
||||||
}
|
|
||||||
catch (JsonException)
|
|
||||||
{
|
|
||||||
// TODO remove once validation use JObject
|
|
||||||
List<EventGridFaultyEvent> events = JsonConvert.DeserializeObject<List<EventGridFaultyEvent>>(jsonArray);
|
|
||||||
validationEvent = JsonConvert.DeserializeObject<SubscriptionValidationEvent>(events[0].Data);
|
|
||||||
}
|
|
||||||
SubscriptionValidationResponse validationResponse = new SubscriptionValidationResponse { ValidationResponse = validationEvent.ValidationCode };
|
SubscriptionValidationResponse validationResponse = new SubscriptionValidationResponse { ValidationResponse = validationEvent.ValidationCode };
|
||||||
var returnMessage = new HttpResponseMessage(HttpStatusCode.OK);
|
var returnMessage = new HttpResponseMessage(HttpStatusCode.OK);
|
||||||
returnMessage.Content = new StringContent(JsonConvert.SerializeObject(validationResponse));
|
returnMessage.Content = new StringContent(JsonConvert.SerializeObject(validationResponse));
|
||||||
|
_tracer.Trace(new TraceEvent(System.Diagnostics.TraceLevel.Info,
|
||||||
|
$"perform handshake with eventGrid for endpoint: {req.RequestUri}"));
|
||||||
return returnMessage;
|
return returnMessage;
|
||||||
}
|
}
|
||||||
else if (String.Equals(eventTypeHeader, "Notification", StringComparison.OrdinalIgnoreCase))
|
else if (String.Equals(eventTypeHeader, "Notification", StringComparison.OrdinalIgnoreCase))
|
||||||
|
|
|
@ -16,7 +16,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.EventGrid
|
||||||
|
|
||||||
// Register our extension configuration provider
|
// Register our extension configuration provider
|
||||||
// done by the function runtime
|
// done by the function runtime
|
||||||
config.RegisterExtensionConfigProvider(new EventGridExtensionConfig() { IsTest = true });
|
config.RegisterExtensionConfigProvider(new EventGridExtensionConfig());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
using Microsoft.Azure.WebJobs.Host.Executors;
|
using Microsoft.Azure.WebJobs.Host.Executors;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
|
|
@ -14,12 +14,5 @@ namespace Microsoft.Azure.WebJobs.Extensions.EventGrid
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public EventGridTriggerAttribute(string publisher)
|
|
||||||
{
|
|
||||||
Publisher = publisher;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Connection { get; set; } = null;
|
|
||||||
public string Publisher { get; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ using Microsoft.Azure.WebJobs.Host.Bindings;
|
||||||
using Microsoft.Azure.WebJobs.Host.Listeners;
|
using Microsoft.Azure.WebJobs.Host.Listeners;
|
||||||
using Microsoft.Azure.WebJobs.Host.Protocols;
|
using Microsoft.Azure.WebJobs.Host.Protocols;
|
||||||
using Microsoft.Azure.WebJobs.Host.Triggers;
|
using Microsoft.Azure.WebJobs.Host.Triggers;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
@ -38,53 +40,41 @@ namespace Microsoft.Azure.WebJobs.Extensions.EventGrid
|
||||||
return Task.FromResult<ITriggerBinding>(null);
|
return Task.FromResult<ITriggerBinding>(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// depends on the publisher, we could have different expectation for paramter
|
if (!isSupportBindingType(parameter.ParameterType))
|
||||||
// TODO javascript, you cannot sepcify parameterType?
|
|
||||||
string publisherName = attribute.Publisher;
|
|
||||||
IPublisher publisher = null;
|
|
||||||
// factory pattern
|
|
||||||
if (String.IsNullOrEmpty(publisherName))
|
|
||||||
{
|
|
||||||
publisher = new DefaultPublisher();
|
|
||||||
}
|
|
||||||
else if (String.Equals(publisherName, EventHubCapturePublisher.Name, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
// if publisher is EventHubCapture, Connection String is going to be required
|
|
||||||
publisher = new EventHubCapturePublisher(attribute.Connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
var contract = publisher?.ExtractBindingContract(parameter.ParameterType);
|
|
||||||
if (contract == null)
|
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
|
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
|
||||||
"Can't bind EventGridTriggerAttribute with publisher '{0}' to type '{1}'.", publisherName, parameter.ParameterType));
|
"Can't bind EventGridTriggerAttribute to type '{0}'.", parameter.ParameterType));
|
||||||
}
|
}
|
||||||
// unsupported publisher is caught in attribute constrcutor
|
|
||||||
return Task.FromResult<ITriggerBinding>(new EventGridTriggerBinding(context.Parameter, _extensionConfigProvider, context.Parameter.Member.Name, publisher, contract));
|
return Task.FromResult<ITriggerBinding>(new EventGridTriggerBinding(context.Parameter, _extensionConfigProvider, context.Parameter.Member.Name));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool isSupportBindingType(Type t)
|
||||||
|
{
|
||||||
|
return (t == typeof(EventGridEvent) || t == typeof(string));
|
||||||
|
}
|
||||||
|
|
||||||
private class EventGridTriggerBinding : ITriggerBinding
|
private class EventGridTriggerBinding : ITriggerBinding
|
||||||
{
|
{
|
||||||
private readonly ParameterInfo _parameter;
|
private readonly ParameterInfo _parameter;
|
||||||
private readonly IReadOnlyDictionary<string, Type> _bindingContract;
|
private readonly Dictionary<string, Type> _bindingContract;
|
||||||
private EventGridExtensionConfig _listenersStore;
|
private EventGridExtensionConfig _listenersStore;
|
||||||
private readonly string _functionName;
|
private readonly string _functionName;
|
||||||
private readonly IPublisher _publisher;
|
|
||||||
|
|
||||||
public EventGridTriggerBinding(ParameterInfo parameter, EventGridExtensionConfig listenersStore, string functionName, IPublisher publisher, Dictionary<string, Type> contract)
|
public EventGridTriggerBinding(ParameterInfo parameter, EventGridExtensionConfig listenersStore, string functionName)
|
||||||
{
|
{
|
||||||
_publisher = publisher;
|
|
||||||
_listenersStore = listenersStore;
|
_listenersStore = listenersStore;
|
||||||
_parameter = parameter;
|
_parameter = parameter;
|
||||||
_functionName = functionName;
|
_functionName = functionName;
|
||||||
_bindingContract = contract;
|
_bindingContract = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
{"data",typeof(JObject) }
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyDictionary<string, Type> BindingDataContract
|
public IReadOnlyDictionary<string, Type> BindingDataContract
|
||||||
{
|
{
|
||||||
// TODO? not per parameter?
|
|
||||||
get { return _bindingContract; }
|
get { return _bindingContract; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,21 +83,30 @@ namespace Microsoft.Azure.WebJobs.Extensions.EventGrid
|
||||||
get { return typeof(EventGridEvent); }
|
get { return typeof(EventGridEvent); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ITriggerData> BindAsync(object value, ValueBindingContext context)
|
public Task<ITriggerData> BindAsync(object value, ValueBindingContext context)
|
||||||
{
|
{
|
||||||
EventGridEvent triggerValue = value as EventGridEvent;
|
EventGridEvent triggerValue = value as EventGridEvent;
|
||||||
var bindingData = await _publisher.ExtractBindingData(triggerValue, _parameter.ParameterType);
|
var bindingData = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
|
||||||
IValueBinder valueBinder = new EventGridValueBinder(_parameter, _publisher.GetArgument(bindingData), _publisher.Recycles);
|
{
|
||||||
return new TriggerData(valueBinder, bindingData);
|
{"data", triggerValue.Data}
|
||||||
|
};
|
||||||
|
|
||||||
|
object argument;
|
||||||
|
if (_parameter.ParameterType == typeof(string))
|
||||||
|
{
|
||||||
|
argument = JsonConvert.SerializeObject(triggerValue, Formatting.Indented);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
argument = triggerValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
IValueBinder valueBinder = new EventGridValueBinder(_parameter, argument);
|
||||||
|
return Task.FromResult<ITriggerData>(new TriggerData(valueBinder, bindingData));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IListener> CreateListenerAsync(ListenerFactoryContext context)
|
public Task<IListener> CreateListenerAsync(ListenerFactoryContext context)
|
||||||
{
|
{
|
||||||
// listenersStore is of Type "EventGridExtensionConfig"
|
|
||||||
if (_listenersStore.IsTest)
|
|
||||||
{
|
|
||||||
return Task.FromResult<IListener>(new TestListener(context.Executor));
|
|
||||||
}
|
|
||||||
return Task.FromResult<IListener>(new EventGridListener(context.Executor, _listenersStore, _functionName));
|
return Task.FromResult<IListener>(new EventGridListener(context.Executor, _listenersStore, _functionName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +140,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.EventGrid
|
||||||
private readonly object _value;
|
private readonly object _value;
|
||||||
private List<IDisposable> _disposables = null;
|
private List<IDisposable> _disposables = null;
|
||||||
|
|
||||||
public EventGridValueBinder(ParameterInfo parameter, object value, List<IDisposable> disposables)
|
public EventGridValueBinder(ParameterInfo parameter, object value, List<IDisposable> disposables = null)
|
||||||
: base(parameter.ParameterType)
|
: base(parameter.ParameterType)
|
||||||
{
|
{
|
||||||
_value = value;
|
_value = value;
|
||||||
|
|
|
@ -16,85 +16,6 @@ namespace Microsoft.Azure.WebJobs.Extensions.EventGrid
|
||||||
public string ValidationCode { get; set; }
|
public string ValidationCode { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class StorageBlob
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
{
|
|
||||||
"fileUrl": "https://shunsouthcentralus.blob.core.windows.net/archivecontainershun/canaryeh/test/1/2017/07/14/23/09/27.avro",
|
|
||||||
"fileType": "AzureBlockBlob",
|
|
||||||
"partitionId": "1",
|
|
||||||
"sizeInBytes": 0,
|
|
||||||
"eventCount": 0,
|
|
||||||
"firstSequenceNumber": -1,
|
|
||||||
"lastSequenceNumber": -1,
|
|
||||||
"firstEnqueueTime": "0001-01-01T00:00:00",
|
|
||||||
"lastEnqueueTime": "0001-01-01T00:00:00"
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
[JsonProperty(PropertyName = "fileUrl")]
|
|
||||||
public Uri FileUrl { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty(PropertyName = "fileType")]
|
|
||||||
public string FileType { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty(PropertyName = "partitionId")]
|
|
||||||
public int PartitionId { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty(PropertyName = "sizeInBytes")]
|
|
||||||
public int SizeInBytes { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty(PropertyName = "eventCount")]
|
|
||||||
public int EventCount { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty(PropertyName = "firstSequenceNumber")]
|
|
||||||
public int FirstSequenceNumber { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty(PropertyName = "lastSequenceNumber")]
|
|
||||||
public int LastSequenceNumber { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty(PropertyName = "firstEnqueueTime")]
|
|
||||||
public DateTime FirstEnqueueTime { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty(PropertyName = "lastEnqueueTime")]
|
|
||||||
public DateTime LastEnqueueTime { get; set; }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class EventGridFaultyEvent
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
{
|
|
||||||
'id': 'eac180e8-92e0-436d-8699-a0324e2a5fef',
|
|
||||||
'topic': '/subscriptions/5b4b650e-28b9-4790-b3ab-ddbd88d727c4/resourceGroups/canaryeh/providers/microsoft.eventhub/namespaces/canaryeh',
|
|
||||||
'subject': 'eventhubs/test',
|
|
||||||
'data': '{\""validationCode\"":\""85fe9560-f63f-469b-b40a-5a6327db05e6\""}', <-- String instead of JObject
|
|
||||||
'eventType': 'Microsoft.EventGrid/SubscriptionValidationEvent',
|
|
||||||
'eventTime': '2017-07-28T00:43:28.6153503Z'
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
[JsonProperty(PropertyName = "topic")]
|
|
||||||
public string Topic { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty(PropertyName = "subject")]
|
|
||||||
public string Subject { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty(PropertyName = "data")]
|
|
||||||
public string Data { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty(PropertyName = "eventType")]
|
|
||||||
public string EventType { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty(PropertyName = "publishTime")]
|
|
||||||
public DateTime PublishTime { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty(PropertyName = "eventTime")]
|
|
||||||
public DateTime EventTime { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty(PropertyName = "id")]
|
|
||||||
public string Id { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class EventGridEvent
|
public class EventGridEvent
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@ -115,7 +36,6 @@ namespace Microsoft.Azure.WebJobs.Extensions.EventGrid
|
||||||
[JsonProperty(PropertyName = "subject")]
|
[JsonProperty(PropertyName = "subject")]
|
||||||
public string Subject { get; set; }
|
public string Subject { get; set; }
|
||||||
|
|
||||||
// the content of this depends on the publisher
|
|
||||||
[JsonProperty(PropertyName = "data")]
|
[JsonProperty(PropertyName = "data")]
|
||||||
public JObject Data { get; set; }
|
public JObject Data { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -1,129 +0,0 @@
|
||||||
using Microsoft.Azure.WebJobs.Host;
|
|
||||||
using Microsoft.WindowsAzure.Storage;
|
|
||||||
using Microsoft.WindowsAzure.Storage.Auth;
|
|
||||||
using Microsoft.WindowsAzure.Storage.Blob;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.IO;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Extensions.EventGrid
|
|
||||||
{
|
|
||||||
public class EventHubCapturePublisher : IPublisher
|
|
||||||
{
|
|
||||||
public const string Name = "eventHubCapture";
|
|
||||||
private List<IDisposable> _recycles = null;
|
|
||||||
private StorageCredentials _credentials;
|
|
||||||
public EventHubCapturePublisher(string connectionStringName)
|
|
||||||
{
|
|
||||||
if (String.IsNullOrEmpty(connectionStringName))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
|
|
||||||
"Can't bind EventGridTriggerAttribute with publisher '{0}': missing ConnectionString for Storageblob.", Name));
|
|
||||||
}
|
|
||||||
var connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(connectionStringName);
|
|
||||||
_credentials = CloudStorageAccount.Parse(connectionString).Credentials;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string PublisherName
|
|
||||||
{
|
|
||||||
get { return Name; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<IDisposable> Recycles
|
|
||||||
{
|
|
||||||
get { return _recycles; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public Dictionary<string, Type> ExtractBindingContract(Type t)
|
|
||||||
{
|
|
||||||
var contract = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
// TODO we can determine the ACTION in this function, so that when calling ExtractBindingData, we don't have to do the comparison again
|
|
||||||
if (t == typeof(EventGridEvent))
|
|
||||||
{
|
|
||||||
contract.Add("EventGridTrigger", t);
|
|
||||||
}
|
|
||||||
else if (t == typeof(Stream) || t == typeof(string) || t == typeof(CloudBlob) || t == typeof(byte[]))
|
|
||||||
{
|
|
||||||
contract.Add("EventGridTrigger", t);
|
|
||||||
contract.Add("BlobTrigger", typeof(string));
|
|
||||||
contract.Add("Uri", typeof(Uri));
|
|
||||||
contract.Add("Properties", typeof(BlobProperties));
|
|
||||||
contract.Add("Metadata", typeof(IDictionary<string, string>));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// fail
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return contract;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Dictionary<string, object>> ExtractBindingData(EventGridEvent e, Type t)
|
|
||||||
{
|
|
||||||
var bindingData = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
if (t == typeof(EventGridEvent))
|
|
||||||
{
|
|
||||||
bindingData.Add("EventGridTrigger", e);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
StorageBlob data = e.Data.ToObject<StorageBlob>();
|
|
||||||
var blob = new CloudBlob(data.FileUrl, _credentials);
|
|
||||||
// set metadata based on https://github.com/MicrosoftDocs/azure-docs/blob/master/articles/azure-functions/functions-bindings-storage-blob.md#trigger-metadata
|
|
||||||
//BlobTrigger.Type string.The triggering blob path
|
|
||||||
bindingData.Add("BlobTrigger", blob.Container.Name + "/" + blob.Name);
|
|
||||||
//Uri.Type System.Uri.The blob's URI for the primary location.
|
|
||||||
bindingData.Add("Uri", blob.Uri);
|
|
||||||
//Properties.Type Microsoft.WindowsAzure.Storage.Blob.BlobProperties.The blob's system properties.
|
|
||||||
bindingData.Add("Properties", blob.Properties);
|
|
||||||
//Metadata.Type IDictionary<string, string>.The user - defined metadata for the blob
|
|
||||||
bindingData.Add("Metadata", blob.Metadata);
|
|
||||||
// [Blob("output/copy-{name}")] out string output, does not apply here
|
|
||||||
// bindingData.Add("name", blob.Name);
|
|
||||||
if (t == typeof(CloudBlob))
|
|
||||||
{
|
|
||||||
bindingData.Add("EventGridTrigger", blob);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// convert from stream
|
|
||||||
var blobStream = await blob.OpenReadAsync();
|
|
||||||
if (t == typeof(Stream))
|
|
||||||
{
|
|
||||||
_recycles = new List<IDisposable>();
|
|
||||||
_recycles.Add(blobStream); // close after function call
|
|
||||||
bindingData.Add("EventGridTrigger", blobStream);
|
|
||||||
}
|
|
||||||
// copy to memory => use case javascript
|
|
||||||
else if (t == typeof(Byte[]))
|
|
||||||
{
|
|
||||||
using (MemoryStream ms = new MemoryStream())
|
|
||||||
{
|
|
||||||
await blobStream.CopyToAsync(ms);
|
|
||||||
bindingData.Add("EventGridTrigger", ms.ToArray());
|
|
||||||
}
|
|
||||||
blobStream.Close(); // close before the function call
|
|
||||||
}
|
|
||||||
else if (t == typeof(string))
|
|
||||||
{
|
|
||||||
using (StreamReader responseStream = new StreamReader(blobStream))
|
|
||||||
{
|
|
||||||
string blobData = await responseStream.ReadToEndAsync();
|
|
||||||
bindingData.Add("EventGridTrigger", blobData);
|
|
||||||
}
|
|
||||||
blobStream.Close(); // close before the function call
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bindingData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public object GetArgument(Dictionary<string, object> bindingData)
|
|
||||||
{
|
|
||||||
return bindingData["EventGridTrigger"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Extensions.EventGrid
|
|
||||||
{
|
|
||||||
public interface IPublisher
|
|
||||||
{
|
|
||||||
string PublisherName { get; }
|
|
||||||
|
|
||||||
List<IDisposable> Recycles { get; }
|
|
||||||
|
|
||||||
// this method needs to filter invalid datatype
|
|
||||||
// return null
|
|
||||||
Dictionary<string, Type> ExtractBindingContract(Type t);
|
|
||||||
|
|
||||||
Task<Dictionary<string, object>> ExtractBindingData(EventGridEvent e, Type t);
|
|
||||||
|
|
||||||
object GetArgument(Dictionary<string, object> bindingData);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,89 +0,0 @@
|
||||||
using Microsoft.Azure.WebJobs.Host.Executors;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Extensions.EventGrid
|
|
||||||
{
|
|
||||||
public class TestListener : Microsoft.Azure.WebJobs.Host.Listeners.IListener
|
|
||||||
{
|
|
||||||
public ITriggeredFunctionExecutor Executor { private set; get; }
|
|
||||||
|
|
||||||
private System.Timers.Timer _timer;
|
|
||||||
|
|
||||||
public TestListener(ITriggeredFunctionExecutor executor)
|
|
||||||
{
|
|
||||||
Executor = executor;
|
|
||||||
|
|
||||||
// For this sample, we're using a timer to generate
|
|
||||||
// trigger events. You'll replace this with your event source.
|
|
||||||
|
|
||||||
_timer = new System.Timers.Timer(5 * 1000)
|
|
||||||
{
|
|
||||||
AutoReset = false
|
|
||||||
};
|
|
||||||
_timer.Elapsed += OnTimer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task StartAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
_timer.Start();
|
|
||||||
return Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task StopAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
_timer.Stop();
|
|
||||||
return Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_timer.Dispose();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Cancel()
|
|
||||||
{
|
|
||||||
// TODO: cancel any outstanding tasks initiated by this listener
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void OnTimer(object sender, System.Timers.ElapsedEventArgs e)
|
|
||||||
{
|
|
||||||
// invoke the function executor
|
|
||||||
// this is similar to code in ProcessAsync()
|
|
||||||
string stringJson = @"[{
|
|
||||||
'topic': '/subscriptions/5b4b650e-28b9-4790-b3ab-ddbd88d727c4/resourcegroups/canaryeh/providers/Microsoft.EventHub/namespaces/canaryeh',
|
|
||||||
'subject': 'eventhubs/test',
|
|
||||||
'eventType': 'captureFileCreated',
|
|
||||||
'eventTime': '2017-07-14T23:10:27.7689666Z',
|
|
||||||
'id': '7b11c4ce-1c34-4416-848b-1730e766f126',
|
|
||||||
'data': {
|
|
||||||
'fileUrl': 'https://shunsouthcentralus.blob.core.windows.net/debugging/shunBlob.txt',
|
|
||||||
'fileType': 'AzureBlockBlob',
|
|
||||||
'partitionId': '1',
|
|
||||||
'sizeInBytes': 0,
|
|
||||||
'eventCount': 0,
|
|
||||||
'firstSequenceNumber': -1,
|
|
||||||
'lastSequenceNumber': -1,
|
|
||||||
'firstEnqueueTime': '0001-01-01T00:00:00',
|
|
||||||
'lastEnqueueTime': '0001-01-01T00:00:00'
|
|
||||||
},
|
|
||||||
'publishTime': '2017-07-14T23:10:29.5004788Z'
|
|
||||||
}]";
|
|
||||||
List<EventGridEvent> events = JsonConvert.DeserializeObject<List<EventGridEvent>>(stringJson);
|
|
||||||
foreach (var param in events)
|
|
||||||
{
|
|
||||||
TriggeredFunctionData input = new TriggeredFunctionData
|
|
||||||
{
|
|
||||||
TriggerValue = param
|
|
||||||
};
|
|
||||||
|
|
||||||
Executor.TryExecuteAsync(input, CancellationToken.None).Wait();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
using Microsoft.Azure.WebJobs;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Extension.tests
|
||||||
|
{
|
||||||
|
public class FakeTypeLocator<T> : ITypeLocator
|
||||||
|
{
|
||||||
|
public IReadOnlyList<Type> GetTypes()
|
||||||
|
{
|
||||||
|
return new Type[] { typeof(T) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
using Microsoft.Azure.WebJobs;
|
||||||
|
using Microsoft.Azure.WebJobs.Description;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Config;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Extension.tests
|
||||||
|
{
|
||||||
|
[Binding]
|
||||||
|
public class BindingDataAttribute : Attribute
|
||||||
|
{
|
||||||
|
public BindingDataAttribute(string toBeAutoResolve)
|
||||||
|
{
|
||||||
|
ToBeAutoResolve = toBeAutoResolve;
|
||||||
|
}
|
||||||
|
|
||||||
|
[AutoResolve]
|
||||||
|
public string ToBeAutoResolve { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TestExtensionConfig : IExtensionConfigProvider
|
||||||
|
{
|
||||||
|
public void Initialize(ExtensionConfigContext context)
|
||||||
|
{
|
||||||
|
context.AddBindingRule<BindingDataAttribute>().
|
||||||
|
BindToInput<string>(attr => attr.ToBeAutoResolve);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
using Microsoft.Azure.WebJobs;
|
||||||
|
using Microsoft.Azure.WebJobs.Extensions.EventGrid;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Extension.tests
|
||||||
|
{
|
||||||
|
public class TestHelpers
|
||||||
|
{
|
||||||
|
public static JobHost NewHost<T>(EventGridExtensionConfig ext = null)
|
||||||
|
{
|
||||||
|
JobHostConfiguration config = new JobHostConfiguration();
|
||||||
|
config.HostId = Guid.NewGuid().ToString("n");
|
||||||
|
config.StorageConnectionString = null;
|
||||||
|
config.DashboardConnectionString = null;
|
||||||
|
config.TypeLocator = new FakeTypeLocator<T>();
|
||||||
|
config.AddExtension(ext ?? new EventGridExtensionConfig());
|
||||||
|
config.AddExtension(new TestExtensionConfig());
|
||||||
|
var host = new JobHost(config);
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFrameworks>netcoreapp2.0;net46</TargetFrameworks>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0-preview-20170810-02" />
|
||||||
|
<PackageReference Include="xunit" Version="2.3.0-beta2-build3683" />
|
||||||
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.0-beta3-build3705" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\src\EventGridExtension\EventGridExtension.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
|
@ -0,0 +1,93 @@
|
||||||
|
using Microsoft.Azure.WebJobs;
|
||||||
|
using Microsoft.Azure.WebJobs.Extensions.EventGrid;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Xunit;
|
||||||
|
using System.IO;
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Extension.tests
|
||||||
|
{
|
||||||
|
public class JobhostEndToEnd
|
||||||
|
{
|
||||||
|
const string singleEvent = @"{
|
||||||
|
'topic': '/subscriptions/5b4b650e-28b9-4790-b3ab-ddbd88d727c4/resourcegroups/canaryeh/providers/Microsoft.EventHub/namespaces/canaryeh',
|
||||||
|
'subject': 'eventhubs/test',
|
||||||
|
'eventType': 'captureFileCreated',
|
||||||
|
'eventTime': '2017-07-14T23:10:27.7689666Z',
|
||||||
|
'id': '7b11c4ce-1c34-4416-848b-1730e766f126',
|
||||||
|
'data': {
|
||||||
|
'fileUrl': 'https://shunsouthcentralus.blob.core.windows.net/debugging/shunBlob.txt',
|
||||||
|
'fileType': 'AzureBlockBlob',
|
||||||
|
'partitionId': '1',
|
||||||
|
'sizeInBytes': 0,
|
||||||
|
'eventCount': 0,
|
||||||
|
'firstSequenceNumber': -1,
|
||||||
|
'lastSequenceNumber': -1,
|
||||||
|
'firstEnqueueTime': '0001-01-01T00:00:00',
|
||||||
|
'lastEnqueueTime': '0001-01-01T00:00:00'
|
||||||
|
},
|
||||||
|
'publishTime': '2017-07-14T23:10:29.5004788Z'
|
||||||
|
}";
|
||||||
|
static private string functionOut = null;
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ConsumeEventGridEventTest()
|
||||||
|
{
|
||||||
|
|
||||||
|
EventGridEvent eve = JsonConvert.DeserializeObject<EventGridEvent>(singleEvent);
|
||||||
|
var args = new Dictionary<string, object>{
|
||||||
|
{ "value", eve }
|
||||||
|
};
|
||||||
|
|
||||||
|
var host = TestHelpers.NewHost<MyProg1>();
|
||||||
|
|
||||||
|
await host.CallAsync("MyProg1.TestEventGrid", args);
|
||||||
|
Assert.Equal(functionOut, eve.Subject);
|
||||||
|
functionOut = null;
|
||||||
|
|
||||||
|
await host.CallAsync("MyProg1.TestEventGridToString", args);
|
||||||
|
Assert.Equal(functionOut, eve.Subject);
|
||||||
|
functionOut = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UseInputBlobBinding()
|
||||||
|
{
|
||||||
|
EventGridEvent eve = JsonConvert.DeserializeObject<EventGridEvent>(singleEvent);
|
||||||
|
var args = new Dictionary<string, object>{
|
||||||
|
{ "value", eve }
|
||||||
|
};
|
||||||
|
|
||||||
|
var host = TestHelpers.NewHost<MyProg3>();
|
||||||
|
|
||||||
|
await host.CallAsync("MyProg3.TestBlobStream", args);
|
||||||
|
Assert.Equal(@"https://shunsouthcentralus.blob.core.windows.net/debugging/shunBlob.txt", functionOut);
|
||||||
|
functionOut = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MyProg1
|
||||||
|
{
|
||||||
|
public void TestEventGrid([EventGridTrigger] EventGridEvent value)
|
||||||
|
{
|
||||||
|
functionOut = value.Subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TestEventGridToString([EventGridTrigger] string value)
|
||||||
|
{
|
||||||
|
functionOut = JsonConvert.DeserializeObject<EventGridEvent>(value).Subject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MyProg3
|
||||||
|
{
|
||||||
|
public void TestBlobStream(
|
||||||
|
[EventGridTrigger] EventGridEvent value,
|
||||||
|
[BindingData("{data.fileUrl}")] string autoResolve)
|
||||||
|
{
|
||||||
|
functionOut = autoResolve;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
using Microsoft.Azure.WebJobs;
|
||||||
|
using Microsoft.Azure.WebJobs.Extensions.EventGrid;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Xunit;
|
||||||
|
using System.IO;
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Config;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace Extension.tests
|
||||||
|
{
|
||||||
|
public class TestListener : IWebHookProvider
|
||||||
|
{
|
||||||
|
|
||||||
|
Uri IWebHookProvider.GetUrl(IExtensionConfigProvider extension)
|
||||||
|
{
|
||||||
|
// Called by configuration registration. URI here doesn't matter.
|
||||||
|
return new Uri("http://localhost");
|
||||||
|
}
|
||||||
|
|
||||||
|
static private StringBuilder _log = new StringBuilder();
|
||||||
|
|
||||||
|
public TestListener()
|
||||||
|
{
|
||||||
|
_log.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsubscribe gives a 202.
|
||||||
|
[Fact]
|
||||||
|
public async Task TestUnsubscribe()
|
||||||
|
{
|
||||||
|
var ext = new EventGridExtensionConfig();
|
||||||
|
|
||||||
|
var host = TestHelpers.NewHost<MyProg1>(ext);
|
||||||
|
|
||||||
|
|
||||||
|
await host.StartAsync(); // add listener
|
||||||
|
|
||||||
|
|
||||||
|
var request = CreateUnsubscribeRequest("TestEventGrid");
|
||||||
|
IAsyncConverter <HttpRequestMessage, HttpResponseMessage> handler = ext;
|
||||||
|
var response = await handler.ConvertAsync(request, CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.Equal(HttpStatusCode.Accepted, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Test that an event payload with multiple events causes multiple dispatches,
|
||||||
|
/// and that each instance has correct binding data .
|
||||||
|
// This is the fundamental difference between a regular HTTP trigger and a EventGrid trigger.
|
||||||
|
[Fact]
|
||||||
|
public async Task TestDispatch()
|
||||||
|
{
|
||||||
|
var ext = new EventGridExtensionConfig();
|
||||||
|
|
||||||
|
var host = TestHelpers.NewHost<MyProg1>(ext);
|
||||||
|
|
||||||
|
await host.StartAsync(); // add listener
|
||||||
|
|
||||||
|
var request = CreateDispatchRequest("TestEventGrid", new EventGridEvent
|
||||||
|
{
|
||||||
|
Subject = "One",
|
||||||
|
Data = JObject.FromObject(new FakePayload
|
||||||
|
{
|
||||||
|
Prop = "alpha"
|
||||||
|
})
|
||||||
|
},
|
||||||
|
new EventGridEvent
|
||||||
|
{
|
||||||
|
Subject = "Two",
|
||||||
|
Data = JObject.FromObject(new FakePayload
|
||||||
|
{
|
||||||
|
Prop = "beta"
|
||||||
|
})
|
||||||
|
});
|
||||||
|
IAsyncConverter<HttpRequestMessage, HttpResponseMessage> handler = ext;
|
||||||
|
var response = await handler.ConvertAsync(request, CancellationToken.None);
|
||||||
|
|
||||||
|
// Verify that the user function was dispatched twice, in order.
|
||||||
|
// Also verifies each instance gets its own proper binding data (from FakePayload.Prop)
|
||||||
|
Assert.Equal("[Dispatch:One,alpha][Dispatch:Two,beta]", _log.ToString());
|
||||||
|
|
||||||
|
// TODO - Verify that we return from webhook before the dispatch is finished
|
||||||
|
// https://github.com/Azure/azure-functions-eventing-extension/issues/10
|
||||||
|
Assert.Equal(HttpStatusCode.Accepted, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static HttpRequestMessage CreateUnsubscribeRequest(string funcName)
|
||||||
|
{
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/?functionName=" + funcName);
|
||||||
|
request.Headers.Add("aeg-event-type", "Unsubscribe");
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HttpRequestMessage CreateDispatchRequest(string funcName, params EventGridEvent[] items)
|
||||||
|
{
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/?functionName=" + funcName);
|
||||||
|
request.Headers.Add("aeg-event-type", "Notification");
|
||||||
|
request.Content = new StringContent(
|
||||||
|
JsonConvert.SerializeObject(items),
|
||||||
|
Encoding.UTF8,
|
||||||
|
"application/json");
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FakePayload
|
||||||
|
{
|
||||||
|
public string Prop { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MyProg1
|
||||||
|
{
|
||||||
|
public void TestEventGrid(
|
||||||
|
[EventGridTrigger] EventGridEvent value,
|
||||||
|
[BindingData("{data.prop}")] string prop)
|
||||||
|
{
|
||||||
|
_log.Append("[Dispatch:" + value.Subject + "," + prop + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard2.0;net46</TargetFrameworks>
|
<TargetFrameworks>net46;netcoreapp2.0</TargetFrameworks>
|
||||||
<PackageId>Microsoft.Azure.WebJobs.Extensions.EventGrid.Test</PackageId>
|
<PackageId>Microsoft.Azure.WebJobs.Extensions.EventGrid.Test</PackageId>
|
||||||
<Description></Description>
|
<Description></Description>
|
||||||
<ApplicationIcon />
|
<ApplicationIcon />
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using Microsoft.Azure.WebJobs.Extensions.EventGrid;
|
using Microsoft.Azure.WebJobs;
|
||||||
|
using Microsoft.Azure.WebJobs.Extensions.EventGrid;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
|
@ -9,28 +10,19 @@ namespace EventGridBinding
|
||||||
{
|
{
|
||||||
// TODO test fields
|
// TODO test fields
|
||||||
public void TestEventGrid([EventGridTrigger] EventGridEvent value)
|
public void TestEventGrid([EventGridTrigger] EventGridEvent value)
|
||||||
|
{
|
||||||
|
Console.WriteLine(value.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TestEventGridToString([EventGridTrigger] string value)
|
||||||
{
|
{
|
||||||
Console.WriteLine(value);
|
Console.WriteLine(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TestInputStream([EventGridTrigger("eventhubcapture", Connection = "ShunTestConnectionString")] Stream myBlob, string blobTrigger)
|
public void TestBlobStream([EventGridTrigger] EventGridEvent value, [Blob("{data.container}/{data.blob}", FileAccess.Read, Connection = "ShunTestConnectionString")]Stream myBlob)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"file name {blobTrigger}");
|
|
||||||
var reader = new StreamReader(myBlob);
|
var reader = new StreamReader(myBlob);
|
||||||
Console.WriteLine(reader.ReadToEnd());
|
Console.WriteLine(reader.ReadToEnd());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TestByteArray([EventGridTrigger("eventhubcapture", Connection = "ShunTestConnectionString")] byte[] myBlob)
|
|
||||||
{
|
|
||||||
foreach (var b in myBlob)
|
|
||||||
{
|
|
||||||
Console.Write(b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void TestString([EventGridTrigger("eventhubcapture", Connection = "ShunTestConnectionString")] string myBlob)
|
|
||||||
{
|
|
||||||
Console.WriteLine(myBlob);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче