public async Task LogFunctionCompletedAsync_CallsTraceWriter() { FunctionDescriptor descriptor = new FunctionDescriptor { ShortName = "TestJob" }; FunctionCompletedMessage successMessage = new FunctionCompletedMessage { Function = descriptor }; Exception ex = new Exception("Kaboom!"); FunctionCompletedMessage failureMessage = new FunctionCompletedMessage { Function = descriptor, Failure = new FunctionFailure { Exception = ex }, FunctionInstanceId = new Guid("8d71c9e3-e809-4cfb-bb78-48ae25c7d26d") }; _mockTraceWriter.Setup(p => p.Trace(TraceLevel.Info, Host.TraceSource.Execution, "Executed: 'TestJob' (Succeeded)", null)); _mockTraceWriter.Setup(p => p.Trace(TraceLevel.Error, Host.TraceSource.Execution, "Executed: 'TestJob' (Failed)", ex)); _mockTraceWriter.Setup(p => p.Trace(TraceLevel.Error, Host.TraceSource.Host, " Function had errors. See Azure WebJobs SDK dashboard for details. Instance ID is '8d71c9e3-e809-4cfb-bb78-48ae25c7d26d'", null)); await _logger.LogFunctionCompletedAsync(successMessage, CancellationToken.None); await _logger.LogFunctionCompletedAsync(failureMessage, CancellationToken.None); _mockTraceWriter.VerifyAll(); }
private static async Task<Dictionary<string, object>> GetFunctionArgumentsAsync(FunctionDescriptor function, HttpRequestMessage request) { ParameterDescriptor triggerParameter = function.Parameters.First(p => p.IsTrigger); Dictionary<string, object> arguments = new Dictionary<string, object>(); object triggerArgument = null; if (triggerParameter.Type == typeof(HttpRequestMessage)) { triggerArgument = request; } else { // We'll replace the trigger argument but still want to flow the request // so add it to the arguments, as a system argument arguments.Add(ScriptConstants.DefaultSystemTriggerParameterName, request); HttpTriggerBindingMetadata httpFunctionMetadata = (HttpTriggerBindingMetadata)function.Metadata.InputBindings.FirstOrDefault(p => p.Type == BindingType.HttpTrigger); if (!string.IsNullOrEmpty(httpFunctionMetadata.WebHookType)) { WebHookHandlerContext webHookContext; if (request.Properties.TryGetValue(ScriptConstants.AzureFunctionsWebHookContextKey, out webHookContext)) { triggerArgument = GetWebHookData(triggerParameter.Type, webHookContext); } } if (triggerArgument == null) { triggerArgument = await request.Content.ReadAsAsync(triggerParameter.Type); } } arguments.Add(triggerParameter.Name, triggerArgument); return arguments; }
public async Task CreateAsync_SkipsDisabledFunctions(Type jobType, string methodName) { Environment.SetEnvironmentVariable("EnvironmentSettingTrue", "True"); Mock<IFunctionDefinition> mockFunctionDefinition = new Mock<IFunctionDefinition>(); Mock<IFunctionInstanceFactory> mockInstanceFactory = new Mock<IFunctionInstanceFactory>(MockBehavior.Strict); Mock<IListenerFactory> mockListenerFactory = new Mock<IListenerFactory>(MockBehavior.Strict); SingletonManager singletonManager = new SingletonManager(); TestTraceWriter traceWriter = new TestTraceWriter(TraceLevel.Verbose); // create a bunch of function definitions that are disabled List<FunctionDefinition> functions = new List<FunctionDefinition>(); FunctionDescriptor descriptor = new FunctionDescriptor { Method = jobType.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static), ShortName = string.Format("{0}.{1}", jobType.Name, methodName) }; FunctionDefinition definition = new FunctionDefinition(descriptor, mockInstanceFactory.Object, mockListenerFactory.Object); functions.Add(definition); // Create the composite listener - this will fail if any of the // function definitions indicate that they are not disabled HostListenerFactory factory = new HostListenerFactory(functions, singletonManager, DefaultJobActivator.Instance, null, traceWriter); IListener listener = await factory.CreateAsync(CancellationToken.None); Assert.Equal(1, traceWriter.Traces.Count); Assert.Equal(TraceLevel.Info, traceWriter.Traces[0].Level); Assert.Equal(TraceSource.Host, traceWriter.Traces[0].Source); Assert.Equal(string.Format("Function '{0}' is disabled", descriptor.ShortName), traceWriter.Traces[0].Message); Environment.SetEnvironmentVariable("EnvironmentSettingTrue", null); }
public async Task<HttpResponseMessage> HandleRequestAsync(FunctionDescriptor function, HttpRequestMessage request, Func<HttpRequestMessage, Task<HttpResponseMessage>> invokeFunction) { // First check if there is a registered WebHook Receiver for this request, and if // so use it HttpTriggerBindingMetadata httpFunctionMetadata = (HttpTriggerBindingMetadata)function.Metadata.InputBindings.FirstOrDefault(p => p.Type == BindingType.HttpTrigger); string webHookReceiver = httpFunctionMetadata.WebHookType; IWebHookReceiver receiver = null; if (string.IsNullOrEmpty(webHookReceiver) || !_receiverLookup.TryGetValue(webHookReceiver, out receiver)) { // The function is not correctly configured. Log an error and return 500 string configurationError = string.Format(CultureInfo.InvariantCulture, "Invalid WebHook configuration. Unable to find a receiver for WebHook type '{0}'", webHookReceiver); function.Invoker.OnError(new FunctionInvocationException(configurationError)); return new HttpResponseMessage(System.Net.HttpStatusCode.InternalServerError); } HttpRequestContext context = new HttpRequestContext { Configuration = _httpConfiguration }; request.SetConfiguration(_httpConfiguration); // add the anonymous handler function from above to the request properties // so our custom WebHookHandler can invoke it at the right time request.Properties.Add(AzureFunctionsCallbackKey, invokeFunction); // Request content can't be read multiple // times, so this forces it to buffer await request.Content.LoadIntoBufferAsync(); string receiverId = function.Name.ToLowerInvariant(); return await receiver.ReceiveAsync(receiverId, context, request); }
public FunctionExecutorTests() { _descriptor = new FunctionDescriptor(); _mockFunctionInstance = new Mock<IFunctionInstance>(MockBehavior.Strict); _mockFunctionInstance.Setup(p => p.FunctionDescriptor).Returns(_descriptor); _cancellationTokenSource = new CancellationTokenSource(); _traceWriter = new TestTraceWriter(TraceLevel.Verbose); }
public FunctionInstance(Guid id, Guid? parentId, ExecutionReason reason, IBindingSource bindingSource, IFunctionInvoker invoker, FunctionDescriptor functionDescriptor) { _id = id; _parentId = parentId; _reason = reason; _bindingSource = bindingSource; _invoker = invoker; _functionDescriptor = functionDescriptor; }
public void Add(IFunctionDefinition function, FunctionDescriptor descriptor, MethodInfo method) { string id = descriptor.Id; if (_functionsById.ContainsKey(id)) { throw new InvalidOperationException("Method overloads are not supported. " + "There are multiple methods with the name '" + id + "'."); } _functionsById.Add(id, function); _functionsByMethod.Add(method, function); _functionDescriptors.Add(descriptor); }
public void FormatReason_BlobTriggeredFunction_ReturnsExpectedReason() { FunctionStartedMessage message = new FunctionStartedMessage(); message.Reason = ExecutionReason.AutomaticTrigger; BlobTriggerParameterDescriptor triggerParameterDescriptor = new BlobTriggerParameterDescriptor { Name = "paramName" }; FunctionDescriptor function = new FunctionDescriptor(); function.Parameters = new ParameterDescriptor[] { triggerParameterDescriptor }; message.Function = function; message.Arguments = new Dictionary<string, string>() { { "paramName", "blob/path" } }; string result = message.FormatReason(); Assert.Equal("New blob detected: blob/path", result); }
public async Task Generate_EndToEnd() { // construct our TimerTrigger attribute ([TimerTrigger("00:00:02", RunOnStartup = true)]) Collection<ParameterDescriptor> parameters = new Collection<ParameterDescriptor>(); ParameterDescriptor parameter = new ParameterDescriptor("timerInfo", typeof(TimerInfo)); ConstructorInfo ctorInfo = typeof(TimerTriggerAttribute).GetConstructor(new Type[] { typeof(string) }); PropertyInfo runOnStartupProperty = typeof(TimerTriggerAttribute).GetProperty("RunOnStartup"); CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder( ctorInfo, new object[] { "00:00:02" }, new PropertyInfo[] { runOnStartupProperty }, new object[] { true }); parameter.CustomAttributes.Add(attributeBuilder); parameters.Add(parameter); // create the FunctionDefinition FunctionMetadata metadata = new FunctionMetadata(); TestInvoker invoker = new TestInvoker(); FunctionDescriptor function = new FunctionDescriptor("TimerFunction", invoker, metadata, parameters); Collection<FunctionDescriptor> functions = new Collection<FunctionDescriptor>(); functions.Add(function); // generate the Type Type functionType = FunctionGenerator.Generate("TestScriptHost", "TestFunctions", functions); // verify the generated function MethodInfo method = functionType.GetMethod("TimerFunction"); ParameterInfo triggerParameter = method.GetParameters()[0]; TimerTriggerAttribute triggerAttribute = triggerParameter.GetCustomAttribute<TimerTriggerAttribute>(); Assert.NotNull(triggerAttribute); // start the JobHost which will start running the timer function JobHostConfiguration config = new JobHostConfiguration() { TypeLocator = new TypeLocator(functionType) }; config.UseTimers(); JobHost host = new JobHost(config); await host.StartAsync(); await Task.Delay(3000); await host.StopAsync(); // verify our custom invoker was called Assert.True(invoker.InvokeCount >= 2); }
public string GetFunctionGroupString(FunctionDescriptor.FunctionGroup grp) { switch (grp) { case FunctionDescriptor.FunctionGroup.NumberGroup: return StrRes.GetString(StrRes.STR_NUMBER_GROUP); case FunctionDescriptor.FunctionGroup.CoordinateGroup: return StrRes.GetString(StrRes.STR_COORDINATE_GROUP); case FunctionDescriptor.FunctionGroup.TextGroup: return StrRes.GetString(StrRes.STR_TEXT_GROUP); case FunctionDescriptor.FunctionGroup.ContextGroup: return StrRes.GetString(StrRes.STR_CONTEXT_GROUP); case FunctionDescriptor.FunctionGroup.TrigonometricGroup: return StrRes.GetString(StrRes.STR_TRIGONOMETRIC_GROUP); default: return StrRes.GetString(StrRes.STR_UNKNOWN_GROUP); } }
public void FormatReason_QueueTriggeredFunction_ReturnsExpectedReason() { FunctionStartedMessage message = new FunctionStartedMessage(); QueueTriggerParameterDescriptor triggerParameterDescriptor = new QueueTriggerParameterDescriptor { Name = "paramName", QueueName = "testqueue" }; FunctionDescriptor function = new FunctionDescriptor { Parameters = new ParameterDescriptor[] { triggerParameterDescriptor } }; message.Function = function; message.Reason = ExecutionReason.AutomaticTrigger; string result = message.FormatReason(); Assert.Equal("New queue message detected on 'testqueue'.", result); }
public async Task LogFunctionCompletedAsync_CallsTraceWriter() { FunctionDescriptor descriptor = new FunctionDescriptor { ShortName = "TestJob", FullName = "TestNamespace.TestJob" }; FunctionCompletedMessage successMessage = new FunctionCompletedMessage { Function = descriptor, FunctionInstanceId = Guid.NewGuid() }; Exception ex = new Exception("Kaboom!"); FunctionCompletedMessage failureMessage = new FunctionCompletedMessage { Function = descriptor, Failure = new FunctionFailure { Exception = ex }, FunctionInstanceId = new Guid("8d71c9e3-e809-4cfb-bb78-48ae25c7d26d") }; await _logger.LogFunctionCompletedAsync(successMessage, CancellationToken.None); await _logger.LogFunctionCompletedAsync(failureMessage, CancellationToken.None); Assert.Equal(3, _traceWriter.Traces.Count); TraceEvent traceEvent = _traceWriter.Traces[0]; Assert.Equal(TraceLevel.Info, traceEvent.Level); Assert.Equal(Host.TraceSource.Execution, traceEvent.Source); Assert.Equal("Executed: 'TestJob' (Succeeded)", traceEvent.Message); traceEvent = _traceWriter.Traces[1]; Assert.Equal(TraceLevel.Error, traceEvent.Level); Assert.Equal(Host.TraceSource.Execution, traceEvent.Source); Assert.Equal("Executed: 'TestJob' (Failed)", traceEvent.Message); Assert.Same(ex, traceEvent.Exception); traceEvent = _traceWriter.Traces[2]; Assert.Equal(TraceLevel.Error, traceEvent.Level); Assert.Equal(Host.TraceSource.Host, traceEvent.Source); Assert.Equal(" Function had errors. See Azure WebJobs SDK dashboard for details. Instance ID is '8d71c9e3-e809-4cfb-bb78-48ae25c7d26d'", traceEvent.Message); Assert.Same(ex, traceEvent.Exception); }
public async Task<HttpResponseMessage> HandleRequestAsync(FunctionDescriptor function, HttpRequestMessage request, CancellationToken cancellationToken) { // All authentication is assumed to have been done on the request // BEFORE this method is called // Invoke the function ParameterDescriptor triggerParameter = function.Parameters.First(p => p.IsTrigger); Dictionary<string, object> arguments = new Dictionary<string, object> { { triggerParameter.Name, request } }; await Instance.CallAsync(function.Name, arguments, cancellationToken); // Get the response HttpResponseMessage response = null; if (!request.Properties.TryGetValue<HttpResponseMessage>("MS_AzureFunctionsHttpResponse", out response)) { // the function was successful but did not write an explicit response response = new HttpResponseMessage(HttpStatusCode.OK); } return response; }
public async Task<HttpResponseMessage> HandleRequestAsync(FunctionDescriptor function, HttpRequestMessage request, CancellationToken cancellationToken) { // All authentication is assumed to have been done on the request // BEFORE this method is called Dictionary<string, object> arguments = await GetFunctionArgumentsAsync(function, request); // Suspend the current synchronization context so we don't pass the ASP.NET // context down to the function. using (var syncContextSuspensionScope = new SuspendedSynchronizationContextScope()) { await Instance.CallAsync(function.Name, arguments, cancellationToken); } // Get the response HttpResponseMessage response = null; if (!request.Properties.TryGetValue<HttpResponseMessage>("MS_AzureFunctionsHttpResponse", out response)) { // the function was successful but did not write an explicit response response = new HttpResponseMessage(HttpStatusCode.OK); } return response; }
/// <summary> /// Initializes a new instance of the <see cref="DSVarArgFunction"/> class. /// </summary> /// <param name="descriptor">Function descritor.</param> public DSVarArgFunction(FunctionDescriptor descriptor) : base(new ZeroTouchVarArgNodeController <FunctionDescriptor>(descriptor)) { VarInputController = new ZeroTouchVarInputController(this); defaultNumInputs = descriptor.Parameters.Count(); }
public override async Task <HttpResponseMessage> ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken) { HttpRequestMessage request = controllerContext.Request; // First see if the request maps to an HTTP function FunctionDescriptor function = _scriptHostManager.GetHttpFunctionOrNull(request.RequestUri); if (function == null) { return(new HttpResponseMessage(HttpStatusCode.NotFound)); } // Determine the authorization level of the request SecretManager secretManager = (SecretManager)controllerContext.Configuration.DependencyResolver.GetService(typeof(SecretManager)); AuthorizationLevel authorizationLevel = AuthorizationLevelAttribute.GetAuthorizationLevel(request, secretManager, functionName: function.Name); if (function.Metadata.IsDisabled && authorizationLevel != AuthorizationLevel.Admin) { // disabled functions are not publically addressable w/o Admin level auth return(new HttpResponseMessage(HttpStatusCode.NotFound)); } // Dispatch the request HttpTriggerBindingMetadata httpFunctionMetadata = (HttpTriggerBindingMetadata)function.Metadata.InputBindings.FirstOrDefault(p => p.Type == BindingType.HttpTrigger); bool isWebHook = !string.IsNullOrEmpty(httpFunctionMetadata.WebHookType); HttpResponseMessage response = null; if (isWebHook) { if (authorizationLevel == AuthorizationLevel.Admin) { // Admin level requests bypass the WebHook auth pipeline response = await _scriptHostManager.HandleRequestAsync(function, request, cancellationToken); } else { // This is a WebHook request so define a delegate for the user function. // The WebHook Receiver pipeline will first validate the request fully // then invoke this callback. Func <HttpRequestMessage, Task <HttpResponseMessage> > invokeFunction = async(req) => { // Reset the content stream before passing the request down to the function Stream stream = await req.Content.ReadAsStreamAsync(); stream.Seek(0, SeekOrigin.Begin); return(await _scriptHostManager.HandleRequestAsync(function, req, cancellationToken)); }; response = await _webHookReceiverManager.HandleRequestAsync(function, request, invokeFunction); } } else { // Authorize if (authorizationLevel < httpFunctionMetadata.AuthLevel) { return(new HttpResponseMessage(HttpStatusCode.Unauthorized)); } // Validate the HttpMethod // Note that for WebHook requests, WebHook receiver does its own validation if (httpFunctionMetadata.Methods != null && !httpFunctionMetadata.Methods.Contains(request.Method)) { return(new HttpResponseMessage(HttpStatusCode.MethodNotAllowed)); } // Not a WebHook request so dispatch directly response = await _scriptHostManager.HandleRequestAsync(function, request, cancellationToken); } return(response); }
internal async Task GeneratedMethods_WithOutParams_DoNotCauseDeadlocks(string fixture) { var traceWriter = new TestTraceWriter(TraceLevel.Verbose); ScriptHostConfiguration config = new ScriptHostConfiguration() { RootScriptPath = @"TestScripts\FunctionGeneration", TraceWriter = traceWriter }; string secretsPath = Path.Combine(Path.GetTempPath(), @"FunctionTests\Secrets"); ISecretsRepository repository = new FileSystemSecretsRepository(secretsPath); WebHostSettings webHostSettings = new WebHostSettings(); webHostSettings.SecretsPath = secretsPath; var secretManager = new SecretManager(SettingsManager, repository, NullTraceWriter.Instance); using (var manager = new WebScriptHostManager(config, new TestSecretManagerFactory(secretManager), SettingsManager, webHostSettings)) { Thread runLoopThread = new Thread(_ => { manager.RunAndBlock(CancellationToken.None); }); runLoopThread.IsBackground = true; runLoopThread.Start(); await TestHelpers.Await(() => { return(manager.State == ScriptHostState.Running); }); var request = new HttpRequestMessage(HttpMethod.Get, string.Format("http://localhost/api/httptrigger-{0}", fixture)); FunctionDescriptor function = manager.GetHttpFunctionOrNull(request); SynchronizationContext currentContext = SynchronizationContext.Current; var resetEvent = new ManualResetEventSlim(); try { var requestThread = new Thread(() => { var context = new SingleThreadedSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(context); manager.HandleRequestAsync(function, request, CancellationToken.None) .ContinueWith(task => resetEvent.Set()); Thread.Sleep(500); context.Run(); }); requestThread.IsBackground = true; requestThread.Start(); bool threadSignaled = resetEvent.Wait(TimeSpan.FromSeconds(10)); requestThread.Abort(); Assert.True(threadSignaled, "Thread execution did not complete"); } finally { SynchronizationContext.SetSynchronizationContext(currentContext); manager.Stop(); } } }
public void UpdateEntityDescriptor() { if (!this.EntityDescriptorUpdated) { if (this.entry.MediaResource != null) { if (this.entry.MediaResource.ReadLink != null) { this.EntityDescriptor.ReadStreamUri = this.entry.MediaResource.ReadLink; } if (this.entry.MediaResource.EditLink != null) { this.EntityDescriptor.EditStreamUri = this.entry.MediaResource.EditLink; } if (this.entry.MediaResource.ETag != null) { this.EntityDescriptor.StreamETag = this.entry.MediaResource.ETag; } } foreach (ODataProperty property in this.Properties) { ODataStreamReferenceValue value2 = property.Value as ODataStreamReferenceValue; if (value2 != null) { StreamDescriptor descriptor = this.EntityDescriptor.AddStreamInfoIfNotPresent(property.Name); if (value2.ReadLink != null) { descriptor.SelfLink = value2.ReadLink; } if (value2.EditLink != null) { descriptor.EditLink = value2.EditLink; } descriptor.ETag = value2.ETag; descriptor.ContentType = value2.ContentType; } } foreach (ODataAssociationLink link in this.entry.AssociationLinks) { this.EntityDescriptor.AddAssociationLink(link.Name, link.Url); } foreach (ODataFunction function in this.entry.Functions) { FunctionDescriptor operationDescriptor = new FunctionDescriptor { Title = function.Title, Metadata = function.Metadata, Target = function.Target }; this.EntityDescriptor.AddOperationDescriptor(operationDescriptor); } foreach (ODataAction action in this.entry.Actions) { ActionDescriptor descriptor3 = new ActionDescriptor { Title = action.Title, Metadata = action.Metadata, Target = action.Target }; this.EntityDescriptor.AddOperationDescriptor(descriptor3); } this.EntityDescriptorUpdated = true; } }
public void CreateSnapshot_CreatesExpectedSnapshot() { FunctionDescriptor function = new FunctionDescriptor { Id = "FunctionId", FullName = "FullName", ShortName = "ShortName", Parameters = new ParameterDescriptor[] { new ParameterDescriptor { Name = "param1" }, new ParameterDescriptor { Name = "param2" } } }; FunctionStartedMessage message = new FunctionStartedMessage { FunctionInstanceId = Guid.NewGuid(), HostInstanceId = Guid.NewGuid(), InstanceQueueName = "InstanceQueueName", Reason = ExecutionReason.AutomaticTrigger, ReasonDetails = "A trigger fired!", Heartbeat = new HeartbeatDescriptor { InstanceBlobName = "InstanceBlobName", SharedContainerName = "SharedContainerName", SharedDirectoryName = "SharedDirectoryName", ExpirationInSeconds = 5 }, SharedQueueName = "SharedQueueName", Function = function, Arguments = new Dictionary<string, string> { { "param1", "foo" }, { "param2", "bar" } }, ParentId = Guid.NewGuid(), StartTime = DateTime.Now, OutputBlob = new LocalBlobDescriptor { BlobName = "OutputBlobName", ContainerName = "OutputBlobContainerName" }, ParameterLogBlob = new LocalBlobDescriptor { BlobName = "ParameterLogBlobName", ContainerName = "ParameterLogBlobContainerName" }, WebJobRunIdentifier = new WebJobRunIdentifier { JobName = "JobName", JobType = WebJobTypes.Triggered, RunId = "RunId", WebSiteName = "WebSiteName" } }; FunctionInstanceSnapshot snapshot = FunctionIndexer.CreateSnapshot(message); Assert.Equal(message.FunctionInstanceId, snapshot.Id); Assert.Equal(message.HostInstanceId, snapshot.HostInstanceId); Assert.Equal(message.InstanceQueueName, snapshot.InstanceQueueName); Assert.Same(message.Heartbeat, snapshot.Heartbeat); Assert.Equal("SharedQueueName_FunctionId", snapshot.FunctionId); Assert.Equal(message.Function.FullName, snapshot.FunctionFullName); Assert.Equal(message.Function.ShortName, snapshot.FunctionShortName); Assert.Equal(2, snapshot.Arguments.Count); Assert.Equal("foo", snapshot.Arguments["param1"].Value); Assert.Equal("bar", snapshot.Arguments["param2"].Value); Assert.Equal(message.ParentId, snapshot.ParentId); Assert.Equal(message.ReasonDetails, snapshot.Reason); Assert.Equal(message.StartTime, snapshot.QueueTime); Assert.Equal(message.StartTime, snapshot.StartTime); Assert.Equal(message.OutputBlob, snapshot.OutputBlob); Assert.Same(message.ParameterLogBlob, snapshot.ParameterLogBlob); Assert.Equal(message.WebJobRunIdentifier.WebSiteName, snapshot.WebSiteName); Assert.Equal(message.WebJobRunIdentifier.JobType.ToString(), snapshot.WebJobType); Assert.Equal(message.WebJobRunIdentifier.JobName, snapshot.WebJobName); Assert.Equal(message.WebJobRunIdentifier.RunId, snapshot.WebJobRunId); }
public void Generate_WithOutParams_CorrectlyUpdatesOutput() { string functionName = "FunctionWithOutValue"; Collection<ParameterDescriptor> parameters = new Collection<ParameterDescriptor>(); parameters.Add(new ParameterDescriptor("param1", typeof(string).MakeByRefType()) { Attributes = ParameterAttributes.Out }); FunctionMetadata metadata = new FunctionMetadata(); object[] invocationArguments = null; TestInvoker invoker = new TestInvoker(args => { invocationArguments = args; invocationArguments[0] = "newvalue"; }); FunctionDescriptor function = new FunctionDescriptor(functionName, invoker, metadata, parameters); Collection<FunctionDescriptor> functions = new Collection<FunctionDescriptor>(); functions.Add(function); // generate the Type Type functionType = FunctionGenerator.Generate("TestScriptHost", "TestFunctions", functions); // verify the generated function MethodInfo method = functionType.GetMethod(functionName); ParameterInfo[] functionParams = method.GetParameters(); // Verify that we have the correct number of parameters Assert.Equal(parameters.Count, functionParams.Length); // Verify that the method is invocable DateTimeOffset input = DateTimeOffset.Now; method.Invoke(null, new object[] { null }); // verify our custom invoker was called Assert.Equal(1, invoker.InvokeCount); Assert.NotNull(invocationArguments); Assert.Equal("newvalue", (string)invocationArguments[0]); }
public FunctionInstanceFactory(IFunctionBinding binding, IFunctionInvoker invoker, FunctionDescriptor descriptor) { _binding = binding; _invoker = invoker; _descriptor = descriptor; }
public ZeroTouchSearchElement(FunctionDescriptor functionDescriptor) { this.functionDescriptor = functionDescriptor; Name = functionDescriptor.UserFriendlyName; if (functionDescriptor.IsOverloaded) { var parameters = new StringBuilder(); parameters.Append("("); parameters.Append(String.Join(", ", functionDescriptor.Parameters.Select(x => x.Name))); parameters.Append(")"); Parameters = parameters.ToString(); } FullCategoryName = functionDescriptor.Category; Description = functionDescriptor.Description; Assembly = functionDescriptor.Assembly; ElementType = ElementTypes.ZeroTouch; if (functionDescriptor.IsBuiltIn) { ElementType |= ElementTypes.BuiltIn; } if (functionDescriptor.IsPackageMember) { ElementType |= ElementTypes.Packaged; } inputParameters = new List <Tuple <string, string> >(functionDescriptor.InputParameters); outputParameters = new List <string>() { functionDescriptor.ReturnType.ToShortString() }; foreach (var tag in functionDescriptor.GetSearchTags()) { SearchKeywords.Add(tag); } var weights = functionDescriptor.GetSearchTagWeights(); foreach (var weight in weights) { // Search tag weight can't be more then 1. if (weight <= 1) { keywordWeights.Add(weight); } } int weightsCount = weights.Count(); // If there weren't added weights for search tags, then add default value - 0.5 if (weightsCount != SearchKeywords.Count) { int numberOfLackingWeights = SearchKeywords.Count - weightsCount; // Number of lacking weights should be more than 0. // It can be less then 0 only if there was some mistake in xml file. if (numberOfLackingWeights > 0) { for (int i = 0; i < numberOfLackingWeights; i++) { keywordWeights.Add(0.5); } } } iconName = GetIconName(); }
public override async Task <HttpResponseMessage> ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken) { HttpRequestMessage request = controllerContext.Request; // First see if the request maps to an HTTP function FunctionDescriptor function = _scriptHostManager.GetHttpFunctionOrNull(request); if (function == null) { return(new HttpResponseMessage(HttpStatusCode.NotFound)); } // Determine the authorization level of the request ISecretManager secretManager = controllerContext.Configuration.DependencyResolver.GetService <ISecretManager>(); var settings = controllerContext.Configuration.DependencyResolver.GetService <WebHostSettings>(); var authorizationLevel = settings.IsAuthDisabled ? AuthorizationLevel.Admin : await AuthorizationLevelAttribute.GetAuthorizationLevelAsync(request, secretManager, functionName : function.Name); if (function.Metadata.IsExcluded || (function.Metadata.IsDisabled && authorizationLevel != AuthorizationLevel.Admin)) { // disabled functions are not publicly addressable w/o Admin level auth, // and excluded functions are also ignored here (though the check above will // already exclude them) return(new HttpResponseMessage(HttpStatusCode.NotFound)); } // Dispatch the request HttpTriggerBindingMetadata httpFunctionMetadata = (HttpTriggerBindingMetadata)function.Metadata.InputBindings.FirstOrDefault(p => string.Compare("HttpTrigger", p.Type, StringComparison.OrdinalIgnoreCase) == 0); bool isWebHook = !string.IsNullOrEmpty(httpFunctionMetadata.WebHookType); HttpResponseMessage response = null; if (isWebHook) { if (authorizationLevel == AuthorizationLevel.Admin) { // Admin level requests bypass the WebHook auth pipeline response = await _scriptHostManager.HandleRequestAsync(function, request, cancellationToken); } else { // This is a WebHook request so define a delegate for the user function. // The WebHook Receiver pipeline will first validate the request fully // then invoke this callback. Func <HttpRequestMessage, Task <HttpResponseMessage> > invokeFunction = async(req) => { // Reset the content stream before passing the request down to the function Stream stream = await req.Content.ReadAsStreamAsync(); stream.Seek(0, SeekOrigin.Begin); return(await _scriptHostManager.HandleRequestAsync(function, req, cancellationToken)); }; response = await _webHookReceiverManager.HandleRequestAsync(function, request, invokeFunction); } } else { // Authorize if (authorizationLevel < httpFunctionMetadata.AuthLevel) { return(new HttpResponseMessage(HttpStatusCode.Unauthorized)); } // Not a WebHook request so dispatch directly response = await _scriptHostManager.HandleRequestAsync(function, request, cancellationToken); } return(response); }
private void Log(LogLevel level, FunctionDescriptor descriptor, Guid functionId, string message, Exception exception = null) { ILogger logger = _loggerFactory?.CreateLogger(LogCategories.CreateFunctionCategory(descriptor.LogName)); logger?.Log(level, 0, message, exception, (s, e) => s); }
private void Log(LogLevel level, FunctionDescriptor descriptor, string message, params object[] args) { ILogger logger = _loggerFactory?.CreateLogger(LogCategories.CreateFunctionCategory(descriptor.LogName)); logger?.Log(level, 0, message, args); }
public ZeroTouchNodeController( EngineController engineController, FunctionDescriptor zeroTouchDef) : base(zeroTouchDef) { this.engineController = engineController; }
public DSVarArgFunction(WorkspaceModel workspaceModel, FunctionDescriptor descriptor) : base(workspaceModel, new ZeroTouchVarArgNodeController(workspaceModel.DynamoModel.EngineController, descriptor)) { VarInputController = new ZeroTouchVarInputController(this); }
public async Task Generate_EndToEnd() { // construct our TimerTrigger attribute ([TimerTrigger("00:00:02", RunOnStartup = true)]) Collection <ParameterDescriptor> parameters = new Collection <ParameterDescriptor>(); ParameterDescriptor parameter = new ParameterDescriptor("timerInfo", typeof(TimerInfo)); ConstructorInfo ctorInfo = typeof(TimerTriggerAttribute).GetConstructor(new Type[] { typeof(string) }); PropertyInfo runOnStartupProperty = typeof(TimerTriggerAttribute).GetProperty("RunOnStartup"); CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder( ctorInfo, new object[] { "00:00:02" }, new PropertyInfo[] { runOnStartupProperty }, new object[] { true }); parameter.CustomAttributes.Add(attributeBuilder); parameters.Add(parameter); // create the FunctionDefinition FunctionMetadata metadata = new FunctionMetadata(); TestInvoker invoker = new TestInvoker(); FunctionDescriptor function = new FunctionDescriptor("TimerFunction", invoker, metadata, parameters, null, null, null); Collection <FunctionDescriptor> functions = new Collection <FunctionDescriptor>(); functions.Add(function); // Get the Type Attributes (in this case, a TimeoutAttribute) ScriptJobHostOptions scriptConfig = new ScriptJobHostOptions(); scriptConfig.FunctionTimeout = TimeSpan.FromMinutes(5); Collection <CustomAttributeBuilder> typeAttributes = new Collection <CustomAttributeBuilder>(); // generate the Type Type functionType = FunctionGenerator.Generate("TestScriptHost", "TestFunctions", typeAttributes, functions); // verify the generated function MethodInfo method = functionType.GetMethod("TimerFunction"); ParameterInfo triggerParameter = method.GetParameters()[0]; TimerTriggerAttribute triggerAttribute = triggerParameter.GetCustomAttribute <TimerTriggerAttribute>(); Assert.NotNull(triggerAttribute); // start the JobHost which will start running the timer function var builder = new HostBuilder() .ConfigureWebJobs(b => { b.AddTimers() .AddAzureStorageCoreServices(); }) .ConfigureServices(s => { s.AddSingleton <ITypeLocator>(new TestTypeLocator(functionType)); s.AddSingleton <ILoggerFactory>(new LoggerFactory()); TestHelpers.AddTestAzureBlobStorageProvider(s, TestHelpers.GetTestConfiguration()); TestHostBuilderExtensions.AddMockedSingleton <IScriptHostManager>(s); }); using (var host = builder.Build()) { await host.StartAsync(); await Task.Delay(3000); await host.StopAsync(); } // verify our custom invoker was called Assert.True(invoker.InvokeCount >= 2); }
public void Generate_WithMultipleOutParameters() { string functionName = "FunctionWithOuts"; Collection<ParameterDescriptor> parameters = new Collection<ParameterDescriptor>(); parameters.Add(new ParameterDescriptor("param1", typeof(string))); parameters.Add(new ParameterDescriptor("param2", typeof(string).MakeByRefType()) { Attributes = ParameterAttributes.Out }); parameters.Add(new ParameterDescriptor("param3", typeof(string).MakeByRefType()) { Attributes = ParameterAttributes.Out }); FunctionMetadata metadata = new FunctionMetadata(); TestInvoker invoker = new TestInvoker(); FunctionDescriptor function = new FunctionDescriptor(functionName, invoker, metadata, parameters); Collection<FunctionDescriptor> functions = new Collection<FunctionDescriptor>(); functions.Add(function); // generate the Type Type functionType = FunctionGenerator.Generate("TestScriptHost", "TestFunctions", functions); // verify the generated function MethodInfo method = functionType.GetMethod(functionName); ParameterInfo[] functionParams = method.GetParameters(); // Verify that we have the correct number of parameters Assert.Equal(parameters.Count, functionParams.Length); // Verify that out parameters were correctly generated Assert.True(functionParams[1].IsOut); Assert.True(functionParams[2].IsOut); // Verify that the method is invocable method.Invoke(null, new object[] { "test", null, null }); // verify our custom invoker was called Assert.Equal(1, invoker.InvokeCount); }
public void TypedParametersToStringTest() { //1. Foo(x: double, y : double) -> Foo.double-double //2. Foo(point : Point) -> Foo.Point //3. Foo(a : bool [ ] [ ] , b : var[], c : double[][]) -> Foo.bool2-var1-double2 //4. Foo(arr : var[]..[], a : int) -> Foo.varN-int //5. Foo(a: Autodesk.DesignScript.Geometry.Circle, b: Xxxx.Yyy.Curve) //6. Empty string(a: int) // 1 case var parameters1 = new List <TypedParameter>(); parameters1.Add(new TypedParameter("x", new ProtoCore.Type { Name = "double" })); parameters1.Add(new TypedParameter("y", new ProtoCore.Type { Name = "double" })); var functionItem1 = new FunctionDescriptor(new FunctionDescriptorParams { FunctionName = "Foo", Parameters = parameters1, FunctionType = FunctionType.GenericFunction }); System.Console.WriteLine(functionItem1.Parameters.Count()); Assert.AreEqual("Foo.double-double", Utils.TypedParametersToString(functionItem1)); //2 case var parameters2 = new List <TypedParameter>(); parameters2.Add(new TypedParameter("point", new ProtoCore.Type { Name = "Point" })); var functionItem2 = new FunctionDescriptor(new FunctionDescriptorParams { FunctionName = "Foo", Parameters = parameters2, FunctionType = FunctionType.GenericFunction }); Assert.AreEqual("Foo.Point", Utils.TypedParametersToString(functionItem2)); //3 case var parameters3 = new List <TypedParameter>(); parameters3.Add(new TypedParameter("a", new ProtoCore.Type { Name = "bool [ ] [ ] " })); parameters3.Add(new TypedParameter("b", new ProtoCore.Type { Name = "var[]" })); parameters3.Add(new TypedParameter("c", new ProtoCore.Type { Name = "double[][]" })); var functionItem3 = new FunctionDescriptor(new FunctionDescriptorParams { FunctionName = "Foo", Parameters = parameters3, FunctionType = FunctionType.GenericFunction }); Assert.AreEqual("Foo.bool2-var1-double2", Utils.TypedParametersToString(functionItem3)); //4 case var parameters4 = new List <TypedParameter>(); parameters4.Add(new TypedParameter("arr", new ProtoCore.Type { Name = "var[]..[]" })); parameters4.Add(new TypedParameter("a", new ProtoCore.Type { Name = "int" })); var functionItem4 = new FunctionDescriptor(new FunctionDescriptorParams { FunctionName = "Foo", Parameters = parameters4, FunctionType = FunctionType.GenericFunction }); Assert.AreEqual("Foo.varN-int", Utils.TypedParametersToString(functionItem4)); //5 case var parameters5 = new List <TypedParameter>(); parameters5.Add(new TypedParameter("a", new ProtoCore.Type { Name = "Autodesk.DesignScript.Geometry.Circle" })); parameters5.Add(new TypedParameter("b", new ProtoCore.Type { Name = "Xxxx.Yyy.Curve" })); var functionItem5 = new FunctionDescriptor(new FunctionDescriptorParams { FunctionName = "Foo", Parameters = parameters5, FunctionType = FunctionType.GenericFunction }); Assert.AreEqual("Foo.Circle-Curve", Utils.TypedParametersToString(functionItem5)); //6 case var parameters6 = new List <TypedParameter>(); parameters6.Add(new TypedParameter("a", new ProtoCore.Type { Name = "int" })); var functionItem6 = new FunctionDescriptor(new FunctionDescriptorParams { FunctionName = "", Parameters = parameters6, FunctionType = FunctionType.GenericFunction }); Assert.AreEqual(".int", Utils.TypedParametersToString(functionItem6)); }
public DSFunction(WorkspaceModel workspaceModel, FunctionDescriptor descriptor) : base(workspaceModel, new ZeroTouchNodeController(workspaceModel.DynamoModel.EngineController, descriptor)) { }
public FunctionDefinition(FunctionDescriptor descriptor, IFunctionInstanceFactory instanceFactory, IListenerFactory listenerFactory) { _descriptor = descriptor; _instanceFactory = instanceFactory; _listenerFactory = listenerFactory; }
public TriggeredFunctionExecutor(FunctionDescriptor descriptor, IFunctionExecutor executor, ITriggeredFunctionInstanceFactory <TTriggerValue> instanceFactory) { _descriptor = descriptor; _executor = executor; _instanceFactory = instanceFactory; }
public FunctionCall(FunctionDescriptor descriptor, params IJsonMasherOperator[] arguments) { Descriptor = descriptor; Arguments = arguments.ToList(); }
public async Task Generate_EndToEnd() { // construct our TimerTrigger attribute ([TimerTrigger("00:00:02", RunOnStartup = true)]) Collection <ParameterDescriptor> parameters = new Collection <ParameterDescriptor>(); ParameterDescriptor parameter = new ParameterDescriptor("timerInfo", typeof(TimerInfo)); ConstructorInfo ctorInfo = typeof(TimerTriggerAttribute).GetConstructor(new Type[] { typeof(string) }); PropertyInfo runOnStartupProperty = typeof(TimerTriggerAttribute).GetProperty("RunOnStartup"); CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder( ctorInfo, new object[] { "00:00:02" }, new PropertyInfo[] { runOnStartupProperty }, new object[] { true }); parameter.CustomAttributes.Add(attributeBuilder); parameters.Add(parameter); // create the FunctionDefinition FunctionMetadata metadata = new FunctionMetadata(); TestInvoker invoker = new TestInvoker(); FunctionDescriptor function = new FunctionDescriptor("TimerFunction", invoker, metadata, parameters); Collection <FunctionDescriptor> functions = new Collection <FunctionDescriptor>(); functions.Add(function); // Get the Type Attributes (in this case, a TimeoutAttribute) ScriptHostConfiguration scriptConfig = new ScriptHostConfiguration(); scriptConfig.FunctionTimeout = TimeSpan.FromMinutes(5); Collection <CustomAttributeBuilder> typeAttributes = ScriptHost.CreateTypeAttributes(scriptConfig); // generate the Type Type functionType = FunctionGenerator.Generate("TestScriptHost", "TestFunctions", typeAttributes, functions); // verify the generated function MethodInfo method = functionType.GetMethod("TimerFunction"); TimeoutAttribute timeoutAttribute = (TimeoutAttribute)functionType.GetCustomAttributes().Single(); Assert.Equal(TimeSpan.FromMinutes(5), timeoutAttribute.Timeout); Assert.True(timeoutAttribute.ThrowOnTimeout); Assert.True(timeoutAttribute.TimeoutWhileDebugging); ParameterInfo triggerParameter = method.GetParameters()[0]; TimerTriggerAttribute triggerAttribute = triggerParameter.GetCustomAttribute <TimerTriggerAttribute>(); Assert.NotNull(triggerAttribute); // start the JobHost which will start running the timer function JobHostConfiguration config = new JobHostConfiguration() { TypeLocator = new TypeLocator(functionType) }; config.UseTimers(); JobHost host = new JobHost(config); await host.StartAsync(); await Task.Delay(3000); await host.StopAsync(); // verify our custom invoker was called Assert.True(invoker.InvokeCount >= 2); }
public FunctionCall(FunctionDescriptor descriptor, List <IJsonMasherOperator> arguments) { Descriptor = descriptor; Arguments = arguments; }
internal async Task IndexMethodAsyncCore(MethodInfo method, IFunctionIndexCollector index, CancellationToken cancellationToken) { Debug.Assert(method != null); bool hasNoAutomaticTriggerAttribute = method.GetCustomAttribute <NoAutomaticTriggerAttribute>() != null; ITriggerBinding triggerBinding = null; ParameterInfo triggerParameter = null; IEnumerable <ParameterInfo> parameters = method.GetParameters(); foreach (ParameterInfo parameter in parameters) { ITriggerBinding possibleTriggerBinding = await _triggerBindingProvider.TryCreateAsync(new TriggerBindingProviderContext(parameter, cancellationToken)); if (possibleTriggerBinding != null) { if (triggerBinding == null) { triggerBinding = possibleTriggerBinding; triggerParameter = parameter; } else { throw new InvalidOperationException("More than one trigger per function is not allowed."); } } } Dictionary <string, IBinding> nonTriggerBindings = new Dictionary <string, IBinding>(); IReadOnlyDictionary <string, Type> bindingDataContract; if (triggerBinding != null) { bindingDataContract = triggerBinding.BindingDataContract; // See if a regular binding can handle it. IBinding binding = await _bindingProvider.TryCreateAsync(new BindingProviderContext(triggerParameter, bindingDataContract, cancellationToken)); if (binding != null) { triggerBinding = new TriggerWrapper(triggerBinding, binding); } } else { bindingDataContract = null; } bool hasParameterBindingAttribute = false; Exception invalidInvokeBindingException = null; ReturnParameterInfo returnParameter = null; bool triggerHasReturnBinding = false; if (TypeUtility.TryGetReturnType(method, out Type methodReturnType)) { if (bindingDataContract != null && bindingDataContract.TryGetValue(ReturnParamName, out Type triggerReturnType)) { // The trigger will handle the return value. triggerHasReturnBinding = true; } // We treat binding to the return type the same as binding to an 'out T' parameter. // An explicit return binding takes precedence over an implicit trigger binding. returnParameter = new ReturnParameterInfo(method, methodReturnType); parameters = parameters.Concat(new ParameterInfo[] { returnParameter }); } foreach (ParameterInfo parameter in parameters) { if (parameter == triggerParameter) { continue; } IBinding binding = await _bindingProvider.TryCreateAsync(new BindingProviderContext(parameter, bindingDataContract, cancellationToken)); if (binding == null) { if (parameter == returnParameter) { if (triggerHasReturnBinding) { // Ok. Skip and let trigger own the return binding. continue; } else { // If the method has a return value, then we must have a binding to it. // This is similar to the error we used to throw. invalidInvokeBindingException = new InvalidOperationException("Functions must return Task or void, have a binding attribute for the return value, or be triggered by a binding that natively supports return values."); } } if (triggerBinding != null && !hasNoAutomaticTriggerAttribute) { throw new InvalidOperationException( string.Format(Constants.UnableToBindParameterFormat, parameter.Name, parameter.ParameterType.Name, Constants.ExtensionInitializationMessage)); } else { // Host.Call-only parameter binding = InvokeBinding.Create(parameter.Name, parameter.ParameterType); if (binding == null && invalidInvokeBindingException == null) { // This function might not have any attribute, in which case we shouldn't throw an // exception when we can't bind it. Instead, save this exception for later once we determine // whether or not it is an SDK function. invalidInvokeBindingException = new InvalidOperationException( string.Format(Constants.UnableToBindParameterFormat, parameter.Name, parameter.ParameterType.Name, Constants.ExtensionInitializationMessage)); } } } else if (!hasParameterBindingAttribute) { hasParameterBindingAttribute = binding.FromAttribute; } nonTriggerBindings.Add(parameter.Name, binding); } // Only index functions with some kind of attribute on them. Three ways that could happen: // 1. There's an attribute on a trigger parameter (all triggers come from attributes). // 2. There's an attribute on a non-trigger parameter (some non-trigger bindings come from attributes). if (triggerBinding == null && !hasParameterBindingAttribute) { // 3. There's an attribute on the method itself (NoAutomaticTrigger). if (method.GetCustomAttribute <NoAutomaticTriggerAttribute>() == null) { return; } } if (TypeUtility.IsAsyncVoid(method)) { string msg = $"Function '{method.Name}' is async but does not return a Task. Your function may not run correctly."; _logger?.LogWarning(msg); } if (invalidInvokeBindingException != null) { throw invalidInvokeBindingException; } // Validation: prevent multiple ConsoleOutputs if (nonTriggerBindings.OfType <TraceWriterBinding>().Count() > 1) { throw new InvalidOperationException("Can't have multiple TraceWriter/TextWriter parameters in a single function."); } string triggerParameterName = triggerParameter != null ? triggerParameter.Name : null; FunctionDescriptor functionDescriptor = CreateFunctionDescriptor(method, triggerParameterName, triggerBinding, nonTriggerBindings); IFunctionInvoker invoker = FunctionInvokerFactory.Create(method, _activator); IFunctionDefinition functionDefinition; if (triggerBinding != null) { Type triggerValueType = triggerBinding.TriggerValueType; var methodInfo = typeof(FunctionIndexer).GetMethod("CreateTriggeredFunctionDefinition", BindingFlags.Instance | BindingFlags.NonPublic).MakeGenericMethod(triggerValueType); functionDefinition = (FunctionDefinition)methodInfo.Invoke(this, new object[] { triggerBinding, triggerParameterName, functionDescriptor, nonTriggerBindings, invoker }); if (hasNoAutomaticTriggerAttribute && functionDefinition != null) { functionDefinition = new FunctionDefinition(functionDescriptor, functionDefinition.InstanceFactory, listenerFactory: null); } } else { IFunctionInstanceFactory instanceFactory = new FunctionInstanceFactory(new FunctionBinding(functionDescriptor, nonTriggerBindings, _singletonManager), invoker, functionDescriptor); functionDefinition = new FunctionDefinition(functionDescriptor, instanceFactory, listenerFactory: null); } index.Add(functionDefinition, functionDescriptor, method); }
/// <summary> /// Creates a new instance /// </summary> /// <param name="descriptor">The <see cref="FunctionDescriptor"/> to create a listener for.</param> /// <param name="executor">The <see cref="ITriggeredFunctionExecutor"/> that should be used to invoke the /// target job function when the trigger fires.</param> /// <param name="sharedQueue">The shared queue.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> to use.</param> internal ListenerFactoryContext(FunctionDescriptor descriptor, ITriggeredFunctionExecutor executor, SharedQueueHandler sharedQueue, CancellationToken cancellationToken) : this(descriptor, executor, cancellationToken) { _sharedQueue = sharedQueue; }
public void TryGetFunctionFromException_FunctionMatch() { string stack = "TypeError: Cannot read property 'is' of undefined\n" + "at Timeout._onTimeout(D:\\home\\site\\wwwroot\\HttpTriggerNode\\index.js:7:35)\n" + "at tryOnTimeout (timers.js:224:11)\n" + "at Timer.listOnTimeout(timers.js:198:5)"; Collection<FunctionDescriptor> functions = new Collection<FunctionDescriptor>(); var exception = new InvalidOperationException(stack); // no match - empty functions FunctionDescriptor functionResult = null; bool result = ScriptHost.TryGetFunctionFromException(functions, exception, out functionResult); Assert.False(result); Assert.Null(functionResult); // no match - one non-matching function FunctionMetadata metadata = new FunctionMetadata { Name = "SomeFunction", Source = "D:\\home\\site\\wwwroot\\SomeFunction\\index.js" }; FunctionDescriptor function = new FunctionDescriptor("TimerFunction", new TestInvoker(), metadata, new Collection<ParameterDescriptor>()); functions.Add(function); result = ScriptHost.TryGetFunctionFromException(functions, exception, out functionResult); Assert.False(result); Assert.Null(functionResult); // match - exact metadata = new FunctionMetadata { Name = "HttpTriggerNode", Source = "D:\\home\\site\\wwwroot\\HttpTriggerNode\\index.js" }; function = new FunctionDescriptor("TimerFunction", new TestInvoker(), metadata, new Collection<ParameterDescriptor>()); functions.Add(function); result = ScriptHost.TryGetFunctionFromException(functions, exception, out functionResult); Assert.True(result); Assert.Same(function, functionResult); // match - different file from the same function stack = "TypeError: Cannot read property 'is' of undefined\n" + "at Timeout._onTimeout(D:\\home\\site\\wwwroot\\HttpTriggerNode\\npm\\lib\\foo.js:7:35)\n" + "at tryOnTimeout (timers.js:224:11)\n" + "at Timer.listOnTimeout(timers.js:198:5)"; exception = new InvalidOperationException(stack); result = ScriptHost.TryGetFunctionFromException(functions, exception, out functionResult); Assert.True(result); Assert.Same(function, functionResult); }
public void DescriptorIsFromConstructor() { var descriptor = new FunctionDescriptor(); var eventArgs = new ReceivingResponseEventArgs(new ResponseMessageSimulator(null), descriptor); eventArgs.Descriptor.Should().BeSameAs(descriptor); }
private static FunctionDefinition CreateTriggeredFunctionDefinitionImpl <TTriggerValue>( ITriggerBinding triggerBinding, string parameterName, IFunctionExecutor executor, FunctionDescriptor descriptor, IReadOnlyDictionary <string, IBinding> nonTriggerBindings, IFunctionInvoker invoker, SingletonManager singletonManager) { ITriggeredFunctionBinding <TTriggerValue> functionBinding = new TriggeredFunctionBinding <TTriggerValue>(descriptor, parameterName, triggerBinding, nonTriggerBindings, singletonManager); ITriggeredFunctionInstanceFactory <TTriggerValue> instanceFactory = new TriggeredFunctionInstanceFactory <TTriggerValue>(functionBinding, invoker, descriptor); ITriggeredFunctionExecutor triggerExecutor = new TriggeredFunctionExecutor <TTriggerValue>(descriptor, executor, instanceFactory); IListenerFactory listenerFactory = new ListenerFactory(descriptor, triggerExecutor, triggerBinding); return(new FunctionDefinition(descriptor, instanceFactory, listenerFactory)); }
public DSFunction(FunctionDescriptor definition) { ArgumentLacing = LacingStrategy.Shortest; Definition = definition; Initialize(); }
internal async Task IndexMethodAsyncCore(MethodInfo method, IFunctionIndexCollector index, CancellationToken cancellationToken) { Debug.Assert(method != null); bool hasNoAutomaticTriggerAttribute = method.GetCustomAttribute <NoAutomaticTriggerAttribute>() != null; ITriggerBinding triggerBinding = null; ParameterInfo triggerParameter = null; ParameterInfo[] parameters = method.GetParameters(); foreach (ParameterInfo parameter in parameters) { ITriggerBinding possibleTriggerBinding = await _triggerBindingProvider.TryCreateAsync(new TriggerBindingProviderContext(parameter, cancellationToken)); if (possibleTriggerBinding != null) { if (triggerBinding == null) { triggerBinding = possibleTriggerBinding; triggerParameter = parameter; } else { throw new InvalidOperationException("More than one trigger per function is not allowed."); } } } Dictionary <string, IBinding> nonTriggerBindings = new Dictionary <string, IBinding>(); IReadOnlyDictionary <string, Type> bindingDataContract; if (triggerBinding != null) { bindingDataContract = triggerBinding.BindingDataContract; } else { bindingDataContract = null; } bool hasParameterBindingAttribute = false; Exception invalidInvokeBindingException = null; foreach (ParameterInfo parameter in parameters) { if (parameter == triggerParameter) { continue; } IBinding binding = await _bindingProvider.TryCreateAsync(new BindingProviderContext(parameter, bindingDataContract, cancellationToken)); if (binding == null) { if (triggerBinding != null && !hasNoAutomaticTriggerAttribute) { throw new InvalidOperationException( string.Format(Constants.UnableToBindParameterFormat, parameter.Name, parameter.ParameterType.Name, Constants.ExtensionInitializationMessage)); } else { // Host.Call-only parameter binding = InvokeBinding.Create(parameter.Name, parameter.ParameterType); if (binding == null && invalidInvokeBindingException == null) { // This function might not have any attribute, in which case we shouldn't throw an // exception when we can't bind it. Instead, save this exception for later once we determine // whether or not it is an SDK function. invalidInvokeBindingException = new InvalidOperationException( string.Format(Constants.UnableToBindParameterFormat, parameter.Name, parameter.ParameterType.Name, Constants.ExtensionInitializationMessage)); } } } else if (!hasParameterBindingAttribute) { hasParameterBindingAttribute = binding.FromAttribute; } nonTriggerBindings.Add(parameter.Name, binding); } // Only index functions with some kind of attribute on them. Three ways that could happen: // 1. There's an attribute on a trigger parameter (all triggers come from attributes). // 2. There's an attribute on a non-trigger parameter (some non-trigger bindings come from attributes). if (triggerBinding == null && !hasParameterBindingAttribute) { // 3. There's an attribute on the method itself (NoAutomaticTrigger). if (method.GetCustomAttribute <NoAutomaticTriggerAttribute>() == null) { return; } } Type returnType = method.ReturnType; if (returnType != typeof(void) && returnType != typeof(Task)) { throw new InvalidOperationException("Functions must return Task or void."); } if (invalidInvokeBindingException != null) { throw invalidInvokeBindingException; } // Validation: prevent multiple ConsoleOutputs if (nonTriggerBindings.OfType <TraceWriterBinding>().Count() > 1) { throw new InvalidOperationException("Can't have multiple TraceWriter/TextWriter parameters in a single function."); } string triggerParameterName = triggerParameter != null ? triggerParameter.Name : null; FunctionDescriptor functionDescriptor = CreateFunctionDescriptor(method, triggerParameterName, triggerBinding, nonTriggerBindings); IFunctionInvoker invoker = FunctionInvokerFactory.Create(method, _activator); IFunctionDefinition functionDefinition; if (triggerBinding != null) { functionDefinition = CreateTriggeredFunctionDefinition(triggerBinding, triggerParameterName, _executor, functionDescriptor, nonTriggerBindings, invoker, _singletonManager); if (hasNoAutomaticTriggerAttribute && functionDefinition != null) { functionDefinition = new FunctionDefinition(functionDescriptor, functionDefinition.InstanceFactory, listenerFactory: null); } } else { IFunctionInstanceFactory instanceFactory = new FunctionInstanceFactory(new FunctionBinding(functionDescriptor, nonTriggerBindings, _singletonManager), invoker, functionDescriptor); functionDefinition = new FunctionDefinition(functionDescriptor, instanceFactory, listenerFactory: null); } index.Add(functionDefinition, functionDescriptor, method); }
private static FunctionSnapshot CreateFunctionSnapshot(string queueName, HeartbeatDescriptor heartbeat, FunctionDescriptor function, DateTimeOffset hostVersion) { return new FunctionSnapshot { HostVersion = hostVersion, Id = new FunctionIdentifier(queueName, function.Id).ToString(), QueueName = queueName, HeartbeatSharedContainerName = heartbeat != null ? heartbeat.SharedContainerName : null, HeartbeatSharedDirectoryName = heartbeat != null ? heartbeat.SharedDirectoryName : null, HeartbeatExpirationInSeconds = heartbeat != null ? (int?)heartbeat.ExpirationInSeconds : null, HostFunctionId = function.Id, FullName = function.FullName, ShortName = function.ShortName, Parameters = CreateParameterSnapshots(function.Parameters) }; }
public void Generate_WithValueTypes_Succeeds() { string functionName = "FunctionWithValueTypes"; Collection<ParameterDescriptor> parameters = new Collection<ParameterDescriptor>(); parameters.Add(new ParameterDescriptor("param1", typeof(string))); parameters.Add(new ParameterDescriptor("param2", typeof(DateTimeOffset))); parameters.Add(new ParameterDescriptor("param3", typeof(int))); FunctionMetadata metadata = new FunctionMetadata(); object[] invocationArguments = null; TestInvoker invoker = new TestInvoker(args => { invocationArguments = args; }); FunctionDescriptor function = new FunctionDescriptor(functionName, invoker, metadata, parameters); Collection<FunctionDescriptor> functions = new Collection<FunctionDescriptor>(); functions.Add(function); // generate the Type Type functionType = FunctionGenerator.Generate("TestScriptHost", "TestFunctions", functions); // verify the generated function MethodInfo method = functionType.GetMethod(functionName); ParameterInfo[] functionParams = method.GetParameters(); // Verify that we have the correct number of parameters Assert.Equal(parameters.Count, functionParams.Length); // Verify that the method is invocable DateTimeOffset input = DateTimeOffset.Now; method.Invoke(null, new object[] { "test", input, 44 }); // verify our custom invoker was called Assert.Equal(1, invoker.InvokeCount); Assert.NotNull(invocationArguments); Assert.Equal(input, (DateTimeOffset)invocationArguments[1]); Assert.Equal(44, (int)invocationArguments[2]); }
public override bool TryCreate(JObject function, out FunctionDescriptor functionDescriptor) { functionDescriptor = null; string sourceFileName = (string)function["source"]; string name = (string)function["name"]; if (string.IsNullOrEmpty(name)) { // if a method name isn't explicitly provided, derive it // from the script file name name = Path.GetFileNameWithoutExtension(sourceFileName); name = name.Substring(0, 1).ToUpper() + name.Substring(1); } string scriptFilePath = Path.Combine(_applicationRoot, "scripts", sourceFileName); ScriptInvoker invoker = new ScriptInvoker(scriptFilePath); JObject trigger = (JObject)function["trigger"]; string triggerType = (string)trigger["type"]; string parameterName = (string)trigger["name"]; if (string.IsNullOrEmpty(parameterName)) { // default the name to simply 'input' trigger["name"] = "input"; } ParameterDescriptor triggerParameter = null; switch (triggerType) { case "queue": triggerParameter = ParseQueueTrigger(trigger); break; case "blob": triggerParameter = ParseBlobTrigger(trigger); break; case "serviceBus": triggerParameter = ParseServiceBusTrigger(trigger); break; case "timer": triggerParameter = ParseTimerTrigger(trigger, typeof(TimerInfo)); break; case "webHook": triggerParameter = ParseWebHookTrigger(trigger); break; } Collection<ParameterDescriptor> parameters = new Collection<ParameterDescriptor>(); parameters.Add(triggerParameter); // Add a TraceWriter for logging ParameterDescriptor textWriter = new ParameterDescriptor { Name = "log", Type = typeof(TextWriter) }; parameters.Add(textWriter); functionDescriptor = new FunctionDescriptor { Name = name, Invoker = invoker, Parameters = parameters }; return true; }
public ZeroTouchVarArgNodeController(EngineController engineController, FunctionDescriptor zeroTouchDef) : base(engineController, zeroTouchDef) { }
public DSVarArgFunction(FunctionDescriptor definition) { Definition = definition; Initialize(); }
public IListenerFactory CreateListenerFactory(FunctionDescriptor descriptor, ITriggeredFunctionExecutor executor) { if (_queueName != null) { return new ServiceBusQueueListenerFactory(_account, _queueName, executor, _accessRights); } else { return new ServiceBusSubscriptionListenerFactory(_account, _topicName, _subscriptionName, executor, _accessRights); } }
/// <summary> /// Initializes a new instance of the <see cref="DSFunction"/> class. /// </summary> /// <param name="description">EFunction descritor.</param> public DSFunction(FunctionDescriptor functionDescription) : base(new ZeroTouchNodeController <FunctionDescriptor>(functionDescription)) { }
public DSVarArgFunction(FunctionDescriptor descriptor) : base(new ZeroTouchVarArgNodeController <FunctionDescriptor>(descriptor)) { VarInputController = new ZeroTouchVarInputController(this); }
public QueueListener(QueueClient queue, QueueClient poisonQueue, ITriggerExecutor <QueueMessage> triggerExecutor, IWebJobsExceptionHandler exceptionHandler, ILoggerFactory loggerFactory, SharedQueueWatcher sharedWatcher, QueuesOptions queueOptions, IQueueProcessorFactory queueProcessorFactory, FunctionDescriptor functionDescriptor, string functionId = null, TimeSpan?maxPollingInterval = null) { if (queueOptions == null) { throw new ArgumentNullException(nameof(queueOptions)); } if (queueProcessorFactory == null) { throw new ArgumentNullException(nameof(queueProcessorFactory)); } if (loggerFactory == null) { throw new ArgumentNullException(nameof(loggerFactory)); } if (queueOptions.BatchSize <= 0) { throw new ArgumentException("BatchSize must be greater than zero."); } if (queueOptions.MaxDequeueCount <= 0) { throw new ArgumentException("MaxDequeueCount must be greater than zero."); } _timer = new TaskSeriesTimer(this, exceptionHandler, Task.Delay(0)); _queue = queue; _poisonQueue = poisonQueue; _triggerExecutor = triggerExecutor; _exceptionHandler = exceptionHandler; _queueOptions = queueOptions; _logger = loggerFactory.CreateLogger <QueueListener>(); _functionDescriptor = functionDescriptor ?? throw new ArgumentNullException(nameof(functionDescriptor)); _functionId = functionId ?? _functionDescriptor.Id; // if the function runs longer than this, the invisibility will be updated // on a timer periodically for the duration of the function execution _visibilityTimeout = TimeSpan.FromMinutes(10); if (sharedWatcher != null) { // Call Notify whenever a function adds a message to this queue. sharedWatcher.Register(queue.Name, this); _sharedWatcher = sharedWatcher; } EventHandler <PoisonMessageEventArgs> poisonMessageEventHandler = _sharedWatcher != null ? OnMessageAddedToPoisonQueue : (EventHandler <PoisonMessageEventArgs>)null; _queueProcessor = CreateQueueProcessor(_queue, _poisonQueue, loggerFactory, queueProcessorFactory, _queueOptions, poisonMessageEventHandler); TimeSpan maximumInterval = _queueProcessor.MaxPollingInterval; if (maxPollingInterval.HasValue && maximumInterval > maxPollingInterval.Value) { // enforce the maximum polling interval if specified maximumInterval = maxPollingInterval.Value; } _delayStrategy = new RandomizedExponentialBackoffStrategy(SharedQueuePollingIntervals.Minimum, maximumInterval); _scaleMonitorDescriptor = new ScaleMonitorDescriptor($"{_functionId}-QueueTrigger-{_queue.Name}".ToLower(CultureInfo.InvariantCulture)); _shutdownCancellationTokenSource = new CancellationTokenSource(); }
internal static bool TryGetFunctionFromException(Collection<FunctionDescriptor> functions, Exception exception, out FunctionDescriptor function) { function = null; string errorStack = exception.ToString().ToLowerInvariant(); foreach (var currFunction in functions) { // For each function, we search the entire error stack trace to see if it contains // the function entry/primary script path. If it does, we're virtually certain that // that function caused the error (e.g. as in the case of global unhandled exceptions // coming from Node.js scripts). // We use the directory name for the script rather than the full script path itself to ensure // that we handle cases where the error might be coming from some other script (e.g. an NPM // module) that is part of the function. string absoluteScriptPath = Path.GetFullPath(currFunction.Metadata.Source).ToLowerInvariant(); string functionDirectory = Path.GetDirectoryName(absoluteScriptPath); if (errorStack.Contains(functionDirectory)) { function = currFunction; return true; } } return false; }
public IListenerFactory CreateListenerFactory(FunctionDescriptor descriptor, ITriggeredFunctionExecutor executor) { return new QueueListenerFactory(_queue, _queueConfiguration, _backgroundExceptionDispatcher, _messageEnqueuedWatcherSetter, _sharedContextProvider, _log, executor); }
public void Add(IFunctionDefinition function, FunctionDescriptor descriptor, MethodInfo method) { Functions.Add(descriptor); }
private void AddFunction(string name, FunctionDescriptor.FunctionGroup functionGroup, string[] alternates, Functor functor, string description) { _functionList.Add( new FunctionDescriptor( name, functionGroup, alternates, functor, description ) ); }
internal static bool TryGetFunctionFromException(ICollection <FunctionDescriptor> functions, Exception exception, out FunctionDescriptor function) { function = null; if (functions == null) { return(false); } string errorStack = exception.ToString().ToLowerInvariant(); foreach (var currFunction in functions) { // For each function, we search the entire error stack trace to see if it contains // the function entry/primary script path. If it does, we're virtually certain that // that function caused the error (e.g. as in the case of global unhandled exceptions // coming from Node.js scripts). // We use the directory name for the script rather than the full script path itself to ensure // that we handle cases where the error might be coming from some other script (e.g. an NPM // module) that is part of the function. string absoluteScriptPath = Path.GetFullPath(currFunction.Metadata.ScriptFile).ToLowerInvariant(); string functionDirectory = Path.GetDirectoryName(absoluteScriptPath); if (errorStack.Contains(functionDirectory)) { function = currFunction; return(true); } } return(false); }
/// <summary> /// Creates a new instance /// </summary> /// <param name="descriptor">The <see cref="FunctionDescriptor"/> to create a listener for.</param> /// <param name="executor">The <see cref="ITriggeredFunctionExecutor"/> that should be used to invoke the /// target job function when the trigger fires.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> to use.</param> public ListenerFactoryContext(FunctionDescriptor descriptor, ITriggeredFunctionExecutor executor, CancellationToken cancellationToken) { Descriptor = descriptor; Executor = executor; CancellationToken = cancellationToken; }
public ListenerFactory(FunctionDescriptor descriptor, ITriggeredFunctionExecutor executor, ITriggerBinding binding) { _descriptor = descriptor; _executor = executor; _binding = binding; }