Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
4cddbc5
IALERT-3912: Create abstraction for Jira Cloud processing.
psantos1113 Nov 7, 2025
b1b7268
IALERT-3912: Create copy of converters to isolate fix for Jira Cloud.
psantos1113 Nov 7, 2025
a764725
IALERT-3912: Make the event message sender more generic.
psantos1113 Nov 7, 2025
2f7e759
IALERT-3912: Implement models for the document format.
psantos1113 Nov 10, 2025
15451b7
IALERT-3912: Implement changes to create the data in the format.
psantos1113 Nov 12, 2025
0be02d1
IALERT-3912: Update to send the document format to the Jira REST API.
psantos1113 Nov 12, 2025
76a1016
IALERT-3912: Remove unused classes.
psantos1113 Nov 12, 2025
a815f9b
IALERT-3912: fix long description and duplicate component versions.
psantos1113 Nov 12, 2025
2a9a12d
IALERT-3912: Fix formatting issues for the links.
psantos1113 Nov 13, 2025
33b8ab0
IALERT-3912: Fix formatting of new files.
psantos1113 Nov 13, 2025
39d20ce
IALERT-3912: Reformat the remaining files with the formatter.
psantos1113 Nov 13, 2025
0abc071
IALERT-3912: Fix the description overflow into comments.
psantos1113 Nov 13, 2025
28112f6
IALERT-3912: Add comments to a class.
psantos1113 Nov 13, 2025
530e997
IALERT-3912: Add bullet list formatting to the document builder.
psantos1113 Nov 14, 2025
3eee326
IALERT-3912: Implement review feedback.
psantos1113 Nov 14, 2025
a151577
IALERT-3912 - Add in missing copyright header
DanaMaxfield Nov 14, 2025
6dbe5f9
IALERT-3912 - Correct copyright
DanaMaxfield Nov 14, 2025
123f1c1
Merge pull request #2706 from blackducksoftware/dev/djm/IALERT-3912-a…
DanaMaxfield Nov 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api-channel-issue-tracker/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ ext.moduleName = 'com.blackduck.integration.alert.api-channel-issue-tracker'

