Package net.sf.redmine_mylyn.internal.api.client

Source Code of net.sf.redmine_mylyn.internal.api.client.Api_2_7_ClientImpl

package net.sf.redmine_mylyn.internal.api.client;

import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import net.sf.redmine_mylyn.api.client.IRedmineApiErrorCollector;
import net.sf.redmine_mylyn.api.client.IRedmineApiWebHelper;
import net.sf.redmine_mylyn.api.client.RedmineApiIssueProperty;
import net.sf.redmine_mylyn.api.client.RedmineServerVersion;
import net.sf.redmine_mylyn.api.exception.RedmineApiErrorException;
import net.sf.redmine_mylyn.api.exception.RedmineApiInvalidDataException;
import net.sf.redmine_mylyn.api.exception.RedmineApiRemoteException;
import net.sf.redmine_mylyn.api.model.Attachment;
import net.sf.redmine_mylyn.api.model.Configuration;
import net.sf.redmine_mylyn.api.model.Issue;
import net.sf.redmine_mylyn.api.model.TimeEntry;
import net.sf.redmine_mylyn.api.model.container.AbstractPropertyContainer;
import net.sf.redmine_mylyn.api.model.container.CustomFields;
import net.sf.redmine_mylyn.api.model.container.IssueCategories;
import net.sf.redmine_mylyn.api.model.container.IssuePriorities;
import net.sf.redmine_mylyn.api.model.container.IssueStatuses;
import net.sf.redmine_mylyn.api.model.container.Projects;
import net.sf.redmine_mylyn.api.model.container.Queries;
import net.sf.redmine_mylyn.api.model.container.Trackers;
import net.sf.redmine_mylyn.api.model.container.Users;
import net.sf.redmine_mylyn.api.model.container.Versions;
import net.sf.redmine_mylyn.api.query.Query;
import net.sf.redmine_mylyn.internal.api.Messages;
import net.sf.redmine_mylyn.internal.api.parser.AttachmentParser;
import net.sf.redmine_mylyn.internal.api.parser.AttributeParser;
import net.sf.redmine_mylyn.internal.api.parser.IModelParser;
import net.sf.redmine_mylyn.internal.api.parser.IssueParser;
import net.sf.redmine_mylyn.internal.api.parser.IssuesParser;
import net.sf.redmine_mylyn.internal.api.parser.SettingsParser;
import net.sf.redmine_mylyn.internal.api.parser.StringParser;
import net.sf.redmine_mylyn.internal.api.parser.SubmitedIssueParser;
import net.sf.redmine_mylyn.internal.api.parser.TypedParser;
import net.sf.redmine_mylyn.internal.api.parser.adapter.type.Issues;
import net.sf.redmine_mylyn.internal.api.parser.adapter.type.PartialIssueType;
import net.sf.redmine_mylyn.internal.api.parser.adapter.type.SubmitError;
import net.sf.redmine_mylyn.internal.api.parser.adapter.type.UpdatedIssuesType;

import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.PutMethod;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.mylyn.commons.net.Policy;

public class Api_2_7_ClientImpl extends AbstractClient {

  private final static String URL_SERVER_VERSION = "/mylyn/version"; //$NON-NLS-1$
  private final static String URL_ISSUE_STATUS = "/mylyn/issuestatus"; //$NON-NLS-1$
  private final static String URL_ISSUE_CATEGORIES = "/mylyn/issuecategories"; //$NON-NLS-1$
  private final static String URL_ISSUE_PRIORITIES = "/mylyn/issuepriorities"; //$NON-NLS-1$
  private final static String URL_TRACKERS = "/mylyn/trackers"; //$NON-NLS-1$
  private final static String URL_CUSTOMFIELDS = "/mylyn/customfields"; //$NON-NLS-1$
  private final static String URL_USERS = "/mylyn/users"; //$NON-NLS-1$
  private final static String URL_QUERIES = "/mylyn/queries"; //$NON-NLS-1$
  private final static String URL_PROJECTS = "/mylyn/projects"; //$NON-NLS-1$
  private final static String URL_VERSIONS = "/mylyn/versions"; //$NON-NLS-1$
  private final static String URL_SETTINGS = "/mylyn/settings"; //$NON-NLS-1$

