@@ -22,11 +22,6 @@ deployment.username=
|
||||
deployment.password=
|
||||
deployment.api-key=
|
||||
|
||||
# Jira
|
||||
jira.username=
|
||||
jira.password=
|
||||
jira.url=https://jira.spring.io
|
||||
|
||||
# GPG
|
||||
gpg.keyname=
|
||||
gpg.password=
|
||||
|
||||
@@ -26,7 +26,6 @@ import org.springframework.data.release.build.BuildOperations;
|
||||
import org.springframework.data.release.deployment.DeploymentOperations;
|
||||
import org.springframework.data.release.git.GitOperations;
|
||||
import org.springframework.data.release.issues.github.GitHub;
|
||||
import org.springframework.data.release.issues.jira.Jira;
|
||||
import org.springframework.data.release.sagan.SaganClient;
|
||||
import org.springframework.data.release.utils.Logger;
|
||||
import org.springframework.shell.core.annotation.CliCommand;
|
||||
@@ -45,7 +44,6 @@ class VerifyCommands extends TimedCommand {
|
||||
@NonNull GitHub github;
|
||||
@NonNull DeploymentOperations deployment;
|
||||
@NonNull BuildOperations build;
|
||||
@NonNull Jira jira;
|
||||
@NonNull SaganClient saganClient;
|
||||
@NonNull Logger logger;
|
||||
|
||||
@@ -64,9 +62,6 @@ class VerifyCommands extends TimedCommand {
|
||||
// GitHub verification
|
||||
github.verifyAuthentication();
|
||||
|
||||
// Jira verification
|
||||
jira.verifyAuthentication();
|
||||
|
||||
// Sagan Verification
|
||||
saganClient.verifyAuthentication();
|
||||
|
||||
|
||||
@@ -39,7 +39,6 @@ import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.release.issues.github.GitHubProperties;
|
||||
import org.springframework.data.release.issues.jira.JiraProperties;
|
||||
import org.springframework.data.release.model.Project;
|
||||
import org.springframework.data.release.utils.HttpBasicCredentials;
|
||||
import org.springframework.data.util.Lazy;
|
||||
@@ -88,14 +87,12 @@ class IssueTrackerConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory(JiraProperties jiraProperties,
|
||||
GitHubProperties gitHubProperties) {
|
||||
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory(GitHubProperties gitHubProperties) {
|
||||
|
||||
// Preemptive auth
|
||||
CredentialsProvider credsProvider = new BasicCredentialsProvider();
|
||||
AuthCache authCache = new BasicAuthCache();
|
||||
|
||||
addPreemptiveAuth(credsProvider, authCache, jiraProperties.getApiUrl(), jiraProperties.getCredentials());
|
||||
addPreemptiveAuth(credsProvider, authCache, gitHubProperties.getApiUrl(), gitHubProperties.getHttpCredentials());
|
||||
|
||||
Lazy<CloseableHttpClient> lazy = Lazy
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.data.release.issues.jira;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
/**
|
||||
* Value object for a created JIRA issue.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Value
|
||||
class CreatedJiraIssue {
|
||||
String id, key;
|
||||
}
|
||||
@@ -1,796 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.data.release.issues.jira;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.data.release.issues.Changelog;
|
||||
import org.springframework.data.release.issues.Ticket;
|
||||
import org.springframework.data.release.issues.Tickets;
|
||||
import org.springframework.data.release.issues.jira.JiraIssue.Fields;
|
||||
import org.springframework.data.release.issues.jira.JiraIssue.Resolution;
|
||||
import org.springframework.data.release.issues.jira.JiraIssue.Status;
|
||||
import org.springframework.data.release.issues.jira.JiraIssue.StatusCategory;
|
||||
import org.springframework.data.release.model.ModuleIteration;
|
||||
import org.springframework.data.release.model.Project;
|
||||
import org.springframework.data.release.model.ProjectKey;
|
||||
import org.springframework.data.release.model.Tracker;
|
||||
import org.springframework.data.release.model.TrainIteration;
|
||||
import org.springframework.data.release.utils.Logger;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
import org.springframework.web.client.HttpStatusCodeException;
|
||||
import org.springframework.web.client.RestOperations;
|
||||
import org.springframework.web.util.DefaultUriBuilderFactory;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Component
|
||||
public class Jira implements JiraConnector {
|
||||
|
||||
private static final String CREATE_ISSUES_TEMPLATE = "/issue";
|
||||
private static final String ISSUE_TEMPLATE = "/issue/{ticketId}";
|
||||
private static final String TRANSITION_TEMPLATE = "/issue/{ticketId}/transitions";
|
||||
private static final String PROJECT_VERSIONS_TEMPLATE = "/project/{project}/version?startAt={startAt}";
|
||||
private static final String PROJECT_COMPONENTS_TEMPLATE = "/project/{project}/components";
|
||||
private static final String VERSIONS_TEMPLATE = "/version";
|
||||
private static final String VERSION_TEMPLATE = "/version/{id}";
|
||||
private static final String SEARCH_TEMPLATE = "/search?jql={jql}&fields={fields}&startAt={startAt}";
|
||||
|
||||
private static final String INFRASTRUCTURE_COMPONENT_NAME = "Infrastructure";
|
||||
private static final String IN_PROGRESS_STATUS_CATEGORY = "indeterminate";
|
||||
|
||||
/**
|
||||
* Values/Id's originate from https://jira.spring.io/rest/api/2/issue/(Ticket)/transitions?expand=transitions.fields
|
||||
*/
|
||||
private static final int IN_PROGRESS_TRANSITION = 4;
|
||||
private static final int CLOSE_TRANSITION = 2;
|
||||
private static final int RESOLVE_TRANSITION = 5;
|
||||
private static final String COMPLETE_RESOLUTION = "Complete";
|
||||
|
||||
private final RestOperations operations;
|
||||
private final Logger logger;
|
||||
private final JiraProperties jiraProperties;
|
||||
|
||||
/**
|
||||
* @param templateBuilder
|
||||
* @param logger
|
||||
* @param jiraProperties
|
||||
*/
|
||||
public Jira(@Qualifier("tracker") RestTemplateBuilder templateBuilder, Logger logger, JiraProperties jiraProperties) {
|
||||
|
||||
String baseUri = String.format("%s/rest/api/2", jiraProperties.getApiUrl());
|
||||
|
||||
this.operations = templateBuilder.uriTemplateHandler(new DefaultUriBuilderFactory(baseUri)).build();
|
||||
this.logger = logger;
|
||||
this.jiraProperties = jiraProperties;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.release.jira.JiraConnector#reset()
|
||||
*/
|
||||
@Override
|
||||
@CacheEvict(value = { "release-ticket", "tickets", "changelogs", "release-version" }, allEntries = true)
|
||||
public void reset() {}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.release.jira.IssueTracker#getReleaseTicketFor(org.springframework.data.release.model.ModuleIteration)
|
||||
*/
|
||||
@Override
|
||||
@Cacheable("release-ticket")
|
||||
public Ticket getReleaseTicketFor(ModuleIteration moduleIteration) {
|
||||
|
||||
JqlQuery query = JqlQuery.from(moduleIteration)
|
||||
.and(String.format("summary ~ \"%s\"", Tracker.releaseTicketSummary(moduleIteration)));
|
||||
|
||||
JiraIssues issues = getJiraIssues(query, new HttpHeaders(), 0);
|
||||
|
||||
return issues.stream().map(this::toTicket).collect(Tickets.toTicketsCollector()).getReleaseTicket(moduleIteration);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.release.jira.IssueTracker#findTickets(Project, Collection)
|
||||
*/
|
||||
@Override
|
||||
@Cacheable("tickets")
|
||||
public Collection<Ticket> findTickets(Project project, Collection<String> ticketIds) {
|
||||
|
||||
if (ticketIds.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
logger.log(project, "Retrieving up JIRA issues…");
|
||||
|
||||
JqlQuery query = JqlQuery.from(ticketIds).and(" resolution is not EMPTY");
|
||||
|
||||
Map<String, Object> parameters = newUrlTemplateVariables();
|
||||
parameters.put("jql", query);
|
||||
parameters.put("fields", "summary,status,resolution");
|
||||
parameters.put("startAt", 0);
|
||||
|
||||
JiraIssues issues = operations.exchange(SEARCH_TEMPLATE, HttpMethod.GET, null, JiraIssues.class, parameters)
|
||||
.getBody();
|
||||
|
||||
logger.log(project, "Found %s tickets.", issues.getIssues().size());
|
||||
|
||||
return issues.stream().//
|
||||
map(this::toTicket).//
|
||||
collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tickets findTickets(ModuleIteration moduleIteration, Collection<String> ticketIds) {
|
||||
|
||||
List<String> ids = ticketIds.stream().filter(it -> it.startsWith(moduleIteration.getProjectKey().getKey()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (ids.isEmpty()) {
|
||||
return new Tickets(Collections.emptyList());
|
||||
}
|
||||
|
||||
Map<String, Object> parameters = newUrlTemplateVariables();
|
||||
parameters.put("jql", JqlQuery.from(ids));
|
||||
parameters.put("fields", "summary,status,resolution,fixVersions");
|
||||
parameters.put("startAt", 0);
|
||||
|
||||
logger.log(moduleIteration, "Retrieving up JIRA issues…");
|
||||
|
||||
JiraIssues issues = operations.getForObject(SEARCH_TEMPLATE, JiraIssues.class, parameters);
|
||||
Tickets tickets = issues.stream().map(this::toTicket).filter(it -> {
|
||||
return it.isReleaseTicketFor(moduleIteration) || !it.isReleaseTicket();
|
||||
}).collect(Tickets.toTicketsCollector());
|
||||
|
||||
logger.log(moduleIteration, "Found %s tickets.", tickets.getOverallTotal());
|
||||
|
||||
return tickets;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.release.jira.IssueTracker#getTicketsFor(org.springframework.data.release.model.TrainIteration)
|
||||
*/
|
||||
@Override
|
||||
@Cacheable("tickets")
|
||||
public Tickets getTicketsFor(TrainIteration iteration) {
|
||||
return getTicketsFor(iteration, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.release.jira.IssueTracker#getTicketsFor(org.springframework.data.release.model.TrainIteration, boolean)
|
||||
*/
|
||||
@Override
|
||||
@Cacheable("tickets")
|
||||
public Tickets getTicketsFor(TrainIteration trainIteration, boolean forCurrentUser) {
|
||||
|
||||
if (!trainIteration.stream().anyMatch(it -> supports(it.getProject()))) {
|
||||
return new Tickets(Collections.emptyList());
|
||||
}
|
||||
|
||||
JqlQuery query = JqlQuery
|
||||
.from(trainIteration.stream().filter(moduleIteration -> supports(moduleIteration.getProject())));
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
List<Ticket> tickets = new ArrayList<>();
|
||||
|
||||
if (forCurrentUser) {
|
||||
query = query.and("assignee = currentUser()");
|
||||
logger.log(trainIteration, "Retrieving tickets (for user %s)…", jiraProperties.getUsername());
|
||||
} else {
|
||||
logger.log(trainIteration, "Retrieving tickets…");
|
||||
}
|
||||
|
||||
query = query.orderBy("updatedDate DESC");
|
||||
|
||||
JiraIssues issues = execute(trainIteration.toString(), query, headers, jiraIssues -> {
|
||||
jiraIssues.stream().//
|
||||
filter(jiraIssue -> !jiraIssue.wasBackportedFrom(trainIteration.getTrain())). //
|
||||
forEach(jiraIssue -> tickets.add(toTicket(jiraIssue)));
|
||||
});
|
||||
|
||||
return new Tickets(tickets, issues.getTotal());
|
||||
}
|
||||
|
||||
@Cacheable("tickets")
|
||||
public Tickets getTicketsFor(ModuleIteration moduleIteration) {
|
||||
|
||||
JqlQuery query = JqlQuery.from(moduleIteration);
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
List<Ticket> tickets = new ArrayList<>();
|
||||
|
||||
logger.log(moduleIteration, "Retrieving tickets…");
|
||||
|
||||
query = query.orderBy("updatedDate DESC");
|
||||
|
||||
JiraIssues issues = execute(moduleIteration.toString(), query, headers, jiraIssues -> {
|
||||
jiraIssues.stream().//
|
||||
filter(jiraIssue -> !jiraIssue.wasBackportedFrom(moduleIteration.getTrain())). //
|
||||
forEach(jiraIssue -> tickets.add(toTicket(jiraIssue)));
|
||||
});
|
||||
|
||||
return new Tickets(tickets, issues.getTotal());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.release.jira.JiraConnector#createReleaseVersion(org.springframework.data.release.model.ModuleIteration)
|
||||
*/
|
||||
@Override
|
||||
public void createReleaseVersion(ModuleIteration moduleIteration) {
|
||||
|
||||
Assert.notNull(moduleIteration, "ModuleIteration must not be null.");
|
||||
|
||||
Map<String, Object> parameters = newUrlTemplateVariables();
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
|
||||
Optional<JiraReleaseVersion> versionsForModuleIteration = findJiraReleaseVersion(moduleIteration);
|
||||
|
||||
if (versionsForModuleIteration.isPresent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
JiraVersion jiraVersion = new JiraVersion(moduleIteration);
|
||||
logger.log(moduleIteration, "Creating Jira release version %s", jiraVersion);
|
||||
|
||||
JiraReleaseVersion jiraReleaseVersion = JiraReleaseVersion.of(moduleIteration, jiraVersion);
|
||||
|
||||
try {
|
||||
operations.exchange(VERSIONS_TEMPLATE, HttpMethod.POST, new HttpEntity<Object>(jiraReleaseVersion, httpHeaders),
|
||||
JiraReleaseVersion.class, parameters).getBody();
|
||||
} catch (HttpStatusCodeException e) {
|
||||
System.out.println(e.getResponseBodyAsString());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.release.jira.JiraConnector#retireReleaseVersion(org.springframework.data.release.model.ModuleIteration)
|
||||
*/
|
||||
@Override
|
||||
public void archiveReleaseVersion(ModuleIteration module) {
|
||||
|
||||
Assert.notNull(module, "ModuleIteration must not be null.");
|
||||
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
|
||||
findJiraReleaseVersion(module) //
|
||||
.filter(JiraReleaseVersion::isActive) //
|
||||
.map(JiraReleaseVersion::markArchived) //
|
||||
.ifPresent(version -> {
|
||||
|
||||
logger.log(module, "Marking version %s as archived.", version);
|
||||
|
||||
Map<String, Object> parameters = newUrlTemplateVariables();
|
||||
parameters.put("id", version.getId());
|
||||
|
||||
operations.exchange(VERSION_TEMPLATE, HttpMethod.PUT, new HttpEntity<Object>(version, httpHeaders), Map.class,
|
||||
parameters);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.release.jira.JiraConnector#findJiraReleaseVersion(org.springframework.data.release.model.ModuleIteration)
|
||||
*/
|
||||
@Cacheable("release-version")
|
||||
public Optional<JiraReleaseVersion> findJiraReleaseVersion(ModuleIteration moduleIteration) {
|
||||
|
||||
JiraVersion jiraVersion = new JiraVersion(moduleIteration);
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
|
||||
List<JiraReleaseVersion> versionsForModuleIteration = new ArrayList<>();
|
||||
getReleaseVersions(moduleIteration.toString(), moduleIteration.getProjectKey(), httpHeaders, releaseVersions -> {
|
||||
releaseVersions.stream(). //
|
||||
filter(jiraReleaseVersion -> jiraReleaseVersion.hasSameNameAs(jiraVersion)). //
|
||||
findFirst(). //
|
||||
ifPresent(jiraReleaseVersion -> versionsForModuleIteration.add(jiraReleaseVersion));
|
||||
});
|
||||
|
||||
return versionsForModuleIteration.stream().findFirst();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.release.jira.IssueTracker#createReleaseTicket(org.springframework.data.release.model.ModuleIteration)
|
||||
*/
|
||||
@Override
|
||||
public void createReleaseTicket(ModuleIteration moduleIteration) {
|
||||
|
||||
Assert.notNull(moduleIteration, "ModuleIteration must not be null.");
|
||||
|
||||
Tickets tickets = getTicketsFor(moduleIteration);
|
||||
|
||||
if (tickets.hasReleaseTicket(moduleIteration)) {
|
||||
return;
|
||||
}
|
||||
|
||||
logger.log(moduleIteration, "Creating release ticket…");
|
||||
|
||||
doCreateTicket(moduleIteration, Tracker.releaseTicketSummary(moduleIteration), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
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, assignToCurrentUser);
|
||||
}
|
||||
|
||||
private Ticket doCreateTicket(ModuleIteration moduleIteration, String text, boolean assignToCurrentUser) {
|
||||
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
Map<String, Object> parameters = newUrlTemplateVariables();
|
||||
|
||||
findJiraReleaseVersion(moduleIteration).orElseThrow(
|
||||
() -> new IllegalStateException(String.format("No release version for %s found", moduleIteration)));
|
||||
|
||||
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<Object>(jiraIssue, httpHeaders), CreatedJiraIssue.class, parameters).getBody();
|
||||
|
||||
JiraIssue createdIssue = getJiraIssue(created.getKey())
|
||||
.orElseThrow(() -> new IllegalStateException(String.format("Cannot retrieve ticket %s", created.getKey())));
|
||||
|
||||
return toTicket(createdIssue);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.release.jira.JiraConnector#assignTicketToMe(org.springframework.data.release.jira.Ticket)
|
||||
*/
|
||||
@Override
|
||||
public Ticket assignTicketToMe(Project project, Ticket ticket) {
|
||||
|
||||
Assert.notNull(project, "Project must not be null.");
|
||||
Assert.notNull(ticket, "Ticket must not be null.");
|
||||
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
|
||||
Map<String, Object> parameters = newUrlTemplateVariables();
|
||||
parameters.put("ticketId", ticket.getId());
|
||||
|
||||
JiraIssue currentIssue = getJiraIssue(ticket.getId())
|
||||
.orElseThrow(() -> new IllegalStateException(String.format("Ticket %s does not exist", ticket.getId())));
|
||||
|
||||
if (currentIssue.isAssignedTo(jiraProperties.getUsername())) {
|
||||
logger.log("Ticket", "Skipping self-assignment of %s", ticket);
|
||||
return ticket;
|
||||
}
|
||||
|
||||
JiraIssueUpdate editMeta = JiraIssueUpdate.create().assignTo(jiraProperties.getUsername());
|
||||
|
||||
logger.log("Ticket", "Self-assignment of %s", ticket);
|
||||
|
||||
try {
|
||||
operations.exchange(ISSUE_TEMPLATE, HttpMethod.PUT, new HttpEntity<Object>(editMeta, httpHeaders), String.class,
|
||||
parameters).getBody();
|
||||
} catch (HttpClientErrorException e) {
|
||||
logger.warn("Ticket", "Self-assignment of %s failed with status %s (%s)", ticket, e.getStatusCode(),
|
||||
e.getResponseBodyAsString());
|
||||
}
|
||||
|
||||
return ticket;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.release.jira.IssueTracker#assignReleaseTicketToMe(org.springframework.data.release.model.ModuleIteration)
|
||||
*/
|
||||
@Override
|
||||
public Ticket assignReleaseTicketToMe(ModuleIteration module) {
|
||||
|
||||
Ticket ticket = getReleaseTicketFor(module);
|
||||
assignTicketToMe(module.getProject(), ticket);
|
||||
return ticket;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.release.jira.IssueTracker#startReleaseTicketProgress(org.springframework.data.release.model.ModuleIteration)
|
||||
*/
|
||||
@Override
|
||||
public Ticket startReleaseTicketProgress(ModuleIteration module) {
|
||||
|
||||
Ticket ticket = getReleaseTicketFor(module);
|
||||
startProgress(ticket);
|
||||
return ticket;
|
||||
}
|
||||
|
||||
private void startProgress(Ticket ticket) {
|
||||
|
||||
Assert.notNull(ticket, "Ticket must not be null.");
|
||||
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
|
||||
Map<String, Object> parameters = newUrlTemplateVariables();
|
||||
parameters.put("ticketId", ticket.getId());
|
||||
|
||||
JiraIssue currentIssue = getJiraIssue(ticket.getId())
|
||||
.orElseThrow(() -> new IllegalStateException(String.format("Ticket %s does not exist", ticket.getId())));
|
||||
|
||||
if (isInProgress(currentIssue.getFields())) {
|
||||
return;
|
||||
}
|
||||
|
||||
JiraIssueUpdate editMeta = JiraIssueUpdate.create().transition(IN_PROGRESS_TRANSITION);
|
||||
|
||||
logger.log("Ticket", "Start progress of %s", ticket);
|
||||
|
||||
try {
|
||||
operations.exchange(TRANSITION_TEMPLATE, HttpMethod.POST, new HttpEntity<Object>(editMeta, httpHeaders),
|
||||
String.class, parameters).getBody();
|
||||
} catch (HttpClientErrorException e) {
|
||||
logger.warn("Ticket", "Start progress of %s failed with status %s (%s)", ticket, e.getStatusCode(),
|
||||
e.getResponseBodyAsString());
|
||||
}
|
||||
}
|
||||
|
||||
private void resolve(Ticket ticket) {
|
||||
|
||||
Assert.notNull(ticket, "Ticket must not be null.");
|
||||
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
|
||||
Map<String, Object> parameters = newUrlTemplateVariables();
|
||||
parameters.put("ticketId", ticket.getId());
|
||||
|
||||
JiraIssue currentIssue = getJiraIssue(ticket.getId())
|
||||
.orElseThrow(() -> new IllegalStateException(String.format("Ticket %s does not exist", ticket.getId())));
|
||||
|
||||
if (isInProgress(currentIssue.getFields())) {
|
||||
|
||||
JiraIssueUpdate editMeta = JiraIssueUpdate.create().transition(RESOLVE_TRANSITION)
|
||||
.resolution(COMPLETE_RESOLUTION);
|
||||
|
||||
logger.log("Ticket", "Resolving %s", ticket);
|
||||
|
||||
try {
|
||||
operations.exchange(TRANSITION_TEMPLATE, HttpMethod.POST, new HttpEntity<Object>(editMeta, httpHeaders),
|
||||
String.class, parameters).getBody();
|
||||
} catch (HttpClientErrorException e) {
|
||||
logger.warn("Ticket", "Resolution of %s failed with status %s (%s)", ticket, e.getStatusCode(),
|
||||
e.getResponseBodyAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isInProgress(Fields fields) {
|
||||
|
||||
if (fields.getStatus() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
StatusCategory statusCategory = fields.getStatus().getStatusCategory();
|
||||
|
||||
return statusCategory != null && statusCategory.getKey().equals(IN_PROGRESS_STATUS_CATEGORY);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.release.jira.JiraConnector#verifyBeforeRelease(org.springframework.data.release.model.Train, org.springframework.data.release.model.Iteration)
|
||||
*/
|
||||
@Override
|
||||
public void verifyBeforeRelease(TrainIteration trainIteration) {
|
||||
|
||||
// for each module
|
||||
for (ModuleIteration moduleIteration : trainIteration) {
|
||||
|
||||
Tickets tickets = getTicketsFor(moduleIteration);
|
||||
Ticket releaseTicket = tickets.getReleaseTicket(moduleIteration);
|
||||
|
||||
if (releaseTicket.isResolved()) {
|
||||
throw new IllegalStateException(
|
||||
String.format("Release ticket %s for %s is resolved", releaseTicket, moduleIteration));
|
||||
}
|
||||
|
||||
Tickets issueTickets = tickets.getIssueTickets(moduleIteration);
|
||||
|
||||
List<Ticket> unresolvedTickets = issueTickets.stream().//
|
||||
filter(ticket -> !ticket.isResolved()).//
|
||||
collect(Collectors.toList());
|
||||
|
||||
if (!unresolvedTickets.isEmpty()) {
|
||||
throw new IllegalStateException(
|
||||
String.format("Unresolved tickets for %s: %s", moduleIteration, unresolvedTickets));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.release.jira.JiraConnector#closeIteration(org.springframework.data.release.model.Train, org.springframework.data.release.model.Iteration)
|
||||
*/
|
||||
@Override
|
||||
public void closeIteration(ModuleIteration module) {
|
||||
|
||||
// for each module
|
||||
|
||||
// - close all tickets
|
||||
// -- make sure only one ticket is open
|
||||
// -- resolve open ticket
|
||||
// -- close tickets
|
||||
|
||||
// - mark version as releases
|
||||
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
|
||||
findJiraReleaseVersion(module) //
|
||||
.filter(JiraReleaseVersion::isOpen) //
|
||||
.map(JiraReleaseVersion::markReleased) //
|
||||
.ifPresent(version -> {
|
||||
|
||||
logger.log(module, "Marking version %s as released.", version);
|
||||
|
||||
Map<String, Object> parameters = newUrlTemplateVariables();
|
||||
parameters.put("id", version.getId());
|
||||
|
||||
operations.exchange(VERSION_TEMPLATE, HttpMethod.PUT, new HttpEntity<Object>(version, httpHeaders), Map.class,
|
||||
parameters);
|
||||
});
|
||||
|
||||
resolve(getReleaseTicketFor(module));
|
||||
|
||||
// - if no next version exists, create
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeTicket(ModuleIteration module, Ticket ticket) {
|
||||
resolve(ticket);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.release.jira.JiraConnector#getChangelogFor(org.springframework.data.release.model.Module, org.springframework.data.release.model.Iteration)
|
||||
*/
|
||||
@Override
|
||||
@Cacheable("changelogs")
|
||||
public Changelog getChangelogFor(ModuleIteration moduleIteration) {
|
||||
|
||||
Map<String, Object> parameters = newUrlTemplateVariables();
|
||||
parameters.put("jql", JqlQuery.from(moduleIteration));
|
||||
parameters.put("fields", "summary,status,resolution,fixVersions");
|
||||
parameters.put("startAt", 0);
|
||||
|
||||
logger.log(moduleIteration, "Looking up JIRA issues…");
|
||||
|
||||
JiraIssues issues = operations.getForObject(SEARCH_TEMPLATE, JiraIssues.class, parameters);
|
||||
Tickets tickets = issues.stream().map(this::toTicket).collect(Tickets.toTicketsCollector());
|
||||
logger.log(moduleIteration, "Created changelog with %s entries.", tickets.getOverallTotal());
|
||||
|
||||
return Changelog.of(moduleIteration, tickets);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.plugin.core.Plugin#supports(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Project project) {
|
||||
return project.uses(Tracker.JIRA);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify Jira Authentication.
|
||||
*/
|
||||
public void verifyAuthentication() {
|
||||
|
||||
logger.log("Jira", "Verifying Jira Authentication…");
|
||||
|
||||
ResponseEntity<Object> entity = operations.getForEntity("/myself", Object.class);
|
||||
|
||||
if (!entity.getStatusCode().is2xxSuccessful()) {
|
||||
throw new IllegalStateException("Cannot access Jira user profile");
|
||||
}
|
||||
|
||||
logger.log("Jira", "Authentication verified!");
|
||||
}
|
||||
|
||||
protected JiraComponents getJiraComponents(ProjectKey projectKey) {
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
Map<String, Object> parameters = newUrlTemplateVariables();
|
||||
parameters.put("project", projectKey.getKey());
|
||||
|
||||
List<JiraComponent> components = operations.exchange(PROJECT_COMPONENTS_TEMPLATE, HttpMethod.GET,
|
||||
new HttpEntity<>(headers), new ParameterizedTypeReference<List<JiraComponent>>() {}, parameters).getBody();
|
||||
|
||||
return JiraComponents.of(components);
|
||||
}
|
||||
|
||||
private JiraIssue prepareJiraIssueToCreate(String text, ModuleIteration moduleIteration,
|
||||
JiraComponents jiraComponents) {
|
||||
|
||||
JiraIssue jiraIssue = JiraIssue.createTask();
|
||||
jiraIssue.project(moduleIteration.getProjectKey()).summary(text).fixVersion(moduleIteration);
|
||||
|
||||
Fields fields = jiraIssue.getFields();
|
||||
|
||||
Optional<JiraComponent> component = jiraComponents.findComponent(INFRASTRUCTURE_COMPONENT_NAME);
|
||||
component.ifPresent(jiraComponent -> fields.setComponents(Collections
|
||||
.singletonList(org.springframework.data.release.issues.jira.JiraIssue.Component.of(jiraComponent.getName()))));
|
||||
|
||||
return jiraIssue;
|
||||
}
|
||||
|
||||
private Optional<JiraIssue> getJiraIssue(String ticketId) {
|
||||
|
||||
Map<String, Object> parameters = newUrlTemplateVariables();
|
||||
parameters.put("ticketId", ticketId);
|
||||
|
||||
try {
|
||||
ResponseEntity<JiraIssue> jiraIssue = operations.getForEntity(ISSUE_TEMPLATE, JiraIssue.class, parameters);
|
||||
|
||||
return Optional.of(jiraIssue.getBody());
|
||||
} catch (HttpClientErrorException e) {
|
||||
if (e.getStatusCode() == HttpStatus.NOT_FOUND) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private JiraIssues execute(String context, JqlQuery query, HttpHeaders headers, JiraIssuesCallback callback) {
|
||||
|
||||
JiraIssues issues;
|
||||
int startAt = 0;
|
||||
|
||||
do {
|
||||
issues = getJiraIssues(query, headers, startAt);
|
||||
|
||||
logger.log(context, "Got tickets %s to %s of %s.", startAt, issues.getNextStartAt(), issues.getTotal());
|
||||
|
||||
callback.doWithJiraIssues(issues);
|
||||
|
||||
startAt = issues.getNextStartAt();
|
||||
|
||||
} while (issues.hasMoreResults());
|
||||
|
||||
return issues;
|
||||
}
|
||||
|
||||
private JiraIssues getJiraIssues(JqlQuery query, HttpHeaders headers, int startAt) {
|
||||
|
||||
JiraIssues issues;
|
||||
Map<String, Object> parameters = newUrlTemplateVariables();
|
||||
parameters.put("jql", query);
|
||||
parameters.put("fields", "summary,status,resolution,fixVersions");
|
||||
parameters.put("startAt", startAt);
|
||||
|
||||
issues = operations
|
||||
.exchange(SEARCH_TEMPLATE, HttpMethod.GET, new HttpEntity<>(headers), JiraIssues.class, parameters).getBody();
|
||||
return issues;
|
||||
}
|
||||
|
||||
private JiraReleaseVersions getReleaseVersions(String context, ProjectKey projectKey, HttpHeaders headers,
|
||||
JiraReleaseVersionsCallback callback) {
|
||||
|
||||
JiraReleaseVersions releaseVersions;
|
||||
int startAt = 0;
|
||||
|
||||
do {
|
||||
|
||||
releaseVersions = getJiraReleaseVersions(projectKey, headers, startAt);
|
||||
|
||||
logger.log(context, "Got release versions %s to %s of %s.", startAt, releaseVersions.getNextStartAt(),
|
||||
releaseVersions.getTotal());
|
||||
|
||||
callback.doWithJiraReleaseVersions(releaseVersions);
|
||||
|
||||
startAt = releaseVersions.getNextStartAt();
|
||||
|
||||
} while (releaseVersions.hasMoreResults());
|
||||
|
||||
return releaseVersions;
|
||||
}
|
||||
|
||||
private JiraReleaseVersions getJiraReleaseVersions(ProjectKey projectKey, HttpHeaders headers, int startAt) {
|
||||
|
||||
Map<String, Object> parameters = newUrlTemplateVariables();
|
||||
parameters.put("project", projectKey.getKey());
|
||||
parameters.put("fields", "summary,status,resolution,fixVersions");
|
||||
parameters.put("startAt", startAt);
|
||||
|
||||
try {
|
||||
return operations.exchange(PROJECT_VERSIONS_TEMPLATE, HttpMethod.GET, new HttpEntity<>(headers),
|
||||
JiraReleaseVersions.class, parameters).getBody();
|
||||
} catch (HttpStatusCodeException e) {
|
||||
|
||||
System.out.println(e.getResponseBodyAsString());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private Ticket toTicket(JiraIssue issue) {
|
||||
|
||||
Fields fields = issue.getFields();
|
||||
JiraTicketStatus jiraTicketStatus;
|
||||
|
||||
if (fields.hasStatusAndResolution()) {
|
||||
|
||||
Status status = fields.getStatus();
|
||||
boolean resolved = status.getStatusCategory().getKey().equals("done");
|
||||
Resolution resolution = fields.getResolution();
|
||||
|
||||
jiraTicketStatus = JiraTicketStatus.of(resolved, status.getName(), resolution.getName());
|
||||
} else {
|
||||
jiraTicketStatus = JiraTicketStatus.UNKNOWN;
|
||||
}
|
||||
|
||||
return new Ticket(issue.getKey(), fields.getSummary(),
|
||||
String.format("%s/browse/%s", jiraProperties.getApiUrl(), issue.getKey()),
|
||||
fields.getAssignee() != null ? fields.getAssignee().getName() : null, jiraTicketStatus);
|
||||
}
|
||||
|
||||
private Map<String, Object> newUrlTemplateVariables() {
|
||||
Map<String, Object> parameters = new HashMap<>();
|
||||
return parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for {@link JiraIssues}.
|
||||
*/
|
||||
interface JiraIssuesCallback {
|
||||
|
||||
void doWithJiraIssues(JiraIssues jiraIssues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for {@link JiraReleaseVersions}.
|
||||
*/
|
||||
interface JiraReleaseVersionsCallback {
|
||||
|
||||
void doWithJiraReleaseVersions(JiraReleaseVersions releaseVersions);
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.data.release.issues.jira;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
/**
|
||||
* Value object to bind REST responses to.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Value
|
||||
class JiraComponent {
|
||||
|
||||
String id, name;
|
||||
|
||||
public boolean hasName(String name) {
|
||||
return this.name.equals(name);
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.data.release.issues.jira;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Value object to represent a list of {@link JiraComponent}s.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Value(staticConstructor = "of")
|
||||
class JiraComponents implements Iterable<JiraComponent> {
|
||||
|
||||
@NonNull Collection<JiraComponent> jiraComponents;
|
||||
|
||||
/**
|
||||
* Try to find a component by its {@code name}.
|
||||
*
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public Optional<JiraComponent> findComponent(String name) {
|
||||
return jiraComponents.stream().filter(jiraComponent -> jiraComponent.hasName(name)).findFirst();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Iterable#iterator()
|
||||
*/
|
||||
@Override
|
||||
public Iterator<JiraComponent> iterator() {
|
||||
return jiraComponents.iterator();
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.data.release.issues.jira;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.release.issues.IssueTracker;
|
||||
import org.springframework.data.release.issues.Tickets;
|
||||
import org.springframework.data.release.model.ModuleIteration;
|
||||
import org.springframework.data.release.model.TrainIteration;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
interface JiraConnector extends IssueTracker {
|
||||
|
||||
/**
|
||||
* Verifies the state of all {@link Tickets} before releasing. In particular: Check whether the release ticket exists,
|
||||
* check whether all other issue tickets are in a resolved state.
|
||||
*
|
||||
* @param iteration must not be {@literal null}.
|
||||
*/
|
||||
void verifyBeforeRelease(TrainIteration iteration);
|
||||
|
||||
/**
|
||||
* Lookup a JIRA release version.
|
||||
*
|
||||
* @param moduleIteration must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Optional<JiraReleaseVersion> findJiraReleaseVersion(ModuleIteration moduleIteration);
|
||||
}
|
||||
@@ -1,245 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.data.release.issues.jira;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.release.issues.Ticket;
|
||||
import org.springframework.data.release.model.ModuleIteration;
|
||||
import org.springframework.data.release.model.ProjectKey;
|
||||
import org.springframework.data.release.model.Tracker;
|
||||
import org.springframework.data.release.model.Train;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Value object to bind REST responses to.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Value
|
||||
class JiraIssue {
|
||||
|
||||
String key;
|
||||
Fields fields;
|
||||
|
||||
/**
|
||||
* Creates a new Jira issue value object.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static JiraIssue create() {
|
||||
return new JiraIssue(null, new Fields());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Jira issue value object with a issue type of {@link IssueType#TASK}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static JiraIssue createTask() {
|
||||
JiraIssue jiraIssue = create();
|
||||
jiraIssue.getFields().setIssuetype(IssueType.TASK);
|
||||
return jiraIssue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the ticket is only fixed in the given {@link Train}. {@literal false} indicates the {@link Ticket}
|
||||
* has been resolved for
|
||||
*
|
||||
* @param train
|
||||
* @return
|
||||
*/
|
||||
public boolean wasBackportedFrom(Train train) {
|
||||
|
||||
List<FixVersion> fixVersions = fields.getFixVersions();
|
||||
return fixVersions != null && wasBackportedFrom(train, fixVersions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the ticket is a release ticket.
|
||||
*
|
||||
* @param moduleIteration
|
||||
* @return
|
||||
*/
|
||||
public boolean isReleaseTicket(ModuleIteration moduleIteration) {
|
||||
|
||||
List<FixVersion> fixVersions = fields.getFixVersions();
|
||||
return fixVersions.size() == 1 && isPresent(moduleIteration.getTrain(), fixVersions)
|
||||
&& fields.hasSummary(Tracker.releaseTicketSummary(moduleIteration));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the project to {@code projectKey}.
|
||||
*
|
||||
* @param projectKey must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public JiraIssue project(ProjectKey projectKey) {
|
||||
|
||||
getFields().setProject(Project.from(projectKey));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the issue summary.
|
||||
*
|
||||
* @param summary must not be empty and not {@literal null}.
|
||||
*/
|
||||
public JiraIssue summary(String summary) {
|
||||
|
||||
Assert.hasText(summary, "Summary must not be empty!");
|
||||
getFields().setSummary(summary);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the fix version.
|
||||
*
|
||||
* @param moduleIteration must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public JiraIssue fixVersion(ModuleIteration moduleIteration) {
|
||||
|
||||
Assert.notNull(moduleIteration, "ModuleIteration must not be null!");
|
||||
getFields().setFixVersions(Collections.singletonList(new JiraVersion(moduleIteration).toFixVersion()));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isAssignedTo(String username) {
|
||||
return getFields().getAssignee() != null && getFields().getAssignee().matches(username);
|
||||
}
|
||||
|
||||
private static boolean wasBackportedFrom(Train train, List<FixVersion> fixVersions) {
|
||||
return fixVersions.size() > 1 && isPresent(train, fixVersions);
|
||||
}
|
||||
|
||||
private static boolean isPresent(Train train, List<FixVersion> fixVersions) {
|
||||
return fixVersions.stream().//
|
||||
filter(fixVersion -> fixVersion.isTrain(train)).//
|
||||
findFirst().//
|
||||
isPresent();
|
||||
}
|
||||
|
||||
public void assignTo(String username) {
|
||||
getFields().setAssignee(new JiraUser(username, null));
|
||||
}
|
||||
|
||||
@Data
|
||||
static class Fields {
|
||||
|
||||
Project project;
|
||||
IssueType issuetype;
|
||||
String summary;
|
||||
List<FixVersion> fixVersions;
|
||||
List<Component> components;
|
||||
Status status;
|
||||
Resolution resolution;
|
||||
JiraUser assignee;
|
||||
|
||||
public boolean hasSummary(String other) {
|
||||
return summary.equals(other);
|
||||
}
|
||||
|
||||
public boolean hasStatusAndResolution() {
|
||||
return status != null && resolution != null;
|
||||
}
|
||||
}
|
||||
|
||||
@Value
|
||||
static class Component {
|
||||
|
||||
String id, name;
|
||||
|
||||
public static Component of(String name) {
|
||||
|
||||
Assert.hasText(name, "Name must not be empty!");
|
||||
return new Component(null, name);
|
||||
}
|
||||
}
|
||||
|
||||
@Value
|
||||
static class FixVersion {
|
||||
|
||||
String id, name;
|
||||
|
||||
/**
|
||||
* Returns whether this {@link FixVersion} matches the given {@link Train}.
|
||||
*
|
||||
* @param train
|
||||
* @return
|
||||
*/
|
||||
public boolean isTrain(Train train) {
|
||||
return name.contains(train.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Value
|
||||
static class IssueType {
|
||||
|
||||
public final static IssueType TASK = new IssueType("Task");
|
||||
|
||||
String name;
|
||||
}
|
||||
|
||||
@Value
|
||||
static class Status {
|
||||
|
||||
String name;
|
||||
StatusCategory statusCategory;
|
||||
}
|
||||
|
||||
@Value
|
||||
static class Project {
|
||||
|
||||
String key;
|
||||
|
||||
public static Project from(ProjectKey projectKey) {
|
||||
return new Project(projectKey.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
@Value
|
||||
static class Resolution {
|
||||
|
||||
String name;
|
||||
}
|
||||
|
||||
@Value
|
||||
static class StatusCategory {
|
||||
|
||||
String key;
|
||||
}
|
||||
|
||||
@Value
|
||||
static class JiraUser {
|
||||
|
||||
String name, displayName;
|
||||
|
||||
static JiraUser from(String username) {
|
||||
return new JiraUser(username, null);
|
||||
}
|
||||
|
||||
public boolean matches(String username) {
|
||||
return username.equalsIgnoreCase(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.data.release.issues.jira;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Value;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
|
||||
/**
|
||||
* Value object to represent a Jira issue update via {@code PUT} with {@code update} fields.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Value
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
class JiraIssueUpdate {
|
||||
|
||||
private final Map<String, Object> update;
|
||||
private final Map<String, Object> transition;
|
||||
private final Map<String, Map<String, Object>> fields;
|
||||
|
||||
/**
|
||||
* Create an empty {@link JiraIssueUpdate}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static JiraIssueUpdate create() {
|
||||
return new JiraIssueUpdate(Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign the issue to {@code userId}.
|
||||
*
|
||||
* @param userId must not be {@literal null} or empty.
|
||||
* @return
|
||||
*/
|
||||
public JiraIssueUpdate assignTo(String userId) {
|
||||
|
||||
Assert.hasText(userId, "UserId must not be null or empty!");
|
||||
|
||||
Map<String, Object> update = new LinkedHashMap<>(this.update);
|
||||
update.put("assignee", new AssignTo(userId));
|
||||
|
||||
return new JiraIssueUpdate(update, this.transition, this.fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign a {@code resolution} the issue.
|
||||
*
|
||||
* @param resolution must not be {@literal null} or empty.
|
||||
* @return
|
||||
*/
|
||||
public JiraIssueUpdate resolution(String resolution) {
|
||||
|
||||
Assert.hasText(resolution, "Resolution must not be null or empty!");
|
||||
|
||||
Map<String, Map<String, Object>> fields = new LinkedHashMap<>(this.fields);
|
||||
Map<String, Object> resolutionField = fields.computeIfAbsent("resolution", k -> new LinkedHashMap<>());
|
||||
resolutionField.put("name", resolution);
|
||||
|
||||
return new JiraIssueUpdate(this.update, this.transition, fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign the issue to {@code userId}.
|
||||
*
|
||||
* @param transitionId Jira transition Id
|
||||
* @return
|
||||
*/
|
||||
public JiraIssueUpdate transition(int transitionId) {
|
||||
|
||||
Map<String, Object> transition = new LinkedHashMap<>(this.transition);
|
||||
transition.put("id", transitionId);
|
||||
|
||||
return new JiraIssueUpdate(this.update, transition, this.fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes as [ {"set": "value"} ]
|
||||
*/
|
||||
@Value
|
||||
@JsonSerialize(using = SetSerializer.class)
|
||||
static class AssignTo {
|
||||
final @NonNull String value;
|
||||
}
|
||||
|
||||
static class SetSerializer extends JsonSerializer<AssignTo> {
|
||||
|
||||
@Override
|
||||
public void serialize(AssignTo value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||
|
||||
gen.writeStartArray();
|
||||
|
||||
gen.writeObject(Collections.singletonMap("set", Collections.singletonMap("name", value.getValue())));
|
||||
|
||||
gen.writeEndArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.data.release.issues.jira;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.util.Streamable;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Value
|
||||
class JiraIssues implements Streamable<JiraIssue> {
|
||||
|
||||
int startAt, maxResult;
|
||||
@Getter int total;
|
||||
List<JiraIssue> issues = new ArrayList<>();
|
||||
|
||||
public int getNextStartAt() {
|
||||
return startAt + issues.size();
|
||||
}
|
||||
|
||||
public boolean hasMoreResults() {
|
||||
return startAt + issues.size() < total;
|
||||
}
|
||||
|
||||
public boolean hasIssues() {
|
||||
return !issues.isEmpty();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Iterable#iterator()
|
||||
*/
|
||||
@Override
|
||||
public Iterator<JiraIssue> iterator() {
|
||||
return issues.iterator();
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.data.release.issues.jira;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.data.release.model.Password;
|
||||
import org.springframework.data.release.utils.HttpBasicCredentials;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Configurable properties for JIRA.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "jira")
|
||||
public class JiraProperties {
|
||||
|
||||
private @Getter(AccessLevel.PRIVATE) Password password;
|
||||
private String username, apiUrl;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
|
||||
Assert.hasText(username, "No Jira username (jira.username) configured!");
|
||||
Assert.notNull(password, "No Jira password (jira.password) configured!");
|
||||
Assert.hasText(apiUrl, "No Jira url (jira.api-url) configured!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link HttpBasicCredentials} to be used.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public HttpBasicCredentials getCredentials() {
|
||||
return new HttpBasicCredentials(username, password);
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.data.release.issues.jira;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import org.springframework.data.release.model.ModuleIteration;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
/**
|
||||
* Value object to bind REST responses to.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Value
|
||||
class JiraReleaseVersion {
|
||||
|
||||
String id, name, project, description;
|
||||
boolean released;
|
||||
boolean archived;
|
||||
|
||||
public static JiraReleaseVersion of(ModuleIteration moduleIteration, JiraVersion jiraVersion) {
|
||||
|
||||
Assert.notNull(moduleIteration, "ModuleIteration must not be null.");
|
||||
Assert.notNull(jiraVersion, "JiraVersion must not be null.");
|
||||
|
||||
return new JiraReleaseVersion(null, jiraVersion.toString(), moduleIteration.getProjectKey().getKey(),
|
||||
jiraVersion.getDescription(), false, false);
|
||||
}
|
||||
|
||||
public JiraReleaseVersion markReleased() {
|
||||
return new JiraReleaseVersion(id, name, project, description, true, false);
|
||||
}
|
||||
|
||||
public JiraReleaseVersion markArchived() {
|
||||
return new JiraReleaseVersion(id, name, project, description, released, true);
|
||||
}
|
||||
|
||||
public boolean hasSameNameAs(JiraVersion jiraVersion) {
|
||||
return getName().equals(jiraVersion.toString());
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public boolean isOpen() {
|
||||
return !released;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public boolean isActive() {
|
||||
return !archived;
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.data.release.issues.jira;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.util.Streamable;
|
||||
|
||||
/**
|
||||
* Value object to bind REST responses to.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Value
|
||||
class JiraReleaseVersions implements Streamable<JiraReleaseVersion> {
|
||||
|
||||
int startAt, maxResults, total;
|
||||
List<JiraReleaseVersion> values = new ArrayList<>();
|
||||
|
||||
public int getNextStartAt() {
|
||||
return startAt + values.size();
|
||||
}
|
||||
|
||||
public boolean hasMoreResults() {
|
||||
return startAt + values.size() < total;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Iterable#iterator()
|
||||
*/
|
||||
@Override
|
||||
public Iterator<JiraReleaseVersion> iterator() {
|
||||
return values.iterator();
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.data.release.issues.jira;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
|
||||
import org.springframework.data.release.issues.TicketStatus;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Value(staticConstructor = "of")
|
||||
class JiraTicketStatus implements TicketStatus {
|
||||
|
||||
public static final JiraTicketStatus UNKNOWN = JiraTicketStatus.of(false, "unknown", null);
|
||||
|
||||
boolean resolved;
|
||||
@NonNull String status;
|
||||
String resolution;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.release.jira.TicketStatus#getLabel()
|
||||
*/
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return resolution == null ? status : status + "/" + resolution;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.release.jira.TicketStatus#isResolved()
|
||||
*/
|
||||
@Override
|
||||
public boolean isResolved() {
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.data.release.issues.jira;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import org.springframework.data.release.issues.jira.JiraIssue.FixVersion;
|
||||
import org.springframework.data.release.model.ModuleIteration;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Value
|
||||
class JiraVersion {
|
||||
|
||||
ModuleIteration module;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return module.getMediumVersionString();
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return module.getReleaseVersionString();
|
||||
}
|
||||
|
||||
public FixVersion toFixVersion() {
|
||||
return new FixVersion(null, toString());
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.data.release.issues.jira;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.data.release.model.ModuleIteration;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Value
|
||||
class JqlQuery {
|
||||
|
||||
private static final String PROJECT_VERSION_TEMPLATE = "(project = %s AND fixVersion = \"%s\" )";
|
||||
private static final String ISSUE_KEY_IN_TEMPLATE = "issueKey in (%s)";
|
||||
|
||||
private final String query;
|
||||
|
||||
/**
|
||||
* Creates a new {@link JqlQuery} for the given {@link ModuleIteration}.
|
||||
*
|
||||
* @param iteration must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static JqlQuery from(ModuleIteration iteration) {
|
||||
|
||||
Assert.notNull(iteration, "Module iteration must not be null!");
|
||||
|
||||
JiraVersion version = new JiraVersion(iteration);
|
||||
|
||||
return new JqlQuery(String.format(PROJECT_VERSION_TEMPLATE, iteration.getProjectKey(), version));
|
||||
}
|
||||
|
||||
public static JqlQuery from(Collection<String> ticketIds) {
|
||||
|
||||
String joinedTicketIds = ticketIds.stream().collect(Collectors.joining(", "));
|
||||
|
||||
return new JqlQuery(String.format(ISSUE_KEY_IN_TEMPLATE, joinedTicketIds));
|
||||
}
|
||||
|
||||
public static JqlQuery from(Stream<ModuleIteration> stream) {
|
||||
|
||||
List<String> parts = new ArrayList<>();
|
||||
|
||||
stream.forEach(moduleIteration -> {
|
||||
JiraVersion version = new JiraVersion(moduleIteration);
|
||||
parts.add(String.format(PROJECT_VERSION_TEMPLATE, moduleIteration.getProjectKey(), version));
|
||||
});
|
||||
|
||||
return new JqlQuery(StringUtils.collectionToDelimitedString(parts, " OR "));
|
||||
}
|
||||
|
||||
public JqlQuery and(String clause) {
|
||||
return new JqlQuery(String.format("(%s) AND %s", query, clause));
|
||||
}
|
||||
|
||||
public JqlQuery orderBy(String orderBy) {
|
||||
return new JqlQuery(String.format("%s ORDER BY %s", query, orderBy));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return query;
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,6 @@ class BranchUnitTests {
|
||||
|
||||
Branch branch = Branch.from("issue/DATACMNS-4711");
|
||||
|
||||
assertThat(branch.isIssueBranch(Tracker.JIRA)).isTrue();
|
||||
assertThat(branch.isIssueBranch(Tracker.GITHUB)).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.data.release.issues.jira;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link JiraComponents}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class JiraComponentsUnitTests {
|
||||
|
||||
@Test // #5
|
||||
void returnsComponentByName() {
|
||||
|
||||
JiraComponent fooComponent = new JiraComponent("123", "foo");
|
||||
JiraComponents jiraComponents = JiraComponents.of(Collections.singleton(fooComponent));
|
||||
|
||||
assertThat(jiraComponents.findComponent("foo").isPresent()).isTrue();
|
||||
}
|
||||
|
||||
@Test // #5
|
||||
void returnsEmptyIfComponentMissing() {
|
||||
|
||||
JiraComponent fooComponent = new JiraComponent("123", "foo");
|
||||
JiraComponents jiraComponents = JiraComponents.of(Collections.singleton(fooComponent));
|
||||
|
||||
assertThat(jiraComponents.findComponent("baz").isPresent()).isFalse();
|
||||
}
|
||||
|
||||
@Test // #5
|
||||
void failsOnNullArgumentConstruction() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> JiraComponents.of(null));
|
||||
}
|
||||
}
|
||||
@@ -1,333 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.data.release.issues.jira;
|
||||
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.*;
|
||||
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.assertj.core.api.Assumptions.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.release.AbstractIntegrationTests;
|
||||
import org.springframework.data.release.WireMockExtension;
|
||||
import org.springframework.data.release.issues.Ticket;
|
||||
import org.springframework.data.release.model.Iteration;
|
||||
import org.springframework.data.release.model.ModuleIteration;
|
||||
import org.springframework.data.release.model.ProjectKey;
|
||||
import org.springframework.data.release.model.Projects;
|
||||
import org.springframework.data.release.model.ReleaseTrains;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder;
|
||||
import com.github.tomakehurst.wiremock.common.ClasspathFileSource;
|
||||
|
||||
/**
|
||||
* Integration Tests for {@link Jira} using a local {@link WireMockExtension} server.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class JiraConnectorIntegrationTests extends AbstractIntegrationTests {
|
||||
|
||||
static final String CREATE_ISSUE_URI = "/rest/api/2/issue";
|
||||
static final String CREATE_VERSION_URI = "/rest/api/2/version";
|
||||
static final String UPDATE_VERSION_URI = "/rest/api/2/version/15475";
|
||||
static final String SEARCH_URI = "/rest/api/2/search";
|
||||
static final String PROJECT_VERSION_URI = "/rest/api/2/project/%s/version";
|
||||
static final String PROJECT_COMPONENTS_URI = "/rest/api/2/project/%s/components";
|
||||
static final ModuleIteration REST_HOPPER_RC1 = ReleaseTrains.HOPPER.getModuleIteration(Projects.REST, Iteration.RC1);
|
||||
|
||||
@RegisterExtension WireMockExtension mockService = new WireMockExtension(
|
||||
wireMockConfig().port(8888).fileSource(new ClasspathFileSource("integration/jira")));
|
||||
|
||||
@Autowired JiraConnector jira;
|
||||
@Autowired JiraProperties properties;
|
||||
|
||||
@BeforeEach
|
||||
void before() {
|
||||
|
||||
UriComponents uriComponents = UriComponentsBuilder.fromUriString(properties.getApiUrl()).build();
|
||||
assumeThat(uriComponents.getHost()).isEqualTo("localhost");
|
||||
|
||||
properties.setUsername("dummy");
|
||||
jira.reset();
|
||||
}
|
||||
|
||||
@Test // #5
|
||||
void findResolvedTicketsByTicketIds() {
|
||||
|
||||
mockSearchWith("DATAREDIS-1andDATAJPA-1.json");
|
||||
|
||||
Collection<Ticket> tickets = jira.findTickets(Projects.COMMONS, Arrays.asList("DATAREDIS-1", "DATAJPA-1"));
|
||||
assertThat(tickets).hasSize(2);
|
||||
}
|
||||
|
||||
@Test // #5
|
||||
void ignoresUnknownTicketsByTicketId() {
|
||||
|
||||
mockSearchWith("emptyTickets.json");
|
||||
|
||||
Collection<Ticket> tickets = jira.findTickets(Projects.COMMONS, Arrays.asList("XYZ-1", "UNKOWN-1"));
|
||||
assertThat(tickets).isEmpty();
|
||||
}
|
||||
|
||||
@Test // #5
|
||||
void emptyResultWithEmptyTicketIds() {
|
||||
|
||||
Collection<Ticket> tickets = jira.findTickets(Projects.COMMONS, Collections
|
||||
.emptyList());
|
||||
assertThat(tickets).isEmpty();
|
||||
}
|
||||
|
||||
@Test // #5
|
||||
void getReleaseTicketForReturnsTheReleaseTicket() {
|
||||
|
||||
mockSearchWith("releaseTickets.json");
|
||||
|
||||
Ticket releaseTicket = jira.getReleaseTicketFor(REST_HOPPER_RC1);
|
||||
assertThat(releaseTicket.getId()).isEqualTo("DATAREST-782");
|
||||
}
|
||||
|
||||
@Test // #5
|
||||
void noReleaseTicketFound() {
|
||||
|
||||
mockSearchWith("emptyTickets.json");
|
||||
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> jira.getReleaseTicketFor(REST_HOPPER_RC1))
|
||||
.withMessageContaining("Did not find a release ticket for Spring Data REST 2.5 RC1");
|
||||
}
|
||||
|
||||
@Test // #5
|
||||
void getReleaseVersion() {
|
||||
|
||||
mockGetProjectVersionsWith("releaseVersions.json", REST_HOPPER_RC1.getProjectKey());
|
||||
|
||||
Optional<JiraReleaseVersion> optional = jira.findJiraReleaseVersion(REST_HOPPER_RC1);
|
||||
|
||||
assertThat(optional).isPresent();
|
||||
assertThat(optional.get().getName()).isEqualTo("2.5 RC1 (Hopper)");
|
||||
}
|
||||
|
||||
@Test // #5
|
||||
void createReleaseVersionShouldCreateAVersion() {
|
||||
|
||||
mockGetProjectVersionsWith("emptyReleaseVersions.json", REST_HOPPER_RC1.getProjectKey());
|
||||
mockCreateVersionWith("versionCreated.json");
|
||||
|
||||
jira.createReleaseVersion(REST_HOPPER_RC1);
|
||||
|
||||
verify(postRequestedFor(urlPathMatching(CREATE_VERSION_URI)).withRequestBody(equalToJson(
|
||||
"{\"name\":\"2.5 RC1 (Hopper)\",\"project\":\"DATAREST\",\"description\":\"Hopper RC1\", \"released\":false, \"archived\":false}")));
|
||||
}
|
||||
|
||||
@Test // #5
|
||||
void createReleaseVersionShouldFindExistingReleaseVersion() {
|
||||
|
||||
ModuleIteration moduleIteration = ReleaseTrains.HOPPER.getModuleIteration(Projects.REST, Iteration.RC1);
|
||||
|
||||
mockGetProjectVersionsWith("releaseVersions.json", moduleIteration.getProjectKey());
|
||||
|
||||
jira.createReleaseVersion(moduleIteration);
|
||||
|
||||
verify(0, postRequestedFor(urlPathMatching(CREATE_VERSION_URI)));
|
||||
}
|
||||
|
||||
@Test // #56
|
||||
void archiveReleaseVersionShouldArchiveReleaseVersion() {
|
||||
|
||||
ModuleIteration moduleIteration = ReleaseTrains.HOPPER.getModuleIteration(Projects.REST, Iteration.RC1);
|
||||
|
||||
mockGetProjectVersionsWith("releaseVersions.json", moduleIteration.getProjectKey());
|
||||
mockService.stubFor(put(urlPathMatching(UPDATE_VERSION_URI)).//
|
||||
willReturn(json("versionCreated.json")));
|
||||
|
||||
jira.archiveReleaseVersion(moduleIteration);
|
||||
|
||||
verify(putRequestedFor(urlPathMatching(UPDATE_VERSION_URI))
|
||||
.withRequestBody(equalToJson("{\"id\":\"15475\",\"name\":\"2.5 RC1 (Hopper)\","
|
||||
+ "\"description\":\"Hopper RC1\", \"released\":true,\"archived\":true}")));
|
||||
}
|
||||
|
||||
@Test // #5
|
||||
void createReleaseTicketShouldCreateReleaseTicket() {
|
||||
|
||||
ModuleIteration moduleIteration = ReleaseTrains.HOPPER.getModuleIteration(Projects.REST, Iteration.RC1);
|
||||
|
||||
mockGetProjectVersionsWith("releaseVersions.json", moduleIteration.getProjectKey());
|
||||
mockGetProjectComponentsWith("projectComponents.json", moduleIteration.getProjectKey());
|
||||
mockSearchWith("emptyTickets.json");
|
||||
prepareCreateIssueAndReturn("issueCreated.json");
|
||||
mockService.stubFor(get(urlPathMatching("/rest/api/2/issue/DATAREDIS-42")).//
|
||||
willReturn(json("existingTicket.json")));
|
||||
|
||||
jira.createReleaseTicket(moduleIteration);
|
||||
|
||||
verify(postRequestedFor(urlPathMatching(CREATE_ISSUE_URI))
|
||||
.withRequestBody(equalToJson("{\"fields\":{\"project\":{\"key\":\"DATAREST\"},"
|
||||
+ "\"issuetype\":{\"name\":\"Task\"},\"summary\":\"Release 2.5 RC1 (Hopper)\","
|
||||
+ "\"fixVersions\":[{\"name\":\"2.5 RC1 (Hopper)\"}],"
|
||||
+ "\"components\":[{\"name\":\"Infrastructure\"}]}}")));
|
||||
}
|
||||
|
||||
@Test // #5
|
||||
void createReleaseTicketShouldCreateReleaseTicketWithoutComponent() {
|
||||
|
||||
ModuleIteration moduleIteration = ReleaseTrains.HOPPER.getModuleIteration(Projects.REST, Iteration.RC1);
|
||||
|
||||
mockGetProjectVersionsWith("releaseVersions.json", moduleIteration.getProjectKey());
|
||||
mockGetProjectComponentsWith("emptyProjectComponents.json", moduleIteration.getProjectKey());
|
||||
mockSearchWith("emptyTickets.json");
|
||||
prepareCreateIssueAndReturn("issueCreated.json");
|
||||
mockService.stubFor(get(urlPathMatching("/rest/api/2/issue/DATAREDIS-42")).//
|
||||
willReturn(json("existingTicket.json")));
|
||||
|
||||
jira.createReleaseTicket(moduleIteration);
|
||||
|
||||
verify(postRequestedFor(urlPathMatching(CREATE_ISSUE_URI))
|
||||
.withRequestBody(equalToJson("{\"fields\":{\"project\":{\"key\":\"DATAREST\"},"
|
||||
+ "\"issuetype\":{\"name\":\"Task\"},\"summary\":\"Release 2.5 RC1 (Hopper)\","
|
||||
+ "\"fixVersions\":[{\"name\":\"2.5 RC1 (Hopper)\"}]}}")));
|
||||
}
|
||||
|
||||
@Test // #5
|
||||
void createReleaseTicketShouldFailWithNoReleaseVersion() {
|
||||
|
||||
ModuleIteration moduleIteration = ReleaseTrains.HOPPER.getModuleIteration(Projects.REST, Iteration.RC1);
|
||||
|
||||
mockSearchWith("emptyTickets.json");
|
||||
mockGetProjectVersionsWith("emptyReleaseVersions.json", moduleIteration.getProjectKey());
|
||||
|
||||
assertThatIllegalStateException().isThrownBy(() -> jira.createReleaseTicket(moduleIteration))
|
||||
.withMessageContaining("No release version for Spring Data REST 2.5 RC1 (Hopper) found");
|
||||
}
|
||||
|
||||
@Test // #5
|
||||
void createReleaseTicketShouldFindExistingTicket() {
|
||||
|
||||
ModuleIteration moduleIteration = ReleaseTrains.HOPPER.getModuleIteration(Projects.REST, Iteration.RC1);
|
||||
|
||||
mockGetProjectVersionsWith("releaseVersions.json", moduleIteration.getProjectKey());
|
||||
mockGetProjectComponentsWith("projectComponents.json", moduleIteration.getProjectKey());
|
||||
mockSearchWith("releaseTickets.json");
|
||||
|
||||
jira.createReleaseTicket(moduleIteration);
|
||||
|
||||
verify(0, postRequestedFor(urlPathMatching(CREATE_ISSUE_URI)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #5, #54
|
||||
*/
|
||||
@Test
|
||||
void assignTicketToMe() {
|
||||
|
||||
mockService.stubFor(get(urlPathMatching("/rest/api/2/issue/DATAREDIS-302")).//
|
||||
willReturn(json("existingTicket.json")));
|
||||
|
||||
mockService.stubFor(put(urlPathMatching("/rest/api/2/issue/DATAREDIS-302")).//
|
||||
willReturn(aResponse().withStatus(204)));
|
||||
|
||||
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\":{}}")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #5, #53
|
||||
*/
|
||||
@Test
|
||||
void skipTicketAssignmentIfAssigned() {
|
||||
|
||||
properties.setUsername("mp911de");
|
||||
|
||||
mockService.stubFor(get(urlPathMatching("/rest/api/2/issue/DATACASS-302")).//
|
||||
willReturn(json("existingTicket.json")));
|
||||
|
||||
mockService.stubFor(post(urlPathMatching("/rest/api/2/issue/DATACASS-302")).//
|
||||
willReturn(aResponse().withStatus(204)));
|
||||
|
||||
jira.assignTicketToMe(Projects.CASSANDRA, new Ticket("DATACASS-302", "", null));
|
||||
|
||||
verify(0, postRequestedFor(urlPathMatching("/rest/api/2/issue/DATACASS-302")));
|
||||
}
|
||||
|
||||
@Test // #94
|
||||
void closeIterationShouldResolveReleaseTicket() {
|
||||
|
||||
ModuleIteration moduleIteration = ReleaseTrains.HOPPER.getModuleIteration(Projects.REST, Iteration.RC1);
|
||||
|
||||
properties.setUsername("mp911de");
|
||||
|
||||
mockGetProjectVersionsWith("releaseVersions.json", moduleIteration.getProjectKey());
|
||||
mockSearchWith("releaseTickets.json");
|
||||
|
||||
mockService.stubFor(get(urlPathMatching("/rest/api/2/issue/DATAREST-782")).//
|
||||
willReturn(json("releaseTicket.json")));
|
||||
|
||||
mockService.stubFor(post(urlPathMatching("/rest/api/2/issue/DATAREST-782/transitions")) //
|
||||
.willReturn(ResponseDefinitionBuilder.responseDefinition().withStatus(200)));
|
||||
|
||||
jira.closeIteration(moduleIteration);
|
||||
|
||||
verify(postRequestedFor(urlPathMatching("/rest/api/2/issue/DATAREST-782/transitions")).withRequestBody(
|
||||
equalToJson("{\"update\":{},\"transition\":{\"id\":5},\"fields\":{\"resolution\":{\"name\":\"Complete\"}}}")));
|
||||
}
|
||||
|
||||
private void mockSearchWith(String fromClassPath) {
|
||||
mockService.stubFor(get(urlPathMatching(SEARCH_URI)).//
|
||||
willReturn(json(fromClassPath)));
|
||||
}
|
||||
|
||||
private void mockGetProjectVersionsWith(String fromClassPath, ProjectKey projectKey) {
|
||||
mockService.stubFor(get(urlPathMatching(String.format(PROJECT_VERSION_URI, projectKey))).//
|
||||
willReturn(json(fromClassPath)));
|
||||
}
|
||||
|
||||
private void mockGetProjectComponentsWith(String fromClassPath, ProjectKey projectKey) {
|
||||
mockService.stubFor(get(urlPathMatching(String.format(PROJECT_COMPONENTS_URI, projectKey))).//
|
||||
willReturn(json(fromClassPath)));
|
||||
}
|
||||
|
||||
private void prepareCreateIssueAndReturn(String fromClassPath) {
|
||||
mockService.stubFor(post(urlPathMatching(CREATE_ISSUE_URI)).//
|
||||
willReturn(json(fromClassPath)));
|
||||
}
|
||||
|
||||
private void mockCreateVersionWith(String fromClassPath) {
|
||||
mockService.stubFor(post(urlPathMatching(CREATE_VERSION_URI)).//
|
||||
willReturn(json(fromClassPath)));
|
||||
}
|
||||
|
||||
private ResponseDefinitionBuilder json(String fromClassPathFile) {
|
||||
return aResponse().//
|
||||
withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).//
|
||||
withBodyFile(fromClassPathFile);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.data.release.issues.jira;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.release.model.Iteration;
|
||||
import org.springframework.data.release.model.ModuleIteration;
|
||||
import org.springframework.data.release.model.Projects;
|
||||
import org.springframework.data.release.model.ReleaseTrains;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link JiraVersion}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class JiraVersionUnitTests {
|
||||
|
||||
@Test
|
||||
void rendersJiraGaVersionCorrectly() {
|
||||
|
||||
assertIterationVersion(Iteration.M1, "1.8 M1 (Dijkstra)");
|
||||
assertIterationVersion(Iteration.RC1, "1.8 RC1 (Dijkstra)");
|
||||
assertIterationVersion(Iteration.GA, "1.8 GA (Dijkstra)");
|
||||
|
||||
assertIterationVersion(Iteration.SR1, "1.8.1 (Dijkstra SR1)");
|
||||
assertIterationVersion(Iteration.SR2, "1.8.2 (Dijkstra SR2)");
|
||||
assertIterationVersion(Iteration.SR3, "1.8.3 (Dijkstra SR3)");
|
||||
assertIterationVersion(Iteration.SR4, "1.8.4 (Dijkstra SR4)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void rendersGithubCalverCorrectly() {
|
||||
|
||||
ModuleIteration m1 = ReleaseTrains.OCKHAM.getModuleIteration(Projects.COMMONS, Iteration.M1);
|
||||
ModuleIteration rc1 = ReleaseTrains.OCKHAM.getModuleIteration(Projects.COMMONS, Iteration.RC1);
|
||||
ModuleIteration ga = ReleaseTrains.OCKHAM.getModuleIteration(Projects.COMMONS, Iteration.GA);
|
||||
|
||||
assertThat(new JiraVersion(m1).getDescription()).isEqualTo("2020.0.0-M1");
|
||||
assertThat(new JiraVersion(rc1).getDescription()).isEqualTo("2020.0.0-RC1");
|
||||
assertThat(new JiraVersion(ga).getDescription()).isEqualTo("2020.0.0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesCustomModuleIterationStartVersion() {
|
||||
|
||||
ModuleIteration module = ReleaseTrains.DIJKSTRA.getModuleIteration(Projects.ELASTICSEARCH, Iteration.M1);
|
||||
|
||||
JiraVersion version = new JiraVersion(module);
|
||||
assertThat(version).hasToString("1.0 M1 (Dijkstra)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void doesNotUseCustomIterationOnNonFirstiterations() {
|
||||
|
||||
ModuleIteration module = ReleaseTrains.DIJKSTRA.getModuleIteration(Projects.ELASTICSEARCH, Iteration.RC1);
|
||||
|
||||
JiraVersion version = new JiraVersion(module);
|
||||
assertThat(version).hasToString("1.0 RC1 (Dijkstra)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void rendersDescriptionCorrectly() {
|
||||
|
||||
ModuleIteration module = ReleaseTrains.DIJKSTRA.getModuleIteration(Projects.ELASTICSEARCH, Iteration.M1);
|
||||
|
||||
JiraVersion version = new JiraVersion(module);
|
||||
assertThat(version.getDescription()).isEqualTo("Dijkstra M2");
|
||||
}
|
||||
|
||||
private void assertIterationVersion(Iteration iteration, String expected) {
|
||||
|
||||
ModuleIteration module = ReleaseTrains.DIJKSTRA.getModuleIteration(Projects.COMMONS, iteration);
|
||||
|
||||
JiraVersion version = new JiraVersion(module);
|
||||
assertThat(version).hasToString(expected);
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.data.release.issues.jira;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.data.release.issues.Ticket;
|
||||
import org.springframework.data.release.issues.Tickets;
|
||||
import org.springframework.data.release.model.Iteration;
|
||||
import org.springframework.data.release.model.Projects;
|
||||
import org.springframework.data.release.model.ReleaseTrains;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link Tickets}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class TicketsUnitTests {
|
||||
|
||||
@Test
|
||||
void hasReleaseTicketShouldReturnTrue() {
|
||||
|
||||
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));
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasReleaseTickeForTicketWithoutTrainNameShouldReturnFalse() {
|
||||
|
||||
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));
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getReleaseTicketReturnsReleaseTicket() {
|
||||
|
||||
Ticket ticket = new Ticket("1234", "Release 1.10 GA (Hopper)", JiraTicketStatus.of(false, "", ""));
|
||||
Tickets tickets = new Tickets(Collections.singletonList(ticket));
|
||||
|
||||
Ticket releaseTicket = tickets
|
||||
.getReleaseTicket(ReleaseTrains.HOPPER.getModuleIteration(Projects.JPA, Iteration.GA));
|
||||
assertThat(releaseTicket).isEqualTo(ticket);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getReleaseTicketReturnsCalverReleaseTicket() {
|
||||
|
||||
Tickets tickets = new Tickets(
|
||||
Collections
|
||||
.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));
|
||||
assertThat(releaseTicket).isNotNull();
|
||||
|
||||
tickets = new Tickets(
|
||||
Collections
|
||||
.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)", JiraTicketStatus.of(false, "", ""))));
|
||||
|
||||
releaseTicket = tickets.getReleaseTicket(ReleaseTrains.OCKHAM.getModuleIteration(Projects.JPA, Iteration.SR1));
|
||||
assertThat(releaseTicket).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getReleaseTicketThrowsExceptionWithoutAReleaseTicket() {
|
||||
|
||||
Ticket ticket = new Ticket("1234", "Release 1.10 GA", JiraTicketStatus.of(false, "", ""));
|
||||
Tickets tickets = new Tickets(Collections.singletonList(ticket));
|
||||
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> tickets.getReleaseTicket(ReleaseTrains.HOPPER.getModuleIteration(Projects.JPA, Iteration.GA)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getResolvedReleaseTicket() {
|
||||
|
||||
Ticket ticket = new Ticket("1234", "Release 1.10 GA (Hopper)", JiraTicketStatus.of(true, "", ""));
|
||||
Tickets tickets = new Tickets(Collections.singletonList(ticket));
|
||||
|
||||
Ticket releaseTicket = tickets
|
||||
.getReleaseTicket(ReleaseTrains.HOPPER.getModuleIteration(Projects.JPA, Iteration.GA));
|
||||
assertThat(releaseTicket).isEqualTo(ticket);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getReleaseTicketsReturnsReleaseTickets() {
|
||||
|
||||
Ticket ticket = new Ticket("1234", "Release 1.10 GA (Hopper)", JiraTicketStatus.of(false, "", ""));
|
||||
Tickets tickets = new Tickets(Collections.singletonList(ticket));
|
||||
|
||||
Tickets result = tickets
|
||||
.getReleaseTickets(ReleaseTrains.HOPPER.getModuleIteration(Projects.JPA, Iteration.GA).getTrainIteration());
|
||||
assertThat(result.getTickets().contains(ticket)).isTrue();
|
||||
}
|
||||
}
|
||||
@@ -28,26 +28,6 @@ import org.junit.jupiter.api.Test;
|
||||
*/
|
||||
class TrackerTest {
|
||||
|
||||
@Test
|
||||
void testMatchesTicketNumber() throws Exception {
|
||||
|
||||
Pattern pattern = Pattern.compile(Tracker.JIRA.getTicketPattern());
|
||||
Matcher matcher = pattern.matcher("DATAREDIS-1");
|
||||
matcher.find();
|
||||
|
||||
assertThat(matcher.group(1)).isEqualTo("DATAREDIS-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMatchesBranchNamedLikeTicket() throws Exception {
|
||||
|
||||
Pattern pattern = Pattern.compile(Tracker.JIRA.getTicketPattern());
|
||||
Matcher matcher = pattern.matcher("issues/DATAREDIS-1-dummy");
|
||||
matcher.find();
|
||||
|
||||
assertThat(matcher.group(1)).isEqualTo("DATAREDIS-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVersionBranch() throws Exception {
|
||||
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
{
|
||||
"expand": "schema,names",
|
||||
"startAt": 0,
|
||||
"maxResults": 50,
|
||||
"total": 2,
|
||||
"issues": [
|
||||
{
|
||||
"expand": "operations,editmeta,changelog,transitions,renderedFields",
|
||||
"id": "35482",
|
||||
"self": "https://jira.spring.io/rest/api/2/issue/35482",
|
||||
"key": "DATAREDIS-1",
|
||||
"fields": {
|
||||
"summary": "Connections obtained from pool are never returned",
|
||||
"resolution": {
|
||||
"self": "https://jira.spring.io/rest/api/2/resolution/8",
|
||||
"id": "8",
|
||||
"description": "",
|
||||
"name": "Complete"
|
||||
},
|
||||
"status": {
|
||||
"self": "https://jira.spring.io/rest/api/2/status/5",
|
||||
"description": "A resolution has been taken, and it is awaiting verification by reporter. From here issues are either reopened, or are closed.",
|
||||
"iconUrl": "https://jira.spring.io/images/icons/statuses/resolved.png",
|
||||
"name": "Resolved",
|
||||
"id": "5",
|
||||
"statusCategory": {
|
||||
"self": "https://jira.spring.io/rest/api/2/statuscategory/3",
|
||||
"id": 3,
|
||||
"key": "done",
|
||||
"colorName": "green",
|
||||
"name": "Done"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"expand": "operations,editmeta,changelog,transitions,renderedFields",
|
||||
"id": "35060",
|
||||
"self": "https://jira.spring.io/rest/api/2/issue/35060",
|
||||
"key": "DATAJPA-1",
|
||||
"fields": {
|
||||
"summary": "Move JPA parts of Hades library into Spring JPA",
|
||||
"resolution": {
|
||||
"self": "https://jira.spring.io/rest/api/2/resolution/1",
|
||||
"id": "1",
|
||||
"description": "A fix for this issue is checked into the tree and tested.",
|
||||
"name": "Fixed"
|
||||
},
|
||||
"status": {
|
||||
"self": "https://jira.spring.io/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://jira.spring.io/images/icons/statuses/closed.png",
|
||||
"name": "Closed",
|
||||
"id": "6",
|
||||
"statusCategory": {
|
||||
"self": "https://jira.spring.io/rest/api/2/statuscategory/3",
|
||||
"id": 3,
|
||||
"key": "done",
|
||||
"colorName": "green",
|
||||
"name": "Done"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
[
|
||||
]
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/project/DATAREST/version?maxResults=50&startAt=0",
|
||||
"maxResults": 50,
|
||||
"startAt": 0,
|
||||
"total": 0,
|
||||
"isLast": true,
|
||||
"values": []
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"startAt": 0,
|
||||
"maxResults": 50,
|
||||
"total": 0,
|
||||
"issues": []
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
{
|
||||
"expand": "renderedFields,names,schema,transitions,operations,editmeta,changelog",
|
||||
"id": "68967",
|
||||
"self": "https://jira.spring.io/rest/api/2/issue/68967",
|
||||
"key": "DATACASS-302",
|
||||
"fields": {
|
||||
"issuetype": {
|
||||
"self": "https://jira.spring.io/rest/api/2/issuetype/3",
|
||||
"id": "3",
|
||||
"description": "A task that needs to be done.",
|
||||
"iconUrl": "https://jira.spring.io/images/icons/issuetypes/task.png",
|
||||
"name": "Task",
|
||||
"subtask": false
|
||||
},
|
||||
"timespent": null,
|
||||
"customfield_10150": null,
|
||||
"project": {
|
||||
"self": "https://jira.spring.io/rest/api/2/project/11403",
|
||||
"id": "11403",
|
||||
"key": "DATACASS",
|
||||
"name": "Spring Data for Apache Cassandra",
|
||||
"avatarUrls": {
|
||||
"48x48": "https://jira.spring.io/secure/projectavatar?pid=11403&avatarId=12504",
|
||||
"24x24": "https://jira.spring.io/secure/projectavatar?size=small&pid=11403&avatarId=12504",
|
||||
"16x16": "https://jira.spring.io/secure/projectavatar?size=xsmall&pid=11403&avatarId=12504",
|
||||
"32x32": "https://jira.spring.io/secure/projectavatar?size=medium&pid=11403&avatarId=12504"
|
||||
},
|
||||
"projectCategory": {
|
||||
"self": "https://jira.spring.io/rest/api/2/projectCategory/10000",
|
||||
"id": "10000",
|
||||
"description": "Top-level Spring projects",
|
||||
"name": "Spring Projects"
|
||||
}
|
||||
},
|
||||
"fixVersions": [],
|
||||
"aggregatetimespent": null,
|
||||
"resolution": null,
|
||||
"resolutiondate": null,
|
||||
"workratio": -1,
|
||||
"lastViewed": null,
|
||||
"watches": {
|
||||
"self": "https://jira.spring.io/rest/api/2/issue/DATACASS-302/watchers",
|
||||
"watchCount": 1,
|
||||
"isWatching": true
|
||||
},
|
||||
"customfield_10180": "27907200",
|
||||
"customfield_10181": "true",
|
||||
"customfield_10380": "9223372036854775807",
|
||||
"customfield_10182": "mp911de(mp911de)",
|
||||
"created": "2016-06-20T11:54:21.542+0000",
|
||||
"customfield_10381": "9223372036854775807",
|
||||
"customfield_10140": null,
|
||||
"customfield_10580": null,
|
||||
"customfield_10142": null,
|
||||
"priority": {
|
||||
"self": "https://jira.spring.io/rest/api/2/priority/4",
|
||||
"iconUrl": "https://jira.spring.io/images/icons/priorities/minor.png",
|
||||
"name": "Minor",
|
||||
"id": "4"
|
||||
},
|
||||
"customfield_10981": null,
|
||||
"labels": [],
|
||||
"timeestimate": null,
|
||||
"aggregatetimeoriginalestimate": null,
|
||||
"versions": [],
|
||||
"issuelinks": [],
|
||||
"assignee": {
|
||||
"self": "https://jira.spring.io/rest/api/2/user?username=mp911de",
|
||||
"name": "mp911de",
|
||||
"key": "mp911de",
|
||||
"emailAddress": "mpaluch at pivotal dot io",
|
||||
"avatarUrls": {
|
||||
"48x48": "https://jira.spring.io/secure/useravatar?ownerId=mp911de&avatarId=14015",
|
||||
"24x24": "https://jira.spring.io/secure/useravatar?size=small&ownerId=mp911de&avatarId=14015",
|
||||
"16x16": "https://jira.spring.io/secure/useravatar?size=xsmall&ownerId=mp911de&avatarId=14015",
|
||||
"32x32": "https://jira.spring.io/secure/useravatar?size=medium&ownerId=mp911de&avatarId=14015"
|
||||
},
|
||||
"displayName": "Mark Paluch",
|
||||
"active": true,
|
||||
"timeZone": "Europe/Berlin"
|
||||
},
|
||||
"updated": "2016-06-20T11:54:42.720+0000",
|
||||
"status": {
|
||||
"self": "https://jira.spring.io/rest/api/2/status/1",
|
||||
"description": "The issue is open and ready for the assignee to start work on it.",
|
||||
"iconUrl": "https://jira.spring.io/images/icons/statuses/open.png",
|
||||
"name": "Open",
|
||||
"id": "1",
|
||||
"statusCategory": {
|
||||
"self": "https://jira.spring.io/rest/api/2/statuscategory/2",
|
||||
"id": 2,
|
||||
"key": "new",
|
||||
"colorName": "blue-gray",
|
||||
"name": "To Do"
|
||||
}
|
||||
},
|
||||
"components": [],
|
||||
"customfield_10170": null,
|
||||
"customfield_10171": null,
|
||||
"timeoriginalestimate": null,
|
||||
"description": "Add support for Cassandra {{time}} data type.\r\n\r\n{{time}} represents a Time string, such as 13:30:54.234 but it's mapped to the {{long}} Java type. The {{long}} value denotes millis since midnight. Updates require quoting of the value:\r\n\r\n{code}\r\nUPDATE mytable SET mytimefield ='123456';\r\n{code}\r\n\r\nPlease see also https://issues.apache.org/jira/browse/CASSANDRA-11798",
|
||||
"timetracking": {},
|
||||
"attachment": [],
|
||||
"aggregatetimeestimate": null,
|
||||
"summary": "Support Cassandra time columns",
|
||||
"creator": {
|
||||
"self": "https://jira.spring.io/rest/api/2/user?username=mp911de",
|
||||
"name": "mp911de",
|
||||
"key": "mp911de",
|
||||
"emailAddress": "mpaluch at pivotal dot io",
|
||||
"avatarUrls": {
|
||||
"48x48": "https://jira.spring.io/secure/useravatar?ownerId=mp911de&avatarId=14015",
|
||||
"24x24": "https://jira.spring.io/secure/useravatar?size=small&ownerId=mp911de&avatarId=14015",
|
||||
"16x16": "https://jira.spring.io/secure/useravatar?size=xsmall&ownerId=mp911de&avatarId=14015",
|
||||
"32x32": "https://jira.spring.io/secure/useravatar?size=medium&ownerId=mp911de&avatarId=14015"
|
||||
},
|
||||
"displayName": "Mark Paluch",
|
||||
"active": true,
|
||||
"timeZone": "Europe/Berlin"
|
||||
},
|
||||
"customfield_10280": "9223372036854775807",
|
||||
"subtasks": [],
|
||||
"customfield_10480": null,
|
||||
"customfield_10680": null,
|
||||
"customfield_10120": null,
|
||||
"reporter": {
|
||||
"self": "https://jira.spring.io/rest/api/2/user?username=mp911de",
|
||||
"name": "mp911de",
|
||||
"key": "mp911de",
|
||||
"emailAddress": "mpaluch at pivotal dot io",
|
||||
"avatarUrls": {
|
||||
"48x48": "https://jira.spring.io/secure/useravatar?ownerId=mp911de&avatarId=14015",
|
||||
"24x24": "https://jira.spring.io/secure/useravatar?size=small&ownerId=mp911de&avatarId=14015",
|
||||
"16x16": "https://jira.spring.io/secure/useravatar?size=xsmall&ownerId=mp911de&avatarId=14015",
|
||||
"32x32": "https://jira.spring.io/secure/useravatar?size=medium&ownerId=mp911de&avatarId=14015"
|
||||
},
|
||||
"displayName": "Mark Paluch",
|
||||
"active": true,
|
||||
"timeZone": "Europe/Berlin"
|
||||
},
|
||||
"customfield_10000": null,
|
||||
"customfield_10880": "0|i09a87:",
|
||||
"aggregateprogress": {
|
||||
"progress": 0,
|
||||
"total": 0
|
||||
},
|
||||
"customfield_10002": null,
|
||||
"customfield_10684": null,
|
||||
"environment": null,
|
||||
"duedate": null,
|
||||
"progress": {
|
||||
"progress": 0,
|
||||
"total": 0
|
||||
},
|
||||
"comment": {
|
||||
"startAt": 0,
|
||||
"maxResults": 0,
|
||||
"total": 0,
|
||||
"comments": []
|
||||
},
|
||||
"votes": {
|
||||
"self": "https://jira.spring.io/rest/api/2/issue/DATACASS-302/votes",
|
||||
"votes": 0,
|
||||
"hasVoted": false
|
||||
},
|
||||
"worklog": {
|
||||
"startAt": 0,
|
||||
"maxResults": 20,
|
||||
"total": 0,
|
||||
"worklogs": []
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"id": "10000",
|
||||
"key": "DATAREDIS-42",
|
||||
"self": "https://www.example.com/jira/rest/api/2/issue/10000"
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
[
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/component/12775",
|
||||
"id": "12775",
|
||||
"name": "Infrastructure",
|
||||
"assigneeType": "PROJECT_DEFAULT",
|
||||
"assignee": {
|
||||
"self": "https://jira.spring.io/rest/api/2/user?username=olivergierke",
|
||||
"key": "olivergierke",
|
||||
"name": "olivergierke",
|
||||
"avatarUrls": {
|
||||
"16x16": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=16",
|
||||
"24x24": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=24",
|
||||
"32x32": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=32",
|
||||
"48x48": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=48"
|
||||
},
|
||||
"displayName": "Oliver Gierke",
|
||||
"active": true
|
||||
},
|
||||
"realAssigneeType": "PROJECT_DEFAULT",
|
||||
"realAssignee": {
|
||||
"self": "https://jira.spring.io/rest/api/2/user?username=olivergierke",
|
||||
"key": "olivergierke",
|
||||
"name": "olivergierke",
|
||||
"avatarUrls": {
|
||||
"16x16": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=16",
|
||||
"24x24": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=24",
|
||||
"32x32": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=32",
|
||||
"48x48": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=48"
|
||||
},
|
||||
"displayName": "Oliver Gierke",
|
||||
"active": true
|
||||
},
|
||||
"isAssigneeTypeValid": true,
|
||||
"project": "DATAREST",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/component/12748",
|
||||
"id": "12748",
|
||||
"name": "Content negotiation",
|
||||
"assigneeType": "PROJECT_DEFAULT",
|
||||
"assignee": {
|
||||
"self": "https://jira.spring.io/rest/api/2/user?username=olivergierke",
|
||||
"key": "olivergierke",
|
||||
"name": "olivergierke",
|
||||
"avatarUrls": {
|
||||
"16x16": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=16",
|
||||
"24x24": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=24",
|
||||
"32x32": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=32",
|
||||
"48x48": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=48"
|
||||
},
|
||||
"displayName": "Oliver Gierke",
|
||||
"active": true
|
||||
},
|
||||
"realAssigneeType": "PROJECT_DEFAULT",
|
||||
"realAssignee": {
|
||||
"self": "https://jira.spring.io/rest/api/2/user?username=olivergierke",
|
||||
"key": "olivergierke",
|
||||
"name": "olivergierke",
|
||||
"avatarUrls": {
|
||||
"16x16": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=16",
|
||||
"24x24": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=24",
|
||||
"32x32": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=32",
|
||||
"48x48": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=48"
|
||||
},
|
||||
"displayName": "Oliver Gierke",
|
||||
"active": true
|
||||
},
|
||||
"isAssigneeTypeValid": true,
|
||||
"project": "DATAREST",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/component/12747",
|
||||
"id": "12747",
|
||||
"name": "Documentation",
|
||||
"assigneeType": "PROJECT_DEFAULT",
|
||||
"assignee": {
|
||||
"self": "https://jira.spring.io/rest/api/2/user?username=olivergierke",
|
||||
"key": "olivergierke",
|
||||
"name": "olivergierke",
|
||||
"avatarUrls": {
|
||||
"16x16": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=16",
|
||||
"24x24": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=24",
|
||||
"32x32": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=32",
|
||||
"48x48": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=48"
|
||||
},
|
||||
"displayName": "Oliver Gierke",
|
||||
"active": true
|
||||
},
|
||||
"realAssigneeType": "PROJECT_DEFAULT",
|
||||
"realAssignee": {
|
||||
"self": "https://jira.spring.io/rest/api/2/user?username=olivergierke",
|
||||
"key": "olivergierke",
|
||||
"name": "olivergierke",
|
||||
"avatarUrls": {
|
||||
"16x16": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=16",
|
||||
"24x24": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=24",
|
||||
"32x32": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=32",
|
||||
"48x48": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=48"
|
||||
},
|
||||
"displayName": "Oliver Gierke",
|
||||
"active": true
|
||||
},
|
||||
"isAssigneeTypeValid": true,
|
||||
"project": "DATAREST",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/component/12746",
|
||||
"id": "12746",
|
||||
"name": "Repositories",
|
||||
"assigneeType": "PROJECT_DEFAULT",
|
||||
"assignee": {
|
||||
"self": "https://jira.spring.io/rest/api/2/user?username=olivergierke",
|
||||
"key": "olivergierke",
|
||||
"name": "olivergierke",
|
||||
"avatarUrls": {
|
||||
"16x16": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=16",
|
||||
"24x24": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=24",
|
||||
"32x32": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=32",
|
||||
"48x48": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=48"
|
||||
},
|
||||
"displayName": "Oliver Gierke",
|
||||
"active": true
|
||||
},
|
||||
"realAssigneeType": "PROJECT_DEFAULT",
|
||||
"realAssignee": {
|
||||
"self": "https://jira.spring.io/rest/api/2/user?username=olivergierke",
|
||||
"key": "olivergierke",
|
||||
"name": "olivergierke",
|
||||
"avatarUrls": {
|
||||
"16x16": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=16",
|
||||
"24x24": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=24",
|
||||
"32x32": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=32",
|
||||
"48x48": "https://secure.gravatar.com/avatar/27320c872264d5db7a38f65137d423db?d=mm&s=48"
|
||||
},
|
||||
"displayName": "Oliver Gierke",
|
||||
"active": true
|
||||
},
|
||||
"isAssigneeTypeValid": true,
|
||||
"project": "DATAREST",
|
||||
"projectId": 10901
|
||||
}
|
||||
]
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"expand": "operations,editmeta,changelog,transitions,renderedFields",
|
||||
"id": "67908",
|
||||
"self": "https://jira.spring.io/rest/api/2/issue/67908",
|
||||
"key": "DATAREST-782",
|
||||
"fields": {
|
||||
"summary": "Release 2.5 RC1 (Hopper)",
|
||||
"resolution": {
|
||||
"self": "https://jira.spring.io/rest/api/2/resolution/1",
|
||||
"id": "1",
|
||||
"description": "A fix for this issue is checked into the tree and tested.",
|
||||
"name": "Fixed"
|
||||
},
|
||||
"status": {
|
||||
"name": "In Progress",
|
||||
"id": "6",
|
||||
"statusCategory": {
|
||||
"key": "indeterminate"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
{
|
||||
"expand": "names,schema",
|
||||
"startAt": 0,
|
||||
"maxResults": 50,
|
||||
"total": 1,
|
||||
"issues": [
|
||||
{
|
||||
"expand": "operations,editmeta,changelog,transitions,renderedFields",
|
||||
"id": "67908",
|
||||
"self": "https://jira.spring.io/rest/api/2/issue/67908",
|
||||
"key": "DATAREST-782",
|
||||
"fields": {
|
||||
"summary": "Release 2.5 RC1 (Hopper)",
|
||||
"resolution": {
|
||||
"self": "https://jira.spring.io/rest/api/2/resolution/1",
|
||||
"id": "1",
|
||||
"description": "A fix for this issue is checked into the tree and tested.",
|
||||
"name": "Fixed"
|
||||
},
|
||||
"status": {
|
||||
"self": "https://jira.spring.io/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://jira.spring.io/images/icons/statuses/closed.png",
|
||||
"name": "Closed",
|
||||
"id": "6",
|
||||
"statusCategory": {
|
||||
"self": "https://jira.spring.io/rest/api/2/statuscategory/3",
|
||||
"id": 3,
|
||||
"key": "done",
|
||||
"colorName": "green",
|
||||
"name": "Done"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,446 +0,0 @@
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/project/DATAREST/version?maxResults=50&startAt=0",
|
||||
"maxResults": 50,
|
||||
"startAt": 0,
|
||||
"total": 44,
|
||||
"isLast": true,
|
||||
"values": [
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/12843",
|
||||
"id": "12843",
|
||||
"name": "1.0 - M1",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2012-04-24",
|
||||
"userReleaseDate": "24/Apr/12",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/12844",
|
||||
"id": "12844",
|
||||
"name": "1.0 - M2",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2012-05-16",
|
||||
"userReleaseDate": "16/May/12",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/12845",
|
||||
"id": "12845",
|
||||
"name": "1.0.0.RC1",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2012-06-26",
|
||||
"userReleaseDate": "26/Jun/12",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/13203",
|
||||
"id": "13203",
|
||||
"name": "1.0.0.RC2",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2012-07-31",
|
||||
"userReleaseDate": "31/Jul/12",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/13598",
|
||||
"id": "13598",
|
||||
"name": "1.0.0.RC3",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2012-09-14",
|
||||
"userReleaseDate": "14/Sep/12",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/12846",
|
||||
"id": "12846",
|
||||
"name": "1.0.0 GA",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/14009",
|
||||
"id": "14009",
|
||||
"name": "1.1.0.M1",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/13003",
|
||||
"id": "13003",
|
||||
"name": "2.0 M1 (Codd)",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2013-11-21",
|
||||
"userReleaseDate": "21/Nov/13",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/14259",
|
||||
"id": "14259",
|
||||
"name": "2.0 RC1 (Codd)",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2014-01-29",
|
||||
"userReleaseDate": "29/Jan/14",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/14260",
|
||||
"id": "14260",
|
||||
"name": "2.0 GA (Codd)",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2014-02-24",
|
||||
"userReleaseDate": "24/Feb/14",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/14384",
|
||||
"id": "14384",
|
||||
"name": "2.0.1 (Codd SR1)",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2014-03-13",
|
||||
"userReleaseDate": "13/Mar/14",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/14385",
|
||||
"id": "14385",
|
||||
"name": "2.1 M1 (Dijkstra)",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2014-04-01",
|
||||
"userReleaseDate": "01/Apr/14",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/14412",
|
||||
"id": "14412",
|
||||
"name": "2.0.2 (Codd SR2)",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2014-04-15",
|
||||
"userReleaseDate": "15/Apr/14",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/14386",
|
||||
"id": "14386",
|
||||
"name": "2.1 RC1 (Dijkstra)",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2014-05-02",
|
||||
"userReleaseDate": "02/May/14",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/14520",
|
||||
"id": "14520",
|
||||
"name": "2.0.3 (Codd SR3)",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2014-06-18",
|
||||
"userReleaseDate": "18/Jun/14",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/14387",
|
||||
"id": "14387",
|
||||
"name": "2.1 GA (Dijkstra)",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2014-05-20",
|
||||
"userReleaseDate": "20/May/14",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/14607",
|
||||
"id": "14607",
|
||||
"name": "2.1.1 (Dijkstra SR1)",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2014-06-30",
|
||||
"userReleaseDate": "30/Jun/14",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/14614",
|
||||
"id": "14614",
|
||||
"name": "2.2 M1 (Evans)",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2014-07-10",
|
||||
"userReleaseDate": "10/Jul/14",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/14656",
|
||||
"id": "14656",
|
||||
"name": "2.1.2 (Dijkstra SR2)",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2014-07-28",
|
||||
"userReleaseDate": "28/Jul/14",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/14675",
|
||||
"id": "14675",
|
||||
"name": "2.2 RC1 (Evans)",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2014-08-13",
|
||||
"userReleaseDate": "13/Aug/14",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/14695",
|
||||
"id": "14695",
|
||||
"name": "2.1.4 (Dijkstra SR4)",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2014-08-27",
|
||||
"userReleaseDate": "27/Aug/14",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/14710",
|
||||
"id": "14710",
|
||||
"name": "2.2 GA (Evans)",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2014-09-05",
|
||||
"userReleaseDate": "05/Sep/14",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/14731",
|
||||
"id": "14731",
|
||||
"name": "2.2.1 (Evans SR1)",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2014-10-31",
|
||||
"userReleaseDate": "31/Oct/14",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/14732",
|
||||
"id": "14732",
|
||||
"name": "2.3 M1 (Fowler)",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2014-12-01",
|
||||
"userReleaseDate": "01/Dec/14",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/14721",
|
||||
"id": "14721",
|
||||
"name": "2.1.5 (Dijkstra SR5)",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2015-01-27",
|
||||
"userReleaseDate": "27/Jan/15",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/14819",
|
||||
"id": "14819",
|
||||
"name": "2.2.2 (Evans SR2)",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2015-01-28",
|
||||
"userReleaseDate": "28/Jan/15",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/14852",
|
||||
"id": "14852",
|
||||
"name": "2.3 RC1 (Fowler)",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2015-03-05",
|
||||
"userReleaseDate": "05/Mar/15",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/14949",
|
||||
"id": "14949",
|
||||
"name": "2.3 GA (Fowler)",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2015-03-23",
|
||||
"userReleaseDate": "23/Mar/15",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/14981",
|
||||
"id": "14981",
|
||||
"name": "2.4 M1 (Gosling)",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2015-06-02",
|
||||
"userReleaseDate": "02/Jun/15",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/14970",
|
||||
"id": "14970",
|
||||
"name": "2.3.1 (Fowler SR1)",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2015-07-01",
|
||||
"userReleaseDate": "01/Jul/15",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/14925",
|
||||
"id": "14925",
|
||||
"name": "2.2.3 (Evans SR3)",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2015-07-01",
|
||||
"userReleaseDate": "01/Jul/15",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/14912",
|
||||
"id": "14912",
|
||||
"name": "2.1.6 (Dijkstra SR6)",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2015-07-01",
|
||||
"userReleaseDate": "01/Jul/15",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/15240",
|
||||
"id": "15240",
|
||||
"name": "2.3.2 (Fowler SR2)",
|
||||
"archived": false,
|
||||
"released": true,
|
||||
"releaseDate": "2015-07-28",
|
||||
"userReleaseDate": "28/Jul/15",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/15205",
|
||||
"id": "15205",
|
||||
"name": "2.4 RC1 (Gosling)",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2015-08-04",
|
||||
"userReleaseDate": "04/Aug/15",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/15279",
|
||||
"id": "15279",
|
||||
"name": "2.4 GA (Gosling)",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2015-09-01",
|
||||
"userReleaseDate": "01/Sep/15",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/15348",
|
||||
"id": "15348",
|
||||
"description": "Evans SR4",
|
||||
"name": "2.2.4 (Evans SR4)",
|
||||
"archived": false,
|
||||
"released": true,
|
||||
"releaseDate": "2015-10-14",
|
||||
"userReleaseDate": "14/Oct/15",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/15310",
|
||||
"id": "15310",
|
||||
"description": "Gosling SR1",
|
||||
"name": "2.4.1 (Gosling SR1)",
|
||||
"archived": true,
|
||||
"released": true,
|
||||
"releaseDate": "2015-11-15",
|
||||
"userReleaseDate": "15/Nov/15",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/15416",
|
||||
"id": "15416",
|
||||
"description": "Gosling SR2",
|
||||
"name": "2.4.2 (Gosling SR2)",
|
||||
"archived": false,
|
||||
"released": true,
|
||||
"releaseDate": "2015-12-18",
|
||||
"userReleaseDate": "18/Dec/15",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/15311",
|
||||
"id": "15311",
|
||||
"description": "Hopper M1",
|
||||
"name": "2.5 M1 (Hopper)",
|
||||
"archived": false,
|
||||
"released": true,
|
||||
"releaseDate": "2016-02-12",
|
||||
"userReleaseDate": "12/Feb/16",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/15455",
|
||||
"id": "15455",
|
||||
"description": "Gosling SR4",
|
||||
"name": "2.4.4 (Gosling SR4)",
|
||||
"archived": false,
|
||||
"released": true,
|
||||
"releaseDate": "2016-02-23",
|
||||
"userReleaseDate": "23/Feb/16",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/15475",
|
||||
"id": "15475",
|
||||
"description": "Hopper RC1",
|
||||
"name": "2.5 RC1 (Hopper)",
|
||||
"archived": false,
|
||||
"released": true,
|
||||
"releaseDate": "2016-03-18",
|
||||
"userReleaseDate": "18/Mar/16",
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/15260",
|
||||
"id": "15260",
|
||||
"description": "Fowler SR3",
|
||||
"name": "2.3.3 (Fowler SR3)",
|
||||
"archived": false,
|
||||
"released": false,
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/15512",
|
||||
"id": "15512",
|
||||
"name": "2.4.5 (Gosling SR5)",
|
||||
"archived": false,
|
||||
"released": false,
|
||||
"projectId": 10901
|
||||
},
|
||||
{
|
||||
"self": "https://jira.spring.io/rest/api/2/version/15513",
|
||||
"id": "15513",
|
||||
"name": "2.5 GA (Hopper)",
|
||||
"archived": false,
|
||||
"released": false,
|
||||
"projectId": 10901
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"description": "An excellent version",
|
||||
"name": "2.5 RC1 (Hopper)",
|
||||
"archived": false,
|
||||
"released": false,
|
||||
"project": "DATAREST",
|
||||
"projectId": 10000
|
||||
}
|
||||
Reference in New Issue
Block a user