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