private async Task <IHttpActionResult> ProcessWebHook(string webHookReceiver, string id)
        {
            IWebHookReceiverManager receiverManager = Configuration.DependencyResolver.GetReceiverManager();
            IWebHookReceiver        receiver        = receiverManager.GetReceiver(webHookReceiver);

            if (receiver == null)
            {
                string msg = string.Format(CultureInfo.CurrentCulture, @"No WebHook receiver is registered with the name '{0}'.", webHookReceiver);
                Configuration.DependencyResolver.GetLogger().Error(msg);
                return(NotFound());
            }

            try
            {
                string msg = string.Format(CultureInfo.CurrentCulture, @"Processing incoming WebHook request with receiver '{0}' and id '{1}'.", webHookReceiver, id);
                Configuration.DependencyResolver.GetLogger().Info(msg);
                HttpResponseMessage response = await receiver.ReceiveAsync(id, RequestContext, Request);

                return(ResponseMessage(response));
            }
            catch (HttpResponseException rex)
            {
                return(ResponseMessage(rex.Response));
            }
            catch (Exception ex)
            {
                Exception inner = ex.GetBaseException();
                string    msg   = string.Format(CultureInfo.CurrentCulture, @"WebHook receiver '{0}' could not process WebHook due to error: {1}", webHookReceiver, inner.Message);
                Configuration.DependencyResolver.GetLogger().Error(msg, inner);
                throw;
            }
        }
        public async Task RegisterRoute(Uri route, ParameterInfo triggerParameter, ITriggeredFunctionExecutor executor)
        {
            await EnsureServerOpen();

            string routeKey = route.LocalPath.ToLowerInvariant();

            if (_functions.ContainsKey(routeKey))
            {
                throw new InvalidOperationException(string.Format("Duplicate route detected. There is already a route registered for '{0}'", routeKey));
            }

            _functions.AddOrUpdate(routeKey, executor, (k, v) => { return(executor); });

            WebHookTriggerAttribute attribute = triggerParameter.GetCustomAttribute <WebHookTriggerAttribute>();
            IWebHookReceiver        receiver  = null;
            string receiverId  = string.Empty;
            string receiverLog = string.Empty;

            if (attribute != null && _webHookReceiverManager.TryParseReceiver(route.LocalPath, out receiver, out receiverId))
            {
                receiverLog = string.Format(" (Receiver: '{0}', Id: '{1}')", receiver.Name, receiverId);
            }

            MethodInfo method     = (MethodInfo)triggerParameter.Member;
            string     methodName = string.Format("{0}.{1}", method.DeclaringType, method.Name);

            _trace.Verbose(string.Format("WebHook route '{0}' registered for function '{1}'{2}", route.LocalPath, methodName, receiverLog));
        }
예제 #3
0
        private async Task <IHttpActionResult> ProcessWebHook(string webHookReceiver, string id)
        {
            IWebHookReceiverManager receiverManager = Configuration.DependencyResolver.GetReceiverManager();
            IWebHookReceiver        receiver        = receiverManager.GetReceiver(webHookReceiver);

            if (receiver == null)
            {
                string msg = string.Format(CultureInfo.CurrentCulture, ReceiverResources.ReceiverController_Unknown, webHookReceiver);
                Configuration.DependencyResolver.GetLogger().Error(msg);
                return(NotFound());
            }

            try
            {
                string msg = string.Format(CultureInfo.CurrentCulture, ReceiverResources.ReceiverController_Processing, webHookReceiver);
                Configuration.DependencyResolver.GetLogger().Info(msg);
                HttpResponseMessage response = await receiver.ReceiveAsync(id, RequestContext, Request);

                return(ResponseMessage(response));
            }
            catch (HttpResponseException rex)
            {
                return(ResponseMessage(rex.Response));
            }
            catch (Exception ex)
            {
                ex = ex.GetBaseException();
                string msg = string.Format(CultureInfo.CurrentCulture, ReceiverResources.ReceiverController_Failure, webHookReceiver, ex.Message);
                Configuration.DependencyResolver.GetLogger().Error(msg, ex);
                HttpResponseMessage response = Request.CreateErrorResponse(HttpStatusCode.InternalServerError, msg, ex);
                return(ResponseMessage(response));
            }
        }
