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
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26430.6
|
||||
VisualStudioVersion = 15.0.26730.3
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D0455F87-5E51-4AFD-ACF1-2F50A352F3D5}"
|
||||
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
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{B40A0AB6-1971-4DE9-954A-DD92AF1694CC}"
|
||||
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
|
||||
Global
|
||||
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}.Release|Any CPU.ActiveCfg = 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
|
||||
{828D7696-5D52-4AEB-8973-4382E9760094}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{828D7696-5D52-4AEB-8973-4382E9760094}.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}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{694416E6-094C-4F52-8B0F-D6B8197E5660}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{694416E6-094C-4F52-8B0F-D6B8197E5660}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{694416E6-094C-4F52-8B0F-D6B8197E5660}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{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
|
||||
EndGlobal
|
||||
|
|
|
@ -36,7 +36,9 @@ build_script:
|
|||
dotnet build -v q
|
||||
|
||||
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:
|
||||
- path: buildoutput\*.nupkg
|
||||
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,13 +11,13 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="$(TargetFramework)=='net46'">
|
||||
<PackageReference Include="Microsoft.Azure.WebJobs" Version="2.1.0-beta1" />
|
||||
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions" Version="2.1.0-beta1" />
|
||||
<Reference Include="System.Web" />
|
||||
<PackageReference Include="Microsoft.Azure.WebJobs" Version="2.1.0-beta1-10976" />
|
||||
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions" Version="2.1.0-beta1-10489" />
|
||||
<Reference Include="System.Web" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="$(TargetFramework)=='netstandard2.0'">
|
||||
<PackageReference Include="Microsoft.Azure.WebJobs" Version="3.0.0-beta1-10941" />
|
||||
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions" Version="3.0.0-beta1-10001" />
|
||||
<PackageReference Include="Microsoft.Azure.WebJobs" Version="3.0.0-beta1-10941" />
|
||||
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions" Version="3.0.0-beta1-10001" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
@ -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 Newtonsoft.Json;
|
||||
using System;
|
||||
|
@ -15,12 +16,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.EventGrid
|
|||
public class EventGridExtensionConfig : IExtensionConfigProvider,
|
||||
IAsyncConverter<HttpRequestMessage, HttpResponseMessage>
|
||||
{
|
||||
private bool _isTest = false;
|
||||
public bool IsTest
|
||||
{
|
||||
get { return _isTest; }
|
||||
set { _isTest = value; }
|
||||
}
|
||||
private TraceWriter _tracer = null;
|
||||
|
||||
public void Initialize(ExtensionConfigContext context)
|
||||
{
|
||||
|
@ -28,6 +24,11 @@ namespace Microsoft.Azure.WebJobs.Extensions.EventGrid
|
|||
{
|
||||
throw new ArgumentNullException("context");
|
||||
}
|
||||
else if (context.Trace == null)
|
||||
{
|
||||
throw new ArgumentNullException("context.Trace");
|
||||
}
|
||||
_tracer = context.Trace;
|
||||
|
||||
Uri url = context.GetWebhookHandler();
|
||||
|
||||
|
@ -50,6 +51,9 @@ namespace Microsoft.Azure.WebJobs.Extensions.EventGrid
|
|||
|
||||
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"];
|
||||
if (String.IsNullOrEmpty(functionName) || !_listeners.ContainsKey(functionName))
|
||||
{
|
||||
|
@ -67,20 +71,13 @@ namespace Microsoft.Azure.WebJobs.Extensions.EventGrid
|
|||
{
|
||||
string jsonArray = await req.Content.ReadAsStringAsync();
|
||||
SubscriptionValidationEvent validationEvent = null;
|
||||
try
|
||||
{
|
||||
List<EventGridEvent> events = JsonConvert.DeserializeObject<List<EventGridEvent>>(jsonArray);
|
||||
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);
|
||||
}
|
||||
List<EventGridEvent> events = JsonConvert.DeserializeObject<List<EventGridEvent>>(jsonArray);
|
||||
validationEvent = events[0].Data.ToObject<SubscriptionValidationEvent>();
|
||||
SubscriptionValidationResponse validationResponse = new SubscriptionValidationResponse { ValidationResponse = validationEvent.ValidationCode };
|
||||
var returnMessage = new HttpResponseMessage(HttpStatusCode.OK);
|
||||
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;
|
||||
}
|
||||
else if (String.Equals(eventTypeHeader, "Notification", StringComparison.OrdinalIgnoreCase))
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.EventGrid
|
|||
|
||||
// Register our extension configuration provider
|
||||
// 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 Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
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.Protocols;
|
||||
using Microsoft.Azure.WebJobs.Host.Triggers;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
@ -38,53 +40,41 @@ namespace Microsoft.Azure.WebJobs.Extensions.EventGrid
|
|||
return Task.FromResult<ITriggerBinding>(null);
|
||||
}
|
||||
|
||||
// depends on the publisher, we could have different expectation for paramter
|
||||
// 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)
|
||||
if (!isSupportBindingType(parameter.ParameterType))
|
||||
{
|
||||
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 readonly ParameterInfo _parameter;
|
||||
private readonly IReadOnlyDictionary<string, Type> _bindingContract;
|
||||
private readonly Dictionary<string, Type> _bindingContract;
|
||||
private EventGridExtensionConfig _listenersStore;
|
||||
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;
|
||||
_parameter = parameter;
|
||||
_functionName = functionName;
|
||||
_bindingContract = contract;
|
||||
_bindingContract = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{"data",typeof(JObject) }
|
||||
};
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<string, Type> BindingDataContract
|
||||
{
|
||||
// TODO? not per parameter?
|
||||
get { return _bindingContract; }
|
||||
}
|
||||
|
||||
|
@ -93,21 +83,30 @@ namespace Microsoft.Azure.WebJobs.Extensions.EventGrid
|
|||
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;
|
||||
var bindingData = await _publisher.ExtractBindingData(triggerValue, _parameter.ParameterType);
|
||||
IValueBinder valueBinder = new EventGridValueBinder(_parameter, _publisher.GetArgument(bindingData), _publisher.Recycles);
|
||||
return new TriggerData(valueBinder, bindingData);
|
||||
var bindingData = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{"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)
|
||||
{
|
||||
// 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));
|
||||
}
|
||||
|
||||
|
@ -141,7 +140,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.EventGrid
|
|||
private readonly object _value;
|
||||
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)
|
||||
{
|
||||
_value = value;
|
||||
|
|
|
@ -16,85 +16,6 @@ namespace Microsoft.Azure.WebJobs.Extensions.EventGrid
|
|||
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
|
||||
{
|
||||
/*
|
||||
|
@ -115,7 +36,6 @@ namespace Microsoft.Azure.WebJobs.Extensions.EventGrid
|
|||
[JsonProperty(PropertyName = "subject")]
|
||||
public string Subject { get; set; }
|
||||
|
||||
// the content of this depends on the publisher
|
||||
[JsonProperty(PropertyName = "data")]
|
||||
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">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;net46</TargetFrameworks>
|
||||
<TargetFrameworks>net46;netcoreapp2.0</TargetFrameworks>
|
||||
<PackageId>Microsoft.Azure.WebJobs.Extensions.EventGrid.Test</PackageId>
|
||||
<Description></Description>
|
||||
<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.IO;
|
||||
|
||||
|
@ -9,28 +10,19 @@ namespace EventGridBinding
|
|||
{
|
||||
// TODO test fields
|
||||
public void TestEventGrid([EventGridTrigger] EventGridEvent value)
|
||||
{
|
||||
Console.WriteLine(value.ToString());
|
||||
}
|
||||
|
||||
public void TestEventGridToString([EventGridTrigger] string 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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче