From 80f511f333653528f9d64a76c2528dc4766bcb2d Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 12 Jan 2021 13:51:43 +0100 Subject: [PATCH] Allow ticket assignment on ticket creation. See #159 --- .../data/release/issues/IssueTracker.java | 3 ++- .../release/issues/IssueTrackerCommands.java | 4 ++-- .../data/release/issues/Ticket.java | 21 ++++++++++++++++ .../data/release/issues/github/GitHub.java | 24 ++++++++++++++----- .../release/issues/github/GitHubIssue.java | 4 ++++ .../issues/github/GitHubReadIssue.java | 6 +++++ .../issues/github/GitHubWriteIssue.java | 8 +++++++ .../data/release/issues/jira/Jira.java | 16 +++++++++---- .../data/release/issues/jira/JiraIssue.java | 4 ++++ .../jira/JiraConnectorIntegrationTests.java | 4 ++-- .../release/issues/jira/TicketsUnitTests.java | 18 +++++++------- 11 files changed, 87 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/springframework/data/release/issues/IssueTracker.java b/src/main/java/org/springframework/data/release/issues/IssueTracker.java index c6fc6f6..b049b93 100644 --- a/src/main/java/org/springframework/data/release/issues/IssueTracker.java +++ b/src/main/java/org/springframework/data/release/issues/IssueTracker.java @@ -110,9 +110,10 @@ public interface IssueTracker extends Plugin { * @param module must not be {@literal null}. * @param text the text to use. * @param ticketType the ticket type. + * @param assignToCurrentUser * @return the created ticket. */ - Ticket createTicket(ModuleIteration module, String text, TicketType ticketType); + Ticket createTicket(ModuleIteration module, String text, TicketType ticketType, boolean assignToCurrentUser); /** * Assigns the ticket to the current user. diff --git a/src/main/java/org/springframework/data/release/issues/IssueTrackerCommands.java b/src/main/java/org/springframework/data/release/issues/IssueTrackerCommands.java index 586c6d4..b0ab1b8 100644 --- a/src/main/java/org/springframework/data/release/issues/IssueTrackerCommands.java +++ b/src/main/java/org/springframework/data/release/issues/IssueTrackerCommands.java @@ -127,11 +127,11 @@ class IssueTrackerCommands extends TimedCommand { } @CliCommand(value = "tracker create tickets") - public String createTickets(@CliOption(key = "", mandatory = true) TrainIteration iteration, + public String createTickets(@CliOption(key = "iteration", mandatory = true) TrainIteration iteration, @CliOption(key = "text", mandatory = true) String text) { return iteration.stream().// - map(module -> getTrackerFor(module).createTicket(module, text, IssueTracker.TicketType.Task)) + map(module -> getTrackerFor(module).createTicket(module, text, IssueTracker.TicketType.Task, false)) .collect(Tickets.toTicketsCollector()) .toString(); } diff --git a/src/main/java/org/springframework/data/release/issues/Ticket.java b/src/main/java/org/springframework/data/release/issues/Ticket.java index bacd9e8..ed94cf1 100644 --- a/src/main/java/org/springframework/data/release/issues/Ticket.java +++ b/src/main/java/org/springframework/data/release/issues/Ticket.java @@ -33,8 +33,25 @@ public class Ticket { String id, summary; String url; + String assignee; TicketStatus ticketStatus; + public Ticket(String id, String summary, String url, String assignee, TicketStatus ticketStatus) { + this.id = id; + this.summary = summary; + this.url = url; + this.assignee = assignee; + this.ticketStatus = ticketStatus; + } + + public Ticket(String id, String summary, TicketStatus ticketStatus) { + this.id = id; + this.summary = summary; + this.assignee = null; + this.url = null; + this.ticketStatus = ticketStatus; + } + /* * (non-Javadoc) * @see java.lang.Object#toString() @@ -78,4 +95,8 @@ public class Ticket { public boolean isReleaseTicketFor(TrainIteration train) { return train.stream().anyMatch(this::isReleaseTicketFor); } + + public boolean isAssignedTo(String username) { + return username.equals(assignee); + } } diff --git a/src/main/java/org/springframework/data/release/issues/github/GitHub.java b/src/main/java/org/springframework/data/release/issues/github/GitHub.java index 24681e2..0e3d6cc 100644 --- a/src/main/java/org/springframework/data/release/issues/github/GitHub.java +++ b/src/main/java/org/springframework/data/release/issues/github/GitHub.java @@ -18,6 +18,7 @@ package org.springframework.data.release.issues.github; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -251,18 +252,20 @@ class GitHub extends GitHubSupport implements IssueTracker { logger.log(moduleIteration, "Creating release ticket…"); - doCreateTicket(moduleIteration, Tracker.releaseTicketSummary(moduleIteration), TicketType.Task); + doCreateTicket(moduleIteration, Tracker.releaseTicketSummary(moduleIteration), TicketType.Task, false); } @Override - public Ticket createTicket(ModuleIteration moduleIteration, String text, TicketType ticketType) { + public Ticket createTicket(ModuleIteration moduleIteration, String text, TicketType ticketType, + boolean assignToCurrentUser) { logger.log(moduleIteration, "Creating ticket…"); - return doCreateTicket(moduleIteration, text, ticketType); + return doCreateTicket(moduleIteration, text, ticketType, assignToCurrentUser); } - private Ticket doCreateTicket(ModuleIteration moduleIteration, String text, TicketType ticketType) { + private Ticket doCreateTicket(ModuleIteration moduleIteration, String text, TicketType ticketType, + boolean assignToCurrentUser) { String repositoryName = GitProject.of(moduleIteration.getProject()).getRepositoryName(); Milestone milestone = getMilestone(moduleIteration, repositoryName); @@ -271,6 +274,10 @@ class GitHub extends GitHubSupport implements IssueTracker { GitHubWriteIssue gitHubIssue = GitHubWriteIssue.of(text, milestone).withLabel(label.getName()); + if (assignToCurrentUser) { + gitHubIssue = gitHubIssue.withAssignees(Collections.singletonList(properties.getUsername())); + } + Map parameters = newUrlTemplateVariables(); parameters.put("repoName", repositoryName); @@ -294,8 +301,12 @@ class GitHub extends GitHubSupport implements IssueTracker { Assert.notNull(ticket, "Ticket must not be null."); - String repositoryName = GitProject.of(project).getRepositoryName(); + if (ticket.isAssignedTo(properties.getUsername())) { + logger.log("Ticket", "Skipping self-assignment of %s", ticket); + return ticket; + } + String repositoryName = GitProject.of(project).getRepositoryName(); Map parameters = newUrlTemplateVariables(); parameters.put("repoName", repositoryName); parameters.put("id", stripHash(ticket)); @@ -673,7 +684,8 @@ class GitHub extends GitHubSupport implements IssueTracker { } private static Ticket toTicket(GitHubIssue issue) { - return new Ticket(issue.getId(), issue.getTitle(), issue.getUrl(), new GithubTicketStatus(issue.getState())); + return new Ticket(issue.getId(), issue.getTitle(), issue.getUrl(), + issue.getAssignees().isEmpty() ? null : issue.getAssignees().get(0), new GithubTicketStatus(issue.getState())); } } diff --git a/src/main/java/org/springframework/data/release/issues/github/GitHubIssue.java b/src/main/java/org/springframework/data/release/issues/github/GitHubIssue.java index 5c3522a..9ed0edb 100644 --- a/src/main/java/org/springframework/data/release/issues/github/GitHubIssue.java +++ b/src/main/java/org/springframework/data/release/issues/github/GitHubIssue.java @@ -15,6 +15,8 @@ */ package org.springframework.data.release.issues.github; +import java.util.List; + /** * @author Mark Paluch */ @@ -30,6 +32,8 @@ public interface GitHubIssue { String getState(); + List getAssignees(); + /** * @return HTML URL. */ diff --git a/src/main/java/org/springframework/data/release/issues/github/GitHubReadIssue.java b/src/main/java/org/springframework/data/release/issues/github/GitHubReadIssue.java index caaf7d9..dee724d 100644 --- a/src/main/java/org/springframework/data/release/issues/github/GitHubReadIssue.java +++ b/src/main/java/org/springframework/data/release/issues/github/GitHubReadIssue.java @@ -18,6 +18,7 @@ package org.springframework.data.release.issues.github; import lombok.Value; import java.util.List; +import java.util.stream.Collectors; import com.fasterxml.jackson.annotation.JsonProperty; @@ -51,6 +52,11 @@ class GitHubReadIssue implements Comparable, GitHubIssue { this.labels = labels; } + @Override + public List getAssignees() { + return assignees.stream().map(GitHubUser::getName).collect(Collectors.toList()); + } + @Override public int compareTo(GitHubReadIssue o) { diff --git a/src/main/java/org/springframework/data/release/issues/github/GitHubWriteIssue.java b/src/main/java/org/springframework/data/release/issues/github/GitHubWriteIssue.java index e093c4d..5d33f78 100644 --- a/src/main/java/org/springframework/data/release/issues/github/GitHubWriteIssue.java +++ b/src/main/java/org/springframework/data/release/issues/github/GitHubWriteIssue.java @@ -23,6 +23,8 @@ import lombok.With; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; import org.springframework.util.Assert; @@ -66,4 +68,10 @@ class GitHubWriteIssue implements GitHubIssue { return withLabels(labels); } + + public List getAssignees() { + return assignees == null ? Collections.emptyList() + : assignees.stream().map(Objects::toString).collect(Collectors.toList()); + } + } diff --git a/src/main/java/org/springframework/data/release/issues/jira/Jira.java b/src/main/java/org/springframework/data/release/issues/jira/Jira.java index face465..d91da9f 100644 --- a/src/main/java/org/springframework/data/release/issues/jira/Jira.java +++ b/src/main/java/org/springframework/data/release/issues/jira/Jira.java @@ -313,20 +313,21 @@ class Jira implements JiraConnector { logger.log(moduleIteration, "Creating release ticket…"); - doCreateTicket(moduleIteration, Tracker.releaseTicketSummary(moduleIteration)); + doCreateTicket(moduleIteration, Tracker.releaseTicketSummary(moduleIteration), false); } @Override - public Ticket createTicket(ModuleIteration moduleIteration, String text, TicketType ticketType) { + public Ticket createTicket(ModuleIteration moduleIteration, String text, TicketType ticketType, + boolean assignToCurrentUser) { Assert.notNull(moduleIteration, "ModuleIteration must not be null."); logger.log(moduleIteration, "Creating ticket…"); - return doCreateTicket(moduleIteration, text); + return doCreateTicket(moduleIteration, text, assignToCurrentUser); } - private Ticket doCreateTicket(ModuleIteration moduleIteration, String text) { + private Ticket doCreateTicket(ModuleIteration moduleIteration, String text, boolean assignToCurrentUser) { HttpHeaders httpHeaders = new HttpHeaders(); Map parameters = newUrlTemplateVariables(); @@ -337,6 +338,10 @@ class Jira implements JiraConnector { JiraComponents jiraComponents = getJiraComponents(moduleIteration.getProjectKey()); JiraIssue jiraIssue = prepareJiraIssueToCreate(text, moduleIteration, jiraComponents); + if (assignToCurrentUser) { + jiraIssue.assignTo(jiraProperties.getUsername()); + } + CreatedJiraIssue created = operations.exchange(CREATE_ISSUES_TEMPLATE, HttpMethod.POST, new HttpEntity(jiraIssue, httpHeaders), CreatedJiraIssue.class, parameters).getBody(); @@ -739,7 +744,8 @@ class Jira implements JiraConnector { } return new Ticket(issue.getKey(), fields.getSummary(), - String.format("%s/browse/%s", jiraProperties.getApiUrl(), issue.getKey()), jiraTicketStatus); + String.format("%s/browse/%s", jiraProperties.getApiUrl(), issue.getKey()), + fields.getAssignee() != null ? fields.getAssignee().getName() : null, jiraTicketStatus); } private Map newUrlTemplateVariables() { diff --git a/src/main/java/org/springframework/data/release/issues/jira/JiraIssue.java b/src/main/java/org/springframework/data/release/issues/jira/JiraIssue.java index 3292f51..99fd67e 100644 --- a/src/main/java/org/springframework/data/release/issues/jira/JiraIssue.java +++ b/src/main/java/org/springframework/data/release/issues/jira/JiraIssue.java @@ -139,6 +139,10 @@ class JiraIssue { isPresent(); } + public void assignTo(String username) { + getFields().setAssignee(new JiraUser(username, null)); + } + @Data static class Fields { diff --git a/src/test/java/org/springframework/data/release/issues/jira/JiraConnectorIntegrationTests.java b/src/test/java/org/springframework/data/release/issues/jira/JiraConnectorIntegrationTests.java index 0814f70..74d606f 100644 --- a/src/test/java/org/springframework/data/release/issues/jira/JiraConnectorIntegrationTests.java +++ b/src/test/java/org/springframework/data/release/issues/jira/JiraConnectorIntegrationTests.java @@ -251,7 +251,7 @@ class JiraConnectorIntegrationTests extends AbstractIntegrationTests { mockService.stubFor(put(urlPathMatching("/rest/api/2/issue/DATAREDIS-302")).// willReturn(aResponse().withStatus(204))); - jira.assignTicketToMe(Projects.REDIS, new Ticket("DATAREDIS-302", "", null, null)); + jira.assignTicketToMe(Projects.REDIS, new Ticket("DATAREDIS-302", "", null)); verify(putRequestedFor(urlPathMatching("/rest/api/2/issue/DATAREDIS-302")).withRequestBody(equalToJson( "{\"update\":{\"assignee\":[ {\"set\":{\"name\":\"dummy\"}} ] }, \"transition\":{}, \"fields\":{}}"))); @@ -271,7 +271,7 @@ class JiraConnectorIntegrationTests extends AbstractIntegrationTests { mockService.stubFor(post(urlPathMatching("/rest/api/2/issue/DATACASS-302")).// willReturn(aResponse().withStatus(204))); - jira.assignTicketToMe(Projects.CASSANDRA, new Ticket("DATACASS-302", "", null, null)); + jira.assignTicketToMe(Projects.CASSANDRA, new Ticket("DATACASS-302", "", null)); verify(0, postRequestedFor(urlPathMatching("/rest/api/2/issue/DATACASS-302"))); } diff --git a/src/test/java/org/springframework/data/release/issues/jira/TicketsUnitTests.java b/src/test/java/org/springframework/data/release/issues/jira/TicketsUnitTests.java index 467932c..3668f68 100644 --- a/src/test/java/org/springframework/data/release/issues/jira/TicketsUnitTests.java +++ b/src/test/java/org/springframework/data/release/issues/jira/TicketsUnitTests.java @@ -37,7 +37,7 @@ class TicketsUnitTests { @Test void hasReleaseTicketShouldReturnTrue() { - Ticket ticket = new Ticket("1234", "Release 1.10 GA (Hopper)", null, JiraTicketStatus.of(false, "", "")); + Ticket ticket = new Ticket("1234", "Release 1.10 GA (Hopper)", JiraTicketStatus.of(false, "", "")); Tickets tickets = new Tickets(Collections.singletonList(ticket)); boolean result = tickets.hasReleaseTicket(ReleaseTrains.HOPPER.getModuleIteration(Projects.JPA, Iteration.GA)); @@ -47,7 +47,7 @@ class TicketsUnitTests { @Test void hasReleaseTickeForTicketWithoutTrainNameShouldReturnFalse() { - Ticket ticket = new Ticket("1234", "Release 1.10 GA", null, JiraTicketStatus.of(false, "", "")); + Ticket ticket = new Ticket("1234", "Release 1.10 GA", JiraTicketStatus.of(false, "", "")); Tickets tickets = new Tickets(Collections.singletonList(ticket)); boolean result = tickets.hasReleaseTicket(ReleaseTrains.HOPPER.getModuleIteration(Projects.JPA, Iteration.GA)); @@ -57,7 +57,7 @@ class TicketsUnitTests { @Test void getReleaseTicketReturnsReleaseTicket() { - Ticket ticket = new Ticket("1234", "Release 1.10 GA (Hopper)", null, JiraTicketStatus.of(false, "", "")); + Ticket ticket = new Ticket("1234", "Release 1.10 GA (Hopper)", JiraTicketStatus.of(false, "", "")); Tickets tickets = new Tickets(Collections.singletonList(ticket)); Ticket releaseTicket = tickets @@ -70,7 +70,7 @@ class TicketsUnitTests { Tickets tickets = new Tickets( Collections - .singletonList(new Ticket("1234", "Release 2.4 GA (2020.0.0)", null, JiraTicketStatus.of(false, "", "")))); + .singletonList(new Ticket("1234", "Release 2.4 GA (2020.0.0)", JiraTicketStatus.of(false, "", "")))); Ticket releaseTicket = tickets .getReleaseTicket(ReleaseTrains.OCKHAM.getModuleIteration(Projects.JPA, Iteration.GA)); @@ -78,14 +78,14 @@ class TicketsUnitTests { tickets = new Tickets( Collections - .singletonList(new Ticket("1234", "Release 2.4 M1 (2020.0.0)", null, JiraTicketStatus.of(false, "", "")))); + .singletonList(new Ticket("1234", "Release 2.4 M1 (2020.0.0)", JiraTicketStatus.of(false, "", "")))); releaseTicket = tickets.getReleaseTicket(ReleaseTrains.OCKHAM.getModuleIteration(Projects.JPA, Iteration.M1)); assertThat(releaseTicket).isNotNull(); tickets = new Tickets( Collections - .singletonList(new Ticket("1234", "Release 2.4.1 (2020.0.1)", null, JiraTicketStatus.of(false, "", "")))); + .singletonList(new Ticket("1234", "Release 2.4.1 (2020.0.1)", JiraTicketStatus.of(false, "", "")))); releaseTicket = tickets.getReleaseTicket(ReleaseTrains.OCKHAM.getModuleIteration(Projects.JPA, Iteration.SR1)); assertThat(releaseTicket).isNotNull(); @@ -94,7 +94,7 @@ class TicketsUnitTests { @Test void getReleaseTicketThrowsExceptionWithoutAReleaseTicket() { - Ticket ticket = new Ticket("1234", "Release 1.10 GA", null, JiraTicketStatus.of(false, "", "")); + Ticket ticket = new Ticket("1234", "Release 1.10 GA", JiraTicketStatus.of(false, "", "")); Tickets tickets = new Tickets(Collections.singletonList(ticket)); assertThatIllegalArgumentException().isThrownBy( @@ -104,7 +104,7 @@ class TicketsUnitTests { @Test void getResolvedReleaseTicket() { - Ticket ticket = new Ticket("1234", "Release 1.10 GA (Hopper)", null, JiraTicketStatus.of(true, "", "")); + Ticket ticket = new Ticket("1234", "Release 1.10 GA (Hopper)", JiraTicketStatus.of(true, "", "")); Tickets tickets = new Tickets(Collections.singletonList(ticket)); Ticket releaseTicket = tickets @@ -115,7 +115,7 @@ class TicketsUnitTests { @Test void getReleaseTicketsReturnsReleaseTickets() { - Ticket ticket = new Ticket("1234", "Release 1.10 GA (Hopper)", null, JiraTicketStatus.of(false, "", "")); + Ticket ticket = new Ticket("1234", "Release 1.10 GA (Hopper)", JiraTicketStatus.of(false, "", "")); Tickets tickets = new Tickets(Collections.singletonList(ticket)); Tickets result = tickets