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
24 changes: 24 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 @@ -92,6 +92,30 @@ interface JiraProjectGroup {
* for the timeframe to end and will get process
*/
EventProcessing processing();

/**
* Allows customizing formatting options.
*/
Formatting formatting();
}

interface Formatting {

/**
* Specify how the label is formatted for a downstream issue.
* <p>
* Template receives a single String token that is an upstream label. {@code %s}
* <b>must</b> be used in the template as it will be replaced by a {@code .+}
* regex to find matches in the already synced labels.
*/
@WithDefault("upstream-%s")
String labelTemplate();

/**
* Specify how {@link java.time.ZonedDateTime} is formatted to string.
*/
@WithDefault("EEEE, MMMM dd, yyyy 'at' HH:mm:ss z(Z)")
String timestampFormat();
}

interface EventProcessing {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package org.hibernate.infra.replicate.jira.service.jira;

import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Pattern;

import org.hibernate.infra.replicate.jira.JiraConfig;
import org.hibernate.infra.replicate.jira.service.jira.client.JiraRestClient;
Expand Down Expand Up @@ -37,6 +40,8 @@ public final class HandlerProjectContext implements AutoCloseable {
private final JiraUser notMappedAssignee;

private final Map<String, HandlerProjectContext> allProjectsContextMap;
private final Pattern sourceLabelPattern;
private final DateTimeFormatter formatter;

public HandlerProjectContext(String projectName, String projectGroupName, JiraRestClient sourceJiraClient,
JiraRestClient destinationJiraClient, HandlerProjectGroupContext projectGroupContext,
Expand All @@ -55,6 +60,9 @@ public HandlerProjectContext(String projectName, String projectGroupName, JiraRe
.map(v -> new JiraUser(projectGroup().users().mappedPropertyName(), v)).orElse(null);

this.allProjectsContextMap = allProjectsContextMap;
this.sourceLabelPattern = Pattern
.compile(projectGroupContext.projectGroup().formatting().labelTemplate().formatted(".+"));
this.formatter = DateTimeFormatter.ofPattern(projectGroupContext.projectGroup().formatting().timestampFormat());
}

public JiraConfig.JiraProject project() {
Expand Down Expand Up @@ -217,4 +225,12 @@ public Optional<HandlerProjectContext> contextForProjectInSameGroup(String proje
}
return Optional.ofNullable(allProjectsContextMap.get(project));
}

public boolean isSourceLabel(String label) {
return sourceLabelPattern.matcher(label).matches();
}

public String formatTimestamp(ZonedDateTime time) {
return time != null ? time.format(formatter) : "";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,19 @@ private JiraComment prepareComment(JiraIssue issue, JiraComment source) {
private String prepareCommentQuote(JiraIssue issue, JiraComment comment) {
URI jiraCommentUri = createJiraCommentUri(issue, comment);
UserData userData = userData(comment.self, comment.author, "the user %s");
UserData editUserData = userData(comment.self, comment.updateAuthor, "the user %s");
String content = """
{quote}This [comment|%s] was posted by [%s|%s].{quote}
{quote}This [comment|%s] was posted by [%s|%s] on %s.%s{quote}


""".formatted(jiraCommentUri, userData.name(), userData.uri());
""".formatted(jiraCommentUri, userData.name(), userData.uri(), context.formatTimestamp(comment.created),
comment.isUpdatedSameAsCreated()
? ""
: """

[%s|%s] edited the comment on %s.
""".formatted(editUserData.name(), editUserData.uri(),
context.formatTimestamp(comment.updated)));
return truncateContent(content);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
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.HandlerProjectContext;
Expand All @@ -20,6 +21,8 @@

abstract class JiraIssueAbstractEventHandler extends JiraEventHandler {

private static final Pattern FIX_VERSION_PATTERN = Pattern.compile("Fix_version:.++");

public JiraIssueAbstractEventHandler(ReportingConfig reportingConfig, HandlerProjectContext context, Long id) {
super(reportingConfig, context, id);
}
Expand All @@ -30,7 +33,8 @@ protected void applyTransition(JiraIssue sourceIssue, String destinationKey) {
}

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

updateIssue(destinationKey, issue, sourceIssue, context.notMappedAssignee());
}
Expand Down Expand Up @@ -83,7 +87,7 @@ protected JiraRemoteLink remoteSelfLink(JiraIssue sourceIssue) {
return link;
}

protected JiraIssue issueToCreate(JiraIssue sourceIssue) {
protected JiraIssue issueToCreate(JiraIssue sourceIssue, JiraIssue downstreamIssue) {
JiraIssue destinationIssue = new JiraIssue();
destinationIssue.fields = new JiraFields();

Expand All @@ -93,17 +97,7 @@ protected JiraIssue issueToCreate(JiraIssue sourceIssue) {
Objects.toString(sourceIssue.fields.description, ""));
destinationIssue.fields.description = truncateContent(destinationIssue.fields.description);

destinationIssue.fields.labels = sourceIssue.fields.labels;
// let's also add fix versions to the labels
if (sourceIssue.fields.fixVersions != null) {
if (destinationIssue.fields.labels == null) {
destinationIssue.fields.labels = List.of();
}
destinationIssue.fields.labels = new ArrayList<>(destinationIssue.fields.labels);
for (JiraSimpleObject fixVersion : sourceIssue.fields.fixVersions) {
destinationIssue.fields.labels.add("Fix version:%s".formatted(fixVersion.name).replace(' ', '_'));
}
}
destinationIssue.fields.labels = prepareLabels(sourceIssue, downstreamIssue);

// if we can map the priority - great we'll do that, if no: we'll keep it blank
// and let Jira use its default instead:
Expand Down Expand Up @@ -155,6 +149,38 @@ protected JiraIssue issueToCreate(JiraIssue sourceIssue) {
return destinationIssue;
}

private List<String> prepareLabels(JiraIssue sourceIssue, JiraIssue downstreamIssue) {
List<String> labelsToSet = new ArrayList<>();

for (String label : sourceIssue.fields.labels) {
labelsToSet.add(asUpstreamLabel(label));
}

// let's also add fix versions to the labels
if (sourceIssue.fields.fixVersions != null) {
for (JiraSimpleObject fixVersion : sourceIssue.fields.fixVersions) {
String fixVersionLabel = "Fix version:%s".formatted(fixVersion.name).replace(' ', '_');
labelsToSet.add(fixVersionLabel);
}
}

for (String label : downstreamIssue.fields.labels) {
if (!(context.isSourceLabel(label) || isFixVersion(label))) {
labelsToSet.add(label);
}
}

return labelsToSet;
}

private boolean isFixVersion(String label) {
return FIX_VERSION_PATTERN.matcher(label).matches();
}

private String asUpstreamLabel(String label) {
return context.projectGroup().formatting().labelTemplate().formatted(label);
}

private JiraUser toUser(String value) {
return new JiraUser(context.projectGroup().users().mappedPropertyName(), value);
}
Expand Down Expand Up @@ -218,13 +244,19 @@ private String prepareDescriptionQuote(JiraIssue issue) {

Reported by: %s.

Upstream status: %s.{quote}
Upstream status: %s.

Created: %s.

Last updated: %s.{quote}


""".formatted(issue.key, issueUri,
assignee == null ? " Unassigned" : "[%s|%s]".formatted(assignee.name(), assignee.uri()),
reporter == null ? " Unknown" : "[%s|%s]".formatted(reporter.name(), reporter.uri()),
issue.fields.status != null ? issue.fields.status.name : "Unknown");
issue.fields.status != null ? issue.fields.status.name : "Unknown",
context.formatTimestamp(issue.fields.created),
context.formatTimestamp(issue.fields.updated != null ? issue.fields.updated : issue.fields.created));
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.hibernate.infra.replicate.jira.service.jira.model.rest;

import java.net.URI;
import java.time.ZonedDateTime;

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

Expand All @@ -9,12 +10,19 @@ public class JiraComment extends JiraBaseObject {
public String id;
public URI self;
public JiraUser author = new JiraUser();
public JiraUser updateAuthor;
public String body;
public ZonedDateTime created;
public ZonedDateTime updated;

public JiraComment() {
}

public JiraComment(String id) {
this.id = id;
}

public boolean isUpdatedSameAsCreated() {
return updated != null && updated.equals(created);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.hibernate.infra.replicate.jira.service.jira.model.rest;

import java.time.ZonedDateTime;
import java.util.List;

import org.hibernate.infra.replicate.jira.service.jira.model.JiraBaseObject;
Expand All @@ -22,6 +23,8 @@ public class JiraFields extends JiraBaseObject {
public List<JiraIssueLink> issuelinks;
public JiraComments comment;
public JiraIssue parent;
public ZonedDateTime created;
public ZonedDateTime updated;

@Override
public String toString() {
Expand Down
Loading