public async Task GenerateMessages_ReturnsNotificationMessage_WhenActionIsEmailNotificationAction() { // arrange var workflowEventTrigger = new WorkflowEventTrigger { Action = new EmailNotificationAction() }; _workflowEventTriggers.Add(workflowEventTrigger); // act var messages = await WorkflowEventsMessagesHelper.GenerateMessages( _userId, _revisionId, _userName, _transactionId, _workflowEventTriggers, _baseArtifactVersionControlInfo, _projectName, _modifiedProperties, _currentState, _artifactUrl, _baseUrl, _ancestorArtifactTypeIds, _mockUsersRepository.Object, _mockServiceLogRepository.Object, _mockWebhooksRepository.Object, _mockProjectMetaRepository.Object); // assert Assert.IsTrue(messages.Count == 1); Assert.IsTrue(messages.Single() is NotificationMessage); }
public async Task GenerateMessages_ReturnsNoMessage_WhenActionIsGenerateUserStoriesActionAndPredefinedTypeIsNotProcess() { // arrange var workflowEventTrigger = new WorkflowEventTrigger { Action = new GenerateUserStoriesAction() }; _workflowEventTriggers.Add(workflowEventTrigger); _baseArtifactVersionControlInfo.PredefinedType = ItemTypePredefined.None; _mockServiceLogRepository.Setup(m => m.LogInformation(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <string>(), It.IsAny <string>(), It.IsAny <int>())).Returns(Task.FromResult(true)); // act var messages = await WorkflowEventsMessagesHelper.GenerateMessages( _userId, _revisionId, _userName, _transactionId, _workflowEventTriggers, _baseArtifactVersionControlInfo, _projectName, _modifiedProperties, _currentState, _artifactUrl, _baseUrl, _ancestorArtifactTypeIds, _mockUsersRepository.Object, _mockServiceLogRepository.Object, _mockWebhooksRepository.Object, _mockProjectMetaRepository.Object); // assert Assert.IsTrue(messages.Count == 0); }
public async Task GetEmailValues_ReturnsEmails_WhenEmailNotificationActionHasPropertyTypeId() { // arrange const int numEmails = 7; var emailNotificationAction = new EmailNotificationAction { PropertyTypeId = 123 }; var userInfos = new List <UserInfo>(); for (int i = 0; i < numEmails; i++) { var userInfo = new UserInfo() { Email = $"email{i}" }; userInfos.Add(userInfo); } _mockUsersRepository.Setup(m => m.GetUserInfoForWorkflowArtifactForAssociatedUserProperty(It.IsAny <int>(), It.IsAny <int>(), It.IsAny <int>(), It.IsAny <IDbTransaction>())).ReturnsAsync(userInfos); // act var emails = await WorkflowEventsMessagesHelper.GetEmailValues(_revisionId, 456, emailNotificationAction, _mockUsersRepository.Object); // assert Assert.AreEqual(numEmails, emails.Count); }
public async Task GetEmailValues_ReturnsEmails_WhenEmailNotificationActionContainsEmails() { // arrange const int numEmails = 5; var emailNotificationAction = new EmailNotificationAction(); for (int i = 0; i < numEmails; i++) { emailNotificationAction.Emails.Add($"email{i}"); } // act var emails = await WorkflowEventsMessagesHelper.GetEmailValues(_revisionId, 123, emailNotificationAction, _mockUsersRepository.Object); // assert Assert.AreEqual(numEmails, emails.Count); }
internal static async Task <bool> ProcessCreatedArtifacts(TenantInformation tenant, ArtifactsPublishedMessage message, IArtifactsPublishedRepository repository, IServiceLogRepository serviceLogRepository, IWorkflowMessagingProcessor messageProcessor, int transactionCommitWaitTimeInMilliSeconds = 60000) { var createdArtifacts = message?.Artifacts?.Where(p => p.IsFirstTimePublished && repository.WorkflowRepository.IsWorkflowSupported((ItemTypePredefined)p.Predefined)).ToList(); if (createdArtifacts == null || createdArtifacts.Count <= 0) { Logger.Log("No created artifacts found", message, tenant); return(false); } Logger.Log($"{createdArtifacts.Count} created artifacts found", message, tenant); var artifactIds = createdArtifacts.Select(a => a.Id).ToHashSet(); var artifactInfos = (await repository.WorkflowRepository.GetWorkflowMessageArtifactInfoAsync(message.UserId, artifactIds, message.RevisionId)).ToDictionary(k => k.Id); Logger.Log($"{artifactInfos.Count} artifact infos found", message, tenant); var notificationMessages = new Dictionary <int, List <IWorkflowMessage> >(); foreach (var createdArtifact in createdArtifacts) { WorkflowMessageArtifactInfo artifactInfo; if (!artifactInfos.TryGetValue(createdArtifact.Id, out artifactInfo)) { await serviceLogRepository.LogInformation(LogSource, $"Could not recover information for artifact Id: {createdArtifact.Id} and Name: {createdArtifact.Name} and Project Id: {createdArtifact.ProjectId}"); Logger.Log($"Could not recover information for artifact Id: {createdArtifact.Id} and Name: {createdArtifact.Name} and Project Id: {createdArtifact.ProjectId}", message, tenant); continue; } var eventTriggers = await repository.WorkflowRepository.GetWorkflowEventTriggersForNewArtifactEvent(message.UserId, new[] { createdArtifact.Id }, message.RevisionId, true); if (eventTriggers?.AsynchronousTriggers == null || eventTriggers.AsynchronousTriggers.Count == 0) { Logger.Log($"Found no async triggers for artifact with ID {createdArtifact.Id}", message, tenant); continue; } Logger.Log($"Found {eventTriggers.AsynchronousTriggers.Count} async triggers for artifact with ID {createdArtifact.Id}", message, tenant); int artifactId = createdArtifact.Id; var currentState = await repository.WorkflowRepository.GetStateForArtifactAsync(message.UserId, createdArtifact.Id, int.MaxValue, true); var actionMessages = await WorkflowEventsMessagesHelper.GenerateMessages(message.UserId, message.RevisionId, message.UserName, message.TransactionId, eventTriggers.AsynchronousTriggers, artifactInfo, artifactInfo.ProjectName, new Dictionary <int, IList <Property> >(), currentState, createdArtifact.Url, createdArtifact.BaseUrl, createdArtifact.AncestorArtifactTypeIds, repository.UsersRepository, serviceLogRepository, repository.WebhooksRepository, repository.ProjectMetaRepository); if (actionMessages == null || actionMessages.Count == 0) { continue; } if (!notificationMessages.ContainsKey(artifactId)) { notificationMessages.Add(artifactId, new List <IWorkflowMessage>()); } notificationMessages[artifactId].AddRange(actionMessages); } if (notificationMessages.Count == 0) { Logger.Log("None of the created artifacts have async triggers", message, tenant); return(false); } Logger.Log($"Sending async trigger messages for artifacts: {string.Join(", ", notificationMessages.Select(kvp => kvp.Key))}", message, tenant); foreach (var notificationMessage in notificationMessages.Where(m => m.Value != null)) { await WorkflowEventsMessagesHelper.ProcessMessages(LogSource, tenant, serviceLogRepository, notificationMessage.Value, $"Error on new artifact creation with Id: {notificationMessage.Key}", messageProcessor); } return(true); }
private static async Task ProcessCustomPropertyChange(TenantInformation tenant, ArtifactsPublishedMessage message, IArtifactsPublishedRepository repository, List <EmailNotificationAction> notifications, HashSet <int> modifiedCustomPropertiesSet, SqlWorkFlowStateInformation currentStateInfo, List <PublishedArtifactInformation> updatedArtifacts, int artifactId, Dictionary <int, SqlWorkFlowStateInformation> workflowStates, List <SqlProject> projects, Dictionary <int, List <IWorkflowMessage> > notificationMessages) { if (modifiedCustomPropertiesSet.Count == 0) { return; } ////Dictionary<int, List<int>> instancePropertyTypeIds var instancePropertyTypeIds = await repository.GetInstancePropertyTypeIdsMap(modifiedCustomPropertiesSet); Logger.Log( $"{instancePropertyTypeIds.Count} instance property type IDs found: {string.Join(", ", instancePropertyTypeIds.Select(k => k.Key))}", message, tenant, LogLevel.Debug); foreach (var notificationAction in notifications) { Logger.Log("Processing notification action", message, tenant, LogLevel.Debug); if (!notificationAction.EventPropertyTypeId.HasValue) { continue; } int eventPropertyTypeId = notificationAction.EventPropertyTypeId.Value; if (!instancePropertyTypeIds.ContainsKey(eventPropertyTypeId)) { Logger.Log( $"The property type ID {notificationAction.EventPropertyTypeId} was not found in the dictionary of instance property type IDs.", message, tenant, LogLevel.Debug); continue; } List <int> propertyTypeIds; if (!instancePropertyTypeIds.TryGetValue(eventPropertyTypeId, out propertyTypeIds) || propertyTypeIds.IsEmpty()) { Logger.Log( $"The property type ID {notificationAction.EventPropertyTypeId} was not found in the dictionary of instance property type IDs.", message, tenant, LogLevel.Debug); continue; } if (notificationAction.ConditionalStateId.HasValue && (currentStateInfo == null || currentStateInfo.WorkflowStateId != notificationAction.ConditionalStateId.Value)) { // The conditional state id is present, but either the current state info is not present or the current state is not same as conditional state var currentStateId = currentStateInfo?.WorkflowStateId.ToString() ?? "none"; Logger.Log( $"Conditional state ID {notificationAction.ConditionalStateId.Value} does not match current state ID: {currentStateId}", message, tenant, LogLevel.Debug); continue; } var artifact = updatedArtifacts.First(a => a.Id == artifactId); string messageHeader = I18NHelper.FormatInvariant("You are being notified because artifact with Id: {0} has been updated.", artifactId); var artifactPartUrl = artifact.Url ?? ServerUriHelper.GetArtifactUrl(artifactId, true); var blueprintUrl = artifact.BaseUrl ?? ServerUriHelper.GetBaseHostUri()?.ToString(); var emails = await WorkflowEventsMessagesHelper.GetEmailValues(message.RevisionId, artifactId, notificationAction, repository.UsersRepository); var notificationMessage = new NotificationMessage { TransactionId = message.TransactionId, ArtifactName = workflowStates[artifactId].Name, ProjectName = projects.First(p => p.ItemId == artifact.ProjectId).Name, Subject = notificationAction.Subject, From = notificationAction.FromDisplayName, To = emails, Header = messageHeader, Message = notificationAction.Message, RevisionId = message.RevisionId, UserId = message.UserId, ArtifactTypeId = currentStateInfo.ItemTypeId, ArtifactId = artifactId, ArtifactUrl = artifactPartUrl, ArtifactTypePredefined = artifact.Predefined, ProjectId = artifact.ProjectId, BlueprintUrl = blueprintUrl }; if (notificationMessages.ContainsKey(artifactId)) { notificationMessages[artifactId].Add(notificationMessage); } else { notificationMessages.Add(artifactId, new List <IWorkflowMessage> { notificationMessage }); } } }
private static async Task ProcessSystemPropertyChange(TenantInformation tenant, ArtifactsPublishedMessage message, IArtifactsPublishedRepository repository, List <EmailNotificationAction> notifications, HashSet <PropertyTypePredefined> modifiedSystemPropertiesSet, SqlWorkFlowStateInformation currentStateInfo, List <PublishedArtifactInformation> updatedArtifacts, int artifactId, Dictionary <int, SqlWorkFlowStateInformation> workflowStates, List <SqlProject> projects, Dictionary <int, List <IWorkflowMessage> > notificationMessages) { if (modifiedSystemPropertiesSet.Count == 0) { return; } foreach (var notificationAction in notifications) { Logger.Log("Processing notification action", message, tenant, LogLevel.Debug); if (!notificationAction.EventPropertyTypeId.HasValue) { continue; } int eventPropertyTypeId = notificationAction.EventPropertyTypeId.Value; // If system property provided is neither name or description if (eventPropertyTypeId != WorkflowConstants.PropertyTypeFakeIdName && eventPropertyTypeId != WorkflowConstants.PropertyTypeFakeIdDescription) { Logger.Log( $"The system property type ID {notificationAction.EventPropertyTypeId} is not supported. Only Name and Description are supported.", message, tenant, LogLevel.Debug); continue; } // If modified properties does not conatin event property type Id if (!modifiedSystemPropertiesSet.Contains(GetPropertyTypePredefined(notificationAction.EventPropertyTypeId.Value))) { continue; } if (notificationAction.ConditionalStateId.HasValue && (currentStateInfo == null || currentStateInfo.WorkflowStateId != notificationAction.ConditionalStateId.Value)) { // The conditional state id is present, but either the current state info is not present or the current state is not same as conditional state var currentStateId = currentStateInfo?.WorkflowStateId.ToString() ?? "none"; Logger.Log( $"Conditional state ID {notificationAction.ConditionalStateId.Value} does not match current state ID: {currentStateId}", message, tenant, LogLevel.Debug); continue; } var artifact = updatedArtifacts.First(a => a.Id == artifactId); string messageHeader = I18NHelper.FormatInvariant("You are being notified because artifact with Id: {0} has been updated.", artifactId); var artifactPartUrl = artifact.Url ?? ServerUriHelper.GetArtifactUrl(artifactId, true); var blueprintUrl = artifact.BaseUrl ?? ServerUriHelper.GetBaseHostUri()?.ToString(); var emails = await WorkflowEventsMessagesHelper.GetEmailValues(message.RevisionId, artifactId, notificationAction, repository.UsersRepository); var notificationMessage = new NotificationMessage { TransactionId = message.TransactionId, ArtifactName = workflowStates[artifactId].Name, ProjectName = projects.First(p => p.ItemId == artifact.ProjectId).Name, Subject = notificationAction.Subject, From = notificationAction.FromDisplayName, To = emails, Header = messageHeader, Message = notificationAction.Message, RevisionId = message.RevisionId, UserId = message.UserId, ArtifactTypeId = currentStateInfo.ItemTypeId, ArtifactId = artifactId, ArtifactUrl = artifactPartUrl, ArtifactTypePredefined = artifact.Predefined, ProjectId = artifact.ProjectId, BlueprintUrl = blueprintUrl }; if (notificationMessages.ContainsKey(artifactId)) { notificationMessages[artifactId].Add(notificationMessage); } else { notificationMessages.Add(artifactId, new List <IWorkflowMessage> { notificationMessage }); } } }
internal static async Task <bool> ProcessUpdatedArtifacts( TenantInformation tenant, ArtifactsPublishedMessage message, IArtifactsPublishedRepository repository, IServiceLogRepository serviceLogRepository, IActionsParser actionsParser, IWorkflowMessagingProcessor messageProcessor) { var allUpdatedArtifacts = message?.Artifacts?.Where(p => !p.IsFirstTimePublished).ToList(); if (allUpdatedArtifacts == null || allUpdatedArtifacts.Count <= 0) { Logger.Log("No updated artifacts found", message, tenant); return(false); } Logger.Log($"{allUpdatedArtifacts.Count} updated artifacts found", message, tenant); var updatedArtifacts = allUpdatedArtifacts.Where(a => a.ModifiedProperties?.Count > 0).ToList(); if (updatedArtifacts.Count == 0) { Logger.Log("No modified properties found for updated artifacts", message, tenant); return(false); } // Get artifacts which have modified properties list populated var allArtifactsModifiedProperties = updatedArtifacts.ToDictionary(k => k.Id, v => v.ModifiedProperties ?? new List <PublishedPropertyInformation>()); Logger.Log( $"{allArtifactsModifiedProperties.Count} artifacts found: {string.Join(", ", allArtifactsModifiedProperties.Select(k => k.Key))}", message, tenant, LogLevel.Debug); if (allArtifactsModifiedProperties.Count == 0) { return(false); } // Get property transitions for published artifact ids. var publishedArtifactIds = updatedArtifacts.Select(a => a.Id).ToHashSet(); var artifactPropertyEvents = await repository.GetWorkflowPropertyTransitionsForArtifactsAsync(message.UserId, message.RevisionId, (int)TransitionType.Property, publishedArtifactIds); // If no property transitions found, then call does not need to proceed Logger.Log($"{artifactPropertyEvents?.Count ?? 0} workflow property events found", message, tenant, LogLevel.Debug); if (artifactPropertyEvents == null || artifactPropertyEvents.Count == 0) { Logger.Log("No property change triggers found", message, tenant); return(false); } // Convert all property transitions to a dictionary with artifact id as key var activePropertyTransitions = new Dictionary <int, IList <SqlWorkflowEvent> >(); var publishedArtifactEvents = artifactPropertyEvents.Where(ape => publishedArtifactIds.Contains(ape.VersionItemId)); // key = artifact id, value = all events foreach (var artifactPropertyEvent in publishedArtifactEvents) { if (activePropertyTransitions.ContainsKey(artifactPropertyEvent.VersionItemId)) { activePropertyTransitions[artifactPropertyEvent.VersionItemId].Add(artifactPropertyEvent); } else { activePropertyTransitions.Add(artifactPropertyEvent.VersionItemId, new List <SqlWorkflowEvent> { artifactPropertyEvent }); } } var sqlWorkflowStates = await repository.GetWorkflowStatesForArtifactsAsync(message.UserId, activePropertyTransitions.Keys, message.RevisionId); var workflowStates = sqlWorkflowStates.Where(w => w.WorkflowStateId > 0).ToDictionary(k => k.ArtifactId); Logger.Log( $"{workflowStates.Count} workflow states found for artifacts: {string.Join(", ", workflowStates.Select(k => k.Key))}", message, tenant, LogLevel.Debug); if (workflowStates.Count == 0) { return(false); } // Get project names var projectIds = updatedArtifacts.Select(a => a.ProjectId).ToList(); var projects = await repository.GetProjectNameByIdsAsync(projectIds); Logger.Log($"{projects.Count} project names found for project IDs: {string.Join(", ", projectIds)}", message, tenant, LogLevel.Debug); var notificationMessages = new Dictionary <int, List <IWorkflowMessage> >(); // For artifacts in active property transitions foreach (var artifactId in activePropertyTransitions.Keys) { Logger.Log($"Processing artifact with ID: {artifactId}", message, tenant, LogLevel.Debug); var artifactTransitionInfo = activePropertyTransitions[artifactId]; var notifications = actionsParser.GetNotificationActions(artifactTransitionInfo).ToList(); Logger.Log($"{notifications.Count} Notification actions found", message, tenant, LogLevel.Debug); if (notifications.Count == 0) { continue; } List <PublishedPropertyInformation> artifactModifiedProperties; if (!allArtifactsModifiedProperties.TryGetValue(artifactId, out artifactModifiedProperties)) { Logger.Log($"modified properties not found for {artifactId}", message, tenant, LogLevel.Debug); continue; } Logger.Log($"{artifactModifiedProperties?.Count ?? 0} modified properties found", message, tenant, LogLevel.Debug); if (artifactModifiedProperties == null || !artifactModifiedProperties.Any()) { continue; } SqlWorkFlowStateInformation currentStateInfo; if (!workflowStates.TryGetValue(artifactId, out currentStateInfo)) { continue; } var modifiedSystemPropertiesSet = artifactModifiedProperties.Where(a => a.PredefinedType == (int)PropertyTypePredefined.Name || a.PredefinedType == (int)PropertyTypePredefined.Description) .Select(a => (PropertyTypePredefined)a.PredefinedType) .ToHashSet(); await ProcessSystemPropertyChange(tenant, message, repository, notifications, modifiedSystemPropertiesSet, currentStateInfo, updatedArtifacts, artifactId, workflowStates, projects, notificationMessages); var modifiedCustomPropertiesSet = artifactModifiedProperties.Where(a => a.PredefinedType == (int)PropertyTypePredefined.CustomGroup) .Select(a => a.TypeId) .ToHashSet(); // Process custom properties Logger.Log( $"{modifiedCustomPropertiesSet.Count} instance property type IDs being located: {string.Join(", ", modifiedCustomPropertiesSet)}", message, tenant, LogLevel.Debug); await ProcessCustomPropertyChange(tenant, message, repository, notifications, modifiedCustomPropertiesSet, currentStateInfo, updatedArtifacts, artifactId, workflowStates, projects, notificationMessages); } if (notificationMessages.Count == 0) { Logger.Log("None of the modified properties have property change notification triggers", message, tenant); return(false); } Logger.Log($"Sending property change notifications for artifacts: {string.Join(", ", notificationMessages.Select(kvp => kvp.Key))}", message, tenant); foreach (var notificationMessage in notificationMessages) { await WorkflowEventsMessagesHelper.ProcessMessages(LogSource, tenant, serviceLogRepository, notificationMessage.Value, $"Error on new artifact creation with Id: {notificationMessage.Key}", messageProcessor); } return(true); }