  private final static String URL_ISSUES_UPDATED = "/mylyn/issues/updatedsince?issues=%s&unixtime=%d"; //$NON-NLS-1$
  private final static String URL_ISSUES_LIST = "/mylyn/issues/list?issues=%s"; //$NON-NLS-1$
  private final static String URL_ISSUE = "/mylyn/issue/%d"; //$NON-NLS-1$
  private final static String URL_QUERY = "/mylyn/issues"; //$NON-NLS-1$

  private final static String URL_UPDATE_ISSUE = "/issues/%d.xml"; //$NON-NLS-1$
 
  private final static String URL_GET_ATTACHMENT = "/mylyn/attachment/%d/%s"; //$NON-NLS-1$

  private final static String URL_GET_AUTHENTICITY_TOKEN = "/mylyn/token"; //$NON-NLS-1$
 
  private final static String HEADER_CSRF_TOKEN = "X-CSRF-Token";
 
  private Map<String, IModelParser<? extends AbstractPropertyContainer<?>>> parserByClass;
 
  private SettingsParser settingsParser;
  private TypedParser<UpdatedIssuesType> updatedIssuesParser;
  private IssueParser issueParser;
  private IssuesParser issuesParser;
  private TypedParser<RedmineServerVersion> versionParser;
 
  private SubmitedIssueParser submitIssueParser;
  private AttachmentParser attachmentParser;
  private StringParser stringParser;
 
  private Configuration configuration;
 
  Api_2_7_ClientImpl(IRedmineApiWebHelper webHelper) {
    this(webHelper, new Configuration());
  }

  public Api_2_7_ClientImpl(IRedmineApiWebHelper webHelper, Configuration initialConfiguration) {
    super(webHelper);
   
    this.configuration = initialConfiguration;
    buildParser();
  }
 
  @Override
  public Configuration getConfiguration() {
    return configuration;
  }

  @Override
  public RedmineServerVersion detectServerVersion(IProgressMonitor monitor) throws RedmineApiErrorException {
    monitor = Policy.monitorFor(monitor);
    monitor.beginTask(Messages.PROGRESS_DETECT_REDMINE_VERSION, 1);

    GetMethod method = new GetMethod(URL_SERVER_VERSION);
    RedmineServerVersion version = executeMethod(method, versionParser, monitor);

    if(monitor.isCanceled()) {
      throw new OperationCanceledException();
    } else {
      monitor.worked(1);
    }

    return version;
  }
 
  @Override
  public void updateConfiguration(IProgressMonitor monitor) throws RedmineApiErrorException {
    monitor = Policy.monitorFor(monitor);

    Configuration conf = new Configuration();
   
    monitor.beginTask(Messages.PROGRESS_UPDATING_ATTRIBUTES, parserByClass.size()+1);
    GetMethod method = null;
   
    for (Entry<String, IModelParser<? extends AbstractPropertyContainer<?>>> entry : parserByClass.entrySet()) {
      method = new GetMethod(entry.getKey());
      AbstractPropertyContainer<?> propCt = executeMethod(method, entry.getValue(), monitor);

      if(monitor.isCanceled()) {
        throw new OperationCanceledException();
      } else {
        conf.setPropertyContainer(propCt);
        monitor.worked(1);
      }
    }

    method = new GetMethod(URL_SETTINGS);
    conf.setSettings(executeMethod(method, settingsParser, monitor));
    if(monitor.isCanceled()) {
      throw new OperationCanceledException();
    } else {
      monitor.worked(1);
    }
   
    getConfiguration().copy(conf);
  }
 
  @Override
  public int[] getUpdatedIssueIds(int[] issues, Date updatedSince, IProgressMonitor monitor) throws RedmineApiErrorException {
    if (issues==null || issues.length==0) {
      return null;
    }
   
    long unixtime = updatedSince.getTime()/1000l;
    //workaround: bug in redmine plugin
    unixtime += 1l;
   
    monitor = Policy.monitorFor(monitor);
    monitor.beginTask(Messages.PROGRESS_SEARCH_UPDATED_ISSUES, 1);

    String uri = String.format(URL_ISSUES_UPDATED, Arrays.toString(issues).replaceAll("[\\[\\] ]", ""), unixtime); //$NON-NLS-1$ //$NON-NLS-2$
    GetMethod method = new GetMethod(uri);
   
    UpdatedIssuesType result = executeMethod(method, updatedIssuesParser, monitor);

    if(monitor.isCanceled()) {
      throw new OperationCanceledException();
    } else {
      monitor.worked(1);
    }
   
    return result.updatedIssueIds;
  }
 
