Skip to content

Commit

Permalink
Add handling of more automation events
Browse files Browse the repository at this point in the history
  • Loading branch information
marko-bekhta committed Dec 4, 2024
1 parent c1add9d commit 165ec42
Show file tree
Hide file tree
Showing 16 changed files with 251 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public String somethingHappenedUpstream(@RestPath @NotNull @ConfiguredProject St
@POST
@Path("/mirror/{project}")
@Consumes(MediaType.APPLICATION_JSON)
public String somethingHappenedDownstream(@RestPath @NotNull @ConfiguredProject(upstream = false) String project,
public String somethingHappenedDownstream(@RestPath @NotNull @ConfiguredProject String project,
JiraActionEvent data) {
Log.tracef("Received a downstream notification about %s project: %s...", project, data);
jiraService.downstreamAcknowledge(project, data);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -371,4 +371,8 @@ public boolean isDownstreamUserIgnored(String triggeredByUser) {
public String upstreamUser(String mappedValue) {
return projectGroupContext.upstreamUser(mappedValue);
}

public String upstreamStatus(String mappedValue) {
return projectGroupContext.upstreamStatus(mappedValue);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public final class HandlerProjectGroupContext implements AutoCloseable {
private final Semaphore downstreamRateLimiter;
private final JiraConfig.JiraProjectGroup projectGroup;
private final Map<String, String> invertedUsers;
private final Map<String, String> invertedStatuses;

public HandlerProjectGroupContext(JiraConfig.JiraProjectGroup projectGroup) {
this.projectGroup = projectGroup;
Expand All @@ -54,11 +55,16 @@ public HandlerProjectGroupContext(JiraConfig.JiraProjectGroup projectGroup) {
downstreamEventHandlingExecutor = new ThreadPoolExecutor(processing.threads(), processing.threads(), 0L,
TimeUnit.MILLISECONDS, downstreamWorkQueue);

Map<String, String> invertedUsers = new HashMap<>();
for (var entry : projectGroup.users().mapping().entrySet()) {
invertedUsers.put(entry.getValue(), entry.getKey());
this.invertedUsers = invert(projectGroup.users().mapping());
this.invertedStatuses = invert(projectGroup.statuses().mapping());
}

private static Map<String, String> invert(Map<String, String> map) {
Map<String, String> result = new HashMap<>();
for (var entry : map.entrySet()) {
result.put(entry.getValue(), entry.getKey());
}
this.invertedUsers = Collections.unmodifiableMap(invertedUsers);
return Collections.unmodifiableMap(result);
}

public void startProcessingEvent() throws InterruptedException {
Expand Down Expand Up @@ -116,4 +122,9 @@ private static void closeEventExecutor(ExecutorService executor) {
public String upstreamUser(String mappedValue) {
return invertedUsers.get(mappedValue);
}

public String upstreamStatus(String mappedValue) {
return invertedStatuses.get(mappedValue);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ public void acknowledge(String project, JiraWebHookEvent event, String triggered
}

if (context.isUserIgnored(triggeredByUser)) {
Log.infof("Event was triggered by %s user that is in the ignore list.", triggeredByUser);
Log.infof("Event was triggered by %s user that is in the ignore list: %.200s", triggeredByUser, event);
return;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
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;
Expand All @@ -13,10 +12,8 @@
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 @@ -101,26 +98,6 @@ protected Optional<String> statusToTransition(String from, String to, Supplier<O
"%s->%s".formatted(from, to), tk -> transitionFinder.get().orElse(null)));
}

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 Optional.empty();
}

protected Optional<String> linkType(String sourceId) {
JiraConfig.ValueMapping mappedValues = context.projectGroup().issueLinkTypes();
return Optional.ofNullable(JiraStaticFieldMappingCache.linkType(context.projectGroupName(), sourceId, pk -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,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;
import org.hibernate.infra.replicate.jira.service.reporting.ReportingConfig;
Expand Down Expand Up @@ -231,7 +232,9 @@ protected Optional<JiraTransition> prepareTransition(JiraSimpleObject sourceStat

protected Optional<JiraTransition> prepareTransition(String downstreamStatus, JiraIssue destIssue) {
return statusToTransition(destIssue.fields.status.name, downstreamStatus,
() -> findRequiredTransitionId(downstreamStatus, destIssue)).map(JiraTransition::new);
() -> JiraTransitions.findRequiredTransitionId(context.destinationJiraClient(), failureCollector,
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 @@ -10,6 +10,7 @@ public class JiraStaticFieldMappingCache {
private static final Map<String, Map<String, String>> priorities = new ConcurrentHashMap<>();
private static final Map<String, Map<String, String>> issueType = new ConcurrentHashMap<>();
private static final Map<String, Map<String, String>> status = new ConcurrentHashMap<>();
private static final Map<String, Map<String, String>> statusUpstream = new ConcurrentHashMap<>();
private static final Map<String, Map<String, String>> linkType = new ConcurrentHashMap<>();
private static final Map<String, Map<String, String>> user = new ConcurrentHashMap<>();

Expand All @@ -24,6 +25,15 @@ public static String issueType(String projectGroup, String sourceId,
}

public static String status(String projectGroup, String transitionKey, Function<String, String> onMissing) {
return status(status, projectGroup, transitionKey, onMissing);
}

public static String statusUpstream(String projectGroup, String transitionKey, Function<String, String> onMissing) {
return status(statusUpstream, projectGroup, transitionKey, onMissing);
}

private static String status(Map<String, Map<String, 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);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.hibernate.infra.replicate.jira.service.jira.handler.action;

import java.util.ArrayList;
import java.util.List;

import org.hibernate.infra.replicate.jira.service.jira.HandlerProjectContext;
import org.hibernate.infra.replicate.jira.service.jira.model.action.JiraActionEvent;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraFields;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraIssue;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraVersion;
import org.hibernate.infra.replicate.jira.service.reporting.ReportingConfig;

abstract class JiraAbstractVersionActionEventHandler extends JiraActionEventHandler {

public JiraAbstractVersionActionEventHandler(ReportingConfig reportingConfig, HandlerProjectContext context,
JiraActionEvent event) {
super(reportingConfig, context, event);
}

@Override
protected void doRun() {
JiraIssue issue = context.destinationJiraClient().getIssue(event.key);

JiraIssue updated = new JiraIssue();
updated.fields = JiraFields.empty();

List<JiraVersion> versionList = versionList(issue);

List<JiraVersion> versions;
if (versionList != null) {
versions = new ArrayList<>(versionList.size());
for (JiraVersion ver : versionList) {
JiraVersion version = new JiraVersion();
version.name = ver.name;
versions.add(version);
}
} else {
versions = List.of();
}

setVersionList(updated, versions);

context.sourceJiraClient().update(toSourceKey(event.key), updated);
}

protected abstract void setVersionList(JiraIssue issue, List<JiraVersion> versions);

protected abstract List<JiraVersion> versionList(JiraIssue issue);

@Override
public String toString() {
return this.getClass().getSimpleName() + "[" + "event=" + event + ", project=" + context.projectName() + ']';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.hibernate.infra.replicate.jira.service.jira.handler.action;

import java.util.List;

import org.hibernate.infra.replicate.jira.service.jira.HandlerProjectContext;
import org.hibernate.infra.replicate.jira.service.jira.model.action.JiraActionEvent;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraIssue;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraVersion;
import org.hibernate.infra.replicate.jira.service.reporting.ReportingConfig;

public class JiraAffectsVersionActionEventHandler extends JiraAbstractVersionActionEventHandler {

public JiraAffectsVersionActionEventHandler(ReportingConfig reportingConfig, HandlerProjectContext context,
JiraActionEvent event) {
super(reportingConfig, context, event);
}

protected void setVersionList(JiraIssue issue, List<JiraVersion> versions) {
issue.fields.versions = versions;
}

protected List<JiraVersion> versionList(JiraIssue issue) {
return issue.fields.versions;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.hibernate.infra.replicate.jira.service.jira.handler.action;

import java.util.List;

import org.hibernate.infra.replicate.jira.service.jira.HandlerProjectContext;
import org.hibernate.infra.replicate.jira.service.jira.model.action.JiraActionEvent;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraIssue;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraVersion;
import org.hibernate.infra.replicate.jira.service.reporting.ReportingConfig;

public class JiraFixVersionActionEventHandler extends JiraAbstractVersionActionEventHandler {

public JiraFixVersionActionEventHandler(ReportingConfig reportingConfig, HandlerProjectContext context,
JiraActionEvent event) {
super(reportingConfig, context, event);
}

protected void setVersionList(JiraIssue issue, List<JiraVersion> versions) {
issue.fields.fixVersions = versions;
}

protected List<JiraVersion> versionList(JiraIssue issue) {
return issue.fields.fixVersions;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package org.hibernate.infra.replicate.jira.service.jira.handler.action;

import java.util.Locale;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;

import org.hibernate.infra.replicate.jira.service.jira.HandlerProjectContext;
import org.hibernate.infra.replicate.jira.service.jira.handler.JiraStaticFieldMappingCache;
import org.hibernate.infra.replicate.jira.service.jira.model.action.JiraActionEvent;
import org.hibernate.infra.replicate.jira.service.jira.model.rest.JiraIssue;
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.reporting.ReportingConfig;

public class JiraTransitionActionEventHandler extends JiraActionEventHandler {

public JiraTransitionActionEventHandler(ReportingConfig reportingConfig, HandlerProjectContext context,
JiraActionEvent event) {
super(reportingConfig, context, event);
}

@Override
protected void doRun() {
String sourceKey = toSourceKey(event.key);
JiraIssue issue = context.destinationJiraClient().getIssue(event.key);
JiraIssue sourceIssue = context.sourceJiraClient().getIssue(sourceKey);

String statusDownstream = issue.fields.status.name.toLowerCase(Locale.ROOT);
String statusCurrent = sourceIssue.fields.status.name.toLowerCase(Locale.ROOT);

if (context.projectGroup().statuses().ignoreTransitionCondition().getOrDefault(statusCurrent, Set.of())
.contains(statusDownstream)) {
return;
}

String statusNew = context.upstreamStatus(statusDownstream);

prepareTransition(statusNew, sourceIssue)
.ifPresent(jiraTransition -> context.sourceJiraClient().transition(sourceKey, jiraTransition));
}

protected Optional<JiraTransition> prepareTransition(String upstreamStatus, JiraIssue issue) {
return statusToTransition(issue.fields.status.name, upstreamStatus, () -> JiraTransitions
.findRequiredTransitionId(context.sourceJiraClient(), failureCollector, upstreamStatus, issue))
.map(JiraTransition::new);
}

protected Optional<String> statusToTransition(String from, String to, Supplier<Optional<String>> transitionFinder) {
return Optional.ofNullable(JiraStaticFieldMappingCache.statusUpstream(context.projectGroupName(),
"%s->%s".formatted(from, to), tk -> transitionFinder.get().orElse(null)));
}

@Override
public String toString() {
return "JiraAssigneeActionEventHandler[" + "event=" + event + ", project=" + context.projectName() + ']';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class JiraActionEvent extends JiraBaseObject {
public String key;
public String event;
public String assignee;
public String status;
public String value;

public String triggeredByUser;

Expand All @@ -21,6 +21,6 @@ public Optional<JiraActionEventType> eventType() {
@Override
public String toString() {
return "JiraActionEvent{" + "id='" + id + '\'' + ", key='" + key + '\'' + ", event='" + event + '\''
+ ", assignee='" + assignee + '\'' + ", status='" + status + '\'' + '}';
+ ", assignee='" + assignee + '\'' + ", value='" + value + '\'' + '}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@
import java.util.Optional;

import org.hibernate.infra.replicate.jira.service.jira.HandlerProjectContext;
import org.hibernate.infra.replicate.jira.service.jira.handler.action.JiraAffectsVersionActionEventHandler;
import org.hibernate.infra.replicate.jira.service.jira.handler.action.JiraAssigneeActionEventHandler;
import org.hibernate.infra.replicate.jira.service.jira.handler.action.JiraFixVersionActionEventHandler;
import org.hibernate.infra.replicate.jira.service.jira.handler.action.JiraTransitionActionEventHandler;
import org.hibernate.infra.replicate.jira.service.jira.model.action.JiraActionEvent;
import org.hibernate.infra.replicate.jira.service.reporting.ReportingConfig;

public enum JiraActionEventType {
ISSUE_ASSIGNED("jira:issue_assigned") {
ISSUE_ASSIGNED("jira:issue_update_assignee") {
@Override
public Collection<Runnable> handlers(ReportingConfig reportingConfig, JiraActionEvent event,
HandlerProjectContext context) {
Expand All @@ -22,11 +25,25 @@ public Collection<Runnable> handlers(ReportingConfig reportingConfig, JiraAction
return List.of(new JiraAssigneeActionEventHandler(reportingConfig, context, event));
}
},
ISSUE_TRANSITIONED("jira:issue_transitioned") {
ISSUE_TRANSITIONED("jira:issue_update_status") {
@Override
public Collection<Runnable> handlers(ReportingConfig reportingConfig, JiraActionEvent event,
HandlerProjectContext context) {
throw new UnsupportedOperationException("jira:issue_transitioned not supported yet");
return List.of(new JiraTransitionActionEventHandler(reportingConfig, context, event));
}
},
FIX_VERSION_CHANGED("jira:issue_update_fixversions") {
@Override
public Collection<Runnable> handlers(ReportingConfig reportingConfig, JiraActionEvent event,
HandlerProjectContext context) {
return List.of(new JiraFixVersionActionEventHandler(reportingConfig, context, event));
}
},
AFFECTS_VERSION_CHANGED("jira:issue_update_versions") {
@Override
public Collection<Runnable> handlers(ReportingConfig reportingConfig, JiraActionEvent event,
HandlerProjectContext context) {
return List.of(new JiraAffectsVersionActionEventHandler(reportingConfig, context, event));
}
};

Expand Down
Loading

0 comments on commit 165ec42

Please sign in to comment.