예제 #4
0
        public bool TryParseReceiver(string route, out IWebHookReceiver receiver, out string receiverId)
        {
            receiver   = null;
            receiverId = null;

            string[] routeSegements = route.ToLowerInvariant().TrimStart('/').Split('/');
            if (routeSegements.Length == 1 || routeSegements.Length == 2)
            {
                string receiverName = routeSegements[0];
                if (!_receiverLookup.TryGetValue(receiverName, out receiver))
                {
                    return(false);
                }

                // parse the optional WebHook ID from the route if specified
                if (routeSegements.Length == 2)
                {
                    receiverId = routeSegements[1];
                }

                return(true);
            }

            return(false);
        }
예제 #5
0
        public async Task <HttpResponseMessage> TryHandle(HttpRequestMessage request, Func <HttpRequestMessage, Task <HttpResponseMessage> > invokeJobFunction)
        {
            // First check if there is a registered WebHook Receiver for this request, and if
            // so use it
            string           route      = request.RequestUri.LocalPath.ToLowerInvariant();
            IWebHookReceiver receiver   = null;
            string           receiverId = null;

            if (TryParseReceiver(route, out receiver, out receiverId))
            {
                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(WebHookJobFunctionInvokerKey, invokeJobFunction);

                return(await receiver.ReceiveAsync(receiverId, context, request));
            }

            return(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
            HttpBindingMetadata httpFunctionMetadata = (HttpBindingMetadata)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))
            {
                // If the function is a not a correctly configured WebHook return 500
                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);

            // TODO: Is there a better way? Requests content can't be read multiple
            // times, so this forces it to buffer
            await request.Content.ReadAsStringAsync();

            string receiverId = function.Name.ToLowerInvariant();

            return(await receiver.ReceiveAsync(receiverId, context, request));
        }
        public bool TryParseReceiver(string route, out IWebHookReceiver receiver, out string receiverId)
        {
            receiver = null;
            receiverId = null;

            string[] routeSegements = route.ToLowerInvariant().TrimStart('/').Split('/');
            if (routeSegements.Length == 1 || routeSegements.Length == 2)
            {
                string receiverName = routeSegements[0];
                if (!_receiverLookup.TryGetValue(receiverName, out receiver))
                {
                    return false;
                }

                // parse the optional WebHook ID from the route if specified
                if (routeSegements.Length == 2)
                {
                    receiverId = routeSegements[1];
                }

                return true;
            }

            return false;
        }
        public void GetReceiver_Returns_Null_IfNoReceivers()
        {
            // Arrange
            IWebHookReceiverManager manager = new WebHookReceiverManager(_receivers, _loggerMock.Object);

            // Act
            IWebHookReceiver actual = manager.GetReceiver(MockReceiverName);

            // Assert
            Assert.Null(actual);
        }
        public void GetReceiver_Returns_ReceiverWithSingleName(string receiverName)
        {
            // Arrange
            _receivers.Add(_receiver);
            IWebHookReceiverManager manager = new WebHookReceiverManager(_receivers, _loggerMock.Object);

            // Act
            IWebHookReceiver actual = manager.GetReceiver(receiverName);

            // Assert
            Assert.Same(_receiver, actual);
        }