  @Override
  public Issue getIssue(int id, IProgressMonitor monitor) throws RedmineApiErrorException {
    if(id < 1) {
      return null;
    }
   
    monitor = Policy.monitorFor(monitor);
    monitor.beginTask(Messages.PROGRESS_FETCH_ISSUE, 1);

    String uri = String.format(URL_ISSUE, id);
    GetMethod method = new GetMethod(uri);
   
    Issue issue = executeMethod(method, issueParser, monitor);

    if(monitor.isCanceled()) {
      throw new OperationCanceledException();
    } else {
      monitor.worked(1);
    }
   
    return issue;
  }
 
  @Override
  public Issue[] getIssues(IProgressMonitor monitor, int... issueIds) throws RedmineApiErrorException {
    if (issueIds==null || issueIds.length==0) {
      return null;
    }
   
    monitor = Policy.monitorFor(monitor);
    monitor.beginTask(Messages.PROGRESS_FETCH_ISSUES, 1);

    String uri = String.format(URL_ISSUES_LIST, Arrays.toString(issueIds).replaceAll("[\\[\\] ]", "")); //$NON-NLS-1$ //$NON-NLS-2$
    GetMethod method = new GetMethod(uri);
   
    Issues issues = executeMethod(method, issuesParser, monitor);

    if(monitor.isCanceled()) {
      throw new OperationCanceledException();
    } else {
      monitor.worked(1);
    }
   
    return issues.getAll().toArray(new Issue[issues.getAll().size()]);
  }

  @Override
  public Issue[] query(Query query, IProgressMonitor monitor) throws RedmineApiErrorException {
    monitor = Policy.monitorFor(monitor);
    monitor.beginTask(Messages.PROGRESS_EXECUTE_QUERY, 1);

    GetMethod method = new GetMethod(URL_QUERY);
    List<NameValuePair> params = query.getParams();
   
    if(params.size()>0) {
      method.setQueryString(params.toArray(new NameValuePair[params.size()]));
    }
    Issues partialIssues = executeMethod(method, issuesParser, monitor);

    if(monitor.isCanceled()) {
      throw new OperationCanceledException();
    } else {
      monitor.worked(1);
    }
   
    return partialIssues.getAll().toArray(new Issue[partialIssues.getAll().size()]);
  }

  @Override
  public Issue createIssue(Issue issue, IRedmineApiErrorCollector errorCollector, IProgressMonitor monitor) throws RedmineApiInvalidDataException, RedmineApiErrorException {
    monitor = Policy.monitorFor(monitor);
    monitor.beginTask(Messages.PROGRESS_UPLOAD_TASK, 1);
   
    PostMethod method = new PostMethod("/issues.xml"); //$NON-NLS-1$
    try {
      //Workaround: remote method CREATE dosn't support API-Keys, we need a session
      String token = getAuthenticityToken(monitor);
     
      method.setRequestEntity(new IssueRequestEntity(issue));
      method.addRequestHeader(HEADER_CSRF_TOKEN, token);
     
    } catch (UnsupportedEncodingException e) {
      throw new RedmineApiErrorException(Messages.ERRMSG_METHOD_EXECUTION_FAILED_INVALID_ENCODING, e, "UTF-8"); //$NON-NLS-2$ //$NON-NLS-1$
    }
   
    Object response = executeMethod(method, submitIssueParser, monitor, HttpStatus.SC_CREATED, HttpStatus.SC_UNPROCESSABLE_ENTITY);
   
    if(monitor.isCanceled()) {
      throw new OperationCanceledException();
    } else {
      monitor.worked(1);
    }

    if(response instanceof PartialIssueType) {
      return ((PartialIssueType)response).toIssue();
    } else {
      SubmitError error = (SubmitError)response;
      for (String errMsg : error.errors) {
        errorCollector.accept(errMsg);
      }
     
      throw new RedmineApiInvalidDataException();
    }
  }
 
