diff --git a/pom.xml b/pom.xml
index 30790b1..1484333 100644
--- a/pom.xml
+++ b/pom.xml
@@ -14,7 +14,7 @@
UTF-8
quarkus-bom
io.quarkus.platform
- 3.16.2
+ 3.16.3
1.12.0
3.26.0
true
diff --git a/src/main/java/org/hibernate/infra/replicate/jira/service/jira/HandlerProjectContext.java b/src/main/java/org/hibernate/infra/replicate/jira/service/jira/HandlerProjectContext.java
index 5dbf5f6..9fb346a 100644
--- a/src/main/java/org/hibernate/infra/replicate/jira/service/jira/HandlerProjectContext.java
+++ b/src/main/java/org/hibernate/infra/replicate/jira/service/jira/HandlerProjectContext.java
@@ -2,10 +2,14 @@
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
+import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.Supplier;
import java.util.regex.Pattern;
import org.hibernate.infra.replicate.jira.JiraConfig;
@@ -16,6 +20,7 @@
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraIssueBulkResponse;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraIssues;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraUser;
+import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraVersion;
import io.quarkus.logging.Log;
@@ -26,6 +31,7 @@ public final class HandlerProjectContext implements AutoCloseable {
private static final int ISSUES_PER_REQUEST = 25;
private static final String SYNC_ISSUE_PLACEHOLDER_SUMMARY = "Sync issue placeholder";
private final ReentrantLock lock = new ReentrantLock();
+ private final ReentrantLock versionLock = new ReentrantLock();
private final String projectName;
private final String projectGroupName;
@@ -43,6 +49,8 @@ public final class HandlerProjectContext implements AutoCloseable {
private final Pattern sourceLabelPattern;
private final DateTimeFormatter formatter;
+ private final Map destFixVersions;
+
public HandlerProjectContext(String projectName, String projectGroupName, JiraRestClient sourceJiraClient,
JiraRestClient destinationJiraClient, HandlerProjectGroupContext projectGroupContext,
Map allProjectsContextMap) {
@@ -63,6 +71,9 @@ public HandlerProjectContext(String projectName, String projectGroupName, JiraRe
this.sourceLabelPattern = Pattern
.compile(projectGroupContext.projectGroup().formatting().labelTemplate().formatted(".+"));
this.formatter = DateTimeFormatter.ofPattern(projectGroupContext.projectGroup().formatting().timestampFormat());
+
+ this.destFixVersions = getAndCreateMissingCurrentFixVersions(project, projectGroupContext, sourceJiraClient,
+ destinationJiraClient);
}
public JiraConfig.JiraProject project() {
@@ -233,4 +244,107 @@ public boolean isSourceLabel(String label) {
public String formatTimestamp(ZonedDateTime time) {
return time != null ? time.format(formatter) : "";
}
+
+ public JiraVersion fixVersion(JiraVersion version) {
+ return fixVersion(version, false);
+ }
+
+ public JiraVersion fixVersion(JiraVersion version, boolean force) {
+ if (!force) {
+ JiraVersion v = destFixVersions.get(version.name);
+ if (v != null) {
+ return v;
+ }
+ }
+ versionLock.lock();
+ try {
+ if (force) {
+ return destFixVersions.compute(version.name, (name, current) -> upsert(project, projectGroupContext,
+ destinationJiraClient, version, List.of()));
+ } else {
+ return destFixVersions.computeIfAbsent(version.name,
+ name -> upsert(project, projectGroupContext, destinationJiraClient, version, List.of()));
+ }
+ } catch (Exception e) {
+ Log.errorf(e,
+ "Couldn't create a copy of the fix version %s, version will not be synced for a particular Jira ticket.",
+ version.name);
+ return null;
+ } finally {
+ versionLock.unlock();
+ }
+ }
+
+ public void refreshFixVersions() {
+ versionLock.lock();
+ try {
+ destFixVersions.clear();
+ destFixVersions.putAll(getAndCreateMissingCurrentFixVersions(project, projectGroupContext, sourceJiraClient,
+ destinationJiraClient));
+ } finally {
+ versionLock.unlock();
+ }
+ }
+
+ private static Map getAndCreateMissingCurrentFixVersions(JiraConfig.JiraProject project,
+ HandlerProjectGroupContext projectGroupContext, JiraRestClient sourceJiraClient,
+ JiraRestClient destinationJiraClient) {
+ Map result = new HashMap<>();
+
+ try {
+ List upstreamVersions = sourceJiraClient.versions(project.originalProjectKey());
+ List downstreamVersions = destinationJiraClient.versions(project.projectKey());
+
+ for (JiraVersion upstreamVersion : upstreamVersions) {
+ JiraVersion downstreamVersion = upsert(project, projectGroupContext, destinationJiraClient,
+ upstreamVersion, downstreamVersions);
+ result.put(upstreamVersion.name, downstreamVersion);
+ }
+ } catch (Exception e) {
+ Log.errorf(e, "Encountered a problem while building the fix version map for %s: %s", project.projectKey(),
+ e.getMessage());
+ }
+
+ return result;
+ }
+
+ private static JiraVersion upsert(JiraConfig.JiraProject project, HandlerProjectGroupContext projectGroupContext,
+ JiraRestClient jiraRestClient, JiraVersion upstreamVersion, List downstreamVersions) {
+ Optional version = JiraVersion.findVersion(upstreamVersion.id, downstreamVersions);
+ JiraVersion downstreamVersion = null;
+ if (version.isEmpty()) {
+ Log.infof("Creating a new fix version for project %s: %s", project.projectKey(), upstreamVersion.name);
+ downstreamVersion = processJiraVersion(project, projectGroupContext, upstreamVersion,
+ () -> jiraRestClient.create(upstreamVersion.copyForProject(project)));
+ } else if (versionNeedsUpdate(upstreamVersion, version.get())) {
+ Log.infof("Updating a fix version for project %s: %s", project.projectKey(), upstreamVersion.name);
+ downstreamVersion = processJiraVersion(project, projectGroupContext, upstreamVersion,
+ () -> jiraRestClient.update(version.get().id, upstreamVersion.copyForProject(project)));
+ } else {
+ downstreamVersion = version.get();
+ }
+ return downstreamVersion;
+ }
+
+ private static JiraVersion processJiraVersion(JiraConfig.JiraProject project,
+ HandlerProjectGroupContext projectGroupContext, JiraVersion upstreamVersion, Supplier action) {
+ try {
+ projectGroupContext.startProcessingEvent();
+ return action.get();
+ } catch (InterruptedException e) {
+ Log.error("Interrupted while trying to process fix version", e);
+ Thread.currentThread().interrupt();
+ } catch (Exception e) {
+ Log.errorf(e, "Ignoring fix version sync. Unable to process fix %s version for project %s: %s",
+ upstreamVersion.name, project.projectKey(), e.getMessage());
+ }
+ return null;
+ }
+
+ private static boolean versionNeedsUpdate(JiraVersion upstreamVersion, JiraVersion downstreamVersion) {
+ return !Objects.equals(upstreamVersion.name, downstreamVersion.name)
+ || !Objects.equals(upstreamVersion.prepareVersionDescriptionForCopy(), downstreamVersion.description)
+ || upstreamVersion.released != downstreamVersion.released
+ || !Objects.equals(upstreamVersion.releaseDate, downstreamVersion.releaseDate);
+ }
}
diff --git a/src/main/java/org/hibernate/infra/replicate/jira/service/jira/JiraService.java b/src/main/java/org/hibernate/infra/replicate/jira/service/jira/JiraService.java
index 2a0c92a..bd586cf 100644
--- a/src/main/java/org/hibernate/infra/replicate/jira/service/jira/JiraService.java
+++ b/src/main/java/org/hibernate/infra/replicate/jira/service/jira/JiraService.java
@@ -256,6 +256,18 @@ public void registerManagementRoutes(@Observes ManagementInterface mi) {
triggerCommentSyncEvents(project, null, comments);
rc.end();
});
+ mi.router().get("/sync/fix-versions/:project").blockingHandler(rc -> {
+ String project = rc.pathParam("project");
+
+ HandlerProjectContext context = contextPerProject.get(project);
+
+ if (context == null) {
+ throw new IllegalArgumentException("Unknown project '%s'".formatted(project));
+ }
+
+ context.submitTask(context::refreshFixVersions);
+ rc.end();
+ });
}
/**
diff --git a/src/main/java/org/hibernate/infra/replicate/jira/service/jira/client/JiraRestClient.java b/src/main/java/org/hibernate/infra/replicate/jira/service/jira/client/JiraRestClient.java
index 336daae..348ab6c 100644
--- a/src/main/java/org/hibernate/infra/replicate/jira/service/jira/client/JiraRestClient.java
+++ b/src/main/java/org/hibernate/infra/replicate/jira/service/jira/client/JiraRestClient.java
@@ -16,6 +16,7 @@
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraSimpleObject;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraTransition;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraUser;
+import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraVersion;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -148,6 +149,22 @@ JiraIssues find(@QueryParam("jql") String query, @QueryParam("startAt") int star
@Path("/issue/{issueKey}/archive")
void archive(@PathParam("issueKey") String issueKey);
+ @GET
+ @Path("/version/{id}")
+ JiraVersion version(@PathParam("id") Long id);
+
+ @GET
+ @Path("/project/{projectKey}/versions")
+ List versions(@PathParam("projectKey") String projectKey);
+
+ @POST
+ @Path("/version")
+ JiraVersion create(JiraVersion version);
+
+ @PUT
+ @Path("/version/{id}")
+ JiraVersion update(@PathParam("id") String id, JiraVersion version);
+
@ClientObjectMapper
static ObjectMapper objectMapper(ObjectMapper defaultObjectMapper) {
return defaultObjectMapper.copy().setDefaultPropertyInclusion(JsonInclude.Include.NON_EMPTY);
diff --git a/src/main/java/org/hibernate/infra/replicate/jira/service/jira/client/JiraRestClientBuilder.java b/src/main/java/org/hibernate/infra/replicate/jira/service/jira/client/JiraRestClientBuilder.java
index a6d0145..1fd871d 100644
--- a/src/main/java/org/hibernate/infra/replicate/jira/service/jira/client/JiraRestClientBuilder.java
+++ b/src/main/java/org/hibernate/infra/replicate/jira/service/jira/client/JiraRestClientBuilder.java
@@ -22,6 +22,7 @@
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraSimpleObject;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraTransition;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraUser;
+import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraVersion;
import org.jboss.resteasy.reactive.RestResponse;
import org.jboss.resteasy.reactive.client.api.ClientLogger;
@@ -254,6 +255,26 @@ public void archive(String issueKey) {
withRetry(() -> delegate.archive(issueKey));
}
+ @Override
+ public JiraVersion version(Long id) {
+ return withRetry(() -> delegate.version(id));
+ }
+
+ @Override
+ public List versions(String projectKey) {
+ return withRetry(() -> delegate.versions(projectKey));
+ }
+
+ @Override
+ public JiraVersion create(JiraVersion version) {
+ return withRetry(() -> delegate.create(version));
+ }
+
+ @Override
+ public JiraVersion update(String id, JiraVersion version) {
+ return withRetry(() -> delegate.update(id, version));
+ }
+
private static final int RETRIES = 5;
private static final Duration WAIT_BETWEEN_RETRIES = Duration.of(2, ChronoUnit.SECONDS);
diff --git a/src/main/java/org/hibernate/infra/replicate/jira/service/jira/handler/JiraIssueAbstractEventHandler.java b/src/main/java/org/hibernate/infra/replicate/jira/service/jira/handler/JiraIssueAbstractEventHandler.java
index 4ee0069..0f21ea7 100644
--- a/src/main/java/org/hibernate/infra/replicate/jira/service/jira/handler/JiraIssueAbstractEventHandler.java
+++ b/src/main/java/org/hibernate/infra/replicate/jira/service/jira/handler/JiraIssueAbstractEventHandler.java
@@ -19,6 +19,7 @@
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraSimpleObject;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraTransition;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraUser;
+import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraVersion;
import org.hibernate.infra.replicate.jira.service.reporting.ReportingConfig;
abstract class JiraIssueAbstractEventHandler extends JiraEventHandler {
@@ -163,6 +164,16 @@ protected JiraIssue issueToCreate(JiraIssue sourceIssue, JiraIssue downstreamIss
.put(epicLinkDestinationLabelCustomFieldName, sourceEpicLabel));
}
+ if (sourceIssue.fields.fixVersions != null) {
+ destinationIssue.fields.fixVersions = new ArrayList<>();
+ for (JiraVersion version : sourceIssue.fields.fixVersions) {
+ JiraVersion downstream = context.fixVersion(version);
+ if (downstream != null) {
+ destinationIssue.fields.fixVersions.add(downstream);
+ }
+ }
+ }
+
return destinationIssue;
}
@@ -175,7 +186,7 @@ private List prepareLabels(JiraIssue sourceIssue, JiraIssue downstreamIs
// let's also add fix versions to the labels
if (sourceIssue.fields.fixVersions != null) {
- for (JiraSimpleObject fixVersion : sourceIssue.fields.fixVersions) {
+ for (JiraVersion fixVersion : sourceIssue.fields.fixVersions) {
String fixVersionLabel = "Fix version:%s".formatted(fixVersion.name).replace(' ', '_');
labelsToSet.add(fixVersionLabel);
}
diff --git a/src/main/java/org/hibernate/infra/replicate/jira/service/jira/handler/JiraVersionUpsertEventHandler.java b/src/main/java/org/hibernate/infra/replicate/jira/service/jira/handler/JiraVersionUpsertEventHandler.java
new file mode 100644
index 0000000..4a4889c
--- /dev/null
+++ b/src/main/java/org/hibernate/infra/replicate/jira/service/jira/handler/JiraVersionUpsertEventHandler.java
@@ -0,0 +1,23 @@
+package org.hibernate.infra.replicate.jira.service.jira.handler;
+
+import org.hibernate.infra.replicate.jira.service.jira.HandlerProjectContext;
+import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraVersion;
+import org.hibernate.infra.replicate.jira.service.reporting.ReportingConfig;
+
+public class JiraVersionUpsertEventHandler extends JiraEventHandler {
+
+ public JiraVersionUpsertEventHandler(ReportingConfig reportingConfig, HandlerProjectContext context, Long id) {
+ super(reportingConfig, context, id);
+ }
+
+ @Override
+ protected void doRun() {
+ JiraVersion version = context.sourceJiraClient().version(objectId);
+ context.fixVersion(version, true);
+ }
+
+ @Override
+ public String toString() {
+ return "JiraVersionUpsertEventHandler{" + "objectId=" + objectId + '}';
+ }
+}
diff --git a/src/main/java/org/hibernate/infra/replicate/jira/service/jira/model/hook/JiraWebHookEvent.java b/src/main/java/org/hibernate/infra/replicate/jira/service/jira/model/hook/JiraWebHookEvent.java
index 86df3e6..5df55a7 100644
--- a/src/main/java/org/hibernate/infra/replicate/jira/service/jira/model/hook/JiraWebHookEvent.java
+++ b/src/main/java/org/hibernate/infra/replicate/jira/service/jira/model/hook/JiraWebHookEvent.java
@@ -19,6 +19,7 @@ public class JiraWebHookEvent extends JiraBaseObject {
public JiraWebHookObject comment;
public JiraWebHookIssue issue;
public JiraWebHookIssueLink issueLink;
+ public JiraWebHookObject version;
public Optional eventType() {
return JiraWebhookEventType.of(webhookEvent);
@@ -27,6 +28,6 @@ public Optional eventType() {
@Override
public String toString() {
return "JiraWebHookEvent{" + "webhookEvent='" + webhookEvent + '\'' + ", comment=" + comment + ", issue="
- + issue + ", otherProperties=" + properties() + '}';
+ + issue + ", otherProperties=" + properties() + ", version=" + version + '}';
}
}
diff --git a/src/main/java/org/hibernate/infra/replicate/jira/service/jira/model/hook/JiraWebhookEventType.java b/src/main/java/org/hibernate/infra/replicate/jira/service/jira/model/hook/JiraWebhookEventType.java
index e44aeda..0d7182a 100644
--- a/src/main/java/org/hibernate/infra/replicate/jira/service/jira/model/hook/JiraWebhookEventType.java
+++ b/src/main/java/org/hibernate/infra/replicate/jira/service/jira/model/hook/JiraWebhookEventType.java
@@ -12,6 +12,7 @@
import org.hibernate.infra.replicate.jira.service.jira.handler.JiraIssueLinkDeleteEventHandler;
import org.hibernate.infra.replicate.jira.service.jira.handler.JiraIssueLinkUpsertEventHandler;
import org.hibernate.infra.replicate.jira.service.jira.handler.JiraIssueUpsertEventHandler;
+import org.hibernate.infra.replicate.jira.service.jira.handler.JiraVersionUpsertEventHandler;
import org.hibernate.infra.replicate.jira.service.reporting.ReportingConfig;
public enum JiraWebhookEventType {
@@ -119,6 +120,28 @@ public Collection handlers(ReportingConfig reportingConfig, JiraWebHoo
return List
.of(new JiraCommentDeleteEventHandler(reportingConfig, context, event.comment.id, event.issue.id));
}
+ },
+ VERSION_CREATED("jira:version_created") {
+ @Override
+ public Collection handlers(ReportingConfig reportingConfig, JiraWebHookEvent event,
+ HandlerProjectContext context) {
+ if (event.version == null || event.version.id == null) {
+ throw new IllegalStateException(
+ "Trying to handle a version event but version id is null: %s".formatted(event));
+ }
+ return List.of(new JiraVersionUpsertEventHandler(reportingConfig, context, event.version.id));
+ }
+ },
+ VERSION_UPDATED("jira:version_updated") {
+ @Override
+ public Collection handlers(ReportingConfig reportingConfig, JiraWebHookEvent event,
+ HandlerProjectContext context) {
+ if (event.version == null || event.version.id == null) {
+ throw new IllegalStateException(
+ "Trying to handle a version event but version id is null: %s".formatted(event));
+ }
+ return List.of(new JiraVersionUpsertEventHandler(reportingConfig, context, event.version.id));
+ }
};
private final String name;
diff --git a/src/main/java/org/hibernate/infra/replicate/jira/service/jira/model/rest/JiraFields.java b/src/main/java/org/hibernate/infra/replicate/jira/service/jira/model/rest/JiraFields.java
index 3e367d2..53e77c5 100644
--- a/src/main/java/org/hibernate/infra/replicate/jira/service/jira/model/rest/JiraFields.java
+++ b/src/main/java/org/hibernate/infra/replicate/jira/service/jira/model/rest/JiraFields.java
@@ -17,7 +17,7 @@ public class JiraFields extends JiraBaseObject {
public JiraUser assignee;
public JiraUser reporter;
- public List fixVersions;
+ public List fixVersions;
// NOTE: this one is for "read-only" purposes, to create links a different API
// has to be used
public List issuelinks;
diff --git a/src/main/java/org/hibernate/infra/replicate/jira/service/jira/model/rest/JiraVersion.java b/src/main/java/org/hibernate/infra/replicate/jira/service/jira/model/rest/JiraVersion.java
new file mode 100644
index 0000000..1b0d71c
--- /dev/null
+++ b/src/main/java/org/hibernate/infra/replicate/jira/service/jira/model/rest/JiraVersion.java
@@ -0,0 +1,71 @@
+package org.hibernate.infra.replicate.jira.service.jira.model.rest;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.regex.Pattern;
+
+import org.hibernate.infra.replicate.jira.JiraConfig;
+import org.hibernate.infra.replicate.jira.service.jira.model.JiraBaseObject;
+
+import jakarta.ws.rs.core.UriBuilder;
+
+public class JiraVersion extends JiraBaseObject {
+
+ public URI self;
+ public String id;
+ public String name;
+ public String description;
+ public String projectId;
+ public String releaseDate;
+ public boolean released;
+
+ public JiraVersion() {
+ }
+
+ public JiraVersion(String id) {
+ this.id = id;
+ }
+
+ public JiraVersion copyForProject(JiraConfig.JiraProject project) {
+ JiraVersion copy = new JiraVersion();
+ copy.name = name;
+ copy.description = prepareVersionDescriptionForCopy();
+ copy.projectId = project.projectId();
+ copy.releaseDate = releaseDate;
+ copy.released = released;
+ return copy;
+ }
+
+ public String prepareVersionDescriptionForCopy() {
+ URI verisonUri = createJiraVersionUri(this);
+ return """
+ {quote}This [version|%s] was created as a copy of %s{quote}
+
+
+ %s""".formatted(verisonUri, this.name, Objects.toString(this.description, "")).trim();
+ }
+
+ public static Optional findVersion(String versionId, List versions) {
+ if (versions == null || versions.isEmpty()) {
+ return Optional.empty();
+ }
+ // e.g. https://hibernate.atlassian.net/projects/HSEARCH/versions/32220/
+ Pattern pattern = Pattern.compile("(?s)^\\{quote\\}This \\[version.+/versions/%s\\].*".formatted(versionId));
+ for (JiraVersion check : versions) {
+ if (pattern.matcher(check.description).matches()) {
+ return Optional.of(check);
+ }
+ }
+ return Optional.empty();
+ }
+
+ private static URI createJiraVersionUri(JiraVersion version) {
+ // e.g.
+ // https://hibernate.atlassian.net/projects/HSEARCH/versions/32220
+ return UriBuilder.fromUri(version.self).replacePath("projects").path(version.projectId).path("versions")
+ .path(version.id).replaceQuery("").build();
+ }
+
+}
diff --git a/src/test/java/org/hibernate/infra/replicate/jira/export/ExportProjectTest.java b/src/test/java/org/hibernate/infra/replicate/jira/export/ExportProjectTest.java
index 556946f..502e50d 100644
--- a/src/test/java/org/hibernate/infra/replicate/jira/export/ExportProjectTest.java
+++ b/src/test/java/org/hibernate/infra/replicate/jira/export/ExportProjectTest.java
@@ -13,7 +13,7 @@
import org.hibernate.infra.replicate.jira.service.jira.client.JiraRestClientBuilder;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraIssue;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraIssues;
-import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraSimpleObject;
+import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraVersion;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@@ -27,6 +27,7 @@
@TestProfile(ExportProjectTest.Profile.class)
@QuarkusTest
+@Disabled
class ExportProjectTest {
private static final String DEFAULT_REPORTER_NAME = "hibernate-admins@redhat.com";
@@ -153,7 +154,7 @@ private List formatLabels(JiraIssue issue) {
}
if (issue.fields.fixVersions != null) {
- for (JiraSimpleObject fixVersion : issue.fields.fixVersions) {
+ for (JiraVersion fixVersion : issue.fields.fixVersions) {
labels.add("Fix version: %s".formatted(fixVersion.name).replace(' ', '_'));
}
}
diff --git a/src/test/java/org/hibernate/infra/replicate/jira/handler/IssueTest.java b/src/test/java/org/hibernate/infra/replicate/jira/handler/IssueTest.java
index c7a0752..1f88ffa 100644
--- a/src/test/java/org/hibernate/infra/replicate/jira/handler/IssueTest.java
+++ b/src/test/java/org/hibernate/infra/replicate/jira/handler/IssueTest.java
@@ -17,6 +17,7 @@
import org.hibernate.infra.replicate.jira.service.jira.handler.JiraIssueLinkDeleteEventHandler;
import org.hibernate.infra.replicate.jira.service.jira.handler.JiraIssueLinkUpsertEventHandler;
import org.hibernate.infra.replicate.jira.service.jira.handler.JiraIssueUpsertEventHandler;
+import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraIssue;
import org.hibernate.infra.replicate.jira.service.reporting.ReportingConfig;
import org.junit.jupiter.api.AfterEach;
@@ -72,7 +73,7 @@ void testUpsert() {
// - the downstream issue is updated,
// - web link added pointing to the issue
// - transition is performed
- Mockito.verify(destination, Mockito.times(1)).update(eq("JIRATEST2-1"), any());
+ Mockito.verify(destination, Mockito.times(1)).update(eq("JIRATEST2-1"), any(JiraIssue.class));
Mockito.verify(destination, Mockito.times(1)).upsertRemoteLink(eq("JIRATEST2-1"), any());
Mockito.verify(destination, Mockito.times(1)).transition(eq("JIRATEST2-1"), any());
}
@@ -103,7 +104,7 @@ void testRemoveNonExisting() {
// - we called the source jira and it throws 404
// - destination jira is updated (title)
Mockito.verify(source, Mockito.times(1)).getIssue(eq("JIRATEST1-1"));
- Mockito.verify(destination, Mockito.times(1)).update(eq("JIRATEST2-1"), any());
+ Mockito.verify(destination, Mockito.times(1)).update(eq("JIRATEST2-1"), any(JiraIssue.class));
} finally {
source.itemCannotBeFound.set("");
}
diff --git a/src/test/java/org/hibernate/infra/replicate/jira/mock/SampleJiraRestClient.java b/src/test/java/org/hibernate/infra/replicate/jira/mock/SampleJiraRestClient.java
index 3bde855..da52e9a 100644
--- a/src/test/java/org/hibernate/infra/replicate/jira/mock/SampleJiraRestClient.java
+++ b/src/test/java/org/hibernate/infra/replicate/jira/mock/SampleJiraRestClient.java
@@ -3,6 +3,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
@@ -23,6 +24,7 @@
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraSimpleObject;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraTransition;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraUser;
+import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraVersion;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -193,6 +195,26 @@ public void archive(String issueKey) {
// do nothing
}
+ @Override
+ public JiraVersion version(Long id) {
+ return new JiraVersion(Objects.toString(id, null));
+ }
+
+ @Override
+ public List versions(String projectKey) {
+ return List.of(new JiraVersion("version"));
+ }
+
+ @Override
+ public JiraVersion create(JiraVersion version) {
+ return version;
+ }
+
+ @Override
+ public JiraVersion update(String id, JiraVersion version) {
+ return version;
+ }
+
private JiraIssueLink sampleIssueLink(Long id) {
try {
return objectMapper.readValue("""