public void GetNotificationIdFromBatchPartitionKey_InCorrectParameters_ShouldBeSuccess() { // Arrange string batchPartitionKey = "notificationId"; // Act & Assert Assert.Throws <FormatException>(() => PartitionKeyUtility.GetNotificationIdFromBatchPartitionKey(batchPartitionKey)); }
public void GetBatchIdFromBatchPartitionKey_CorrectParameters_ShouldBeSuccess() { // Arrange string batchPartitionKey = "notificationId:1"; var expectedResult = "1"; // Act var result = PartitionKeyUtility.GetBatchIdFromBatchPartitionKey(batchPartitionKey); // Assert Assert.Equal(expectedResult, result); }
public void CreateBatchPartitionKey_CorrectParameters_ShouldBeSuccess() { // Arrange string notificationId = "notificationId"; int batchIndex = 1; var expectedResult = $"{notificationId}:{batchIndex}"; // Act var result = PartitionKeyUtility.CreateBatchPartitionKey(notificationId, batchIndex); // Assert Assert.Equal(expectedResult, result); }
public static async Task RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { var batchPartitionKey = context.GetInput <string>(); var notificationId = PartitionKeyUtility.GetNotificationIdFromBatchPartitionKey(batchPartitionKey); if (!context.IsReplaying) { log.LogInformation($"About to get pending recipients (with no conversation id in database)."); } var recipients = await context.CallActivityWithRetryAsync <IEnumerable <SentNotificationDataEntity> >( FunctionNames.GetPendingRecipientsActivity, FunctionSettings.DefaultRetryOptions, batchPartitionKey); var count = recipients.ToList().Count; if (count == 0) { log.LogInformation("No pending recipients."); return; } if (!context.IsReplaying) { log.LogInformation($"About to create 1:1 conversations with {count} recipients."); } // Create conversation. var tasks = new List <Task>(); foreach (var recipient in recipients) { // Update batch partition key to actual notification Id. // Because batch partition key is used only for batching data. // Actual state and data is stored against the notification id record in SentNotificationData Table. recipient.PartitionKey = notificationId; var task = context.CallActivityWithRetryAsync( FunctionNames.TeamsConversationActivity, FunctionSettings.DefaultRetryOptions, (notificationId, batchPartitionKey, recipient)); tasks.Add(task); } // Fan-out Fan-in. await Task.WhenAll(tasks); }
public static async Task RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { var batchPartitionKey = context.GetInput <string>(); var notificationId = PartitionKeyUtility.GetNotificationIdFromBatchPartitionKey(batchPartitionKey); var batchId = PartitionKeyUtility.GetBatchIdFromBatchPartitionKey(batchPartitionKey); if (!context.IsReplaying) { log.LogInformation($"About to get recipients from batch {batchId}."); } var recipients = await context.CallActivityWithRetryAsync <IEnumerable <SentNotificationDataEntity> >( FunctionNames.GetRecipientsActivity, FunctionSettings.DefaultRetryOptions, batchPartitionKey); // Use the SendQueue's maximum number of messages in a batch request number because // the list is being broken into batches in order to be added to that queue. var batches = recipients.AsBatches(SendQueue.MaxNumberOfMessagesInBatchRequest).ToList(); var totalBatchCount = batches.Count; if (!context.IsReplaying) { log.LogInformation($"About to process {totalBatchCount} batches."); } var tasks = new List <Task>(); for (var batchIndex = 0; batchIndex < totalBatchCount; batchIndex++) { if (!context.IsReplaying) { log.LogInformation($"About to process batch {batchIndex + 1} / {totalBatchCount}"); } var task = context.CallActivityWithRetryAsync( FunctionNames.SendBatchMessagesActivity, FunctionSettings.DefaultRetryOptions, (notificationId, batches[batchIndex])); tasks.Add(task); } // Fan-out Fan-in await Task.WhenAll(tasks); }
public async Task BatchRecipients_AllParameters_ShouldBeSuccess() { // Arrange var recipientService = this.GetRecipientsService(); string notificationId = "notificationId"; var recipients = new List <SentNotificationDataEntity>() { new SentNotificationDataEntity() { PartitionKey = notificationId, ConversationId = "conversationId" }, new SentNotificationDataEntity() { PartitionKey = notificationId, ConversationId = string.Empty }, new SentNotificationDataEntity() { PartitionKey = notificationId, ConversationId = "conversationId2" }, }; this.sentNotificationRepository .Setup(x => x.BatchInsertOrMergeAsync(It.IsAny <IEnumerable <SentNotificationDataEntity> >())) .Returns(Task.CompletedTask); var recipientBatches = recipients.AsBatches(Constants.MaximumNumberOfRecipientsInBatch).ToList(); // Act RecipientsInfo recipientsInfo = default; Func <Task> task = async() => recipientsInfo = await recipientService.BatchRecipients(recipients); // Assert await task.Should().NotThrowAsync(); Assert.Equal(recipientsInfo.TotalRecipientCount, recipients.Count); Assert.True(recipientsInfo.HasRecipientsPendingInstallation); Assert.Equal(notificationId, PartitionKeyUtility.GetNotificationIdFromBatchPartitionKey(recipientsInfo.BatchKeys.First())); this.sentNotificationRepository.Verify(x => x.BatchInsertOrMergeAsync(It.IsAny <IEnumerable <SentNotificationDataEntity> >()), Times.Exactly(recipientBatches.Count)); }
public async Task TeamsConversationRunOrchestratorTest() { // Arrange string batchPartitionKey = "notificationId:1"; IEnumerable <SentNotificationDataEntity> recipients = new List <SentNotificationDataEntity>() { new SentNotificationDataEntity() { ConversationId = "conversationId1", PartitionKey = batchPartitionKey }, new SentNotificationDataEntity() { ConversationId = "conversationId2", PartitionKey = batchPartitionKey }, }; var notificationId = PartitionKeyUtility.GetNotificationIdFromBatchPartitionKey(batchPartitionKey); this.mockContext .Setup(x => x.GetInput <string>()) .Returns(batchPartitionKey); this.mockContext .Setup(x => x.CallActivityWithRetryAsync <IEnumerable <SentNotificationDataEntity> >(It.IsAny <string>(), It.IsAny <RetryOptions>(), It.IsAny <string>())) .ReturnsAsync(recipients); this.mockContext .Setup(x => x.CallActivityWithRetryAsync(It.IsAny <string>(), It.IsAny <RetryOptions>(), It.IsAny <object>())) .Returns(Task.CompletedTask); // Act Func <Task> task = async() => await TeamsConversationOrchestrator.RunOrchestrator(this.mockContext.Object, this.mockLogger.Object); // Assert await task.Should().NotThrowAsync <Exception>(); this.mockContext.Verify(x => x.CallActivityWithRetryAsync <IEnumerable <SentNotificationDataEntity> >(It.Is <string>(x => x.Equals(FunctionNames.GetPendingRecipientsActivity)), It.IsAny <RetryOptions>(), It.IsAny <string>()), Times.Once); this.mockContext .Verify(x => x.CallActivityWithRetryAsync(It.Is <string>(x => x.Equals(FunctionNames.TeamsConversationActivity)), It.IsAny <RetryOptions>(), It.Is <(string notificationId, string batchPartitionKey, SentNotificationDataEntity recipients)>(x => x.recipients.PartitionKey.Equals(notificationId))), Times.Exactly(recipients.Count())); }
/// <inheritdoc/> public async Task <RecipientsInfo> BatchRecipients(IEnumerable <SentNotificationDataEntity> recipients) { if (recipients == null) { throw new ArgumentNullException(nameof(IEnumerable <SentNotificationDataEntity>)); } var notificationId = recipients.FirstOrDefault().PartitionKey; var recipientBatches = recipients.AsBatches(Constants.MaximumNumberOfRecipientsInBatch); var recipientInfo = new RecipientsInfo(notificationId) { TotalRecipientCount = recipients.ToList().Count, }; int batchIndex = 1; foreach (var recipientBatch in recipientBatches) { var recipientBatchList = recipientBatch.ToList(); // Update PartitionKey to Batch Key recipientBatchList.ForEach(s => { s.PartitionKey = PartitionKeyUtility.CreateBatchPartitionKey(s.PartitionKey, batchIndex); // Update if there is any recipient which has no conversation id. recipientInfo.HasRecipientsPendingInstallation |= string.IsNullOrEmpty(s.ConversationId); }); // Store. await this.sentNotificationDataRepository.BatchInsertOrMergeAsync(recipientBatch); recipientInfo.BatchKeys.Add(recipientBatch.FirstOrDefault().PartitionKey); batchIndex++; } return(recipientInfo); }