  @Override
  public void updateIssue(int issueId, Map<RedmineApiIssueProperty, String> issueValues, String comment, TimeEntry timeEntry, IRedmineApiErrorCollector errorCollector, IProgressMonitor monitor) throws RedmineApiInvalidDataException, RedmineApiErrorException {
    monitor = Policy.monitorFor(monitor);
    monitor.beginTask(Messages.PROGRESS_UPLOAD_TASK, 1);

    try {
      issueValues.put(RedmineApiIssueProperty.NOTES, comment);
      updateIssue(issueId, new IssueRequestEntity(issueValues, comment, timeEntry), errorCollector, monitor);
    } catch (UnsupportedEncodingException e) {
      throw new RedmineApiErrorException(Messages.ERRMSG_METHOD_EXECUTION_FAILED_INVALID_ENCODING, e, "UTF-8"); //$NON-NLS-2$ //$NON-NLS-1$
    } finally {
      if(monitor.isCanceled()) {
        throw new OperationCanceledException();
      } else {
        monitor.worked(1);
      }
    }
  }
 
  @Override
  public void updateIssue(Issue issue, String comment, TimeEntry timeEntry, IRedmineApiErrorCollector errorCollector, IProgressMonitor monitor) throws RedmineApiInvalidDataException, RedmineApiErrorException {
    monitor = Policy.monitorFor(monitor);
    monitor.beginTask(Messages.PROGRESS_UPLOAD_TASK, 1);

    try {
      issue.setNotes(comment);
      updateIssue(issue.getId(), new IssueRequestEntity(issue, comment, timeEntry), errorCollector, monitor);
    } catch (UnsupportedEncodingException e) {
      throw new RedmineApiErrorException(Messages.ERRMSG_METHOD_EXECUTION_FAILED_INVALID_ENCODING, e, "UTF-8"); //$NON-NLS-2$ //$NON-NLS-1$
    } finally {
      if(monitor.isCanceled()) {
        throw new OperationCanceledException();
      } else {
        monitor.worked(1);
      }
    }
  }
 
  private void updateIssue(int issueId, IssueRequestEntity requestEntity, IRedmineApiErrorCollector errorCollector, IProgressMonitor monitor) throws RedmineApiInvalidDataException, RedmineApiErrorException {
    //Workaround: remote method UPDATE dosn't support API-Keys, we need a session
    String token = getAuthenticityToken(monitor);
   
    PutMethod method = new PutMethod(String.format(URL_UPDATE_ISSUE, issueId));
    method.setRequestEntity(requestEntity);
    method.addRequestHeader(HEADER_CSRF_TOKEN, token);
   
    Object response = executeMethod(method, submitIssueParser, monitor, HttpStatus.SC_OK, HttpStatus.SC_UNPROCESSABLE_ENTITY);
   
    if(response instanceof SubmitError) {
      SubmitError error = (SubmitError)response;
      for (String errMsg : error.errors) {
        errorCollector.accept(errMsg);
      }
     
      throw new RedmineApiInvalidDataException();
    }
  }
 
  @Override
  public InputStream getAttachmentContent(int attachmentId, String fileName, IProgressMonitor monitor) throws RedmineApiErrorException {
    monitor = Policy.monitorFor(monitor);
    monitor.beginTask(Messages.PROGRESS_DOWNLOAD_ATTACHMENT, 1);
   
    //WOrkaround: Attachments::download dosn't support API-AUTH - this starts a session
    String token = getAuthenticityToken(monitor);
    if(token==null) {
      throw new RedmineApiRemoteException(Messages.ERRMSG_AUTH_TOKEN_REQUEST_FAILED);
    }
   
    GetMethod method = new GetMethod(String.format(URL_GET_ATTACHMENT, attachmentId, fileName));
    InputStream attachment = executeMethod(method, attachmentParser, monitor, HttpStatus.SC_OK, HttpStatus.SC_NOT_FOUND);

    if(monitor.isCanceled()) {
      throw new OperationCanceledException();
    } else {
      monitor.worked(1);
    }

    return attachment;
  }
 
