/// <summary> /// Initializes a new instance of the <see cref="NotificationService"/> class. /// </summary> /// <param name="globalSendingNotificationDataRepository">The global sending notification data repository.</param> /// <param name="sentNotificationDataRepository">The sent notification data repository.</param> public NotificationService( GlobalSendingNotificationDataRepository globalSendingNotificationDataRepository, SentNotificationDataRepository sentNotificationDataRepository) { this.globalSendingNotificationDataRepository = globalSendingNotificationDataRepository ?? throw new ArgumentNullException(nameof(globalSendingNotificationDataRepository)); this.sentNotificationDataRepository = sentNotificationDataRepository ?? throw new ArgumentNullException(nameof(sentNotificationDataRepository)); }
/// <summary> /// Initializes a new instance of the <see cref="DelaySendingNotificationService"/> class. /// </summary> /// <param name="globalSendingNotificationDataRepository">The global sending notification data repository.</param> /// <param name="sendQueue">The send queue.</param> public DelaySendingNotificationService( GlobalSendingNotificationDataRepository globalSendingNotificationDataRepository, SendQueue sendQueue) { this.globalSendingNotificationDataRepository = globalSendingNotificationDataRepository; this.sendQueue = sendQueue; }
/// <summary> /// Initializes a new instance of the <see cref="PrecheckService"/> class. /// </summary> /// <param name="globalSendingNotificationDataRepository">The global sending notification data repository.</param> /// <param name="sentNotificationDataRepository">The sent notification data repository.</param> /// <param name="sendQueue">The send queue.</param> public PrecheckService( GlobalSendingNotificationDataRepository globalSendingNotificationDataRepository, SentNotificationDataRepository sentNotificationDataRepository, SendQueue sendQueue) { this.globalSendingNotificationDataRepository = globalSendingNotificationDataRepository; this.sentNotificationDataRepository = sentNotificationDataRepository; this.sendQueue = sendQueue; }
public async Task Run( [ServiceBusTrigger(CompanyCommunicatorSendFunction.SendQueueName, Connection = "ServiceBusConnection")] string myQueueItem, int deliveryCount, DateTime enqueuedTimeUtc, string messageId, ILogger log, ExecutionContext context) { log.LogInformation($"C# ServiceBus queue trigger function processed message: {myQueueItem}"); IConfiguration configuration = new ConfigurationBuilder() .SetBasePath(context.FunctionAppDirectory) .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true) .AddEnvironmentVariables() .Build(); var messageContent = JsonConvert.DeserializeObject <ServiceBusSendQueueMessageContent>(myQueueItem); var totalNumberOfThrottles = 0; try { // Simply initialize the variable for certain build environments and versions var maxNumberOfAttempts = 0; // If parsing fails, out variable is set to 0, so need to set the default if (!int.TryParse(configuration["MaxNumberOfAttempts"], out maxNumberOfAttempts)) { maxNumberOfAttempts = 1; } CompanyCommunicatorSendFunction.httpClient = CompanyCommunicatorSendFunction.httpClient ?? new HttpClient(); CompanyCommunicatorSendFunction.userDataRepository = CompanyCommunicatorSendFunction.userDataRepository ?? new UserDataRepository(configuration, isFromAzureFunction: true); CompanyCommunicatorSendFunction.sendingNotificationDataRepository = CompanyCommunicatorSendFunction.sendingNotificationDataRepository ?? new SendingNotificationDataRepository(configuration, isFromAzureFunction: true); CompanyCommunicatorSendFunction.globalSendingNotificationDataRepository = CompanyCommunicatorSendFunction.globalSendingNotificationDataRepository ?? new GlobalSendingNotificationDataRepository(configuration, isFromAzureFunction: true); CompanyCommunicatorSendFunction.sentNotificationDataRepository = CompanyCommunicatorSendFunction.sentNotificationDataRepository ?? new SentNotificationDataRepository(configuration, isFromAzureFunction: true); if (CompanyCommunicatorSendFunction.botAccessToken == null || CompanyCommunicatorSendFunction.botAccessTokenExpiration == null || DateTime.UtcNow > CompanyCommunicatorSendFunction.botAccessTokenExpiration) { await this.FetchTokenAsync(configuration, CompanyCommunicatorSendFunction.httpClient); } CompanyCommunicatorSendFunction.sendQueueServiceBusMessageSender = CompanyCommunicatorSendFunction.sendQueueServiceBusMessageSender ?? new MessageSender(configuration["ServiceBusConnection"], CompanyCommunicatorSendFunction.SendQueueName); var getActiveNotificationEntityTask = CompanyCommunicatorSendFunction.sendingNotificationDataRepository.GetAsync( PartitionKeyNames.NotificationDataTable.SendingNotificationsPartition, messageContent.NotificationId); var getGlobalSendingNotificationDataEntityTask = CompanyCommunicatorSendFunction.globalSendingNotificationDataRepository .GetGlobalSendingNotificationDataEntity(); var incomingUserDataEntity = messageContent.UserDataEntity; var incomingConversationId = incomingUserDataEntity.ConversationId; var getUserDataEntityTask = string.IsNullOrWhiteSpace(incomingConversationId) ? CompanyCommunicatorSendFunction.userDataRepository.GetAsync( PartitionKeyNames.UserDataTable.UserDataPartition, incomingUserDataEntity.AadId) : Task.FromResult <UserDataEntity>(null); await Task.WhenAll(getActiveNotificationEntityTask, getGlobalSendingNotificationDataEntityTask, getUserDataEntityTask); var activeNotificationEntity = await getActiveNotificationEntityTask; var globalSendingNotificationDataEntity = await getGlobalSendingNotificationDataEntityTask; var userDataEntity = await getUserDataEntityTask; var conversationId = string.IsNullOrWhiteSpace(incomingConversationId) ? userDataEntity?.ConversationId : incomingConversationId; Task saveUserDataEntityTask = Task.CompletedTask; Task saveSentNotificationDataTask = Task.CompletedTask; Task setDelayTimeAndSendDelayedRetryTask = Task.CompletedTask; if (globalSendingNotificationDataEntity?.SendRetryDelayTime != null && DateTime.UtcNow < globalSendingNotificationDataEntity.SendRetryDelayTime) { await this.SendDelayedRetryOfMessageToSendFunction(configuration, messageContent); return; } if (!string.IsNullOrWhiteSpace(conversationId)) { incomingUserDataEntity.ConversationId = conversationId; // Check if message is intended for a team if (!conversationId.StartsWith("19:")) { incomingUserDataEntity.PartitionKey = PartitionKeyNames.UserDataTable.UserDataPartition; incomingUserDataEntity.RowKey = incomingUserDataEntity.AadId; var operation = TableOperation.InsertOrMerge(incomingUserDataEntity); saveUserDataEntityTask = CompanyCommunicatorSendFunction.userDataRepository.Table.ExecuteAsync(operation); } } else { var isCreateConversationThrottled = false; for (int i = 0; i < maxNumberOfAttempts; i++) { var createConversationUrl = $"{incomingUserDataEntity.ServiceUrl}v3/conversations"; using (var requestMessage = new HttpRequestMessage(HttpMethod.Post, createConversationUrl)) { requestMessage.Headers.Authorization = new AuthenticationHeaderValue( "Bearer", CompanyCommunicatorSendFunction.botAccessToken); var payloadString = "{\"bot\": { \"id\": \"28:" + configuration["MicrosoftAppId"] + "\"},\"isGroup\": false, \"tenantId\": \"" + incomingUserDataEntity.TenantId + "\", \"members\": [{\"id\": \"" + incomingUserDataEntity.UserId + "\"}]}"; requestMessage.Content = new StringContent(payloadString, Encoding.UTF8, "application/json"); using (var sendResponse = await CompanyCommunicatorSendFunction.httpClient.SendAsync(requestMessage)) { if (sendResponse.StatusCode == HttpStatusCode.Created) { var jsonResponseString = await sendResponse.Content.ReadAsStringAsync(); dynamic resp = JsonConvert.DeserializeObject(jsonResponseString); incomingUserDataEntity.PartitionKey = PartitionKeyNames.UserDataTable.UserDataPartition; incomingUserDataEntity.RowKey = incomingUserDataEntity.AadId; incomingUserDataEntity.ConversationId = resp.id; var operation = TableOperation.InsertOrMerge(incomingUserDataEntity); saveUserDataEntityTask = CompanyCommunicatorSendFunction.userDataRepository.Table.ExecuteAsync(operation); isCreateConversationThrottled = false; break; } else if (sendResponse.StatusCode == HttpStatusCode.TooManyRequests) { isCreateConversationThrottled = true; totalNumberOfThrottles++; // Do not delay if already attempted the maximum number of attempts. if (i != maxNumberOfAttempts - 1) { var random = new Random(); await Task.Delay(random.Next(500, 1500)); } } else { await this.SaveSentNotificationData( configuration, messageContent.NotificationId, incomingUserDataEntity.AadId, totalNumberOfThrottles, isStatusCodeFromCreateConversation : true, statusCode : sendResponse.StatusCode); return; } } } } if (isCreateConversationThrottled) { await this.SetDelayTimeAndSendDelayedRetry(configuration, messageContent); return; } } var isSendMessageThrottled = false; for (int i = 0; i < maxNumberOfAttempts; i++) { var conversationUrl = $"{incomingUserDataEntity.ServiceUrl}v3/conversations/{incomingUserDataEntity.ConversationId}/activities"; using (var requestMessage = new HttpRequestMessage(HttpMethod.Post, conversationUrl)) { requestMessage.Headers.Authorization = new AuthenticationHeaderValue( "Bearer", CompanyCommunicatorSendFunction.botAccessToken); var attachmentJsonString = JsonConvert.DeserializeObject(activeNotificationEntity.Content); var messageString = "{ \"type\": \"message\", \"attachments\": [ { \"contentType\": \"application/vnd.microsoft.card.adaptive\", \"content\": " + attachmentJsonString + " } ] }"; requestMessage.Content = new StringContent(messageString, Encoding.UTF8, "application/json"); using (var sendResponse = await CompanyCommunicatorSendFunction.httpClient.SendAsync(requestMessage)) { if (sendResponse.StatusCode == HttpStatusCode.Created) { log.LogInformation("MESSAGE SENT SUCCESSFULLY"); saveSentNotificationDataTask = this.SaveSentNotificationData( configuration, messageContent.NotificationId, incomingUserDataEntity.AadId, totalNumberOfThrottles, isStatusCodeFromCreateConversation: false, statusCode: sendResponse.StatusCode); isSendMessageThrottled = false; break; } else if (sendResponse.StatusCode == HttpStatusCode.TooManyRequests) { log.LogError("MESSAGE THROTTLED"); isSendMessageThrottled = true; totalNumberOfThrottles++; // Do not delay if already attempted the maximum number of attempts. if (i != maxNumberOfAttempts - 1) { var random = new Random(); await Task.Delay(random.Next(500, 1500)); } } else { log.LogError($"MESSAGE FAILED: {sendResponse.StatusCode}"); saveSentNotificationDataTask = this.SaveSentNotificationData( configuration, messageContent.NotificationId, incomingUserDataEntity.AadId, totalNumberOfThrottles, isStatusCodeFromCreateConversation: false, statusCode: sendResponse.StatusCode); await Task.WhenAll(saveUserDataEntityTask, saveSentNotificationDataTask); return; } } } } if (isSendMessageThrottled) { setDelayTimeAndSendDelayedRetryTask = this.SetDelayTimeAndSendDelayedRetry(configuration, messageContent); } await Task.WhenAll( saveUserDataEntityTask, saveSentNotificationDataTask, setDelayTimeAndSendDelayedRetryTask); } catch (Exception e) { log.LogError(e, $"ERROR: {e.Message}, {e.GetType()}"); var statusCodeToStore = HttpStatusCode.Continue; if (deliveryCount >= CompanyCommunicatorSendFunction.MaxDeliveryCountForDeadLetter) { statusCodeToStore = HttpStatusCode.InternalServerError; } await this.SaveSentNotificationData( configuration, messageContent.NotificationId, messageContent.UserDataEntity.AadId, totalNumberOfThrottles, isStatusCodeFromCreateConversation : false, statusCode : statusCodeToStore); throw e; } }
public async Task RunAsync( [TimerTrigger("0 */30 * * * *")] TimerInfo scheduleInitializationTimer, ILogger log) { scheduleInitializationTimer = scheduleInitializationTimer ?? throw new ArgumentNullException(nameof(scheduleInitializationTimer)); log.LogInformation($"Schedule Initialization Timer executed at: {scheduleInitializationTimer.ToString()}."); CompanyCommunicatorScheduleFunction.configuration = CompanyCommunicatorScheduleFunction.configuration ?? new ConfigurationBuilder() .AddEnvironmentVariables() .Build(); CompanyCommunicatorScheduleFunction.adaptiveCardCreator = CompanyCommunicatorScheduleFunction.adaptiveCardCreator ?? new AdaptiveCardCreator(); CompanyCommunicatorScheduleFunction.tableRowKeyGenerator = CompanyCommunicatorScheduleFunction.tableRowKeyGenerator ?? new TableRowKeyGenerator(); CompanyCommunicatorScheduleFunction.adGroupsDataRepository = CompanyCommunicatorScheduleFunction.adGroupsDataRepository ?? new ADGroupsDataRepository(CompanyCommunicatorScheduleFunction.configuration, true); CompanyCommunicatorScheduleFunction.notificationDataRepository = CompanyCommunicatorScheduleFunction.notificationDataRepository ?? new NotificationDataRepository(CompanyCommunicatorScheduleFunction.configuration, CompanyCommunicatorScheduleFunction.adGroupsDataRepository, tableRowKeyGenerator, true); CompanyCommunicatorScheduleFunction.userDataRepository = CompanyCommunicatorScheduleFunction.userDataRepository ?? new UserDataRepository(CompanyCommunicatorScheduleFunction.configuration, true); CompanyCommunicatorScheduleFunction.teamDataRepository = CompanyCommunicatorScheduleFunction.teamDataRepository ?? new TeamDataRepository(CompanyCommunicatorScheduleFunction.configuration, true); CompanyCommunicatorScheduleFunction.sendingNotificationDataRepository = CompanyCommunicatorScheduleFunction.sendingNotificationDataRepository ?? new SendingNotificationDataRepository(CompanyCommunicatorScheduleFunction.configuration, true); CompanyCommunicatorScheduleFunction.globalSendingNotificationDataRepository = CompanyCommunicatorScheduleFunction.globalSendingNotificationDataRepository ?? new GlobalSendingNotificationDataRepository(CompanyCommunicatorScheduleFunction.configuration, true); CompanyCommunicatorScheduleFunction.sentNotificationDataRepository = CompanyCommunicatorScheduleFunction.sentNotificationDataRepository ?? new SentNotificationDataRepository(CompanyCommunicatorScheduleFunction.configuration, true); CompanyCommunicatorScheduleFunction.metadataProvider = CompanyCommunicatorScheduleFunction.metadataProvider ?? new MetadataProvider(CompanyCommunicatorScheduleFunction.configuration, userDataRepository, teamDataRepository, notificationDataRepository, adGroupsDataRepository); CompanyCommunicatorScheduleFunction.sendingNotificationCreator = CompanyCommunicatorScheduleFunction.sendingNotificationCreator ?? new SendingNotificationCreator(CompanyCommunicatorScheduleFunction.configuration, notificationDataRepository, sendingNotificationDataRepository, adaptiveCardCreator); CompanyCommunicatorScheduleFunction.scheduleNotificationDelivery = CompanyCommunicatorScheduleFunction.scheduleNotificationDelivery ?? new ScheduleNotificationDelivery(CompanyCommunicatorScheduleFunction.configuration, notificationDataRepository, metadataProvider, sendingNotificationCreator, scheduleNotificationDataRepository, teamDataRepository); CompanyCommunicatorScheduleFunction.scheduleNotificationDataRepository = CompanyCommunicatorScheduleFunction.scheduleNotificationDataRepository ?? new ScheduleNotificationDataRepository(CompanyCommunicatorScheduleFunction.configuration, notificationDataRepository, metadataProvider, sendingNotificationCreator, scheduleNotificationDelivery, true); // Generate filter condition to get pending notifications. var rowKeyFilter = TableQuery.GenerateFilterConditionForDate( nameof(ScheduleNotificationDataEntity.NotificationDate), QueryComparisons.LessThanOrEqual, DateTime.UtcNow); // Get records which are pending to send notification. var pendingNotifications = await CompanyCommunicatorScheduleFunction.scheduleNotificationDataRepository.GetWithFilterAsync( rowKeyFilter); // Repeat all pending notifications. foreach (var pendingNotification in pendingNotifications) { try { log.LogInformation($"Schedule notification triggered for " + pendingNotification.NotificationId); string returnMessage = await CompanyCommunicatorScheduleFunction.scheduleNotificationDataRepository.SendNotificationandCreateScheduleAsync(pendingNotification); if (string.IsNullOrEmpty(returnMessage)) { log.LogInformation($"Schedule notification success for " + pendingNotification.NotificationId); } else { log.LogInformation($"Schedule notification failed for " + pendingNotification.NotificationId); } } catch (Exception ex) { log.LogError($"Error while sending schedule notification:" + pendingNotification.NotificationId, ex); } } }
public async Task <IActionResult> RunAsync( [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req, ILogger log) { // scheduleInitializationTimer = scheduleInitializationTimer ?? throw new ArgumentNullException(nameof(scheduleInitializationTimer)); //log.LogInformation($"Schedule Initialization Timer executed at: {scheduleInitializationTimer.ToString()}."); SchduleNotificationTestFunction.configuration = SchduleNotificationTestFunction.configuration ?? new ConfigurationBuilder() .AddEnvironmentVariables() .Build(); SchduleNotificationTestFunction.adaptiveCardCreator = SchduleNotificationTestFunction.adaptiveCardCreator ?? new AdaptiveCardCreator(); SchduleNotificationTestFunction.tableRowKeyGenerator = SchduleNotificationTestFunction.tableRowKeyGenerator ?? new TableRowKeyGenerator(); SchduleNotificationTestFunction.adGroupsDataRepository = SchduleNotificationTestFunction.adGroupsDataRepository ?? new ADGroupsDataRepository(SchduleNotificationTestFunction.configuration, true); SchduleNotificationTestFunction.notificationDataRepository = SchduleNotificationTestFunction.notificationDataRepository ?? new NotificationDataRepository(SchduleNotificationTestFunction.configuration, SchduleNotificationTestFunction.adGroupsDataRepository, tableRowKeyGenerator, true); SchduleNotificationTestFunction.userDataRepository = SchduleNotificationTestFunction.userDataRepository ?? new UserDataRepository(SchduleNotificationTestFunction.configuration, true); SchduleNotificationTestFunction.teamDataRepository = SchduleNotificationTestFunction.teamDataRepository ?? new TeamDataRepository(SchduleNotificationTestFunction.configuration, true); SchduleNotificationTestFunction.sendingNotificationDataRepository = SchduleNotificationTestFunction.sendingNotificationDataRepository ?? new SendingNotificationDataRepository(SchduleNotificationTestFunction.configuration, true); SchduleNotificationTestFunction.globalSendingNotificationDataRepository = SchduleNotificationTestFunction.globalSendingNotificationDataRepository ?? new GlobalSendingNotificationDataRepository(SchduleNotificationTestFunction.configuration, true); SchduleNotificationTestFunction.sentNotificationDataRepository = SchduleNotificationTestFunction.sentNotificationDataRepository ?? new SentNotificationDataRepository(SchduleNotificationTestFunction.configuration, true); SchduleNotificationTestFunction.metadataProvider = SchduleNotificationTestFunction.metadataProvider ?? new MetadataProvider(SchduleNotificationTestFunction.configuration, userDataRepository, teamDataRepository, notificationDataRepository, adGroupsDataRepository); SchduleNotificationTestFunction.sendingNotificationCreator = SchduleNotificationTestFunction.sendingNotificationCreator ?? new SendingNotificationCreator(SchduleNotificationTestFunction.configuration, notificationDataRepository, sendingNotificationDataRepository, adaptiveCardCreator); SchduleNotificationTestFunction.scheduleNotificationDelivery = SchduleNotificationTestFunction.scheduleNotificationDelivery ?? new ScheduleNotificationDelivery(SchduleNotificationTestFunction.configuration, notificationDataRepository, metadataProvider, sendingNotificationCreator, scheduleNotificationDataRepository, teamDataRepository); SchduleNotificationTestFunction.scheduleNotificationDataRepository = SchduleNotificationTestFunction.scheduleNotificationDataRepository ?? new ScheduleNotificationDataRepository(SchduleNotificationTestFunction.configuration, notificationDataRepository, metadataProvider, sendingNotificationCreator, scheduleNotificationDelivery, true); // Generate filter condition to get pending notifications. var rowKeyFilter = TableQuery.GenerateFilterConditionForDate( nameof(ScheduleNotificationDataEntity.NotificationDate), QueryComparisons.LessThanOrEqual, DateTime.UtcNow); // Get records which are pending to send notification. var pendingNotifications = await SchduleNotificationTestFunction.scheduleNotificationDataRepository.GetWithFilterAsync( rowKeyFilter); // Repeat all pending notifications. foreach (var pendingNotification in pendingNotifications) { try { log.LogInformation($"Schedule notification triggered for " + pendingNotification.NotificationId); string returnMessage = await SchduleNotificationTestFunction.scheduleNotificationDataRepository.SendNotificationandCreateScheduleAsync(pendingNotification); if (string.IsNullOrEmpty(returnMessage)) { log.LogInformation($"Schedule notification success for " + pendingNotification.NotificationId); } else { log.LogInformation($"Schedule notification failed for " + pendingNotification.NotificationId); } } catch (Exception ex) { log.LogError($"Error while sending schedule notification:" + pendingNotification.NotificationId, ex); } } return(await Task.FromResult((ActionResult) new OkObjectResult("OK"))); }