PageRenderTime 43ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/src/main/java/com/atlassian/jconnect/jira/IssueHelper.java

https://bitbucket.org/atlassian/jiraconnect-jiraplugin/
Java | 293 lines | 244 code | 40 blank | 9 comment | 28 complexity | 253aaaeec854ec3996ad7cd48d1a2e68 MD5 | raw file
  1. package com.atlassian.jconnect.jira;
  2. import com.atlassian.core.util.FileUtils;
  3. import com.atlassian.crowd.embedded.api.User;
  4. import com.atlassian.jconnect.rest.entities.IssueEntity;
  5. import com.atlassian.jconnect.rest.entities.UploadData;
  6. import com.atlassian.jira.bc.issue.comment.CommentService;
  7. import com.atlassian.jira.bc.project.component.ProjectComponent;
  8. import com.atlassian.jira.bc.project.component.ProjectComponentManager;
  9. import com.atlassian.jira.config.ConstantsManager;
  10. import com.atlassian.jira.event.type.EventDispatchOption;
  11. import com.atlassian.jira.exception.CreateException;
  12. import com.atlassian.jira.exception.UpdateException;
  13. import com.atlassian.jira.issue.*;
  14. import com.atlassian.jira.issue.fields.CustomField;
  15. import com.atlassian.jira.issue.issuetype.IssueType;
  16. import com.atlassian.jira.issue.label.Label;
  17. import com.atlassian.jira.project.DefaultAssigneeException;
  18. import com.atlassian.jira.project.Project;
  19. import com.atlassian.jira.project.ProjectManager;
  20. import com.atlassian.jira.project.version.Version;
  21. import com.atlassian.jira.project.version.VersionManager;
  22. import com.atlassian.jira.util.ErrorCollection;
  23. import com.atlassian.jira.util.SimpleErrorCollection;
  24. import com.atlassian.jira.web.util.AttachmentException;
  25. import com.google.common.collect.ImmutableMap;
  26. import org.ofbiz.core.entity.GenericEntityException;
  27. import org.ofbiz.core.entity.GenericValue;
  28. import org.slf4j.Logger;
  29. import org.slf4j.LoggerFactory;
  30. import java.io.File;
  31. import java.io.IOException;
  32. import java.util.*;
  33. import java.util.regex.Pattern;
  34. /**
  35. * Handles some common issue operations.
  36. */
  37. public class IssueHelper
  38. {
  39. private static final Logger log = LoggerFactory.getLogger(IssueHelper.class);
  40. private static final Map<String, String> SUPPORTED_ATTACHMENT_TYPE_SUFFIXES;
  41. public static final String ENVIRONMENT_FORMAT = "Model: %s\n" +
  42. "OS Version: %s\n" +
  43. "App Version: %s\n" +
  44. "Language: %s\n";
  45. // TODO: all this (and more?) should be stored using ActiveObjects for version 1.0-Final.
  46. // ActiveObjects is currently not in JIRA 4.3
  47. public static final Pattern ENV_FIELD_PATTERN =
  48. Pattern.compile("Model: (.*)[\r\n]*" +
  49. "OS Version: (.*)[\r\n]*" +
  50. "App Version: (.*)[\r\n]*", Pattern.MULTILINE);
  51. static
  52. {
  53. final Map<String, String> typeSuffixes = new HashMap<String, String>();
  54. typeSuffixes.put("image/gif", ".gif");
  55. typeSuffixes.put("image/jpeg", ".jpg");
  56. typeSuffixes.put("image/png", ".png");
  57. typeSuffixes.put("audio/x-caf", ".caf");
  58. typeSuffixes.put("audio/mpeg", ".mp3");
  59. typeSuffixes.put("audio/x-wav", ".wav");
  60. SUPPORTED_ATTACHMENT_TYPE_SUFFIXES = ImmutableMap.copyOf(typeSuffixes);
  61. }
  62. private static String getContentTypeFileSuffix(String contentType)
  63. {
  64. return SUPPORTED_ATTACHMENT_TYPE_SUFFIXES.get(contentType);
  65. }
  66. private final IssueManager issueManager;
  67. private final AttachmentManager attachmentManager;
  68. private final ProjectManager projectManager;
  69. private final VersionManager versionManager;
  70. private final ConstantsManager constantsManager;
  71. private final IssueFactory issueFactory;
  72. private final CommentService commentService;
  73. private final ProjectComponentManager componentManager;
  74. public IssueHelper(IssueManager issueManager, AttachmentManager attachmentManager,
  75. final ProjectManager projectManager, final VersionManager versionManager,
  76. final ConstantsManager constantsManager, final IssueFactory issueFactory,
  77. final CommentService commentService, ProjectComponentManager componentManager)
  78. {
  79. this.issueManager = issueManager;
  80. this.attachmentManager = attachmentManager;
  81. this.projectManager = projectManager;
  82. this.versionManager = versionManager;
  83. this.constantsManager = constantsManager;
  84. this.issueFactory = issueFactory;
  85. this.commentService = commentService;
  86. this.componentManager = componentManager;
  87. }
  88. public MutableIssue getIssue(String issueKey)
  89. {
  90. return issueManager.getIssueObject(issueKey);
  91. }
  92. public Issue createIssue(IssueEntity issueEntity,
  93. CustomField uuid,
  94. Project project,
  95. User user,
  96. List<CustomField> customFields,
  97. List<Object> customValues)
  98. throws AttachmentException, IOException, GenericEntityException, CreateException
  99. {
  100. final IssueType issueType = resolveIssueType(issueEntity);
  101. final MutableIssue issue = issueFactory.getIssue();
  102. final HashMap<String, Version> allVersions = new HashMap<String, Version>();
  103. final Version appVersion = getVersionNamed(project.getId(), issueEntity.getAppVersion());
  104. final Version appShortVersion = getVersionNamed(project.getId(), issueEntity.getAppVersionShort());
  105. addIfNonNull(allVersions, appVersion);
  106. addIfNonNull(allVersions, appShortVersion);
  107. // set issue fields
  108. issue.setProjectId(project.getId());
  109. issue.setIssueTypeId(issueType.getId());
  110. issue.setAffectedVersions(allVersions.values());
  111. final Collection<ProjectComponent> components = setProjectComponents(issueEntity, project, issue);
  112. final String appVersionName = issueEntity.getAppVersionShort() != null ? // use short if provided, else use AppVersion.
  113. issueEntity.getAppVersionShort() : issueEntity.getAppVersion();
  114. final String env = String.format(ENVIRONMENT_FORMAT,
  115. issueEntity.getModel(),
  116. issueEntity.getSystemVersion(),
  117. appVersionName,
  118. issueEntity.getLanguage());
  119. issue.setEnvironment(env);
  120. issue.setSummary(issueEntity.getSummary());
  121. issue.setDescription(issueEntity.getDescription());
  122. issue.setReporter(user);
  123. setDefaultAssignee(project, issue, components);
  124. final Set<Label> labels = new HashSet<Label>(3);
  125. addLabel(labels, "", issueEntity.getSystemVersion());
  126. addLabel(labels, "", issueEntity.getModel());
  127. addLabel(labels, "", issueEntity.getLanguage());
  128. addLabel(labels, "build-", issueEntity.getAppVersion()); // appVersionShort is known as the 'marketing version' appVersion is the build number.
  129. issue.setLabels(labels);
  130. for (int i = 0; i < customFields.size(); i++) {
  131. final CustomField field = customFields.get(i);
  132. final Object value = customValues.get(i);
  133. log.debug("Setting customfield: " + field.getName() + " = " + value);
  134. if (field.getRelevantConfig(issue) != null) {
  135. issue.setCustomFieldValue(field, value);
  136. } else {
  137. log.debug("Field " + field.getName() + " is missing a configuration context for project: " + project.getName() +
  138. ". This field wont be added to the issue.");
  139. }
  140. }
  141. // ensure the uuid is always set. ie. overwrite any uuid fields previously set above.
  142. if (uuid != null && uuid.getRelevantConfig(issue) != null) {
  143. issue.setCustomFieldValue(uuid, issueEntity.getUuid());
  144. } else {
  145. final String missingUUIDMessage = "\n*NB " +
  146. project.getName() + " is missing the custom field called uuid. " +
  147. "JIRAConnect uses this to map mobile users to issues. Without this field, " +
  148. "users will not receive any In-App issue comments.*";
  149. issue.setEnvironment(issue.getEnvironment() + "\n" + missingUUIDMessage);
  150. log.warn(missingUUIDMessage);
  151. }
  152. // store the issue
  153. issueManager.createIssue(user, issue);
  154. log.debug(String.format("User %s created issue %s: %s", user.getName(), issue.getKey(), issue.getSummary()));
  155. return issue;
  156. }
  157. private void setDefaultAssignee(Project project, MutableIssue issue, Collection<ProjectComponent> components) {
  158. final User defaultAssignee;
  159. try {
  160. defaultAssignee = projectManager.getDefaultAssignee(project, components);
  161. if (defaultAssignee != null) {
  162. issue.setAssignee(defaultAssignee);
  163. }
  164. } catch (DefaultAssigneeException e) {
  165. log.info("Not setting default assignee on issue: " + issue.getKey() + " due to: " + e.getLocalizedMessage());
  166. }
  167. }
  168. private void addIfNonNull(Map<String, Version> allVersions, Version appVersion) {
  169. if (appVersion != null) {
  170. allVersions.put(appVersion.getName(), appVersion);
  171. }
  172. }
  173. private Version getVersionNamed(Long projectId, String name) {
  174. if (name == null) {
  175. return null;
  176. }
  177. final Collection<Version> versions = versionManager.getVersions(projectId);
  178. for (Iterator<Version> iterator = versions.iterator(); iterator.hasNext(); ) {
  179. final Version next = iterator.next();
  180. if (name.equals(next.getName())) {
  181. return next;
  182. }
  183. }
  184. return null;
  185. }
  186. private void addLabel(Set<Label> labels, String prefix, String value) {
  187. if (value != null) {
  188. final String label = value.replaceAll("\\s", "-");
  189. labels.add(new Label(null, null, prefix + label));
  190. }
  191. }
  192. private Collection<ProjectComponent> setProjectComponents(IssueEntity issueEntity, Project project, MutableIssue issue) {
  193. final Collection<ProjectComponent> projectComponents = new ArrayList<ProjectComponent>();
  194. if (issueEntity.getComponents() != null)
  195. {
  196. String[] comps = issueEntity.getComponents();
  197. final Collection<GenericValue> components = new ArrayList<GenericValue>(comps.length);
  198. for (int i = 0; i < comps.length; i++) {
  199. final String compName = comps[i];
  200. final ProjectComponent component = componentManager.findByComponentName(project.getId(), compName);
  201. if (component != null) {
  202. components.add(component.getGenericValue());
  203. projectComponents.add(component);
  204. }
  205. }
  206. issue.setComponents(components);
  207. }
  208. return projectComponents;
  209. }
  210. private IssueType resolveIssueType(IssueEntity issueEntity) {
  211. final Collection<IssueType> types = constantsManager.getAllIssueTypeObjects();
  212. for (IssueType next : types) {
  213. if (next.getName().equalsIgnoreCase(issueEntity.getType())) {
  214. return next;
  215. }
  216. }
  217. return types.iterator().next(); // if none are matching, return the first issue type. This should be BUG.
  218. }
  219. public void addAttachment(Issue issue, UploadData data, User user) throws IOException, AttachmentException, GenericEntityException
  220. {
  221. final File attachmentFile = File.createTempFile("jconnect-" + issue.getKey() + "-", getContentTypeFileSuffix(data.getContentType()));
  222. FileUtils.copyFile(data.getInputStream(), attachmentFile, true);
  223. attachmentManager.createAttachment(attachmentFile, data.getFilename(), data.getContentType(), user, issue.getGenericValue());
  224. log.debug(String.format("User %s attached %s to %s", user.getName(), data.getFilename(), issue.getKey()));
  225. }
  226. public ErrorCollection updateIssue(MutableIssue issue, IssueEntity issueEntity, User user) throws UpdateException {
  227. final ErrorCollection errors = new SimpleErrorCollection();
  228. // add any new versions that may exist
  229. final Long projectId = issue.getProjectObject().getId();
  230. final Map<String, Version> uniqueVersions = new HashMap<String, Version>();
  231. addIfNonNull(uniqueVersions, getVersionNamed(projectId, issueEntity.getAppVersion()));
  232. addIfNonNull(uniqueVersions, getVersionNamed(projectId, issueEntity.getAppVersionShort()));
  233. final Collection<Version> affectedVersions = issue.getAffectedVersions();
  234. for (Iterator<Version> iterator = affectedVersions.iterator(); iterator.hasNext(); ) {
  235. addIfNonNull(uniqueVersions, iterator.next());
  236. }
  237. issue.setAffectedVersions(uniqueVersions.values());
  238. issueManager.updateIssue(user, issue, EventDispatchOption.ISSUE_UPDATED, true);
  239. return errors;
  240. }
  241. public ErrorCollection addComment(Issue issue, String bodyText, User user) {
  242. ErrorCollection errors = new SimpleErrorCollection();
  243. commentService.create(user, issue, bodyText, true, errors);
  244. log.debug("Comment was: " + bodyText);
  245. return errors;
  246. }
  247. public Project lookupProjectByName(String projectName) {
  248. return projectManager.getProjectObjByName(projectName);
  249. }
  250. public Project lookupProjectByKey(String projectKey) {
  251. return projectManager.getProjectObjByKey(projectKey);
  252. }
  253. }