package org.jvnet.hudson.plugins.jira.issueversioning.plugin.jira.rest;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Set;
import com.atlassian.jira.exception.CreateException;
import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.issue.IssueFieldConstants;
import com.atlassian.jira.issue.IssueManager;
import com.atlassian.jira.issue.cache.CacheManager;
import com.atlassian.jira.issue.fields.FieldManager;
import com.atlassian.jira.issue.fields.FixVersionsSystemField;
import com.atlassian.jira.issue.index.IndexException;
import com.atlassian.jira.issue.index.IssueIndexManager;
import com.atlassian.jira.project.version.Version;
import com.atlassian.jira.project.version.VersionManager;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import org.jvnet.hudson.plugins.jira.issueversioning.domain.api.model.rest.Project;
import org.ofbiz.core.entity.GenericValue;
// FIXME Must split issues on each project, like DEV-123, DEV-234, TUC-321 --> [DEV-123, DEV-234], [TUC-321]
public class DefaultVersionAssociationCreator implements VersionAssociationCreator {
private final VersionManager versionManager;
private final IssueManager issueManager;
private final CacheManager cacheManager;
private final IssueIndexManager indexManager;
private final FixVersionsSystemField field;
public DefaultVersionAssociationCreator(VersionManager versionManager, IssueManager issueManager,
FieldManager fieldManager, CacheManager cacheManager,
IssueIndexManager indexManager) {
this.versionManager = versionManager;
this.issueManager = issueManager;
this.cacheManager = cacheManager;
this.indexManager = indexManager;
field = (FixVersionsSystemField) fieldManager.getField(IssueFieldConstants.FIX_FOR_VERSIONS);
}
/**
* {@inheritDoc}
*/
public void associateFor(Project project) {
final Iterable<Issue> issues = transformIssueKeys(project.getAllIssues());
final Iterable<Issue> filteredIssues = filterIssues(issues);
if (filteredIssues.iterator().hasNext()) {
final Issue issue = filteredIssues.iterator().next();
try {
Version version = fixVersion(issue, project.getVersionForOkBuild());
addFixVersionToAll(filteredIssues, version);
versionManager.releaseVersion(version, true);
} catch (CreateException e) {
throw new IllegalStateException("Could not create fix version for issue: " + issue, e);
} catch (IndexException e) {
throw new IllegalStateException("Could not reindex issues: " + filteredIssues, e);
}
}
}
private Iterable<Issue> transformIssueKeys(Set<String> issues) {
return Iterables.transform(issues, new Function<String, Issue>() {
public Issue apply(@Nullable String issueKey) {
return issueManager.getIssueObject(issueKey);
}
});
}
/**
* Only include getIssues that is releasable (see {@link #isReleasable})
*
* @param issues getIssues to filter on
* @return a filtered view of the getIssues
*/
private Iterable<Issue> filterIssues(Iterable<Issue> issues) {
return Iterables.filter(issues, new Predicate<Issue>() {
public boolean apply(@Nullable Issue issue) {
return isReleasable(issue);
}
});
}
/**
* A releasable issue is:
* <p/>
* <ul>
* <li> RESOLVED or CLOSED
* </ul>
* <p/>
*
* When only checking on status, there must exist a pre-commit check that only lets a check-in get through if the
* issue is not resolved or "higher".
*
* @param issue issue to check "releasability" on
* @return true if issue is releasable, false if not
*/
private boolean isReleasable(Issue issue) {
final int status = Integer.parseInt(issue.getStatusObject().getId());
// FIXME should a closed issue fail everything? Developer only set it to resolved and this plugin set it to
// closed? Do we need a verification step for a released build?
return status == IssueFieldConstants.RESOLVED_STATUS_ID ||
status == IssueFieldConstants.CLOSED_STATUS_ID;
}
private Version fixVersion(Issue issue, String versionForOkBuild) throws CreateException {
Version version = versionManager.getVersion(issue.getProjectObject().getId(), versionForOkBuild);
if (version == null) {
version = versionManager.
createVersion(versionForOkBuild, new Date(), "", issue.getProjectObject().getId(), null);
}
return version;
}
private void addFixVersionToAll(Iterable<Issue> issues, Version version) throws IndexException {
Collection<GenericValue> changedIssues = new ArrayList<GenericValue>();
for (Issue issue : issues) {
addFixVersionTo(issue, version);
changedIssues.add(issue.getGenericValue());
}
if (!changedIssues.isEmpty()) {
cacheManager.flush(CacheManager.ISSUE_CACHE, changedIssues);
indexManager.reIndexIssues(changedIssues);
}
}
private void addFixVersionTo(Issue issue, Version version) {
final List<Version> versions = new ArrayList<Version>(issue.getFixVersions());
versions.add(version);
field.createValue(issue, versions);
}
}