Example #1
0
        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);
        }
 public async Task<FunctionResult> ExecuteAsync(BrokeredMessage value, CancellationToken cancellationToken)
 {
     Guid? parentId = ServiceBusCausalityHelper.GetOwner(value);
     TriggeredFunctionData input = new TriggeredFunctionData
     {
         ParentId = parentId,
         TriggerValue = value
     };
     return await _innerExecutor.TryExecuteAsync(input, cancellationToken);
 }
Example #3
0
        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);
        }
        internal async Task ProcessMessageAsync(string message, CancellationToken cancellationToken)
        {
            if (!await _redisProcessor.BeginMessageArrivedAsync(message, cancellationToken))
            {
                return;
            }

            TriggeredFunctionData input = new TriggeredFunctionData
            {
                TriggerValue = message
            };

            FunctionResult result = await _triggerExecutor.TryExecuteAsync(input, cancellationToken);

            await _redisProcessor.EndMessageArrivedAsync(message, result, cancellationToken);
        }
 private void CreateTestListener(string expression, bool useMonitor = true)
 {
     _attribute = new TimerTriggerAttribute(expression);
     _attribute.UseMonitor = useMonitor;
     _config = new TimersConfiguration();
     _mockScheduleMonitor = new Mock<ScheduleMonitor>(MockBehavior.Strict);
     _config.ScheduleMonitor = _mockScheduleMonitor.Object;
     _mockTriggerExecutor = new Mock<ITriggeredFunctionExecutor>(MockBehavior.Strict);
     FunctionResult result = new FunctionResult(true);
     _mockTriggerExecutor.Setup(p => p.TryExecuteAsync(It.IsAny<TriggeredFunctionData>(), It.IsAny<CancellationToken>()))
         .Callback<TriggeredFunctionData, CancellationToken>((mockFunctionData, mockToken) =>
         {
             _triggeredFunctionData = mockFunctionData;
         })
         .Returns(Task.FromResult(result));
     _listener = new TimerListener(_attribute, _testTimerName, _config, _mockTriggerExecutor.Object, new TestTraceWriter());
 }
        internal static TraceMonitor CreateTraceMonitor(ParameterInfo parameter, ITriggeredFunctionExecutor executor)
        {
            ErrorTriggerAttribute attribute = parameter.GetCustomAttribute<ErrorTriggerAttribute>(inherit: false);

            // Determine whether this is a method level filter, and if so, create the filter
            Func<TraceEvent, bool> methodFilter = null;
            MethodInfo method = (MethodInfo)parameter.Member;
            string functionLevelMessage = null;
            if (method.Name.EndsWith(ErrorHandlerSuffix, StringComparison.OrdinalIgnoreCase))
            {
                string sourceMethodName = method.Name.Substring(0, method.Name.Length - ErrorHandlerSuffix.Length);
                MethodInfo sourceMethod = method.DeclaringType.GetMethod(sourceMethodName);
                if (sourceMethod != null)
                {
                    string sourceMethodFullName = string.Format("{0}.{1}", method.DeclaringType.FullName, sourceMethod.Name);
                    methodFilter = p =>
                    {
                        FunctionInvocationException functionException = p.Exception as FunctionInvocationException;
                        return p.Level == System.Diagnostics.TraceLevel.Error && functionException != null &&
                               string.Compare(functionException.MethodName, sourceMethodFullName, StringComparison.OrdinalIgnoreCase) == 0;
                    };

                    string sourceMethodShortName = string.Format("{0}.{1}", method.DeclaringType.Name, sourceMethod.Name);
                    functionLevelMessage = string.Format("Function '{0}' failed.", sourceMethodShortName);
                }
            }

            string errorHandlerFullName = string.Format("{0}.{1}", method.DeclaringType.FullName, method.Name);
            ErrorHandlers.Add(errorHandlerFullName);

            // Create the TraceFilter instance
            TraceFilter traceFilter = null;
            if (attribute.FilterType != null)
            {
                if (methodFilter != null)
                {
                    TraceFilter innerTraceFilter = (TraceFilter)Activator.CreateInstance(attribute.FilterType);
                    traceFilter = new CompositeTraceFilter(innerTraceFilter, methodFilter, attribute.Message ?? functionLevelMessage);
                }
                else
                {
                    traceFilter = (TraceFilter)Activator.CreateInstance(attribute.FilterType);
                }
            }
            else if (!string.IsNullOrEmpty(attribute.Window))
            {
                TimeSpan window = TimeSpan.Parse(attribute.Window);
                traceFilter = new SlidingWindowTraceFilter(window, attribute.Threshold, methodFilter, attribute.Message);
            }
            else
            {
                traceFilter = TraceFilter.Create(methodFilter, attribute.Message ?? functionLevelMessage);
            }
            TraceMonitor traceMonitor = new TraceMonitor().Filter(traceFilter);

            // Apply any additional monitor options
            if (!string.IsNullOrEmpty(attribute.Throttle))
            {
                TimeSpan throttle = TimeSpan.Parse(attribute.Throttle);
                traceMonitor.Throttle(throttle);
            }

            // Subscribe the error handler function to the error stream
            traceMonitor.Subscribe(p =>
            {
                TriggeredFunctionData triggerData = new TriggeredFunctionData
                {
                    TriggerValue = p
                };
                Task<FunctionResult> task = executor.TryExecuteAsync(triggerData, CancellationToken.None);
                task.Wait();
            });

            return traceMonitor;
        }
 private void OnTimer(object sender, System.Timers.ElapsedEventArgs e)
 {
     // TODO: When you receive new events from your event source,
     // invoke the function executor
     TriggeredFunctionData input = new TriggeredFunctionData
     {
         TriggerValue = new SampleTriggerValue()
     };
     _executor.TryExecuteAsync(input, CancellationToken.None).Wait();
 }
        /// <summary>
        /// Process the file indicated by the specified <see cref="FileSystemEventArgs"/>.
        /// </summary>
        /// <param name="eventArgs">The <see cref="FileSystemEventArgs"/> indicating the file to process.</param>
        /// <param name="cancellationToken">The <see cref="CancellationToken"/> to use.</param>
        /// <returns>
        /// A <see cref="Task"/> that returns true if the file was processed successfully, false otherwise.
        /// </returns>
        public virtual async Task<bool> ProcessFileAsync(FileSystemEventArgs eventArgs, CancellationToken cancellationToken)
        {
            try
            {
                string filePath = eventArgs.FullPath;
                using (StreamWriter statusWriter = AquireStatusFileLock(filePath, eventArgs.ChangeType))
                {
                    if (statusWriter == null)
                    {
                        return false;
                    }

                    // write an entry indicating the file is being processed
                    StatusFileEntry status = new StatusFileEntry
                    {
                        State = ProcessingState.Processing,
                        Timestamp = DateTime.UtcNow,
                        LastWrite = File.GetLastWriteTimeUtc(filePath),
                        ChangeType = eventArgs.ChangeType,
                        InstanceId = InstanceId
                    };
                    _serializer.Serialize(statusWriter, status);
                    statusWriter.WriteLine();

                    // invoke the job function
                    TriggeredFunctionData input = new TriggeredFunctionData
                    {
                        // TODO: set this properly
                        ParentId = null,
                        TriggerValue = eventArgs
                    };
                    FunctionResult result = await _executor.TryExecuteAsync(input, cancellationToken);

                    if (result.Succeeded)
                    {
                        // write a status entry indicating processing is complete
                        status.State = ProcessingState.Processed;
                        status.Timestamp = DateTime.UtcNow;
                        _serializer.Serialize(statusWriter, status);
                        statusWriter.WriteLine();
                        return true;
                    }
                    else
                    {         
                        // If the function failed, we leave the in progress status
                        // file as is (it will show "Processing"). The file will be
                        // reprocessed later on a clean-up pass.
                        statusWriter.Close();
                        cancellationToken.ThrowIfCancellationRequested();
                        return false;
                    }
                }             
            }
            catch
            {
                return false;
            }
        }
        /// <summary>
        /// Process the file indicated by the specified <see cref="FileSystemEventArgs"/>.
        /// </summary>
        /// <param name="eventArgs">The <see cref="FileSystemEventArgs"/> indicating the file to process.</param>
        /// <param name="cancellationToken">The <see cref="CancellationToken"/> to use.</param>
        /// <returns>
        /// A <see cref="Task"/> that returns true if the file was processed successfully, false otherwise.
        /// </returns>
        public virtual async Task<bool> ProcessFileAsync(FileSystemEventArgs eventArgs, CancellationToken cancellationToken)
        {
            try
            {
                StatusFileEntry status = null;
                string filePath = eventArgs.FullPath;
                using (StreamWriter statusWriter = AcquireStatusFileLock(filePath, eventArgs.ChangeType, out status))
                {
                    if (statusWriter == null)
                    {
                        return false;
                    }

                    // We've acquired the lock. The current status might be either Failed
                    // or Processing (if processing failed before we were unable to update
                    // the file status to Failed)
                    int processCount = 0;
                    if (status != null)
                    {
                        processCount = status.ProcessCount;
                    }

                    while (processCount++ < MaxProcessCount)
                    {
                        FunctionResult result = null;
                        if (result != null)
                        {
                            TimeSpan delay = GetRetryInterval(result, processCount);
                            await Task.Delay(delay);
                        }

                        // write an entry indicating the file is being processed
                        status = new StatusFileEntry
                        {
                            State = ProcessingState.Processing,
                            Timestamp = DateTime.Now,
                            LastWrite = File.GetLastWriteTimeUtc(filePath),
                            ChangeType = eventArgs.ChangeType,
                            InstanceId = InstanceId,
                            ProcessCount = processCount
                        };
                        _serializer.Serialize(statusWriter, status);
                        statusWriter.WriteLine();

                        // invoke the job function
                        TriggeredFunctionData input = new TriggeredFunctionData
                        {
                            TriggerValue = eventArgs
                        };
                        result = await _executor.TryExecuteAsync(input, cancellationToken);

                        // write a status entry indicating the state of processing
                        status.State = result.Succeeded ? ProcessingState.Processed : ProcessingState.Failed;
                        status.Timestamp = DateTime.Now;
                        _serializer.Serialize(statusWriter, status);
                        statusWriter.WriteLine();

                        if (result.Succeeded)
                        {
                            return true;
                        }
                    }

                    return false;
                }             
            }
            catch
            {
                return false;
            }
        }
        internal async Task InvokeJobFunction(DateTime lastOccurrence, bool isPastDue)
        {
            CancellationToken token = _cancellationTokenSource.Token;

            TimerInfo timerInfo = new TimerInfo(_attribute.Schedule);
            timerInfo.IsPastDue = isPastDue;

            TriggeredFunctionData input = new TriggeredFunctionData
            {
                // TODO: how to set this properly?
                ParentId = null,
                TriggerValue = timerInfo
            };
            FunctionResult result = await _executor.TryExecuteAsync(input, token);
            if (!result.Succeeded)
            {
                token.ThrowIfCancellationRequested();
            }

            if (_attribute.UseMonitor)
            {
                DateTime nextOccurrence = _schedule.GetNextOccurrence(lastOccurrence);
                await _scheduleMonitor.UpdateAsync(_timerName, lastOccurrence, nextOccurrence);
            }
        }
        internal async Task InvokeJobFunction(DateTime lastOccurrence, bool isPastDue = false)
        {
            CancellationToken token = _cancellationTokenSource.Token;
            TimerInfo timerInfo = new TimerInfo(_attribute.Schedule, _scheduleStatus, isPastDue);
            TriggeredFunctionData input = new TriggeredFunctionData
            {
                TriggerValue = timerInfo
            };

            try
            {
                FunctionResult result = await _executor.TryExecuteAsync(input, token);
                if (!result.Succeeded)
                {
                    token.ThrowIfCancellationRequested();
                }
            }
            catch
            {
                // We don't want any function errors to stop the execution
                // schedule. Errors will be logged to Dashboard already.
            }

            if (ScheduleMonitor != null)
            {
                _scheduleStatus = new ScheduleStatus
                {
                    Last = lastOccurrence,
                    Next = _schedule.GetNextOccurrence(lastOccurrence)
                };
                await ScheduleMonitor.UpdateStatusAsync(_timerName, _scheduleStatus);
            }
        }
        private async Task<HttpResponseMessage> ProcessRequest(HttpRequestMessage request)
        {
            // First check if we have a WebHook function registered for
            // this route
            string routeKey = request.RequestUri.LocalPath.ToLowerInvariant();
            ITriggeredFunctionExecutor executor = null;
            if (!_functions.TryGetValue(routeKey, out executor))
            {
                // If no WebHook function is registered, we'll see if there is a job function
                // that matches based on name, and if so invoke it directly
                MethodInfo methodInfo = null;
                if (TryGetMethodInfo(routeKey, out methodInfo))
                {
                    // Read the method arguments from the request body
                    // and invoke the function
                    string body = await request.Content.ReadAsStringAsync();
                    IDictionary<string, JToken> parsed = JObject.Parse(body);
                    IDictionary<string, object> args = parsed.ToDictionary(p => p.Key, q => (object)q.Value.ToString());
                    await _host.CallAsync(methodInfo, args);

                    return new HttpResponseMessage(HttpStatusCode.OK);
                }

                return new HttpResponseMessage(HttpStatusCode.NotFound);
            }

            TriggeredFunctionData data = new TriggeredFunctionData
            {
                TriggerValue = request
            };
            FunctionResult result = await executor.TryExecuteAsync(data, CancellationToken.None);

            HttpStatusCode statusCode = result.Succeeded ? HttpStatusCode.OK : HttpStatusCode.InternalServerError;

            return new HttpResponseMessage(statusCode);
        }
        private async Task<HttpResponseMessage> ProcessRequest(HttpRequestMessage request)
        {
            // First check if we have a WebHook function registered for this route
            string routeKey = request.RequestUri.LocalPath.ToLowerInvariant();
            ITriggeredFunctionExecutor executor = null;
            HttpResponseMessage response = null;
            if (!_functions.TryGetValue(routeKey, out executor))
            {
                if (request.Method != HttpMethod.Post)
                {
                    return new HttpResponseMessage(HttpStatusCode.MethodNotAllowed);
                }

                // No WebHook function is registered for this route
                // See see if there is a job function that matches based
                // on name, and if so invoke it directly
                response = await TryInvokeNonWebHook(routeKey, request);
            }
            else
            {
                // Define a function to invoke the job function that we can reuse below
                Func<HttpRequestMessage, Task<HttpResponseMessage>> invokeJobFunction = async (req) =>
                {
                    TriggeredFunctionData data = new TriggeredFunctionData
                    {
                        TriggerValue = req
                    };
                    FunctionResult result = await executor.TryExecuteAsync(data, CancellationToken.None);

                    object value = null;
                    if (request.Properties.TryGetValue(WebHookTriggerBinding.WebHookContextRequestKey, out value))
                    {
                        // If this is a WebHookContext binding, see if a custom response has been set.
                        // If so, we'll return that.
                        WebHookContext context = (WebHookContext)value;
                        if (context.Response != null)
                        {
                            response = context.Response;
                            response.RequestMessage = request;
                        }
                    }

                    if (response != null)
                    {
                        return response;
                    }
                    else
                    {
                        return new HttpResponseMessage(result.Succeeded ? HttpStatusCode.OK : HttpStatusCode.InternalServerError);
                    }
                };

                // See if there is a WebHookReceiver registered for this request
                // Note: receivers will do their own HttpMethod validation (e.g. some
                // support HEAD/GET/etc.
                response = await _webHookReceiverManager.TryHandle(request, invokeJobFunction);

                if (response == null)
                {
                    if (request.Method != HttpMethod.Post)
                    {
                        return new HttpResponseMessage(HttpStatusCode.MethodNotAllowed);
                    }

                    // No WebHook receivers have been registered for this request, so dispatch
                    // it directly.
                    response = await invokeJobFunction(request);
                }
            }

            return response;
        }