You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ra...@apache.org on 2019/12/13 17:49:02 UTC
[sling-org-apache-sling-committer-cli] 01/01: SLING-8392 - Create
sub-command to manage the Jira update when promoting a release
This is an automated email from the ASF dual-hosted git repository.
radu pushed a commit to branch issue/SLING-8392
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-committer-cli.git
commit ef11655082ab96afd9c1e9a720fadd0e50ee3eef
Author: Radu Cotescu <ra...@apache.org>
AuthorDate: Fri Dec 13 18:46:04 2019 +0100
SLING-8392 - Create sub-command to manage the Jira update when promoting a release
* WIP
---
pom.xml | 6 +
src/main/features/app.json | 4 +
.../org/apache/sling/cli/impl/DateProvider.java | 8 +-
.../java/org/apache/sling/cli/impl/jira/Issue.java | 26 +++-
.../{DateProvider.java => jira/Transition.java} | 27 ++--
.../{DateProvider.java => jira/Transitions.java} | 22 +--
.../org/apache/sling/cli/impl/jira/Version.java | 20 ++-
.../apache/sling/cli/impl/jira/VersionClient.java | 164 ++++++++++++++++++---
.../cli/impl/release/CloseJiraVersionCommand.java | 75 ++++++++++
.../sling/cli/impl/http/HttpExchangeHandler.java | 19 +--
.../sling/cli/impl/jira/EditVersionJiraAction.java | 96 ++++++++++++
.../cli/impl/jira/IssuesSearchJiraAction.java | 18 ++-
.../org/apache/sling/cli/impl/jira/MockJira.java | 16 +-
.../sling/cli/impl/jira/TransitionsJiraAction.java | 66 +++++++++
.../sling/cli/impl/jira/VersionClientTest.java | 73 +++++++--
src/test/resources/jira/relatedIssueCounts/0.json | 6 +
...ter-cli-1.0.0.json => committer-cli-1.0.0.json} | 84 ++++++++++-
.../resources/jira/search/transitions-1.0.0.json | 53 +++++++
.../resources/jira/search/transitions-2.0.0.json | 53 +++++++
.../search/unresolved-committer-cli-1.0.0.json | 26 ----
.../resources/jira/transitions/no-transitions.json | 5 +
.../resources/jira/transitions/transitions.json | 41 ++++++
src/test/resources/jira/versions.json | 11 ++
23 files changed, 809 insertions(+), 110 deletions(-)
diff --git a/pom.xml b/pom.xml
index 4aac3cb..59339ec 100644
--- a/pom.xml
+++ b/pom.xml
@@ -200,6 +200,12 @@
</dependency>
<dependency>
<groupId>org.osgi</groupId>
+ <artifactId>osgi.promise</artifactId>
+ <version>7.0.1</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
<artifactId>osgi.core</artifactId>
</dependency>
<dependency>
diff --git a/src/main/features/app.json b/src/main/features/app.json
index 805b29c..4e6e732 100644
--- a/src/main/features/app.json
+++ b/src/main/features/app.json
@@ -66,6 +66,10 @@
"start-level": "3"
},
{
+ "id" : "org.osgi:osgi.promise:7.0.1",
+ "start-level": "4"
+ },
+ {
"id": "org.bouncycastle:bcpg-jdk15on:1.62"
},
{
diff --git a/src/main/java/org/apache/sling/cli/impl/DateProvider.java b/src/main/java/org/apache/sling/cli/impl/DateProvider.java
index 627cca8..9e114d1 100644
--- a/src/main/java/org/apache/sling/cli/impl/DateProvider.java
+++ b/src/main/java/org/apache/sling/cli/impl/DateProvider.java
@@ -27,14 +27,20 @@ import org.osgi.service.component.annotations.Component;
public class DateProvider {
private static final DateTimeFormatter emailDateHeader = DateTimeFormatter.ofPattern("EEE, d MMM yyyy HH:mm:ss Z");
+ private static final DateTimeFormatter jiraReleaseDate = DateTimeFormatter.ofPattern("yyyy-MM-d");
public OffsetDateTime getCurrentDate() {
return OffsetDateTime.now();
}
public String getCurrentDateForEmailHeader() {
- OffsetDateTime offsetDateTime = OffsetDateTime.now();
+ OffsetDateTime offsetDateTime = getCurrentDate();
return offsetDateTime.format(emailDateHeader);
}
+ public String getCurrentDateForJiraRelease() {
+ OffsetDateTime offsetDateTime = getCurrentDate();
+ return offsetDateTime.format(jiraReleaseDate);
+ }
+
}
diff --git a/src/main/java/org/apache/sling/cli/impl/jira/Issue.java b/src/main/java/org/apache/sling/cli/impl/jira/Issue.java
index 1fb3c6c..87c474f 100644
--- a/src/main/java/org/apache/sling/cli/impl/jira/Issue.java
+++ b/src/main/java/org/apache/sling/cli/impl/jira/Issue.java
@@ -33,9 +33,33 @@ public class Issue {
public String getSummary() {
return fields.summary;
}
+
+ public String getStatus() {
+ if (fields.status != null) {
+ return fields.status.name;
+ }
+ return null;
+ }
+
+ public String getResolution() {
+ if (fields.resolution != null) {
+ return fields.resolution.name;
+ }
+ return null;
+ }
static class Fields {
- private String summary;
+ private String summary;
+ private Status status;
+ private Resolution resolution;
+
+ static class Status {
+ private String name;
+ }
+
+ static class Resolution {
+ private String name;
+ }
}
@Override
diff --git a/src/main/java/org/apache/sling/cli/impl/DateProvider.java b/src/main/java/org/apache/sling/cli/impl/jira/Transition.java
similarity index 61%
copy from src/main/java/org/apache/sling/cli/impl/DateProvider.java
copy to src/main/java/org/apache/sling/cli/impl/jira/Transition.java
index 627cca8..e5a10df 100644
--- a/src/main/java/org/apache/sling/cli/impl/DateProvider.java
+++ b/src/main/java/org/apache/sling/cli/impl/jira/Transition.java
@@ -16,25 +16,26 @@
~ specific language governing permissions and limitations
~ under the License.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-package org.apache.sling.cli.impl;
+package org.apache.sling.cli.impl.jira;
-import java.time.OffsetDateTime;
-import java.time.format.DateTimeFormatter;
+public class Transition {
-import org.osgi.service.component.annotations.Component;
+ private int id;
+ private String name;
-@Component(service = DateProvider.class)
-public class DateProvider {
-
- private static final DateTimeFormatter emailDateHeader = DateTimeFormatter.ofPattern("EEE, d MMM yyyy HH:mm:ss Z");
+ public int getId() {
+ return id;
+ }
- public OffsetDateTime getCurrentDate() {
- return OffsetDateTime.now();
+ public void setId(int id) {
+ this.id = id;
}
- public String getCurrentDateForEmailHeader() {
- OffsetDateTime offsetDateTime = OffsetDateTime.now();
- return offsetDateTime.format(emailDateHeader);
+ public String getName() {
+ return name;
}
+ public void setName(String name) {
+ this.name = name;
+ }
}
diff --git a/src/main/java/org/apache/sling/cli/impl/DateProvider.java b/src/main/java/org/apache/sling/cli/impl/jira/Transitions.java
similarity index 61%
copy from src/main/java/org/apache/sling/cli/impl/DateProvider.java
copy to src/main/java/org/apache/sling/cli/impl/jira/Transitions.java
index 627cca8..f0da543 100644
--- a/src/main/java/org/apache/sling/cli/impl/DateProvider.java
+++ b/src/main/java/org/apache/sling/cli/impl/jira/Transitions.java
@@ -16,25 +16,19 @@
~ specific language governing permissions and limitations
~ under the License.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-package org.apache.sling.cli.impl;
+package org.apache.sling.cli.impl.jira;
-import java.time.OffsetDateTime;
-import java.time.format.DateTimeFormatter;
+import java.util.List;
-import org.osgi.service.component.annotations.Component;
+public class Transitions {
-@Component(service = DateProvider.class)
-public class DateProvider {
+ private List<Transition> transitions;
- private static final DateTimeFormatter emailDateHeader = DateTimeFormatter.ofPattern("EEE, d MMM yyyy HH:mm:ss Z");
-
- public OffsetDateTime getCurrentDate() {
- return OffsetDateTime.now();
+ public List<Transition> getTransitions() {
+ return transitions;
}
- public String getCurrentDateForEmailHeader() {
- OffsetDateTime offsetDateTime = OffsetDateTime.now();
- return offsetDateTime.format(emailDateHeader);
+ public void setTransitions(List<Transition> transitions) {
+ this.transitions = transitions;
}
-
}
diff --git a/src/main/java/org/apache/sling/cli/impl/jira/Version.java b/src/main/java/org/apache/sling/cli/impl/jira/Version.java
index 173971e..1cea6fd 100644
--- a/src/main/java/org/apache/sling/cli/impl/jira/Version.java
+++ b/src/main/java/org/apache/sling/cli/impl/jira/Version.java
@@ -20,6 +20,8 @@ public class Version {
private int id;
private String name;
private int issuesFixedCount;
+ private boolean released;
+ private String releaseDate;
public int getId() {
return id;
@@ -44,7 +46,23 @@ public class Version {
public void setRelatedIssuesCount(int relatedIssuesCount) {
this.issuesFixedCount = relatedIssuesCount;
}
-
+
+ public boolean isReleased() {
+ return released;
+ }
+
+ public void setReleased(boolean released) {
+ this.released = released;
+ }
+
+ public String getReleaseDate() {
+ return releaseDate;
+ }
+
+ public void setReleaseDate(String releaseDate) {
+ this.releaseDate = releaseDate;
+ }
+
@Override
public String toString() {
diff --git a/src/main/java/org/apache/sling/cli/impl/jira/VersionClient.java b/src/main/java/org/apache/sling/cli/impl/jira/VersionClient.java
index 0980375..79b3d7d 100644
--- a/src/main/java/org/apache/sling/cli/impl/jira/VersionClient.java
+++ b/src/main/java/org/apache/sling/cli/impl/jira/VersionClient.java
@@ -20,12 +20,15 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
+import java.util.stream.Collectors;
import org.apache.http.HttpHeaders;
import org.apache.http.client.methods.CloseableHttpResponse;
@@ -36,12 +39,18 @@ import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.sling.cli.impl.ComponentContextHelper;
+import org.apache.sling.cli.impl.DateProvider;
import org.apache.sling.cli.impl.http.HttpClientFactory;
import org.apache.sling.cli.impl.release.Release;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
+import org.osgi.util.promise.FailedPromisesException;
+import org.osgi.util.promise.Promise;
+import org.osgi.util.promise.PromiseFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.JsonIOException;
@@ -54,19 +63,29 @@ import com.google.gson.stream.JsonWriter;
*/
@Component(service = VersionClient.class)
public class VersionClient {
-
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(VersionClient.class);
+
private static final String PROJECT_KEY = "SLING";
- private static final String DEFAULT_JIRA_URL_PREFIX = "https://issues.apache.org/jira/rest/api/2/";
+ private static final String DEFAULT_JIRA_URL = "https://issues.apache.org/jira";
private static final String CONTENT_TYPE_JSON = "application/json";
-
+
+ private final PromiseFactory promiseFactory = new PromiseFactory(null, null);
+
@Reference
private HttpClientFactory httpClientFactory;
- private String jiraUrlPrefix;
+
+ @Reference
+ private DateProvider dateProvider;
+
+ private String jiraRESTAPIEntrypoint;
+ private String jiraURL;
@Activate
protected void activate(ComponentContext ctx) {
ComponentContextHelper helper = ComponentContextHelper.wrap(ctx);
- jiraUrlPrefix = helper.getProperty("jira.url.prefix", DEFAULT_JIRA_URL_PREFIX);
+ jiraURL = helper.getProperty("jira.url", DEFAULT_JIRA_URL);
+ jiraRESTAPIEntrypoint = jiraURL + "/rest/api/2/";
}
/**
@@ -160,35 +179,100 @@ public class VersionClient {
}
}
+ private HttpGet newGet(String suffix) {
+ HttpGet get = new HttpGet(jiraRESTAPIEntrypoint + suffix);
+ get.addHeader(HttpHeaders.ACCEPT, CONTENT_TYPE_JSON);
+ return get;
+ }
+
private HttpPost newPost(String suffix) {
- HttpPost post = new HttpPost(jiraUrlPrefix + suffix);
+ HttpPost post = new HttpPost(jiraRESTAPIEntrypoint + suffix);
post.addHeader(HttpHeaders.CONTENT_TYPE, CONTENT_TYPE_JSON);
post.addHeader(HttpHeaders.ACCEPT, CONTENT_TYPE_JSON);
return post;
}
private HttpPut newPut(String suffix) {
- HttpPut put = new HttpPut(jiraUrlPrefix + suffix);
+ HttpPut put = new HttpPut(jiraRESTAPIEntrypoint + suffix);
put.addHeader(HttpHeaders.CONTENT_TYPE, CONTENT_TYPE_JSON);
put.addHeader(HttpHeaders.ACCEPT, CONTENT_TYPE_JSON);
return put;
}
public List<Issue> findUnresolvedIssues(Release release) throws IOException {
- return findIssues("is empty", release);
+ return findIssues(release).stream().filter(issue -> issue.getResolution() == null).collect(Collectors.toList());
}
public List<Issue> findFixedIssues(Release release) throws IOException {
- return findIssues("is not empty", release);
+ return findIssues(release).stream().filter(issue -> issue.getResolution() != null).collect(Collectors.toList());
+ }
+
+ private void closeIssues(List<Issue> issues) throws IOException {
+ List<Promise<Issue>> closedIssues = new ArrayList<>();
+ for (Issue issue : issues) {
+ closedIssues.add(getCloseTransition(issue).then(closeTransition -> closeIssue(issue, closeTransition.getValue())));
+ }
+ Promise<List<Issue>> closedFixedIssues = promiseFactory.all(closedIssues);
+ Throwable failed;
+ try {
+ failed = closedFixedIssues.getFailure();
+ if (failed != null) {
+ if (failed instanceof FailedPromisesException) {
+ FailedPromisesException failedPromisesException = (FailedPromisesException) failed;
+ StringBuilder failureMessages = new StringBuilder();
+ for (Promise<?> promise : failedPromisesException.getFailedPromises()) {
+ failureMessages.append(promise.getFailure().getMessage()).append("\n");
+ }
+ throw new IOException("Unable to close the following issues:\n" + failureMessages.toString());
+ } else {
+ throw new IOException(failed);
+ }
+ }
+ } catch (InterruptedException e) {
+ throw new IOException(e);
+ }
+ }
+
+ public void release(Release release) throws IOException {
+ List<Issue> issues = findIssues(release);
+ List<Issue> unresolvedIssues = new ArrayList<>();
+ issues.forEach(issue -> {
+ if (issue.getResolution() == null) {
+ unresolvedIssues.add(issue);
+ }
+ });
+ if (unresolvedIssues.size() == 0) {
+ closeIssues(issues);
+ Version version = find(release);
+ HttpPut put = newPut("version/" + version.getId());
+ StringWriter w = new StringWriter();
+ try (JsonWriter jw = new Gson().newJsonWriter(w)) {
+ jw.beginObject().name("released").value(true).name("releaseDate").value(dateProvider.getCurrentDateForJiraRelease()).endObject();
+ }
+ put.setEntity(new StringEntity(w.toString()));
+ try (CloseableHttpClient client = httpClientFactory.newClient()) {
+ try (CloseableHttpResponse response = client.execute(put, httpClientFactory.newPreemptiveAuthenticationContext())) {
+ int statusCode = response.getStatusLine().getStatusCode();
+ if (statusCode != 200) {
+ throw new IOException(String.format("Unable to mark %s as released. Got status code %d.", release.getFullName(),
+ statusCode));
+ }
+ }
+ }
+ } else {
+ String report =
+ unresolvedIssues.stream().map(issue -> String.format("%s/browse/%s", jiraURL, issue.getKey())).collect(Collectors.joining(System.lineSeparator()));
+ throw new IllegalStateException("The following issues are not fixed:\n" + report);
+ }
}
- private List<Issue> findIssues(String resolution, Release release) throws IOException {
+ private List<Issue> findIssues(Release release) throws IOException {
try {
HttpGet get = newGet("search");
URIBuilder builder = new URIBuilder(get.getURI());
builder.addParameter("jql",
- String.format("project = %s AND resolution %s AND fixVersion = \"%s\"", PROJECT_KEY, resolution, release.getName()));
- builder.addParameter("fields", "summary");
+ String.format("project = %s AND fixVersion = \"%s\"", PROJECT_KEY, release.getName()));
+ builder.addParameter("fields", "summary,status,resolution");
get.setURI(builder.build());
try (CloseableHttpClient client = httpClientFactory.newClient()) {
@@ -255,12 +339,6 @@ public class VersionClient {
}
}
- private HttpGet newGet(String suffix) {
- HttpGet get = new HttpGet(jiraUrlPrefix + suffix);
- get.addHeader(HttpHeaders.ACCEPT, CONTENT_TYPE_JSON);
- return get;
- }
-
private void populateRelatedIssuesCount(CloseableHttpClient client, Version version) throws IOException {
HttpGet get = newGet("version/" + version.getId() +"/relatedIssueCounts");
@@ -340,4 +418,54 @@ public class VersionClient {
throw new RuntimeException(e);
}
}
+
+ private Promise<Transition> getCloseTransition(Issue issue) {
+ HttpGet get = newGet("issue/" + issue.getId() + "/transitions");
+ try {
+ try (CloseableHttpClient client = httpClientFactory.newClient()) {
+ try (CloseableHttpResponse getResponse = client.execute(get)) {
+ try (InputStream getContent = getResponse.getEntity().getContent();
+ InputStreamReader getReader = new InputStreamReader(getContent)) {
+ if (getResponse.getStatusLine().getStatusCode() != 200) {
+ throw newException(getResponse, getReader);
+ }
+ Gson gson = new Gson();
+ List<Transition> transitions = gson.fromJson(getReader, Transitions.class).getTransitions();
+ Optional<Transition> transition = transitions.stream().filter(t -> "Close Issue".equals(t.getName())).findFirst();
+ if (transition.isPresent()) {
+ return promiseFactory.resolved(transition.get());
+ } else {
+ return promiseFactory
+ .failed(new IllegalStateException(String.format("Issue %s/browse/%s cannot be closed - missing Close " +
+ "transition.", jiraURL,
+ issue.getKey())));
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ return promiseFactory.failed(e);
+ }
+ }
+
+ private Promise<Issue> closeIssue(Issue issue, Transition closeTransition) {
+ HttpPost post = newPost("issue/" + issue.getId() + "/transitions");
+ StringWriter w = new StringWriter();
+ try (JsonWriter jw = new Gson().newJsonWriter(w)) {
+ jw.beginObject().name("transition").beginObject().name("id").value(closeTransition.getId()).endObject().endObject();
+ post.setEntity(new StringEntity(w.toString()));
+ try (CloseableHttpClient client = httpClientFactory.newClient()) {
+ try (CloseableHttpResponse postResponse = client.execute(post, httpClientFactory.newPreemptiveAuthenticationContext())) {
+ if (postResponse.getStatusLine().getStatusCode() == 204) {
+ return promiseFactory.resolved(issue);
+ } else {
+ return promiseFactory.failed(new RuntimeException(String.format("Unable to close issue %s/browse/%s - got status code %d.",
+ jiraURL, issue.getKey(), postResponse.getStatusLine().getStatusCode())));
+ }
+ }
+ }
+ } catch (IOException e) {
+ return promiseFactory.failed(e);
+ }
+ }
}
diff --git a/src/main/java/org/apache/sling/cli/impl/release/CloseJiraVersionCommand.java b/src/main/java/org/apache/sling/cli/impl/release/CloseJiraVersionCommand.java
new file mode 100644
index 0000000..8048436
--- /dev/null
+++ b/src/main/java/org/apache/sling/cli/impl/release/CloseJiraVersionCommand.java
@@ -0,0 +1,75 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied. See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package org.apache.sling.cli.impl.release;
+
+import org.apache.sling.cli.impl.Command;
+import org.apache.sling.cli.impl.jira.VersionClient;
+import org.apache.sling.cli.impl.nexus.RepositoryService;
+import org.apache.sling.cli.impl.nexus.StagingRepository;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import picocli.CommandLine;
+
+@Component(service = Command.class,
+ property = {
+ Command.PROPERTY_NAME_COMMAND_GROUP + "=" + CloseJiraVersionCommand.GROUP,
+ Command.PROPERTY_NAME_COMMAND_NAME + "=" + CloseJiraVersionCommand.NAME
+ }
+)
+@CommandLine.Command(
+ name = CloseJiraVersionCommand.NAME,
+ description = "Closes the found Jira versions by closing all their fixed issues and marking the versions as released with the " +
+ "current date.",
+ subcommands = CommandLine.HelpCommand.class
+)
+public class CloseJiraVersionCommand implements Command {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(CloseJiraVersionCommand.class);
+
+ static final String GROUP = "release";
+ static final String NAME = "close-jira-version";
+
+ @CommandLine.Option(names = {"-r", "--repository"}, description = "Nexus repository id", required = true)
+ private Integer repositoryId;
+
+ @Reference
+ private RepositoryService repositoryService;
+
+ @Reference
+ private VersionClient versionClient;
+
+ @CommandLine.Mixin
+ private ReusableCLIOptions reusableCLIOptions;
+
+ @Override
+ public void run() {
+ try {
+ StagingRepository repo = repositoryService.find(repositoryId);
+ for (Release release : repositoryService.getReleases(repo)) {
+// versionClient.closeFixedIssues(release);
+ versionClient.release(release);
+ }
+ } catch (Exception e) {
+ LOGGER.warn("Failed executing command.", e);
+ }
+ }
+}
diff --git a/src/test/java/org/apache/sling/cli/impl/http/HttpExchangeHandler.java b/src/test/java/org/apache/sling/cli/impl/http/HttpExchangeHandler.java
index 660d34c..53bcc93 100644
--- a/src/test/java/org/apache/sling/cli/impl/http/HttpExchangeHandler.java
+++ b/src/test/java/org/apache/sling/cli/impl/http/HttpExchangeHandler.java
@@ -29,15 +29,16 @@ import com.sun.net.httpserver.HttpExchange;
public interface HttpExchangeHandler {
default void serveFileFromClasspath(HttpExchange ex, String classpathLocation) throws IOException {
- InputStream in = getClass().getResourceAsStream(classpathLocation);
- if ( in == null ) {
- ex.sendResponseHeaders(404, -1);
- return;
- }
-
- ex.sendResponseHeaders(200, 0);
- try ( OutputStream out = ex.getResponseBody() ) {
- IOUtils.copy(in, out);
+ try (InputStream in = getClass().getResourceAsStream(classpathLocation)) {
+ if (in == null) {
+ ex.sendResponseHeaders(404, -1);
+ return;
+ }
+
+ ex.sendResponseHeaders(200, 0);
+ try (OutputStream out = ex.getResponseBody()) {
+ IOUtils.copy(in, out);
+ }
}
}
diff --git a/src/test/java/org/apache/sling/cli/impl/jira/EditVersionJiraAction.java b/src/test/java/org/apache/sling/cli/impl/jira/EditVersionJiraAction.java
new file mode 100644
index 0000000..3b8e295
--- /dev/null
+++ b/src/test/java/org/apache/sling/cli/impl/jira/EditVersionJiraAction.java
@@ -0,0 +1,96 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied. See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package org.apache.sling.cli.impl.jira;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.reflect.Type;
+import java.util.List;
+import java.util.Optional;
+import java.util.regex.Pattern;
+
+import org.apache.sling.cli.impl.DateProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import com.sun.net.httpserver.HttpExchange;
+
+public class EditVersionJiraAction implements JiraAction {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(EditVersionJiraAction.class);
+ private static final Pattern VERSION_ID = Pattern.compile("/jira/rest/api/2/version/\\d+$");
+
+ @Override
+ public boolean tryHandle(HttpExchange ex) throws IOException {
+ if (!VERSION_ID.matcher(ex.getRequestURI().getPath()).matches()) {
+ return false;
+ }
+ String versionIdString = ex.getRequestURI().getPath().substring(25);
+ try {
+ Integer versionId = Integer.parseInt(versionIdString);
+ try (InputStream in = getClass().getResourceAsStream("/jira/versions.json")) {
+ if (in == null) {
+ ex.sendResponseHeaders(404, -1);
+ }
+ try (InputStreamReader reader = new InputStreamReader(in)) {
+ Gson gson = new Gson();
+ Type collectionType = TypeToken.getParameterized(List.class, Version.class).getType();
+ List<Version> versions = gson.fromJson(reader, collectionType);
+ Optional<Version> versionHolder = versions.stream().filter(v -> versionId == v.getId()).findFirst();
+ if (versionHolder.isEmpty()) {
+ ex.sendResponseHeaders(404, -1);
+ } else {
+ if ("PUT".equals(ex.getRequestMethod())) {
+ // version change
+ Version version = versionHolder.get();
+ try (InputStreamReader requestReader = new InputStreamReader(ex.getRequestBody())) {
+ VersionToUpdate versionToUpdate = gson.fromJson(requestReader, VersionToUpdate.class);
+ DateProvider dateProvider = new DateProvider();
+ if (versionToUpdate.released != version.isReleased() && versionToUpdate.released &&
+ dateProvider.getCurrentDateForJiraRelease().equals(versionToUpdate.releaseDate) &&
+ version.getReleaseDate() == null) {
+ ex.sendResponseHeaders(200, -1);
+ }
+ }
+ }
+ ex.sendResponseHeaders(406, -1);
+ }
+ }
+ }
+
+ } catch (NumberFormatException e) {
+ LOGGER.error("Unable to parse version id from " + ex.getRequestURI().getPath(), e);
+ ex.sendResponseHeaders(400, -1);
+ }
+ return true;
+ }
+
+ static class VersionToUpdate {
+ private String description;
+ private String name;
+ private boolean archived;
+ private boolean released;
+ private String releaseDate;
+ private String userReleaseDate;
+ private String projectId;
+ }
+}
diff --git a/src/test/java/org/apache/sling/cli/impl/jira/IssuesSearchJiraAction.java b/src/test/java/org/apache/sling/cli/impl/jira/IssuesSearchJiraAction.java
index 13bed5d..7783cbe 100644
--- a/src/test/java/org/apache/sling/cli/impl/jira/IssuesSearchJiraAction.java
+++ b/src/test/java/org/apache/sling/cli/impl/jira/IssuesSearchJiraAction.java
@@ -28,8 +28,9 @@ import com.sun.net.httpserver.HttpExchange;
public class IssuesSearchJiraAction implements JiraAction {
- private static final String UNRESOLVED_QUERY = "project = SLING AND resolution is empty AND fixVersion = \"Committer CLI 1.0.0\"";
- private static final String FIXED_QUERY = "project = SLING AND resolution is not empty AND fixVersion = \"Committer CLI 1.0.0\"";
+ private static final String COMMITTER_CLI_1_0_0_QUERY = "project = SLING AND fixVersion = \"Committer CLI 1.0.0\"";
+ private static final String TRANSITIONS_1_0_0_QUERY = "project = SLING AND fixVersion = \"Transitions 1.0.0\"";
+ private static final String TRANSITIONS_2_0_0_QUERY = "project = SLING AND fixVersion = \"Transitions 2.0.0\"";
@Override
public boolean tryHandle(HttpExchange ex) throws IOException {
@@ -42,17 +43,20 @@ public class IssuesSearchJiraAction implements JiraAction {
for ( NameValuePair pair : parsed ) {
if ( "jql".equals(pair.getName())) {
- if (UNRESOLVED_QUERY.equals(pair.getValue())) {
- serveFileFromClasspath(ex, "/jira/search/unresolved-committer-cli-1.0.0.json");
+ if (COMMITTER_CLI_1_0_0_QUERY.equals(pair.getValue())) {
+ serveFileFromClasspath(ex, "/jira/search/committer-cli-1.0.0.json");
return true;
- } else if (FIXED_QUERY.equals(pair.getValue())) {
- serveFileFromClasspath(ex, "/jira/search/fixed-committer-cli-1.0.0.json");
+ } else if (TRANSITIONS_1_0_0_QUERY.equals(pair.getValue())) {
+ serveFileFromClasspath(ex, "/jira/search/transitions-1.0.0.json");
+ return true;
+ } else if (TRANSITIONS_2_0_0_QUERY.equals(pair.getValue())) {
+ serveFileFromClasspath(ex, "/jira/search/transitions-2.0.0.json");
return true;
}
}
}
error(ex, new Gson(), er -> er.getErrorMessages().add("Unable to run unknown JQL query, available ones are [" +
- UNRESOLVED_QUERY + "," + FIXED_QUERY +"]"));
+ COMMITTER_CLI_1_0_0_QUERY + "," + TRANSITIONS_1_0_0_QUERY +"]"));
return true;
}
diff --git a/src/test/java/org/apache/sling/cli/impl/jira/MockJira.java b/src/test/java/org/apache/sling/cli/impl/jira/MockJira.java
index 73ccb5b..b67557a 100644
--- a/src/test/java/org/apache/sling/cli/impl/jira/MockJira.java
+++ b/src/test/java/org/apache/sling/cli/impl/jira/MockJira.java
@@ -19,6 +19,8 @@ package org.apache.sling.cli.impl.jira;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
+import java.util.regex.Pattern;
import org.apache.sling.cli.impl.http.HttpExchangeHandler;
import org.junit.rules.ExternalResource;
@@ -34,6 +36,7 @@ public class MockJira extends ExternalResource {
static final String AUTH_USER = "asf-user";
static final String AUTH_PWD = "asf-password";
+ private static final Set<Pattern> GET_PATHS_REQUIRING_AUTH = Set.of(Pattern.compile("/jira/rest/api/2/issue/\\d{1,10}/transitions"));
public static void main(String[] args) throws Throwable {
@@ -57,9 +60,14 @@ public class MockJira extends ExternalResource {
@Override
public Result authenticate(HttpExchange t) {
- // get requests are never authenticated
- if ( t.getRequestMethod().contentEquals("GET") )
+ if (t.getRequestMethod().contentEquals("GET")) {
+ for (Pattern pathPattern : GET_PATHS_REQUIRING_AUTH) {
+ if (pathPattern.matcher(t.getHttpContext().getPath()).matches()) {
+ return super.authenticate(t);
+ }
+ }
return new Authenticator.Success(new HttpPrincipal("anonymous", getClass().getSimpleName()));
+ }
return super.authenticate(t);
}
});
@@ -69,7 +77,9 @@ public class MockJira extends ExternalResource {
actions.add(new GetRelatedIssueCountsForVersionsJiraAction());
actions.add(new CreateVersionJiraAction());
actions.add(new IssuesSearchJiraAction());
-
+ actions.add(new TransitionsJiraAction());
+ actions.add(new EditVersionJiraAction());
+
// fallback, always executed
actions.add(ex -> {
ex.sendResponseHeaders(400, -1);
diff --git a/src/test/java/org/apache/sling/cli/impl/jira/TransitionsJiraAction.java b/src/test/java/org/apache/sling/cli/impl/jira/TransitionsJiraAction.java
new file mode 100644
index 0000000..22d77b7
--- /dev/null
+++ b/src/test/java/org/apache/sling/cli/impl/jira/TransitionsJiraAction.java
@@ -0,0 +1,66 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied. See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package org.apache.sling.cli.impl.jira;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.regex.Pattern;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonParseException;
+import com.sun.net.httpserver.HttpExchange;
+
+public class TransitionsJiraAction implements JiraAction {
+
+ private static final Pattern TRANSITIONS = Pattern.compile("/jira/rest/api/2/issue/\\d+/transitions");
+ private static final Pattern RETURN_NO_TRANSITIONS = Pattern.compile("/jira/rest/api/2/issue/(1|2|3)/transitions");
+
+ @Override
+ public boolean tryHandle(HttpExchange ex) throws IOException {
+ if (!TRANSITIONS.matcher(ex.getRequestURI().getPath()).matches()) {
+ return false;
+ }
+ if (ex.getRequestMethod().equals("GET")) {
+ if (RETURN_NO_TRANSITIONS.matcher(ex.getRequestURI().getPath()).matches()) {
+ serveFileFromClasspath(ex, "/jira/transitions/no-transitions.json");
+ } else {
+ serveFileFromClasspath(ex, "/jira/transitions/transitions.json");
+ }
+ } else if (ex.getRequestMethod().equals("POST")) {
+ Gson gson = new Gson();
+ try ( InputStreamReader reader = new InputStreamReader(ex.getRequestBody())) {
+ TransitionToExecute transitionToExecute = gson.fromJson(reader, TransitionToExecute.class);
+ if (701 == transitionToExecute.transition.getId()) {
+ ex.sendResponseHeaders(204, -1);
+ } else {
+ ex.sendResponseHeaders(400, -1);
+ }
+ } catch (JsonParseException e) {
+ ex.sendResponseHeaders(400, -1);
+ }
+ }
+ return true;
+ }
+
+ private class TransitionToExecute {
+ private Transition transition;
+ }
+
+
+}
diff --git a/src/test/java/org/apache/sling/cli/impl/jira/VersionClientTest.java b/src/test/java/org/apache/sling/cli/impl/jira/VersionClientTest.java
index ae75090..f215930 100644
--- a/src/test/java/org/apache/sling/cli/impl/jira/VersionClientTest.java
+++ b/src/test/java/org/apache/sling/cli/impl/jira/VersionClientTest.java
@@ -16,18 +16,13 @@
*/
package org.apache.sling.cli.impl.jira;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.hasSize;
-import static org.hamcrest.Matchers.notNullValue;
-import static org.hamcrest.Matchers.nullValue;
-import static org.junit.Assert.assertThat;
-
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.sling.cli.impl.CredentialsService;
+import org.apache.sling.cli.impl.DateProvider;
import org.apache.sling.cli.impl.http.HttpClientFactory;
import org.apache.sling.cli.impl.junit.SystemPropertiesRule;
import org.apache.sling.cli.impl.release.Release;
@@ -36,6 +31,14 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
public class VersionClientTest {
private static final Map<String, String> SYSTEM_PROPS = new HashMap<>();
@@ -52,15 +55,15 @@ public class VersionClientTest {
@Rule
public final MockJira mockJira = new MockJira();
-
+
private VersionClient versionClient;
@Before
public void prepareDependencies() throws ReflectiveOperationException {
-
context.registerInjectActivateService(new CredentialsService());
+ context.registerInjectActivateService(new DateProvider());
context.registerInjectActivateService(new HttpClientFactory(), "jira.host", "localhost", "jira.port", mockJira.getBoundPort());
- versionClient = context.registerInjectActivateService(new VersionClient(), "jira.url.prefix", "http://localhost:" + mockJira.getBoundPort() + "/jira/rest/api/2/");
+ versionClient = context.registerInjectActivateService(new VersionClient(), "jira.url", "http://localhost:" + mockJira.getBoundPort() + "/jira");
}
@Test
@@ -111,7 +114,9 @@ public class VersionClientTest {
assertThat(issues, hasSize(2));
assertThat(issues.get(0).getKey(), equalTo("SLING-8338"));
+ assertThat(issues.get(0).getStatus(), equalTo("Open"));
assertThat(issues.get(1).getKey(), equalTo("SLING-8337"));
+ assertThat(issues.get(1).getStatus(), equalTo("Open"));
}
@Test
@@ -120,11 +125,61 @@ public class VersionClientTest {
assertThat(issues, hasSize(7));
assertThat(issues.get(0).getKey(), equalTo("SLING-8707"));
+ assertThat(issues.get(0).getStatus(), equalTo("Resolved"));
assertThat(issues.get(1).getKey(), equalTo("SLING-8699"));
+ assertThat(issues.get(1).getStatus(), equalTo("Resolved"));
assertThat(issues.get(2).getKey(), equalTo("SLING-8395"));
+ assertThat(issues.get(2).getStatus(), equalTo("Resolved"));
assertThat(issues.get(3).getKey(), equalTo("SLING-8394"));
+ assertThat(issues.get(3).getStatus(), equalTo("Resolved"));
assertThat(issues.get(4).getKey(), equalTo("SLING-8393"));
+ assertThat(issues.get(4).getStatus(), equalTo("Resolved"));
assertThat(issues.get(5).getKey(), equalTo("SLING-8392"));
+ assertThat(issues.get(5).getStatus(), equalTo("Resolved"));
assertThat(issues.get(6).getKey(), equalTo("SLING-8338"));
+ assertThat(issues.get(6).getStatus(), equalTo("Resolved"));
}
+
+// @Test
+// public void closeIssues() throws IOException {
+// Release release = Release.fromString("Committer CLI 1.0.0").get(0);
+// versionClient.closeFixedIssues(release);
+// }
+//
+// @Test
+// public void closeIssuesFailWhenNoTransitionsAreAvailable() {
+// Release release = Release.fromString("Transitions 1.0.0").get(0);
+// IOException exception = null;
+// try {
+// versionClient.closeFixedIssues(release);
+// } catch (IOException e) {
+// exception = e;
+// }
+// assertNotNull("Issues without a Close transition should not have been reported as closed.", exception);
+// String message = exception.getMessage();
+// assertTrue("SLING-0001 should have not been reported as closed.", message.contains("SLING-0001"));
+// assertTrue("SLING-0002 should have not been reported as closed.", message.contains("SLING-0002"));
+// assertTrue("SLING-0003 should have not been reported as closed.", message.contains("SLING-0003"));
+// }
+
+ @Test
+ public void releaseWithUnresolvedIssues() throws IOException {
+ Release release = Release.fromString("Committer CLI 1.0.0").get(0);
+ Exception exception = null;
+ try {
+ versionClient.release(release);
+ } catch (IllegalStateException e) {
+ exception = e;
+ }
+ assertNotNull("The VersionClient should not have allowed a release with unresolved issues.", exception);
+ assertTrue("SLING-8337 should have been reported as unresolved.", exception.getMessage().contains("SLING-8337"));
+ assertTrue("SLING-8338 should have been reported as unresolved.", exception.getMessage().contains("SLING-8338"));
+ }
+
+ @Test
+ public void release() throws IOException {
+ Release release = Release.fromString("Transitions 2.0.0").get(0);
+ versionClient.release(release);
+ }
+
}
diff --git a/src/test/resources/jira/relatedIssueCounts/0.json b/src/test/resources/jira/relatedIssueCounts/0.json
new file mode 100644
index 0000000..c4cfeb5
--- /dev/null
+++ b/src/test/resources/jira/relatedIssueCounts/0.json
@@ -0,0 +1,6 @@
+{
+ "self": "https://issues.apache.org/jira/rest/api/2/version/0",
+ "issuesFixedCount": 3,
+ "issuesAffectedCount": 3,
+ "issueCountWithCustomFieldsShowingVersion": 0
+}
diff --git a/src/test/resources/jira/search/fixed-committer-cli-1.0.0.json b/src/test/resources/jira/search/committer-cli-1.0.0.json
similarity index 50%
rename from src/test/resources/jira/search/fixed-committer-cli-1.0.0.json
rename to src/test/resources/jira/search/committer-cli-1.0.0.json
index dbd6685..84916c5 100644
--- a/src/test/resources/jira/search/fixed-committer-cli-1.0.0.json
+++ b/src/test/resources/jira/search/committer-cli-1.0.0.json
@@ -2,7 +2,7 @@
"expand": "schema,names",
"startAt": 0,
"maxResults": 50,
- "total": 7,
+ "total": 9,
"issues": [
{
"expand": "operations,versionedRepresentations,editmeta,changelog,renderedFields",
@@ -10,7 +10,13 @@
"self": "https://issues.apache.org/jira/rest/api/2/issue/13256564",
"key": "SLING-8707",
"fields": {
- "summary": "Try to map a staging Nexus repository to a JIRA release by inspecting the POM files"
+ "summary": "Try to map a staging Nexus repository to a JIRA release by inspecting the POM files",
+ "status": {
+ "name": "Resolved"
+ },
+ "resolution": {
+ "name": "Fixed"
+ }
}
},
{
@@ -19,7 +25,13 @@
"self": "https://issues.apache.org/jira/rest/api/2/issue/13256303",
"key": "SLING-8699",
"fields": {
- "summary": "Create sub-command to moving artifacts to dist.apache.org"
+ "summary": "Create sub-command to moving artifacts to dist.apache.org",
+ "status": {
+ "name": "Resolved"
+ },
+ "resolution": {
+ "name": "Fixed"
+ }
}
},
{
@@ -28,7 +40,13 @@
"self": "https://issues.apache.org/jira/rest/api/2/issue/13231466",
"key": "SLING-8395",
"fields": {
- "summary": "Investigate automatically issuing GitHub PRs with the Committer CLI"
+ "summary": "Investigate automatically issuing GitHub PRs with the Committer CLI",
+ "status": {
+ "name": "Resolved"
+ },
+ "resolution": {
+ "name": "Fixed"
+ }
}
},
{
@@ -37,7 +55,13 @@
"self": "https://issues.apache.org/jira/rest/api/2/issue/13231465",
"key": "SLING-8394",
"fields": {
- "summary": "Create sub-command to update the Sling starter when a release is made"
+ "summary": "Create sub-command to update the Sling starter when a release is made",
+ "status": {
+ "name": "Resolved"
+ },
+ "resolution": {
+ "name": "Fixed"
+ }
}
},
{
@@ -46,7 +70,13 @@
"self": "https://issues.apache.org/jira/rest/api/2/issue/13231464",
"key": "SLING-8393",
"fields": {
- "summary": "Create sub-command to update the Sling website when a release is made"
+ "summary": "Create sub-command to update the Sling website when a release is made",
+ "status": {
+ "name": "Resolved"
+ },
+ "resolution": {
+ "name": "Fixed"
+ }
}
},
{
@@ -55,7 +85,13 @@
"self": "https://issues.apache.org/jira/rest/api/2/issue/13231459",
"key": "SLING-8392",
"fields": {
- "summary": "Create sub-command to manage the Jira update when promoting a release"
+ "summary": "Create sub-command to manage the Jira update when promoting a release",
+ "status": {
+ "name": "Resolved"
+ },
+ "resolution": {
+ "name": "Fixed"
+ }
}
},
{
@@ -64,7 +100,39 @@
"self": "https://issues.apache.org/jira/rest/api/2/issue/13225243",
"key": "SLING-8338",
"fields": {
- "summary": "Create sub-command to manage the Nexus stage repository release when promoting a release"
+ "summary": "Create sub-command to manage the Nexus stage repository release when promoting a release",
+ "status": {
+ "name": "Resolved"
+ },
+ "resolution": {
+ "name": "Fixed"
+ }
+ }
+ },
+ {
+ "expand": "operations,versionedRepresentations,editmeta,changelog,renderedFields",
+ "id": "13225243",
+ "self": "https://issues.apache.org/jira/rest/api/2/issue/13225243",
+ "key": "SLING-8338",
+ "fields": {
+ "summary": "Create sub-command to manage the Nexus stage repository release when promoting a release",
+ "status": {
+ "name": "Open"
+ },
+ "resolution": null
+ }
+ },
+ {
+ "expand": "operations,versionedRepresentations,editmeta,changelog,renderedFields",
+ "id": "13224868",
+ "self": "https://issues.apache.org/jira/rest/api/2/issue/13224868",
+ "key": "SLING-8337",
+ "fields": {
+ "summary": "Create sub-command to manage the Jira update when promoting a release",
+ "status": {
+ "name": "Open"
+ },
+ "resolution": null
}
}
]
diff --git a/src/test/resources/jira/search/transitions-1.0.0.json b/src/test/resources/jira/search/transitions-1.0.0.json
new file mode 100644
index 0000000..bb61a81
--- /dev/null
+++ b/src/test/resources/jira/search/transitions-1.0.0.json
@@ -0,0 +1,53 @@
+{
+ "expand": "schema,names",
+ "startAt": 0,
+ "maxResults": 50,
+ "total": 3,
+ "issues": [
+ {
+ "expand": "operations,versionedRepresentations,editmeta,changelog,renderedFields",
+ "id": "1",
+ "self": "https://issues.apache.org/jira/rest/api/2/issue/1",
+ "key": "SLING-0001",
+ "fields": {
+ "summary": "Test Transitions 1",
+ "status": {
+ "name": "Resolved"
+ },
+ "resolution": {
+ "name": "Fixed"
+ }
+ }
+ },
+ {
+ "expand": "operations,versionedRepresentations,editmeta,changelog,renderedFields",
+ "id": "2",
+ "self": "https://issues.apache.org/jira/rest/api/2/issue/2",
+ "key": "SLING-0002",
+ "fields": {
+ "summary": "Test Transitions 2",
+ "status": {
+ "name": "Resolved"
+ },
+ "resolution": {
+ "name": "Fixed"
+ }
+ }
+ },
+ {
+ "expand": "operations,versionedRepresentations,editmeta,changelog,renderedFields",
+ "id": "3",
+ "self": "https://issues.apache.org/jira/rest/api/2/issue/3",
+ "key": "SLING-0003",
+ "fields": {
+ "summary": "Test Transitions 3",
+ "status": {
+ "name": "Resolved"
+ },
+ "resolution": {
+ "name": "Fixed"
+ }
+ }
+ }
+ ]
+}
diff --git a/src/test/resources/jira/search/transitions-2.0.0.json b/src/test/resources/jira/search/transitions-2.0.0.json
new file mode 100644
index 0000000..08f92aa
--- /dev/null
+++ b/src/test/resources/jira/search/transitions-2.0.0.json
@@ -0,0 +1,53 @@
+{
+ "expand": "schema,names",
+ "startAt": 0,
+ "maxResults": 50,
+ "total": 3,
+ "issues": [
+ {
+ "expand": "operations,versionedRepresentations,editmeta,changelog,renderedFields",
+ "id": "4",
+ "self": "https://issues.apache.org/jira/rest/api/2/issue/4",
+ "key": "SLING-0004",
+ "fields": {
+ "summary": "Test Transitions 4",
+ "status": {
+ "name": "Resolved"
+ },
+ "resolution": {
+ "name": "Fixed"
+ }
+ }
+ },
+ {
+ "expand": "operations,versionedRepresentations,editmeta,changelog,renderedFields",
+ "id": "5",
+ "self": "https://issues.apache.org/jira/rest/api/2/issue/5",
+ "key": "SLING-0005",
+ "fields": {
+ "summary": "Test Transitions 5",
+ "status": {
+ "name": "Resolved"
+ },
+ "resolution": {
+ "name": "Fixed"
+ }
+ }
+ },
+ {
+ "expand": "operations,versionedRepresentations,editmeta,changelog,renderedFields",
+ "id": "6",
+ "self": "https://issues.apache.org/jira/rest/api/2/issue/6",
+ "key": "SLING-0006",
+ "fields": {
+ "summary": "Test Transitions 6",
+ "status": {
+ "name": "Resolved"
+ },
+ "resolution": {
+ "name": "Fixed"
+ }
+ }
+ }
+ ]
+}
diff --git a/src/test/resources/jira/search/unresolved-committer-cli-1.0.0.json b/src/test/resources/jira/search/unresolved-committer-cli-1.0.0.json
deleted file mode 100644
index 6a01267..0000000
--- a/src/test/resources/jira/search/unresolved-committer-cli-1.0.0.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
- "expand": "schema,names",
- "startAt": 0,
- "maxResults": 50,
- "total": 2,
- "issues": [
- {
- "expand": "operations,versionedRepresentations,editmeta,changelog,renderedFields",
- "id": "13225243",
- "self": "https://issues.apache.org/jira/rest/api/2/issue/13225243",
- "key": "SLING-8338",
- "fields": {
- "summary": "Create sub-command to manage the Nexus stage repository release when promoting a release"
- }
- },
- {
- "expand": "operations,versionedRepresentations,editmeta,changelog,renderedFields",
- "id": "13224868",
- "self": "https://issues.apache.org/jira/rest/api/2/issue/13224868",
- "key": "SLING-8337",
- "fields": {
- "summary": "Create sub-command to manage the Jira update when promoting a release"
- }
- }
- ]
-}
diff --git a/src/test/resources/jira/transitions/no-transitions.json b/src/test/resources/jira/transitions/no-transitions.json
new file mode 100644
index 0000000..496bc81
--- /dev/null
+++ b/src/test/resources/jira/transitions/no-transitions.json
@@ -0,0 +1,5 @@
+{
+ "expand" : "transitions",
+ "transitions": [
+ ]
+}
diff --git a/src/test/resources/jira/transitions/transitions.json b/src/test/resources/jira/transitions/transitions.json
new file mode 100644
index 0000000..950f412
--- /dev/null
+++ b/src/test/resources/jira/transitions/transitions.json
@@ -0,0 +1,41 @@
+{
+ "expand" : "transitions",
+ "transitions": [
+ {
+ "id" : "701",
+ "name": "Close Issue",
+ "to" : {
+ "self" : "https://issues.apache.org/jira/rest/api/2/status/6",
+ "description" : "The issue is considered finished, the resolution is correct. Issues which are not closed can be reopened.",
+ "iconUrl" : "https://issues.apache.org/jira/images/icons/statuses/closed.png",
+ "name" : "Closed",
+ "id" : "6",
+ "statusCategory": {
+ "self" : "https://issues.apache.org/jira/rest/api/2/statuscategory/3",
+ "id" : 3,
+ "key" : "done",
+ "colorName": "green",
+ "name" : "Done"
+ }
+ }
+ },
+ {
+ "id" : "3",
+ "name": "Reopen Issue",
+ "to" : {
+ "self" : "https://issues.apache.org/jira/rest/api/2/status/4",
+ "description" : "This issue was once resolved, but the resolution was deemed incorrect. From here issues are either marked assigned or resolved.",
+ "iconUrl" : "https://issues.apache.org/jira/images/icons/statuses/reopened.png",
+ "name" : "Reopened",
+ "id" : "4",
+ "statusCategory": {
+ "self" : "https://issues.apache.org/jira/rest/api/2/statuscategory/2",
+ "id" : 2,
+ "key" : "new",
+ "colorName": "blue-gray",
+ "name" : "To Do"
+ }
+ }
+ }
+ ]
+}
diff --git a/src/test/resources/jira/versions.json b/src/test/resources/jira/versions.json
index d7e5226..a90e94d 100644
--- a/src/test/resources/jira/versions.json
+++ b/src/test/resources/jira/versions.json
@@ -2083,5 +2083,16 @@
"releaseDate": "2010-12-13",
"userReleaseDate": "13/Dec/10",
"projectId": 12310710
+ },
+ {
+ "self": "https://issues.apache.org/jira/rest/api/2/version/0",
+ "id": "0",
+ "description": "Maintenance release",
+ "name": "Transitions 2.0.0",
+ "archived": false,
+ "released": false,
+ "releaseDate": null,
+ "userReleaseDate": null,
+ "projectId": 12310710
}
]