public async Task <FunctionResult> TryExecuteAsync(TriggeredFunctionData input, CancellationToken cancellationToken) { var context = new FunctionInstanceFactoryContext <TTriggerValue>() { TriggerValue = (TTriggerValue)input.TriggerValue, ParentId = input.ParentId, TriggerDetails = input.TriggerDetails }; if (input.InvokeHandler != null) { context.InvokeHandler = async next => { await input.InvokeHandler(next); // NOTE: The InvokeHandler code path currently does not support flowing the return // value back to the trigger. return(null); }; } IFunctionInstance instance = _instanceFactory.Create(context); IDelayedException exception = await _executor.TryExecuteAsync(instance, cancellationToken); FunctionResult result = exception != null ? new FunctionResult(exception.Exception) : new FunctionResult(true); return(result); }
private static async Task <Tuple <object[], IDelayedException> > PrepareParametersAsync(IReadOnlyList <string> parameterNames, IReadOnlyDictionary <string, IValueProvider> parameters) { object[] reflectionParameters = new object[parameterNames.Count]; List <Exception> bindingExceptions = new List <Exception>(); for (int index = 0; index < parameterNames.Count; index++) { string name = parameterNames[index]; IValueProvider provider = parameters[name]; BindingExceptionValueProvider exceptionProvider = provider as BindingExceptionValueProvider; if (exceptionProvider != null) { bindingExceptions.Add(exceptionProvider.Exception); } reflectionParameters[index] = await parameters[name].GetValueAsync(); } IDelayedException delayedBindingException = null; if (bindingExceptions.Count == 1) { delayedBindingException = new DelayedException(bindingExceptions[0]); } else if (bindingExceptions.Count > 1) { delayedBindingException = new DelayedException(new AggregateException(bindingExceptions)); } return(new Tuple <object[], IDelayedException>(reflectionParameters, delayedBindingException)); }
public async Task <bool> ExecuteAsync(BrokeredMessage value, CancellationToken cancellationToken) { Guid?parentId = ServiceBusCausalityHelper.GetOwner(value); IFunctionInstance instance = _instanceFactory.Create(value, parentId); IDelayedException exception = await _innerExecutor.TryExecuteAsync(instance, cancellationToken); return(exception == null); }
private static IFunctionExecutor CreateStubInnerExecutor(IDelayedException result) { Mock <IFunctionExecutor> mock = new Mock <IFunctionExecutor>(MockBehavior.Strict); mock.Setup(e => e.TryExecuteAsync(It.IsAny <IFunctionInstance>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(result)); return(mock.Object); }
public async Task <bool> ExecuteAsync(IStorageQueueMessage value, CancellationToken cancellationToken) { Guid?parentId = QueueCausalityManager.GetOwner(value); IFunctionInstance instance = _instanceFactory.Create(value, parentId); IDelayedException exception = await _innerExecutor.TryExecuteAsync(instance, cancellationToken); return(exception == null); }
public async Task <FunctionResult> TryExecuteAsync(TriggeredFunctionData input, CancellationToken cancellationToken) { IFunctionInstance instance = _instanceFactory.Create((TTriggerValue)input.TriggerValue, input.ParentId); IDelayedException exception = await _executor.TryExecuteAsync(instance, cancellationToken); FunctionResult result = exception != null ? new FunctionResult(exception.Exception) : new FunctionResult(true); return(result); }
private async Task CallAsyncCore(MethodInfo method, IDictionary <string, object> arguments, CancellationToken cancellationToken) { await EnsureHostStartedAsync(cancellationToken); IFunctionDefinition function = ResolveFunctionDefinition(method, _context.FunctionLookup); IFunctionInstance instance = CreateFunctionInstance(function, arguments); IDelayedException exception = await _context.Executor.TryExecuteAsync(instance, cancellationToken); if (exception != null) { exception.Throw(); } }
private async Task CallAsyncCore(IFunctionDefinition function, object functionKey, IDictionary <string, object> arguments, CancellationToken cancellationToken) { Validate(function, functionKey); IFunctionInstance instance = CreateFunctionInstance(function, arguments); IDelayedException exception = null; exception = await _context.Executor.TryExecuteAsync(instance, cancellationToken); if (exception != null) { exception.Throw(); } }
/// <summary>Calls a job method.</summary> /// <param name="name">The name of the function to call.</param> /// <param name="arguments">The argument names and values to bind to parameters in the job method. In addition to parameter values, these may also include binding data values. </param> /// <param name="cancellationToken">The token to monitor for cancellation requests.</param> /// <returns>A <see cref="Task"/> that will call the job method.</returns> public async Task CallAsync(string name, IDictionary <string, object> arguments = null, CancellationToken cancellationToken = default(CancellationToken)) { if (name == null) { throw new ArgumentNullException(nameof(name)); } ThrowIfDisposed(); await EnsureHostInitializedAsync(cancellationToken); IFunctionDefinition function = _context.FunctionLookup.LookupByName(name); Validate(function, name); IFunctionInstance instance = CreateFunctionInstance(function, arguments); IDelayedException exception = await _context.Executor.TryExecuteAsync(instance, cancellationToken); if (exception != null) { exception.Throw(); } }
private static object[] PrepareParameters(IReadOnlyList <string> parameterNames, IReadOnlyDictionary <string, IValueProvider> parameters, out IDelayedException delayedBindingException) { object[] reflectionParameters = new object[parameterNames.Count]; List <Exception> bindingExceptions = new List <Exception>(); for (int index = 0; index < parameterNames.Count; index++) { string name = parameterNames[index]; IValueProvider provider = parameters[name]; BindingExceptionValueProvider exceptionProvider = provider as BindingExceptionValueProvider; if (exceptionProvider != null) { bindingExceptions.Add(exceptionProvider.Exception); } reflectionParameters[index] = parameters[name].GetValue(); } if (bindingExceptions.Count == 0) { delayedBindingException = null; } else if (bindingExceptions.Count == 1) { delayedBindingException = new DelayedException(bindingExceptions[0]); } else { delayedBindingException = new DelayedException(new AggregateException(bindingExceptions)); } return(reflectionParameters); }
private static IFunctionExecutor CreateStubInnerExecutor(IDelayedException result) { Mock<IFunctionExecutor> mock = new Mock<IFunctionExecutor>(MockBehavior.Strict); mock.Setup(e => e.TryExecuteAsync(It.IsAny<IFunctionInstance>(), It.IsAny<CancellationToken>())) .Returns(Task.FromResult(result)); return mock.Object; }
internal static async Task ExecuteWithWatchersAsync(IFunctionInstance instance, IReadOnlyDictionary <string, IValueProvider> parameters, TraceWriter traceWriter, CancellationTokenSource functionCancellationTokenSource) { IFunctionInvoker invoker = instance.Invoker; IReadOnlyList <string> parameterNames = invoker.ParameterNames; Tuple <object[], IDelayedException> preparedParameters = await PrepareParametersAsync(parameterNames, parameters); object[] invokeParameters = preparedParameters.Item1; IDelayedException delayedBindingException = preparedParameters.Item2; if (delayedBindingException != null) { // This is done inside a watcher context so that each binding error is publish next to the binding in // the parameter status log. delayedBindingException.Throw(); } // if the function is a Singleton, aquire the lock SingletonLock singleton = await GetSingletonLockAsync(parameters); if (singleton != null) { await singleton.AcquireAsync(functionCancellationTokenSource.Token); } // Create a source specifically for timeouts using (CancellationTokenSource timeoutTokenSource = new CancellationTokenSource()) { MethodInfo method = instance.FunctionDescriptor.Method; TimeoutAttribute timeoutAttribute = TypeUtility.GetHierarchicalAttributeOrNull <TimeoutAttribute>(method); bool throwOnTimeout = timeoutAttribute == null ? false : timeoutAttribute.ThrowOnTimeout; var timer = StartFunctionTimeout(instance, timeoutAttribute, timeoutTokenSource, traceWriter); TimeSpan timerInterval = timer == null ? TimeSpan.MinValue : TimeSpan.FromMilliseconds(timer.Interval); try { await InvokeAsync(invoker, invokeParameters, timeoutTokenSource, functionCancellationTokenSource, throwOnTimeout, timerInterval, instance); } finally { if (timer != null) { timer.Stop(); timer.Dispose(); } } } // Process any out parameters and persist any pending values. // Ensure IValueBinder.SetValue is called in BindStepOrder. This ordering is particularly important for // ensuring queue outputs occur last. That way, all other function side-effects are guaranteed to have // occurred by the time messages are enqueued. string[] parameterNamesInBindOrder = SortParameterNamesInStepOrder(parameters); foreach (string name in parameterNamesInBindOrder) { IValueProvider provider = parameters[name]; IValueBinder binder = provider as IValueBinder; if (binder != null) { object argument = invokeParameters[GetParameterIndex(parameterNames, name)]; try { // This could do complex things that may fail. Catch the exception. await binder.SetValueAsync(argument, functionCancellationTokenSource.Token); } catch (OperationCanceledException) { throw; } catch (Exception exception) { string message = String.Format(CultureInfo.InvariantCulture, "Error while handling parameter {0} after function returned:", name); throw new InvalidOperationException(message, exception); } } } if (singleton != null) { await singleton.ReleaseAsync(functionCancellationTokenSource.Token); } }
public async Task <bool> ExecuteAsync(IStorageQueueMessage value, CancellationToken cancellationToken) { BlobTriggerMessage message = JsonConvert.DeserializeObject <BlobTriggerMessage>(value.AsString, JsonSerialization.Settings); if (message == null) { throw new InvalidOperationException("Invalid blob trigger message."); } string functionId = message.FunctionId; if (functionId == null) { throw new InvalidOperationException("Invalid function ID."); } // Ensure that the function ID is still valid. Otherwise, ignore this message. ITriggeredFunctionInstanceFactory <IStorageBlob> instanceFactory; if (!_registrations.TryGetValue(functionId, out instanceFactory)) { return(true); } IStorageBlobContainer container = _client.GetContainerReference(message.ContainerName); string blobName = message.BlobName; IStorageBlob blob; switch (message.BlobType) { case StorageBlobType.PageBlob: blob = container.GetPageBlobReference(blobName); break; case StorageBlobType.BlockBlob: default: blob = container.GetBlockBlobReference(blobName); break; } // Ensure the blob still exists with the same ETag. string possibleETag = await _eTagReader.GetETagAsync(blob, cancellationToken); if (possibleETag == null) { // If the blob no longer exists, just ignore this message. return(true); } // If the blob still exists but the ETag is different, delete the message but do a fast path notification. if (!String.Equals(message.ETag, possibleETag, StringComparison.Ordinal)) { _blobWrittenWatcher.Notify(blob); return(true); } //// If the blob still exists and its ETag is still valid, execute. //// Note: it's possible the blob could change/be deleted between now and when the function executes. Guid?parentId = await _causalityReader.GetWriterAsync(blob, cancellationToken); IFunctionInstance instance = instanceFactory.Create(blob, parentId); IDelayedException exception = await _innerExecutor.TryExecuteAsync(instance, cancellationToken); return(exception == null); }