Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>3.16.1</quarkus.platform.version>
<quarkus.platform.version>3.16.2</quarkus.platform.version>
<version.commons-csv>1.12.0</version.commons-csv>
<version.assertj>3.26.0</version.assertj>
<skipITs>true</skipITs>
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/org/hibernate/infra/replicate/jira/JiraConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.Base64;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import io.smallrye.config.ConfigMapping;
import io.smallrye.config.WithDefault;
Expand Down Expand Up @@ -293,6 +294,7 @@ interface IssueTypeValueMapping extends ValueMapping {
* epic-short-label in the upstream (source) Jira instance.
*/
Optional<String> epicLinkSourceLabelCustomFieldName();

/**
* @return The name of a custom field that represents the "epic name" i.e.
* epic-short-label in the downstream (destination) Jira instance.
Expand All @@ -312,6 +314,23 @@ interface StatusesValueMapping extends ValueMapping {
* closing the issue deleted upstream before archiving it.
*/
Optional<String> deletedTransition();

/**
* @return A map where the {@code key} is the upstream status name, and the
* corresponding {@code value} contains the names of the downstream
* status names. If the downstream issue is currently in one of the
* statuses present in the value set then the transition for status
* ({@code key}) should not be applied.
* <p>
* This allows both skipping unnecessary transitions
* {@code "status a" -> "status a" } as well as to keep the downstream
* issue in the status modified downstream if it is within the "group"
* of statuses "mapped" to the upstream one. This can be useful when
* e.g. downstream workflow has both {@code New} and {@code Ready}
* statuses, and users move new issues to the {@code Ready} status
* issues downstream, without an override from an upstream updates.
*/
Map<String, Set<String>> ignoreTransitionCondition();
}

interface UserValueMapping extends ValueMapping {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;

import org.hibernate.infra.replicate.jira.JiraConfig;
Expand All @@ -28,12 +30,27 @@ public JiraIssueAbstractEventHandler(ReportingConfig reportingConfig, HandlerPro
}

protected void applyTransition(JiraIssue sourceIssue, String destinationKey) {
JiraIssue destIssue = context.destinationJiraClient().getIssue(destinationKey);
applyTransition(sourceIssue, destIssue, destinationKey);
}

protected void applyTransition(JiraIssue sourceIssue, JiraIssue destIssue, String destinationKey) {
Set<String> statusesToIgnore = context.projectGroup().statuses().ignoreTransitionCondition()
.getOrDefault(sourceIssue.fields.status.name.toLowerCase(Locale.ROOT).replace(' ', '-'), Set.of());
if (statusesToIgnore.contains(destIssue.fields.status.name.toLowerCase(Locale.ROOT))) {
// no need to apply the transition :)
return;
}
prepareTransition(sourceIssue).ifPresent(
jiraTransition -> context.destinationJiraClient().transition(destinationKey, jiraTransition));
}

protected void updateIssueBody(JiraIssue sourceIssue, String destinationKey) {
JiraIssue destIssue = context.destinationJiraClient().getIssue(destinationKey);
updateIssueBody(sourceIssue, destIssue, destinationKey);
}

protected void updateIssueBody(JiraIssue sourceIssue, JiraIssue destIssue, String destinationKey) {
JiraIssue issue = issueToCreate(sourceIssue, destIssue);

updateIssue(destinationKey, issue, sourceIssue, context.notMappedAssignee());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,16 @@ protected void doRun() {
context.createNextPlaceholderBatch(destinationKey);

try {
updateIssueBody(sourceIssue, destinationKey);
JiraIssue destIssue = context.destinationJiraClient().getIssue(destinationKey);

updateIssueBody(sourceIssue, destIssue, destinationKey);
// remote "aka web" links cannot be added in the same request and are also not
// returned as part of the issue API.
// We "upsert" the remote link pointing to the "original/source" issue that
// triggered the sync with an additional call:
context.destinationJiraClient().upsertRemoteLink(destinationKey, remoteSelfLink(sourceIssue));
// issue status can be updated only through transition:
applyTransition(sourceIssue, destinationKey);
applyTransition(sourceIssue, destIssue, destinationKey);
// and then we want to add a link to a parent, if the issue was actually a
// sub-task which we've created as a task:
prepareParentLink(destinationKey, sourceIssue).ifPresent(context.destinationJiraClient()::upsertIssueLink);
Expand Down
Loading