/// <inheritdoc/> public async Task <IList <NotificationResponse> > ResendNotifications(string applicationName, string[] notificationIds, NotificationType notifType = NotificationType.Mail, bool ignoreAlreadySent = false) { this.logger.TraceInformation($"Started {nameof(this.ResendNotifications)} method of {nameof(EmailHandlerManager)}."); if (string.IsNullOrWhiteSpace(applicationName)) { throw new ArgumentException("Application Name cannot be null or empty.", nameof(applicationName)); } if (notificationIds is null) { throw new ArgumentNullException(nameof(notificationIds)); } IList <NotificationResponse> notificationResponses = new List <NotificationResponse>(); // Queue a single cloud message for all entities created to enable parallel processing. var cloudQueue = this.cloudStorageClient.GetCloudQueue(this.notificationQueue); IList <string> cloudMessages = BusinessUtilities.GetCloudMessagesForIds(applicationName, notificationIds, notifType, ignoreAlreadySent); await this.cloudStorageClient.QueueCloudMessages(cloudQueue, cloudMessages).ConfigureAwait(false); notificationIds.ToList().ForEach(id => { notificationResponses.Add(new NotificationResponse() { NotificationId = id, Status = NotificationItemStatus.Queued, }); }); this.logger.TraceInformation($"Finished {nameof(this.ResendNotifications)} method of {nameof(EmailHandlerManager)}."); return(notificationResponses); }
public ActionResult Index(DrinksViewModel model) { if (ModelState.IsValid) { if (model.SelectedDrink != null) { var selectedDrink = _context.GetCachedDrinks().FirstOrDefault(x => x.ID == model.SelectedDrink); var updatedStocks = BusinessUtilities.UpdateStocksByDrinkPurchased(_context.GetCachedStocks(), selectedDrink); foreach (var stock in updatedStocks) { _context.AddOrUpdateStock(stock); } model.IsSubmitSuccessful = true; _context.AddOrderHistory ( new OrderHistory() { DrinkName = selectedDrink.Name, OrderTime = DateTime.Now } ); } model.AvailableDrinks = BusinessUtilities.FilterDrinksByStocks(_context.GetCachedDrinks(), _context.GetCachedStocks()); } return(View(model)); }
public ActionResult Index() { var availableDrinks = BusinessUtilities.FilterDrinksByStocks(_context.GetCachedDrinks(), _context.GetCachedStocks()); var drinksViewModel = new DrinksViewModel() { AvailableDrinks = availableDrinks, IsSubmitSuccessful = false }; return(View(drinksViewModel)); }
/// <inheritdoc/> public async Task <IList <NotificationResponse> > QueueEmailNotifications(string applicationName, EmailNotificationItem[] emailNotificationItems) { var stopwatch = new Stopwatch(); stopwatch.Start(); var traceProps = new Dictionary <string, string>(); bool result = false; try { this.logger.TraceInformation($"Started {nameof(this.QueueEmailNotifications)} method of {nameof(EmailHandlerManager)}.", traceProps); if (string.IsNullOrWhiteSpace(applicationName)) { throw new ArgumentException("Application Name cannot be null or empty.", nameof(applicationName)); } if (emailNotificationItems is null) { throw new ArgumentNullException(nameof(emailNotificationItems)); } traceProps[AIConstants.Application] = applicationName; traceProps[AIConstants.EmailNotificationCount] = emailNotificationItems.Length.ToString(CultureInfo.InvariantCulture); this.logger.WriteCustomEvent("QueueEmailNotifications Started", traceProps); IList <NotificationResponse> notificationResponses = new List <NotificationResponse>(); IList <EmailNotificationItemEntity> notificationItemEntities = await this.emailManager.CreateNotificationEntities(applicationName, emailNotificationItems, NotificationItemStatus.Queued).ConfigureAwait(false); List <List <EmailNotificationItemEntity> > entitiesToQueue; if (string.Equals(this.configuration?[ConfigConstants.NotificationProviderType], NotificationProviderType.Graph.ToString(), StringComparison.InvariantCultureIgnoreCase)) { entitiesToQueue = BusinessUtilities.SplitList <EmailNotificationItemEntity>(notificationItemEntities.ToList(), this.mSGraphSetting.BatchRequestLimit).ToList(); } else { entitiesToQueue = new List <List <EmailNotificationItemEntity> > { notificationItemEntities.ToList() }; } // Queue a single cloud message for all entities created to enable parallel processing. var cloudQueue = this.cloudStorageClient.GetCloudQueue(this.notificationQueue); foreach (var item in entitiesToQueue) { this.logger.TraceVerbose($"Started {nameof(BusinessUtilities.GetCloudMessagesForEntities)} method of {nameof(EmailHandlerManager)}.", traceProps); IList <string> cloudMessages = BusinessUtilities.GetCloudMessagesForEntities(applicationName, item); this.logger.TraceVerbose($"Completed {nameof(BusinessUtilities.GetCloudMessagesForEntities)} method of {nameof(EmailHandlerManager)}.", traceProps); this.logger.TraceVerbose($"Started {nameof(this.cloudStorageClient.QueueCloudMessages)} method of {nameof(EmailHandlerManager)}.", traceProps); await this.cloudStorageClient.QueueCloudMessages(cloudQueue, cloudMessages).ConfigureAwait(false); this.logger.TraceVerbose($"Completed {nameof(this.cloudStorageClient.QueueCloudMessages)} method of {nameof(EmailHandlerManager)}.", traceProps); } var responses = this.emailManager.NotificationEntitiesToResponse(notificationResponses, notificationItemEntities); this.logger.TraceInformation($"Completed {nameof(this.QueueEmailNotifications)} method of {nameof(EmailHandlerManager)}.", traceProps); result = true; return(responses); } catch (Exception e) { this.logger.WriteException(e, traceProps); result = false; throw; } finally { stopwatch.Stop(); traceProps[AIConstants.Result] = result.ToString(CultureInfo.InvariantCulture); var metrics = new Dictionary <string, double>(); metrics[AIConstants.Duration] = stopwatch.ElapsedMilliseconds; this.logger.WriteCustomEvent("QueueEmailNotifications Completed", traceProps, metrics); } }
/// <summary> /// Processes the notification items as a single batch to Graph. /// </summary> /// <param name="applicationName">The application Name.</param> /// <param name="notificationEntities">List of notification entities to process.</param> /// <param name="selectedAccount">selectedAccount.</param> /// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns> private async Task ProcessEntitiesInBatch(string applicationName, IList <EmailNotificationItemEntity> notificationEntities, Tuple <AuthenticationHeaderValue, AccountCredential> selectedAccount) { var traceProps = new Dictionary <string, string>(); traceProps[AIConstants.Application] = applicationName; traceProps["EmailAccountUsed"] = selectedAccount.Item2.AccountName.Base64Encode(); traceProps[AIConstants.EmailNotificationCount] = notificationEntities.Count.ToString(CultureInfo.InvariantCulture); this.logger.TraceInformation($"Started {nameof(this.ProcessEntitiesInBatch)} method of {nameof(MSGraphNotificationProvider)}.", traceProps); if (notificationEntities is null || notificationEntities.Count == 0) { throw new ArgumentNullException(nameof(notificationEntities)); } var sendForReal = this.mailSettings.Find(a => a.ApplicationName == applicationName).SendForReal; var toOverride = this.mailSettings.Find(a => a.ApplicationName == applicationName).ToOverride; var saveToSent = this.mailSettings.Find(a => a.ApplicationName == applicationName).SaveToSent; // Step 1: Prepare graph requests from input entities that shall be sent in a batch to Graph. List <GraphBatchRequest> batchRequests = new List <GraphBatchRequest>(); List <GraphRequest> graphRequests = new List <GraphRequest>(); List <NotificationBatchItemResponse> batchItemResponses = new List <NotificationBatchItemResponse>(); var nieItems = notificationEntities.ToList(); foreach (var nie in nieItems) { EmailNotificationItemEntity item = nie; try { MessageBody body = await this.emailManager.GetNotificationMessageBodyAsync(applicationName, item).ConfigureAwait(false); EmailMessage message = nie.ToGraphEmailMessage(body); if (!sendForReal) { this.logger.TraceInformation($"Overriding the ToRecipients in {nameof(this.ProcessEntitiesInBatch)} method of {nameof(EmailManager)}.", traceProps); message.ToRecipients = toOverride.Split(Common.ApplicationConstants.SplitCharacter, System.StringSplitOptions.RemoveEmptyEntries) .Select(torecipient => new Recipient { EmailAddress = new EmailAddress { Address = torecipient } }).ToList(); message.CCRecipients = null; message.BCCRecipients = null; message.ReplyToRecipients = null; } graphRequests.Add(new GraphRequest() { Id = nie.NotificationId, Url = this.mSGraphSetting.SendMailUrl.StartsWith("/", StringComparison.OrdinalIgnoreCase) ? this.mSGraphSetting.SendMailUrl : $"/{this.mSGraphSetting.SendMailUrl}", Body = new EmailMessagePayload(message) { SaveToSentItems = saveToSent }, Headers = new GraphRequestHeaders() { ContentType = ApplicationConstants.JsonMIMEType }, Method = ApplicationConstants.POSTHttpVerb, }); } #pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) #pragma warning restore CA1031 // Do not catch general exception types { this.logger.TraceInformation($"Caught exception while creating the graph Message {nameof(this.ProcessEntitiesInBatch)} method of {nameof(MSGraphNotificationProvider)}.", traceProps); batchItemResponses.Add(new NotificationBatchItemResponse { Error = (ex.InnerException != null) ? ex.InnerException.Message : ex.Message, NotificationId = item.NotificationId, Status = HttpStatusCode.PreconditionFailed }); } } // Step 2: Split the full list of requests into smaller chunks as per the Graph Batch request limit. List <List <GraphRequest> > splitGraphRequests = BusinessUtilities.SplitList(graphRequests, this.mSGraphSetting.BatchRequestLimit).ToList(); foreach (var graphRequestChunk in splitGraphRequests) { batchRequests.Add(new GraphBatchRequest() { Requests = graphRequestChunk }); } // Step 3: Invoke the Graph API for each batch request chunk prepared above. foreach (var batchRequest in batchRequests) { batchItemResponses.AddRange(await this.msGraphProvider.ProcessEmailRequestBatch(selectedAccount.Item1, batchRequest).ConfigureAwait(false)); } bool isAccountIndexIncremented = false; // Step 4: Loop through the responses and set the status of the input entities. foreach (var item in notificationEntities) { item.EmailAccountUsed = selectedAccount.Item2.AccountName; item.TryCount++; item.ErrorMessage = string.Empty; // Reset the error message on next retry. var itemResponse = batchItemResponses.Find(resp => resp.NotificationId == item.NotificationId); if (itemResponse?.Status == HttpStatusCode.Accepted) { item.Status = NotificationItemStatus.Sent; } else if (item.TryCount <= this.maxTryCount && (itemResponse?.Status == HttpStatusCode.TooManyRequests || itemResponse?.Status == HttpStatusCode.RequestTimeout)) { // Mark these items as queued and Queue them item.Status = NotificationItemStatus.Retrying; item.ErrorMessage = itemResponse?.Error; isAccountIndexIncremented = this.IsMailboxLimitExchausted(itemResponse?.Error, item.NotificationId, item.EmailAccountUsed, isAccountIndexIncremented, traceProps); } else { this.logger.WriteCustomEvent($"{AIConstants.CustomEventMailSendFailed} for notificationId: {item.NotificationId} "); item.Status = NotificationItemStatus.Failed; item.ErrorMessage = itemResponse?.Error; } } this.logger.TraceInformation($"Finished {nameof(this.ProcessEntitiesInBatch)} method of {nameof(MSGraphNotificationProvider)}."); }