dependencies {
api project(':api-channel')
api 'com.blackduck.integration:int-jira-common'

implementation platform(project(':alert-platform'))
implementation project(':alert-common')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public MessageResult distributeMessages(
)
throws AlertException {

IssueTrackerProcessor<T> processor = processorFactory.createProcessor(distributionDetails, jobExecutionId, notificationIds);
IssueTrackerMessageProcessor<T> processor = processorFactory.createProcessor(distributionDetails, jobExecutionId, notificationIds);
IssueTrackerResponse<T> issueTrackerResponse = processor.processMessages(messages, jobName);

return new MessageResult(issueTrackerResponse.getStatusMessage());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* blackduck-alert
*
* Copyright (c) 2025 Black Duck Software, Inc.
*
* Use subject to the terms and conditions of the Black Duck Software End User Software License and Maintenance Agreement. All rights reserved worldwide.
*/
package com.blackduck.integration.alert.api.channel.issue.tracker;

import java.io.Serializable;

import com.blackduck.integration.alert.api.channel.issue.tracker.model.IssueTrackerResponse;
import com.blackduck.integration.alert.api.common.model.exception.AlertException;
import com.blackduck.integration.alert.api.processor.extract.model.ProviderMessageHolder;

public interface IssueTrackerMessageProcessor<T extends Serializable> {

IssueTrackerResponse<T> processMessages(ProviderMessageHolder messages, String jobName) throws AlertException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,23 @@
import java.util.LinkedList;
import java.util.List;

import com.blackduck.integration.alert.api.channel.issue.tracker.send.IssueTrackerAsyncMessageSender;
import com.blackduck.integration.alert.api.channel.issue.tracker.model.IssueTrackerModelHolder;
import com.blackduck.integration.alert.api.channel.issue.tracker.model.IssueTrackerResponse;
import com.blackduck.integration.alert.api.channel.issue.tracker.send.IssueTrackerAsyncMessageSender;
import com.blackduck.integration.alert.api.common.model.exception.AlertException;
import com.blackduck.integration.alert.api.processor.extract.model.ProviderMessageHolder;
import com.blackduck.integration.alert.api.processor.extract.model.project.ProjectMessage;

public class IssueTrackerProcessor<T extends Serializable> {
public class IssueTrackerProcessor<T extends Serializable> implements IssueTrackerMessageProcessor<T> {
private final IssueTrackerModelExtractor<T> modelExtractor;
private final IssueTrackerAsyncMessageSender<T> messageSender;
private final IssueTrackerAsyncMessageSender<IssueTrackerModelHolder<T>> messageSender;

public IssueTrackerProcessor(IssueTrackerModelExtractor<T> modelExtractor, IssueTrackerAsyncMessageSender<T> messageSender) {
public IssueTrackerProcessor(IssueTrackerModelExtractor<T> modelExtractor, IssueTrackerAsyncMessageSender<IssueTrackerModelHolder<T>> messageSender) {
this.modelExtractor = modelExtractor;
this.messageSender = messageSender;
}

@Override
public final IssueTrackerResponse<T> processMessages(ProviderMessageHolder messages, String jobName) throws AlertException {
List<IssueTrackerModelHolder<T>> issueTrackerModels = new LinkedList<>();
IssueTrackerModelHolder<T> simpleMessageHolder = modelExtractor.extractSimpleMessageIssueModels(messages.getSimpleMessages(), jobName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@
import com.blackduck.integration.alert.common.persistence.model.job.details.DistributionJobDetailsModel;

public interface IssueTrackerProcessorFactory<D extends DistributionJobDetailsModel, T extends Serializable> {
IssueTrackerProcessor<T> createProcessor(D distributionDetails, UUID jobExecutionId, Set<Long> notificationIds) throws AlertException;
IssueTrackerMessageProcessor<T> createProcessor(D distributionDetails, UUID jobExecutionId, Set<Long> notificationIds) throws AlertException;

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,33 +15,33 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.blackduck.integration.alert.api.channel.issue.tracker.model.IssueBomComponentDetails;
import com.blackduck.integration.alert.api.channel.issue.tracker.model.IssueCreationModel;
import com.blackduck.integration.alert.api.channel.issue.tracker.model.IssueTrackerIssueResponseModel;
import com.blackduck.integration.alert.api.channel.issue.tracker.model.IssueTrackerModelHolder;
import com.blackduck.integration.alert.api.channel.issue.tracker.model.ProjectIssueModel;
import com.blackduck.integration.alert.api.channel.issue.tracker.search.ExistingIssueDetails;
import com.blackduck.integration.alert.api.channel.issue.tracker.search.enumeration.IssueCategory;
import com.blackduck.integration.alert.api.channel.issue.tracker.search.enumeration.IssueStatus;
import com.blackduck.integration.alert.api.channel.issue.tracker.send.IssueTrackerMessageSender;
import com.blackduck.integration.alert.api.channel.issue.tracker.send.IssueTrackerMessageSenderFactory;
import com.blackduck.integration.alert.api.channel.issue.tracker.model.IssueBomComponentDetails;
import com.blackduck.integration.alert.api.channel.issue.tracker.model.IssueCreationModel;
import com.blackduck.integration.alert.api.channel.issue.tracker.model.IssueTrackerIssueResponseModel;
import com.blackduck.integration.alert.api.channel.issue.tracker.model.ProjectIssueModel;
import com.blackduck.integration.alert.api.common.model.exception.AlertException;
import com.blackduck.integration.alert.api.descriptor.model.IssueTrackerChannelKey;
import com.blackduck.integration.alert.api.processor.extract.model.ProviderDetails;
import com.blackduck.integration.alert.common.channel.DistributionChannelTestAction;
import com.blackduck.integration.alert.common.channel.issuetracker.enumeration.IssueOperation;
import com.blackduck.integration.alert.common.exception.AlertFieldException;
import com.blackduck.integration.alert.common.message.model.LinkableItem;
import com.blackduck.integration.alert.common.message.model.MessageResult;
import com.blackduck.integration.alert.common.persistence.model.job.DistributionJobModel;
import com.blackduck.integration.alert.common.persistence.model.job.details.DistributionJobDetailsModel;
import com.blackduck.integration.alert.api.descriptor.model.IssueTrackerChannelKey;
import com.blackduck.integration.alert.api.processor.extract.model.ProviderDetails;

public abstract class IssueTrackerTestAction<D extends DistributionJobDetailsModel, T extends Serializable> extends DistributionChannelTestAction {
private final Logger logger = LoggerFactory.getLogger(getClass());

private final IssueTrackerMessageSenderFactory<D, T> messageSenderFactory;
private final IssueTrackerMessageSenderFactory<D, T, IssueTrackerModelHolder<T>> messageSenderFactory;

protected IssueTrackerTestAction(IssueTrackerChannelKey issueTrackerChannelKey, IssueTrackerMessageSenderFactory<D, T> messageSenderFactory) {
protected IssueTrackerTestAction(IssueTrackerChannelKey issueTrackerChannelKey, IssueTrackerMessageSenderFactory<D, T, IssueTrackerModelHolder<T>> messageSenderFactory) {
super(issueTrackerChannelKey);
this.messageSenderFactory = messageSenderFactory;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,37 @@

import com.blackduck.integration.alert.api.channel.issue.tracker.search.ExistingIssueDetails;
import com.blackduck.integration.alert.api.common.model.AlertSerializableModel;
import com.blackduck.integration.jira.common.cloud.model.AtlassianDocumentFormatModel;

public class IssueCommentModel<T extends Serializable> extends AlertSerializableModel {
private final ExistingIssueDetails<T> existingIssueDetails;
private final List<String> comments;
@Nullable
private final ProjectIssueModel source;

// Only used for Jira Cloud channel.
// the reason these Jira Cloud specific fields are here is because of the layers of abstraction prevent having a model specific for Jira cloud.
@Nullable
private final AtlassianDocumentFormatModel atlassianDocumentFormatCommentModel;
@Nullable
private final List<AtlassianDocumentFormatModel> additionalComments;

public IssueCommentModel(ExistingIssueDetails<T> existingIssueDetails, List<String> comments, @Nullable ProjectIssueModel source) {
this(existingIssueDetails, comments, source, null, null);
}

public IssueCommentModel(
ExistingIssueDetails<T> existingIssueDetails,
List<String> comments,
@Nullable ProjectIssueModel source,
@Nullable AtlassianDocumentFormatModel atlassianDocumentFormatCommentModel,
@Nullable List<AtlassianDocumentFormatModel> additionalComments
) {
this.existingIssueDetails = existingIssueDetails;
this.comments = comments;
this.source = source;
this.atlassianDocumentFormatCommentModel = atlassianDocumentFormatCommentModel;
this.additionalComments = additionalComments;
}

public ExistingIssueDetails<T> getExistingIssueDetails() {
Expand All @@ -40,4 +60,12 @@ public Optional<ProjectIssueModel> getSource() {
return Optional.ofNullable(source);
}

public Optional<AtlassianDocumentFormatModel> getAtlassianDocumentFormatCommentModel() {
return Optional.ofNullable(atlassianDocumentFormatCommentModel);
}

public Optional<List<AtlassianDocumentFormatModel>> getAdditionalComments() {
return Optional.ofNullable(additionalComments);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import com.blackduck.integration.alert.api.common.model.AlertSerializableModel;
import com.blackduck.integration.alert.common.message.model.LinkableItem;
import com.blackduck.integration.jira.common.cloud.model.AtlassianDocumentFormatModel;

public class IssueCreationModel extends AlertSerializableModel {
private static final long serialVersionUID = -3568919050386494416L;
Expand All @@ -24,29 +25,67 @@ public class IssueCreationModel extends AlertSerializableModel {

private final LinkableItem provider;
private final ProjectIssueModel source;
// Only used for Jira Cloud channel.
// the reason these Jira Cloud specific fields are here is because of the layers of abstraction prevent having a model specific for Jira cloud.
private final AtlassianDocumentFormatModel atlassianDocumentFormatDescriptionModel;
private final List<AtlassianDocumentFormatModel> atlassianDocumentFormatCommentModel;

public static IssueCreationModel simple(String title, String description, List<String> postCreateComments, LinkableItem provider) {
return new IssueCreationModel(title, description, postCreateComments, provider, null, null);
return new IssueCreationModel(title, description, postCreateComments, provider, null, null, null, null);
}

public static IssueCreationModel simple(
String title,
LinkableItem provider,
AtlassianDocumentFormatModel atlassianDocumentFormatDescriptionModel,
List<AtlassianDocumentFormatModel> atlassianDocumentFormatCommentModel
) {
return new IssueCreationModel(title, "", List.of(), provider, null, null, atlassianDocumentFormatDescriptionModel, atlassianDocumentFormatCommentModel);
}

public static IssueCreationModel project(String title, String description, List<String> postCreateComments, ProjectIssueModel source, @Nullable String queryString) {
return new IssueCreationModel(title, description, postCreateComments, source.getProvider(), source, queryString);
return new IssueCreationModel(title, description, postCreateComments, source.getProvider(), source, queryString, null, null);
}

public static IssueCreationModel project(
String title,
String description,
List<String> postCreateComments,
ProjectIssueModel source,
AtlassianDocumentFormatModel atlassianDocumentFormatDescriptionModel,
List<AtlassianDocumentFormatModel> atlassianDocumentFormatCommentModel,
@Nullable String queryString
) {
return new IssueCreationModel(
title,
description,
postCreateComments,
source.getProvider(),
source,
queryString,
atlassianDocumentFormatDescriptionModel,
atlassianDocumentFormatCommentModel
);
}

private IssueCreationModel(
protected IssueCreationModel(
String title,
String description,
List<String> postCreateComments,
LinkableItem provider,
@Nullable ProjectIssueModel source,
@Nullable String queryString
@Nullable String queryString,
@Nullable AtlassianDocumentFormatModel atlassianDocumentFormatDescriptionModel,
@Nullable List<AtlassianDocumentFormatModel> atlassianDocumentFormatCommentModel
) {
this.title = title;
this.description = description;
this.postCreateComments = postCreateComments;
this.provider = provider;
this.source = source;
this.queryString = queryString;
this.atlassianDocumentFormatDescriptionModel = atlassianDocumentFormatDescriptionModel;
this.atlassianDocumentFormatCommentModel = atlassianDocumentFormatCommentModel;
}

public Optional<String> getQueryString() {
Expand All @@ -73,4 +112,11 @@ public Optional<ProjectIssueModel> getSource() {
return Optional.ofNullable(source);
}

public Optional<AtlassianDocumentFormatModel> getAtlassianDocumentFormatDescriptionModel() {
return Optional.ofNullable(atlassianDocumentFormatDescriptionModel);
}

public Optional<List<AtlassianDocumentFormatModel>> getAtlassianDocumentFormatCommentModel() {
return Optional.ofNullable(atlassianDocumentFormatCommentModel);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* blackduck-alert
*
* Copyright (c) 2025 Black Duck Software, Inc.
*
* Use subject to the terms and conditions of the Black Duck Software End User Software License and Maintenance Agreement. All rights reserved worldwide.
*/
package com.blackduck.integration.alert.api.channel.issue.tracker.model;

import java.util.List;

import com.blackduck.integration.alert.api.event.AlertEvent;

public class IssueTrackerEventModel {
private final List<AlertEvent> creationEvents;
private final List<AlertEvent> commentEvents;
private final List<AlertEvent> transitionEvents;

public IssueTrackerEventModel(List<AlertEvent> creationEvents, List<AlertEvent> commentEvents, List<AlertEvent> transitionEvents) {
this.creationEvents = creationEvents;
this.commentEvents = commentEvents;
this.transitionEvents = transitionEvents;
}

public List<AlertEvent> getIssueCreationEvents() {
if (creationEvents == null) {
return List.of();
}
return creationEvents;
}

public List<AlertEvent> getIssueCommentEvents() {
if (commentEvents == null) {
return List.of();
}
return commentEvents;
}

public List<AlertEvent> getIssueTransitionEvents() {
if (transitionEvents == null) {
return List.of();
}
return transitionEvents;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@

import org.apache.commons.collections4.ListUtils;

public class IssueTrackerModelHolder<T extends Serializable> {
public class IssueTrackerModelHolder<T extends Serializable> implements Serializable {

private final List<IssueCreationModel> issueCreationModels;
private final List<IssueTransitionModel<T>> issueTransitionModels;
private final List<IssueCommentModel<T>> issueCommentModels;
Expand All @@ -24,7 +25,11 @@ public static <T extends Serializable> IssueTrackerModelHolder<T> reduce(IssueTr
return new IssueTrackerModelHolder<>(unifiedIssueCreationModels, unifiedIssueTransitionModels, unifiedIssueCommentModels);
}

public IssueTrackerModelHolder(List<IssueCreationModel> issueCreationModels, List<IssueTransitionModel<T>> issueTransitionModels, List<IssueCommentModel<T>> issueCommentModels) {
public IssueTrackerModelHolder(
List<IssueCreationModel> issueCreationModels,
List<IssueTransitionModel<T>> issueTransitionModels,
List<IssueCommentModel<T>> issueCommentModels
) {
this.issueCreationModels = issueCreationModels;
this.issueTransitionModels = issueTransitionModels;
this.issueCommentModels = issueCommentModels;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* blackduck-alert
*
* Copyright (c) 2025 Black Duck Software, Inc.
*
* Use subject to the terms and conditions of the Black Duck Software End User Software License and Maintenance Agreement. All rights reserved worldwide.
*/
package com.blackduck.integration.alert.api.channel.issue.tracker.send;

import java.util.List;

public interface AsyncMessageSender<T> {
void sendAsyncMessages(List<T> issueTrackerMessages);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* blackduck-alert
*
* Copyright (c) 2025 Black Duck Software, Inc.
*
* Use subject to the terms and conditions of the Black Duck Software End User Software License and Maintenance Agreement. All rights reserved worldwide.
*/
package com.blackduck.integration.alert.api.channel.issue.tracker.send;

import java.io.Serializable;
import java.util.List;
import java.util.function.Function;

import com.blackduck.integration.alert.api.channel.issue.tracker.model.IssueTrackerEventModel;
import com.blackduck.integration.alert.api.channel.issue.tracker.model.IssueTrackerModelHolder;
import com.blackduck.integration.alert.api.event.AlertEvent;

public class DefaultIssueTrackerEventGenerator<T extends Serializable> implements IssueTrackerEventGenerator<IssueTrackerModelHolder<T>> {

private final IssueTrackerCreationEventGenerator issueCreateEventGenerator;
private final IssueTrackerTransitionEventGenerator<T> issueTrackerTransitionEventGenerator;
private final IssueTrackerCommentEventGenerator<T> issueTrackerCommentEventGenerator;

public DefaultIssueTrackerEventGenerator(
IssueTrackerCreationEventGenerator issueCreateEventGenerator,
IssueTrackerTransitionEventGenerator<T> issueTrackerTransitionEventGenerator,
IssueTrackerCommentEventGenerator<T> issueTrackerCommentEventGenerator
) {
this.issueCreateEventGenerator = issueCreateEventGenerator;
this.issueTrackerTransitionEventGenerator = issueTrackerTransitionEventGenerator;
this.issueTrackerCommentEventGenerator = issueTrackerCommentEventGenerator;
}

@Override
public IssueTrackerEventModel generateEvents(IssueTrackerModelHolder<T> model) {
List<AlertEvent> creationEvents = createMessages(model.getIssueCreationModels(), issueCreateEventGenerator::generateEvent);
List<AlertEvent> transitionEvents = createMessages(model.getIssueTransitionModels(), issueTrackerTransitionEventGenerator::generateEvent);
List<AlertEvent> commentEvents = createMessages(model.getIssueCommentModels(), issueTrackerCommentEventGenerator::generateEvent);
return new IssueTrackerEventModel(creationEvents, transitionEvents, commentEvents);
}

private <U> List<AlertEvent> createMessages(List<U> messages, Function<U, AlertEvent> eventGenerator) {
return messages.stream()
.map(eventGenerator)
.toList();
}
}
Loading