Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use status from->to to pick a transition to apply to an issue #63

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
Original file line number Diff line number Diff line change
Expand Up @@ -310,10 +310,10 @@ interface StatusesValueMapping extends ValueMapping {
Optional<String> deletedResolution();

/**
* @return The id of the transition to apply to get the "Close" transition when
* closing the issue deleted upstream before archiving it.
* @return The status name to transition the issue to when handling an issue
* deleted/moved upstream before archiving it.
*/
Optional<String> deletedTransition();
Optional<String> deletedStatus();

/**
* @return A map where the {@code key} is the upstream status name, and the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraRemoteLink;
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.JiraTransitions;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraUser;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraVersion;

Expand Down Expand Up @@ -145,6 +146,10 @@ JiraIssues find(@QueryParam("jql") String query, @QueryParam("startAt") int star
@Path("/issue/{issueKey}/transitions")
void transition(@PathParam("issueKey") String issueKey, JiraTransition transition);

@GET
@Path("/issue/{issueKey}/transitions")
JiraTransitions availableTransitions(String issueKey);

@PUT
@Path("/issue/{issueKey}/archive")
void archive(@PathParam("issueKey") String issueKey);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraRemoteLink;
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.JiraTransitions;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraUser;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraVersion;

Expand Down Expand Up @@ -250,6 +251,11 @@ public void transition(String issueKey, JiraTransition transition) {
withRetry(() -> delegate.transition(issueKey, transition));
}

@Override
public JiraTransitions availableTransitions(String issueKey) {
return withRetry(() -> delegate.availableTransitions(issueKey));
}

@Override
public void archive(String issueKey) {
withRetry(() -> delegate.archive(issueKey));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
package org.hibernate.infra.replicate.jira.service.jira.handler;

import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.regex.Pattern;

import org.hibernate.infra.replicate.jira.JiraConfig;
import org.hibernate.infra.replicate.jira.service.jira.HandlerProjectContext;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraComment;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraIssue;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraIssueTransition;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraSimpleObject;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraTextContent;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraTransitions;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraUser;
import org.hibernate.infra.replicate.jira.service.reporting.FailureCollector;
import org.hibernate.infra.replicate.jira.service.reporting.ReportingConfig;
Expand Down Expand Up @@ -92,21 +96,29 @@ protected Optional<String> issueType(String sourceId) {
}, mappedValues.defaultValue()));
}

protected Optional<String> statusToTransition(String sourceId) {
JiraConfig.ValueMapping mappedValues = context.projectGroup().statuses();
return Optional.ofNullable(JiraStaticFieldMappingCache.status(context.projectGroupName(), sourceId, pk -> {
Map<String, String> mapping = context.projectGroup().statuses().mapping();
if (!mapping.isEmpty()) {
return mapping;
}
protected Optional<String> statusToTransition(String from, String to, Supplier<Optional<String>> transitionFinder) {
return Optional.ofNullable(JiraStaticFieldMappingCache.status(context.projectGroupName(),
"%s->%s".formatted(from, to), tk -> transitionFinder.get().orElse(null)));
}

// Otherwise we'll try to use REST to get the info and match, but that may not
// necessarily work fine
List<JiraSimpleObject> source = context.sourceJiraClient().getStatues();
List<JiraSimpleObject> destination = context.destinationJiraClient().getStatues();
protected Optional<String> findRequiredTransitionId(String downstreamStatus, JiraIssue destIssue) {
if (downstreamStatus != null) {
List<JiraIssueTransition> jiraTransitions = null;
try {
JiraTransitions transitions = context.destinationJiraClient().availableTransitions(destIssue.key);
jiraTransitions = transitions.transitions;
} catch (Exception e) {
failureCollector.warning("Failed to find a transition for %s".formatted(destIssue.key), e);
jiraTransitions = Collections.emptyList();
}
for (JiraIssueTransition transition : jiraTransitions) {
if (transition.to != null && downstreamStatus.equalsIgnoreCase(transition.to.name)) {
return Optional.of(transition.id);
}
}
}

return createMapping(source, destination);
}, mappedValues.defaultValue()));
return Optional.empty();
}

protected Optional<String> linkType(String sourceId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ protected void applyTransition(JiraIssue sourceIssue, String 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());
.getOrDefault(sourceIssue.fields.status.name.toLowerCase(Locale.ROOT), Set.of());
if (statusesToIgnore.contains(destIssue.fields.status.name.toLowerCase(Locale.ROOT))) {
// no need to apply the transition :)
return;
}
prepareTransition(sourceIssue).ifPresent(
prepareTransition(sourceIssue.fields.status, destIssue).ifPresent(
jiraTransition -> context.destinationJiraClient().transition(destinationKey, jiraTransition));
}

Expand Down Expand Up @@ -213,8 +213,15 @@ private JiraUser toUser(String value) {
return new JiraUser(context.projectGroup().users().mappedPropertyName(), value);
}

private Optional<JiraTransition> prepareTransition(JiraIssue sourceIssue) {
return statusToTransition(sourceIssue.fields.status.id).map(JiraTransition::new);
protected Optional<JiraTransition> prepareTransition(JiraSimpleObject sourceStatus, JiraIssue destIssue) {
String downstreamStatus = context.projectGroup().statuses().mapping()
.get(sourceStatus.name.toLowerCase(Locale.ROOT));
return prepareTransition(downstreamStatus, destIssue);
}

protected Optional<JiraTransition> prepareTransition(String downstreamStatus, JiraIssue destIssue) {
return statusToTransition(destIssue.fields.status.name, downstreamStatus,
() -> findRequiredTransitionId(downstreamStatus, destIssue)).map(JiraTransition::new);
}

protected Optional<JiraIssueLink> prepareParentLink(String destinationKey, JiraIssue sourceIssue) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraTransition;
import org.hibernate.infra.replicate.jira.service.reporting.ReportingConfig;

public class JiraIssueDeleteEventHandler extends JiraEventHandler {
public class JiraIssueDeleteEventHandler extends JiraIssueAbstractEventHandler {
private final String key;

public JiraIssueDeleteEventHandler(ReportingConfig reportingConfig, HandlerProjectContext context, Long id,
Expand Down Expand Up @@ -70,7 +70,7 @@ private void handleDeletedMovedIssue(String type) {

context.destinationJiraClient().update(destinationKey, updated);

prepareTransition()
prepareTransition(issue)
.ifPresent(transition -> context.destinationJiraClient().transition(destinationKey, transition));

context.destinationJiraClient().archive(destinationKey);
Expand All @@ -80,11 +80,12 @@ private void handleDeletedMovedIssue(String type) {
}
}

private Optional<JiraTransition> prepareTransition() {
Optional<String> deletedTransition = context.projectGroup().statuses().deletedTransition();
if (deletedTransition.isPresent()) {
private Optional<JiraTransition> prepareTransition(JiraIssue issue) {
Optional<String> deletedStatus = context.projectGroup().statuses().deletedStatus();
if (deletedStatus.isPresent()) {
prepareTransition(deletedStatus.get(), issue);
JiraTransition transition = new JiraTransition();
transition.transition = new JiraIssueTransition(deletedTransition.get());
transition.transition = new JiraIssueTransition(deletedStatus.get());

Optional<String> deletedResolution = context.projectGroup().statuses().deletedResolution();
deletedResolution.ifPresent(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,18 @@ public static String issueType(String projectGroup, String sourceId,
return issueType.computeIfAbsent(projectGroup, onMissing).getOrDefault(sourceId, defaultValue);
}

public static String status(String projectGroup, String sourceId, Function<String, Map<String, String>> onMissing,
String defaultValue) {
return status.computeIfAbsent(projectGroup, onMissing).getOrDefault(sourceId, defaultValue);
public static String status(String projectGroup, String transitionKey, Function<String, String> onMissing) {
Map<String, String> groupStatuses = status.computeIfAbsent(projectGroup, pg -> new ConcurrentHashMap<>());

String id = groupStatuses.get(transitionKey);
if (id == null) {
id = onMissing.apply(transitionKey);
if (id != null) {
groupStatuses.put(transitionKey, id);
}
}

return id;
}

public static String linkType(String projectGroup, String sourceId, Function<String, Map<String, String>> onMissing,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package org.hibernate.infra.replicate.jira.service.jira.model.rest;

import org.hibernate.infra.replicate.jira.service.jira.model.JiraBaseObject;

public class JiraIssueTransition extends JiraBaseObject {
public String id;
public class JiraIssueTransition extends JiraSimpleObject {
public JiraSimpleObject to;

public JiraIssueTransition() {
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.hibernate.infra.replicate.jira.service.jira.model.rest;

import java.util.List;

import org.hibernate.infra.replicate.jira.service.jira.model.JiraBaseObject;

public class JiraTransitions extends JiraBaseObject {
public List<JiraIssueTransition> transitions;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,30 @@ private LoggingFailureCollector() {

@Override
public void warning(String details) {
Log.warn(details);
Log.warn(escape(details));
}

@Override
public void warning(String details, Exception exception) {
Log.warnf(exception, details);
Log.warnf(exception, escape(details));
}

@Override
public void critical(String details) {
Log.error(details);
Log.error(escape(details));
}

@Override
public void critical(String details, Exception exception) {
Log.errorf(exception, details);
Log.errorf(exception, escape(details));
}

@Override
public void close() {
// do nothing, we've already logged all
}

private String escape(String s) {
return s.replace('{', '[').replace('}', ']');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraIssueLink;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraIssueLinkTypes;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraIssueResponse;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraIssueTransition;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraIssues;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraRemoteLink;
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.JiraTransitions;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraUser;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraVersion;

Expand Down Expand Up @@ -190,6 +192,18 @@ public void transition(String issueKey, JiraTransition transition) {
// do nothing
}

@Override
public JiraTransitions availableTransitions(String issueKey) {
JiraTransitions transitions = new JiraTransitions();
JiraIssueTransition transition = new JiraIssueTransition();
transition.name = "To To Do";
transition.id = "100";
transition.to = new JiraSimpleObject("1234");
transition.to.name = "To Do";
transitions.transitions = List.of(transition);
return transitions;
}

@Override
public void archive(String issueKey) {
// do nothing
Expand Down
3 changes: 3 additions & 0 deletions src/test/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ jira.project-group."hibernate".destination.api-uri=http://localhost:8081/api/jir
jira.project-group."hibernate".destination.api-user.email=user-name
jira.project-group."hibernate".destination.api-user.token=user-token
jira.project-group."hibernate".issue-link-types.default-value=10050

jira.project-group."hibernate".statuses.mapping."to\u0020do"=to do

jira.project-group."hibernate".projects.JIRATEST1.security.secret=not-a-secret
jira.project-group."hibernate".projects.JIRATEST1.project-id=10323
jira.project-group."hibernate".projects.JIRATEST1.project-key=JIRATEST2
Expand Down
Loading