public override async Task <WebhookSendResponse> SendWebHookAsync(WebhookWorkItem webHookWorkItem) { // Retry in the following intervals (in minutes): 1, 2, 4, …, 2^(RetryCount-1) var policy = Policy.Handle <WebHookSendException>().WaitAndRetryAsync(RetryCount, retryAttempt => TimeSpan.FromMinutes(Math.Pow(2, retryAttempt - 1))); return(await policy.ExecuteAsync(() => PerformSend(webHookWorkItem))); }
/// <summary> /// Creates an <see cref="HttpRequestMessage"/> containing the headers and body by given <paramref name="workItem"/>. /// </summary> /// <param name="workItem">A <see cref="WebhookWorkItem"/> representing the <see cref="Webhook"/> to be sent.</param> /// <returns>A filled in <see cref="HttpRequestMessage"/>.</returns> //[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Request is disposed by caller.")] protected virtual HttpRequestMessage CreateWebHookRequest(WebhookWorkItem workItem) { if (workItem == null) { throw new ArgumentNullException(nameof(workItem)); } var webHook = workItem.WebHook; // Create WebHook request var request = new HttpRequestMessage(HttpMethod.Post, webHook.Url); // Fills in request body and headers CreateWebHookRequestBody(workItem, request); foreach (var kvp in webHook.RequestParams.Headers) { if (!request.Headers.TryAddWithoutValidation(kvp.Key, kvp.Value) && !request.Content.Headers.TryAddWithoutValidation(kvp.Key, kvp.Value)) { var errorMessage = string.Format(CultureInfo.CurrentCulture, InvalidHeaderTemplate, kvp.Key, webHook.Id); throw new WebHookSendException(errorMessage, webHook.Id, workItem.EventId); } } return(request); }
protected virtual Task HandleResponseAsync(WebhookWorkItem webHookWorkItem, WebhookSendResponse webHookSendResponse) { if (webHookSendResponse.IsSuccessfull) { return(HandleSuccessAsync(webHookWorkItem, webHookSendResponse)); } else { return(HandleErrorAsync(webHookWorkItem, webHookSendResponse)); } }
private static void CreateWebHookRequestBody(WebhookWorkItem workItem, HttpRequestMessage request) { var body = new Dictionary <string, object> { // Set properties from work item [EventIdKey] = workItem.EventId, [AttemptKey] = (workItem.FeedEntry?.AttemptCount ?? 0) + 1, [EventBodyKey] = workItem.WebHook.RequestParams.Body, }; var bodyJObject = JObject.FromObject(body).ToString(); request.Content = new StringContent(bodyJObject, Encoding.UTF8, "application/json"); }
/// <summary> /// Creates <see cref="WebhookFeedEntry"/> from <see cref="webHookWorkItem"/>. If it already has <see cref="WebhookFeedEntry"/>, inreases AttemptCount. /// </summary> /// <param name="webHookWorkItem"></param> /// <param name="webHookSendResponse"></param> /// <returns></returns> protected virtual WebhookFeedEntry GetOrCreateFeedEntry(WebhookWorkItem webHookWorkItem, WebhookSendResponse webHookSendResponse) { var feedEntry = webHookWorkItem.FeedEntry; if (feedEntry == null) { feedEntry = WebHookFeedUtils.CreateErrorEntry(webHookWorkItem, webHookSendResponse); } else { feedEntry.AttemptCount++; } return(feedEntry); }
protected virtual async Task HandleSuccessAsync(WebhookWorkItem webHookWorkItem, WebhookSendResponse webHookSendResponse) { // Clear error records on this sending after success if (webHookWorkItem.FeedEntry?.RecordType == (int)WebhookFeedEntryType.Error && !webHookWorkItem.FeedEntry.IsTransient()) { await _webHookFeedService.DeleteByIdsAsync(new[] { webHookWorkItem.FeedEntry.Id }); } // Create new record (not using and updating existing one!) for proper logging, as all Success entries are accumulated in one record in our current logging policy. var feedEntry = WebHookFeedUtils.CreateSuccessEntry(webHookWorkItem, webHookSendResponse); await _logger.LogAsync(feedEntry); webHookWorkItem.FeedEntry = feedEntry; }
protected virtual async Task HandleErrorAsync(WebhookWorkItem webHookWorkItem, WebhookSendResponse webHookSendResponse) { var feedEntry = GetOrCreateFeedEntry(webHookWorkItem, webHookSendResponse); feedEntry.RecordType = (int)WebhookFeedEntryType.Error; feedEntry.Status = webHookSendResponse?.StatusCode ?? webHookWorkItem.FeedEntry.Status; await _logger.LogAsync(feedEntry); webHookWorkItem.FeedEntry = feedEntry; //delete old FeedEntries except latest by 'LatestErrorCount' var errorFeedEntryIds = await _webHookFeedService.GetAllErrorEntriesExceptLatestAsync(new[] { webHookWorkItem.WebHook.Id }, LatestErrorCount); await _webHookFeedService.DeleteByIdsAsync(errorFeedEntryIds); }
protected virtual async Task <WebhookSendResponse> NotifyWebHook(string eventId, string eventObject, Webhook webHook, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); WebhookSendResponse response = null; var webHookWorkItem = new WebhookWorkItem() { EventId = eventId, WebHook = webHook, }; webHook.RequestParams = new WebhookHttpParams() { Body = eventObject, }; response = await _webHookSender.SendWebHookAsync(webHookWorkItem); return(response); }
private async Task <WebhookSendResponse> PerformSend(WebhookWorkItem webHookWorkItem) { var result = new WebhookSendResponse(); try { var request = CreateWebHookRequest(webHookWorkItem); var httpClient = _httpClientFactory.CreateClient("webhooks"); var response = await httpClient.SendAsync(request); var responseContent = await response.Content.ReadAsStringAsync(); result = CreateSendResponse(response, responseContent); if (!response.IsSuccessStatusCode) { throw new WebHookSendException(responseContent, webHookWorkItem.WebHook.Id, webHookWorkItem.EventId); } return(result); } catch (WebHookSendException ex) { result.Error = GetErrorText(webHookWorkItem.FeedEntry?.AttemptCount + 1 ?? 0, ex.Message); throw; } catch (Exception ex) { result.Error = ex.Message; return(result); } finally { await HandleResponseAsync(webHookWorkItem, result); } }
/// <summary> /// Gets the current <see cref="ILogger"/> instance. /// </summary> /// <inheritdoc /> public abstract Task <WebhookSendResponse> SendWebHookAsync(WebhookWorkItem webHookWorkItem);
public static WebhookFeedEntry CreateErrorEntry(WebhookWorkItem webHookWorkItem, WebhookSendResponse response) { return(CreateFeedEntry(WebhookFeedEntryType.Error, webHookWorkItem.EventId, response, webHookWorkItem.WebHook)); }