  public void uploadAttachment(int issueId, final Attachment attachment, final InputStream content, String comment, IRedmineApiErrorCollector errorCollector, IProgressMonitor monitor) throws RedmineApiInvalidDataException, RedmineApiErrorException {
    monitor = Policy.monitorFor(monitor);
    monitor.beginTask(Messages.PROGRESS_UPLOAD_ATTACHMENT, 1);
   
    Object response = null;
   
    String token = getAuthenticityToken(monitor);
    if(token==null) {
      throw new RedmineApiRemoteException(Messages.ERRMSG_AUTH_TOKEN_REQUEST_FAILED);
    }
   
    try {
      PutMethod method = new PutMethod(String.format(URL_UPDATE_ISSUE, issueId));
     
      Part[] parts = new Part[4];
      parts[0] = new StringPart("authenticity_token", token, characterEncoding); //$NON-NLS-1$
      parts[1] = new StringPart("attachments[1][description]", attachment.getDescription(), characterEncoding); //$NON-NLS-1$
      parts[2] = new StringPart("notes", comment, characterEncoding); //$NON-NLS-1$
     
      //Workaround: http://rack.lighthouseapp.com/projects/22435/tickets/79-multipart-handling-incorrectly-assuming-file-upload
      for(int i=2;i>=0;i--) {
        ((StringPart)parts[i]).setContentType(null);
      }
     
      parts[3] = new FilePart("attachments[1][file]", new AttachmentPartSource(attachment, content), attachment.getContentType(), null) { //$NON-NLS-1$
        //Workaround: avoid 'Content-Type image/png; charset=iso8859-1'
        public String getCharSet() {
          return null;
        };
      };
      method.setRequestEntity(new MultipartRequestEntity(parts, method.getParams()));
     
      response = executeMethod(method, submitIssueParser, monitor, HttpStatus.SC_OK, HttpStatus.SC_UNPROCESSABLE_ENTITY);
    } finally {
      if(monitor.isCanceled()) {
        throw new OperationCanceledException();
      } else {
        monitor.worked(1);
      }
    }
   
    if(response instanceof SubmitError) {
      SubmitError error = (SubmitError)response;
      for (String errMsg : error.errors) {
        errorCollector.accept(errMsg);
      }
     
      throw new RedmineApiInvalidDataException();
    }
  }
 
  private String getAuthenticityToken(IProgressMonitor monitor) throws RedmineApiErrorException {
    monitor.beginTask(Messages.PROGRESS_REQUEST_AUTHTOKEN, 1);
   
    GetMethod method = new GetMethod(URL_GET_AUTHENTICITY_TOKEN);
    String token = executeMethod(method, stringParser, monitor);
   
    if(monitor.isCanceled()) {
      throw new OperationCanceledException();
    } else {
      monitor.worked(1);
    }
   
    return token;
  }
 
  private void buildParser() {
    parserByClass = new HashMap<String, IModelParser<? extends AbstractPropertyContainer<?>>>();
    parserByClass.put(URL_ISSUE_STATUS, new AttributeParser<IssueStatuses>(IssueStatuses.class));
    parserByClass.put(URL_ISSUE_CATEGORIES, new AttributeParser<IssueCategories>(IssueCategories.class));
    parserByClass.put(URL_ISSUE_PRIORITIES, new AttributeParser<IssuePriorities>(IssuePriorities.class));
    parserByClass.put(URL_TRACKERS, new AttributeParser<Trackers>(Trackers.class));
    parserByClass.put(URL_CUSTOMFIELDS, new AttributeParser<CustomFields>(CustomFields.class));
    parserByClass.put(URL_USERS, new AttributeParser<Users>(Users.class));
    parserByClass.put(URL_QUERIES, new AttributeParser<Queries>(Queries.class));
    parserByClass.put(URL_PROJECTS, new AttributeParser<Projects>(Projects.class));
    parserByClass.put(URL_VERSIONS, new AttributeParser<Versions>(Versions.class));
   
    settingsParser = new SettingsParser();
    updatedIssuesParser = new TypedParser<UpdatedIssuesType>(UpdatedIssuesType.class);
    issueParser = new IssueParser(getConfiguration());
    issuesParser = new IssuesParser(getConfiguration());
    versionParser = new TypedParser<RedmineServerVersion>(RedmineServerVersion.class);
    attachmentParser = new AttachmentParser();

    stringParser = new StringParser();
    submitIssueParser = new SubmitedIssueParser();
  }
 
}
TOP

Related Classes of net.sf.redmine_mylyn.internal.api.client.Api_2_7_ClientImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.