예제 #10
0
        /// <summary>
        /// Invokes the Middleware to handle a HttpRequest
        /// </summary>
        /// <param name="httpContext">The HttpContext for the Request</param>
        /// <returns></returns>
        public async Task Invoke(HttpContext httpContext)
        {
            // Get all the Receivers
            IEnumerable <IWebHookReceiver> receivers = (IEnumerable <IWebHookReceiver>)httpContext.RequestServices.GetService(typeof(IEnumerable <IWebHookReceiver>));

            // Find the Matching Receiver and capture the remaining path segment
            PathString       remaining        = new PathString();
            IWebHookReceiver matchingReceiver = receivers.Where(r => httpContext.Request.Path.StartsWithSegments($"/{r.Name}", out remaining)).FirstOrDefault();

            if (matchingReceiver != null)
            {
                // If we found a mathing receiver, use it to build a WebHookHandlerContext
                _logger.LogInformation("WebHooks matched the '{0}' path to the '{1}' receiver by the name '{2}'.",
                                       httpContext.Request.Path,
                                       matchingReceiver.GetType().FullName,
                                       matchingReceiver.Name);

                WebHookHandlerContext context = await matchingReceiver.ReceiveAsync(remaining, httpContext);

                if (context != null)
                {
                    // If the Receiver returned a Context, then find the matching handlers
                    IEnumerable <IWebHookHandler> handlers = (IEnumerable <IWebHookHandler>)httpContext.RequestServices.GetService(typeof(IEnumerable <IWebHookHandler>));

                    if (handlers != null && handlers.Count() > 0)
                    {
                        // Sort any available handlers
                        IEnumerable <IWebHookHandler> orderedHandlers = handlers.OrderBy(h => h.Order);

                        // Execute each handler in order
                        foreach (IWebHookHandler handler in orderedHandlers)
                        {
                            if (String.IsNullOrWhiteSpace(handler.Receiver) || handler.Receiver.Equals(matchingReceiver.Name, StringComparison.CurrentCultureIgnoreCase))
                            {
                                _logger.LogInformation("Executing the '{0}' Handler with the Context from the '{1}' Receiver.", handler.GetType().FullName, matchingReceiver.GetType().FullName);
                                await handler.ExecuteAsync(remaining, context);
                            }
                        }
                    }
                    else
                    {
                        _logger.LogWarning("No Handlers were found to process the context from '{0}' Receiver.", matchingReceiver.Name);
                    }
                }
            }
            else
            {
                _logger.LogDebug("No Matching Receiver was found.");
                await _next(httpContext);
            }
        }
        public void GetReceiver_Returns_Null_IfUnknownReceiver()
        {
            // Arrange
            MockReceiver multiReceiver = new MockReceiver();

            _receivers.Add(multiReceiver);
            IWebHookReceiverManager manager = new WebHookReceiverManager(_receivers, _loggerMock.Object);

            // Act
            IWebHookReceiver actual = manager.GetReceiver("unknown");

            // Assert
            Assert.Null(actual);
        }
예제 #12
0
        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
            var              httpTrigger     = function.GetTriggerAttributeOrNull <HttpTriggerAttribute>();
            string           webHookReceiver = httpTrigger.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));
            }

            // if the code value is specified via header rather than query string
            // promote it to the query string (that's what the WebHook library expects)
            ApplyHeaderValuesToQuery(request);

            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();

            // Get an optional client ID. This information is passed as the receiver ID, allowing
            // the receiver config to map configuration based on the client ID (primarily used for secret resolution).
            string clientId = GetClientID(request);

            string webhookId = $"{receiverId},{clientId}";

            return(await receiver.ReceiveAsync(webhookId, context, request));
        }
        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 => string.Compare("HttpTrigger", p.Type, StringComparison.OrdinalIgnoreCase) == 0);
            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();

            // Get an optional client ID. This information is passed as the receiver ID, allowing
            // the receiver config to map configuration based on the client ID (primarily used for secret resolution).
            string clientId = GetClientID(request);

            string webhookId = $"{receiverId},{clientId}";

            return(await receiver.ReceiveAsync(webhookId, context, request));
        }
예제 #14
0
 public WebHookReceiverManagerTests()
 {
     _receiver = new MockReceiver();
     _receivers = new List<IWebHookReceiver>();
     _loggerMock = new Mock<ILogger>();
 }
 public WebHookReceiverManagerTests()
 {
     _receiver   = new MockReceiver();
     _receivers  = new List <IWebHookReceiver>();
     _loggerMock = new Mock <ILogger>();
 }