private void removeOldIssueLinks(Issue issue, net.rcarz.jiraclient.Issue jiraIssue)
      throws JiraException, NotFoundException {
    Set<String> ids = new HashSet<>();
    for (URL url : Iterables.concat(issue.getBlocks(), issue.getDependsOn()))
      ids.add(getIssueKey(url));

    for (IssueLink link : jiraIssue.getIssueLinks()) {
      net.rcarz.jiraclient.Issue linkedIssue = link.getInwardIssue();
      if (linkedIssue == null) linkedIssue = link.getOutwardIssue();

      if (!ids.contains(linkedIssue.getKey())) link.delete();
    }
  }
  private boolean postComment(Issue issue, Comment comment, boolean parallelRequest)
      throws NotFoundException {
    if (comment.isPrivate())
      Utils.logWarnMessage(
          LOG, "Private comments are not currently supported by " + getClass().getName());

    String trackerId = issue.getTrackerId().orElse(getIssueKey(issue.getURL()));
    String restURI = API_ISSUE_PATH + trackerId + COMMENT_ISSUE_PATH;
    JSONObject req = new JSONObject();
    req.put("body", comment.getBody());
    try {
      // A new JiraClient is created here to allow for comments to be posted in parallel
      // The underlying JiraClient is not thread-safe, hence it is necessary to create a new
      // JiraClient object for each request.
      if (parallelRequest) {
        ICredentials credentials = new BasicCredentials(config.getUsername(), config.getPassword());
        new JiraClient(baseUrl.toString(), credentials).getRestClient().post(restURI, req);
      } else {
        jiraClient.getRestClient().post(restURI, req);
      }
    } catch (Exception ex) {
      throw new NotFoundException("Failed to add comment to issue " + issue.getURL(), ex);
    }
    return true;
  }
  private void addNewIssueLinks(Issue issue, net.rcarz.jiraclient.Issue jiraIssue)
      throws JiraException, NotFoundException {
    for (URL url : issue.getBlocks()) {
      String trackerId = getIssueKey(url);
      net.rcarz.jiraclient.Issue issueToBlock = getIssue(trackerId);
      if (!issueLinkExists(url, issueToBlock.getIssueLinks()))
        issueToBlock.link(jiraIssue.getKey(), "Dependency");
    }

    for (URL url : issue.getDependsOn()) {
      if (!issueLinkExists(url, jiraIssue.getIssueLinks())) {
        String trackerId = getIssueKey(url);
        jiraIssue.link(trackerId, "Dependency");
      }
    }
  }
 private String getUpdateErrorMessage(Issue issue, JiraException e) {
   String msg = e.getMessage();
   if (msg.contains("does not exist or read-only")) {
     for (Map.Entry<Flag, String> entry : FLAG_MAP.entrySet()) {
       if (msg.contains(entry.getValue())) {
         String retMsg = "Flag '%1$s' set in Issue.stage cannot be set for %2$s '%3$s'";
         return getOptionalErrorMessage(
             retMsg, issue.getProduct(), entry.getKey(), issue.getURL());
       }
     }
     if (msg.contains(TARGET_RELEASE)) {
       String retMsg = "Release.milestone cannot be set for %2$s ''%3$s'";
       return getOptionalErrorMessage(retMsg, issue.getProduct(), null, issue.getURL());
     }
   }
   return null;
 }
  /**
   * Known limitations: - Jira api does not allow an issue type to be update (WTF?) - Jira api does
   * not allow project to be changed
   */
  @Override
  public boolean updateIssue(Issue issue) throws NotFoundException, AphroditeException {
    checkHost(issue.getURL());

    try {
      net.rcarz.jiraclient.Issue jiraIssue = getIssue(issue);
      net.rcarz.jiraclient.Issue.FluentUpdate update =
          WRAPPER.issueToFluentUpdate(issue, jiraIssue.update());
      update.execute();
      if (!hasSameIssueStatus(issue, jiraIssue)) {
        String transition = getJiraTransition(issue, jiraIssue);
        jiraIssue.transition().execute(transition);
      }
      addNewIssueLinks(issue, jiraIssue);
      removeOldIssueLinks(issue, jiraIssue);
      return true;
    } catch (JiraException e) {
      throw new AphroditeException(getUpdateErrorMessage(issue, e), e);
    }
  }
 private net.rcarz.jiraclient.Issue getIssue(Issue issue) throws NotFoundException {
   String trackerId = issue.getTrackerId().orElse(getIssueKey(issue.getURL()));
   return getIssue(trackerId);
 }