예제 #1
0
 /// <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;
 }
예제 #3
0
 /// <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;
 }
예제 #4
0
        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")));
        }