/******************************************************************************* * Copyright (c) 2011 GitHub Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Kevin Sawicki (GitHub Inc.) - initial API and implementation *******************************************************************************/ package org.eclipse.egit.github.core.service; import static org.eclipse.egit.github.core.client.IGitHubConstants.CHARSET_UTF8; import static org.eclipse.egit.github.core.client.IGitHubConstants.SEGMENT_COMMENTS; import static org.eclipse.egit.github.core.client.IGitHubConstants.SEGMENT_EVENTS; import static org.eclipse.egit.github.core.client.IGitHubConstants.SEGMENT_ISSUES; import static org.eclipse.egit.github.core.client.IGitHubConstants.SEGMENT_LEGACY; import static org.eclipse.egit.github.core.client.IGitHubConstants.SEGMENT_REPOS; import static org.eclipse.egit.github.core.client.IGitHubConstants.SEGMENT_SEARCH; import static org.eclipse.egit.github.core.client.PagedRequest.PAGE_FIRST; import static org.eclipse.egit.github.core.client.PagedRequest.PAGE_SIZE; import com.google.gson.reflect.TypeToken; import java.io.IOException; import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.egit.github.core.Comment; import org.eclipse.egit.github.core.IRepositoryIdProvider; import org.eclipse.egit.github.core.IResourceProvider; import org.eclipse.egit.github.core.Issue; import org.eclipse.egit.github.core.IssueEvent; import org.eclipse.egit.github.core.Label; import org.eclipse.egit.github.core.Milestone; import org.eclipse.egit.github.core.RepositoryIssue; import org.eclipse.egit.github.core.SearchIssue; import org.eclipse.egit.github.core.User; import org.eclipse.egit.github.core.client.GitHubClient; import org.eclipse.egit.github.core.client.GitHubRequest; import org.eclipse.egit.github.core.client.PageIterator; import org.eclipse.egit.github.core.client.PagedRequest; /** * Issue service class for listing, searching, and fetching {@link Issue} * objects using a {@link GitHubClient}. * * @see <a href="http://developer.github.com/v3/issues">GitHub Issues API * documentation</a> */ public class IssueService extends GitHubService { /** * Filter field key */ public static final String FIELD_FILTER = "filter"; //$NON-NLS-1$ /** * Filter by issue assignee */ public static final String FILTER_ASSIGNEE = "assignee"; //$NON-NLS-1$ /** * Filter by issue's milestone */ public static final String FILTER_MILESTONE = "milestone"; //$NON-NLS-1$ /** * Filter by user mentioned in issue */ public static final String FILTER_MENTIONED = "mentioned"; //$NON-NLS-1$ /** * Filter by subscribed issues for user */ public static final String FILTER_SUBSCRIBED = "subscribed"; //$NON-NLS-1$ /** * Filter by created issues by user */ public static final String FILTER_CREATED = "created"; //$NON-NLS-1$ /** * Filter by assigned issues for user */ public static final String FILTER_ASSIGNED = "assigned"; //$NON-NLS-1$ /** * Filter by issue's labels */ public static final String FILTER_LABELS = "labels"; //$NON-NLS-1$ /** * Filter by issue's state */ public static final String FILTER_STATE = "state"; //$NON-NLS-1$ /** * Issue open state filter value */ public static final String STATE_OPEN = "open"; //$NON-NLS-1$ /** * Issue closed state filter value */ public static final String STATE_CLOSED = "closed"; //$NON-NLS-1$ /** * Issue body field name */ public static final String FIELD_BODY = "body"; //$NON-NLS-1$ /** * Issue title field name */ public static final String FIELD_TITLE = "title"; //$NON-NLS-1$ /** * Since date field */ public static final String FIELD_SINCE = "since"; //$NON-NLS-1$ /** * Sort direction of output */ public static final String FIELD_DIRECTION = "direction"; //$NON-NLS-1$ /** * Ascending direction sort order */ public static final String DIRECTION_ASCENDING = "asc"; //$NON-NLS-1$ /** * Descending direction sort order */ public static final String DIRECTION_DESCENDING = "desc"; //$NON-NLS-1$ /** * Sort field key */ public static final String FIELD_SORT = "sort"; //$NON-NLS-1$ /** * Sort by created at */ public static final String SORT_CREATED = "created"; //$NON-NLS-1$ /** * Sort by updated at */ public static final String SORT_UPDATED = "updated"; //$NON-NLS-1$ /** * Sort by commented on at */ public static final String SORT_COMMENTS = "comments"; //$NON-NLS-1$ private static class IssueContainer implements IResourceProvider<SearchIssue> { private List<SearchIssue> issues; /** * @see org.eclipse.egit.github.core.IResourceProvider#getResources() */ public List<SearchIssue> getResources() { return issues; } } /** * Create issue service */ public IssueService() { super(); } /** * Create issue service * * @param client * cannot be null */ public IssueService(GitHubClient client) { super(client); } /** * Get issues for currently authenticated user * * @return non-null but possibly empty list of issues * @throws IOException */ public List<RepositoryIssue> getIssues() throws IOException { return getIssues(null); } /** * Get issues for currently authenticated user * * @param filterData * @return non-null but possibly empty list of issues * @throws IOException */ public List<RepositoryIssue> getIssues(Map<String, String> filterData) throws IOException { return getAll(pageIssues(filterData)); } /** * Page issues for currently authenticated user * * @return iterator over pages of issues */ public PageIterator<RepositoryIssue> pageIssues() { return pageIssues((Map<String, String>) null); } /** * Page issues for currently authenticated user * * @param filterData * @return iterator over pages of issues */ public PageIterator<RepositoryIssue> pageIssues( Map<String, String> filterData) { return pageIssues(filterData, PAGE_SIZE); } /** * Page issues for currently authenticated user * * @param filterData * @param size * @return iterator over pages of issues */ public PageIterator<RepositoryIssue> pageIssues( Map<String, String> filterData, int size) { return pageIssues(filterData, PAGE_FIRST, size); } /** * Page issues for currently authenticated user * * @param filterData * @param start * @param size * @return iterator over pages of issues */ public PageIterator<RepositoryIssue> pageIssues( Map<String, String> filterData, int start, int size) { PagedRequest<RepositoryIssue> request = createPagedRequest(start, size); request.setParams(filterData); request.setUri(SEGMENT_ISSUES); request.setType(new TypeToken<List<RepositoryIssue>>() { }.getType()); return createPageIterator(request); } /** * Get issue * * @param user * @param repository * @param issueNumber * @return issue * @throws IOException */ public Issue getIssue(String user, String repository, int issueNumber) throws IOException { return getIssue(user, repository, Integer.toString(issueNumber)); } /** * Get issue * * @param user * @param repository * @param issueNumber * @return issue * @throws IOException */ public Issue getIssue(String user, String repository, String issueNumber) throws IOException { verifyRepository(user, repository); String repoId = user + '/' + repository; return getIssue(repoId, issueNumber); } /** * Get issue * * @param repository * @param issueNumber * @return issue * @throws IOException */ public Issue getIssue(IRepositoryIdProvider repository, int issueNumber) throws IOException { return getIssue(repository, Integer.toString(issueNumber)); } /** * Get issue * * @param repository * @param issueNumber * @return issue * @throws IOException */ public Issue getIssue(IRepositoryIdProvider repository, String issueNumber) throws IOException { String repoId = getId(repository); return getIssue(repoId, issueNumber); } private Issue getIssue(String repoId, String issueNumber) throws IOException { if (issueNumber == null) throw new IllegalArgumentException("Issue number cannot be null"); //$NON-NLS-1$ if (issueNumber.length() == 0) throw new IllegalArgumentException("Issue number cannot be empty"); //$NON-NLS-1$ StringBuilder uri = new StringBuilder(SEGMENT_REPOS); uri.append('/').append(repoId); uri.append(SEGMENT_ISSUES); uri.append('/').append(issueNumber); GitHubRequest request = createRequest(); request.setUri(uri); request.setType(Issue.class); return (Issue) client.get(request).getBody(); } /** * Get an issue's comments * * @param user * @param repository * @param issueNumber * @return list of comments * @throws IOException */ public List<Comment> getComments(String user, String repository, int issueNumber) throws IOException { return getComments(user, repository, Integer.toString(issueNumber)); } /** * Get an issue's comments * * @param user * @param repository * @param issueNumber * @return list of comments * @throws IOException */ public List<Comment> getComments(String user, String repository, String issueNumber) throws IOException { verifyRepository(user, repository); String repoId = user + '/' + repository; return getComments(repoId, issueNumber); } /** * Get an issue's comments * * @param repository * @param issueNumber * @return list of comments * @throws IOException */ public List<Comment> getComments(IRepositoryIdProvider repository, int issueNumber) throws IOException { return getComments(repository, Integer.toString(issueNumber)); } /** * Get an issue's comments * * @param repository * @param issueNumber * @return list of comments * @throws IOException */ public List<Comment> getComments(IRepositoryIdProvider repository, String issueNumber) throws IOException { String repoId = getId(repository); return getComments(repoId, issueNumber); } /** * Get an issue's comments * * @param repository * @param issueNumber * @return list of comments * @throws IOException */ private List<Comment> getComments(String repoId, String issueNumber) throws IOException { if (issueNumber == null) throw new IllegalArgumentException("Issue number cannot be null"); //$NON-NLS-1$ if (issueNumber.length() == 0) throw new IllegalArgumentException("Issue number cannot be empty"); //$NON-NLS-1$ StringBuilder uri = new StringBuilder(SEGMENT_REPOS); uri.append('/').append(repoId); uri.append(SEGMENT_ISSUES); uri.append('/').append(issueNumber); uri.append(SEGMENT_COMMENTS); PagedRequest<Comment> request = createPagedRequest(); request.setUri(uri); request.setType(new TypeToken<List<Comment>>() { }.getType()); return getAll(request); } /** * Get bulk issues request * * @param repoId * @param filterData * @param start * @param size * @return paged request */ protected PagedRequest<Issue> createIssuesRequest(String repoId, Map<String, String> filterData, int start, int size) { StringBuilder uri = new StringBuilder(SEGMENT_REPOS); uri.append('/').append(repoId); uri.append(SEGMENT_ISSUES); PagedRequest<Issue> request = createPagedRequest(start, size); request.setParams(filterData).setUri(uri); request.setType(new TypeToken<List<Issue>>() { }.getType()); return request; } /** * Get a list of {@link Issue} objects that match the specified filter data * * @param user * @param repository * @param filterData * @return list of issues * @throws IOException */ public List<Issue> getIssues(String user, String repository, Map<String, String> filterData) throws IOException { return getAll(pageIssues(user, repository, filterData)); } /** * Get a list of {@link Issue} objects that match the specified filter data * * @param repository * @param filterData * @return list of issues * @throws IOException */ public List<Issue> getIssues(IRepositoryIdProvider repository, Map<String, String> filterData) throws IOException { return getAll(pageIssues(repository, filterData)); } /** * Get page iterator over issues query * * @param user * @param repository * @return iterator over issue pages */ public PageIterator<Issue> pageIssues(String user, String repository) { return pageIssues(user, repository, null); } /** * Get page iterator over issues query * * @param user * @param repository * @param filterData * @return iterator */ public PageIterator<Issue> pageIssues(String user, String repository, Map<String, String> filterData) { return pageIssues(user, repository, filterData, PAGE_SIZE); } /** * Get page iterator over issues query * * @param user * @param repository * @param filterData * @param size * @return iterator */ public PageIterator<Issue> pageIssues(String user, String repository, Map<String, String> filterData, int size) { return pageIssues(user, repository, filterData, PAGE_FIRST, size); } /** * Get page iterator over issues query * * @param user * @param repository * @param filterData * @param size * page size * @param start * starting page number * @return iterator */ public PageIterator<Issue> pageIssues(String user, String repository, Map<String, String> filterData, int start, int size) { verifyRepository(user, repository); String repoId = user + '/' + repository; PagedRequest<Issue> request = createIssuesRequest(repoId, filterData, start, size); return createPageIterator(request); } /** * Get page iterator over issues query * * @param repository * @return iterator over issue pages */ public PageIterator<Issue> pageIssues(IRepositoryIdProvider repository) { return pageIssues(repository, null); } /** * Get page iterator over issues query * * @param repository * @param filterData * @return iterator */ public PageIterator<Issue> pageIssues(IRepositoryIdProvider repository, Map<String, String> filterData) { return pageIssues(repository, filterData, PAGE_SIZE); } /** * Get page iterator over issues query * * @param repository * @param filterData * @param size * @return iterator */ public PageIterator<Issue> pageIssues(IRepositoryIdProvider repository, Map<String, String> filterData, int size) { return pageIssues(repository, filterData, PAGE_FIRST, size); } /** * Get page iterator over issues query * * @param repository * @param filterData * @param size * page size * @param start * starting page number * @return iterator */ public PageIterator<Issue> pageIssues(IRepositoryIdProvider repository, Map<String, String> filterData, int start, int size) { String repoId = getId(repository); PagedRequest<Issue> request = createIssuesRequest(repoId, filterData, start, size); return createPageIterator(request); } /** * Create issue map for issue * * @param issue * @param newIssue * @return map */ protected Map<Object, Object> createIssueMap(Issue issue, boolean newIssue) { Map<Object, Object> params = new HashMap<Object, Object>(); if (issue != null) { params.put(FIELD_BODY, issue.getBody()); params.put(FIELD_TITLE, issue.getTitle()); User assignee = issue.getAssignee(); if (assignee != null) params.put(FILTER_ASSIGNEE, assignee.getLogin()); Milestone milestone = issue.getMilestone(); if (milestone != null) { int number = milestone.getNumber(); if (number > 0) params.put(FILTER_MILESTONE, Integer.toString(number)); else { if (!newIssue) params.put(FILTER_MILESTONE, ""); //$NON-NLS-1$ } } List<Label> labels = issue.getLabels(); if (labels != null) { List<String> labelNames = new ArrayList<String>(labels.size()); for (Label label : labels) labelNames.add(label.getName()); params.put(FILTER_LABELS, labelNames); } } return params; } /** * Create issue * * @param user * @param repository * @param issue * @return created issue * @throws IOException */ public Issue createIssue(String user, String repository, Issue issue) throws IOException { verifyRepository(user, repository); String repoId = user + '/' + repository; return createIssue(repoId, issue); } /** * Create issue * * @param repository * @param issue * @return created issue * @throws IOException */ public Issue createIssue(IRepositoryIdProvider repository, Issue issue) throws IOException { String repoId = getId(repository); return createIssue(repoId, issue); } /** * Create issue * * @param repoId * @param issue * @return created issue * @throws IOException */ private Issue createIssue(String repoId, Issue issue) throws IOException { StringBuilder uri = new StringBuilder(SEGMENT_REPOS); uri.append('/').append(repoId); uri.append(SEGMENT_ISSUES); Map<Object, Object> params = createIssueMap(issue, true); return client.post(uri.toString(), params, Issue.class); } /** * Edit issue * * @param user * @param repository * @param issue * @return created issue * @throws IOException */ public Issue editIssue(String user, String repository, Issue issue) throws IOException { verifyRepository(user, repository); String repoId = user + '/' + repository; return editIssue(repoId, issue); } /** * Edit issue * * @param repository * @param issue * @return created issue * @throws IOException */ public Issue editIssue(IRepositoryIdProvider repository, Issue issue) throws IOException { String repoId = getId(repository); return editIssue(repoId, issue); } /** * Edit issue * * @param repoId * @param repository * @param issue * @return created issue * @throws IOException */ private Issue editIssue(String repoId, Issue issue) throws IOException { if (issue == null) throw new IllegalArgumentException("Issue cannot be null"); //$NON-NLS-1$ StringBuilder uri = new StringBuilder(SEGMENT_REPOS); uri.append('/').append(repoId); uri.append(SEGMENT_ISSUES); uri.append('/').append(issue.getNumber()); Map<Object, Object> params = createIssueMap(issue, false); String state = issue.getState(); if (state != null) params.put(FILTER_STATE, state); return client.post(uri.toString(), params, Issue.class); } /** * Create comment on specified issue number * * @param user * @param repository * @param issueNumber * @param comment * @return created issue * @throws IOException */ public Comment createComment(String user, String repository, int issueNumber, String comment) throws IOException { return createComment(user, repository, Integer.toString(issueNumber), comment); } /** * Create comment on specified issue number * * @param user * @param repository * @param issueNumber * @param comment * @return created issue * @throws IOException */ public Comment createComment(String user, String repository, String issueNumber, String comment) throws IOException { verifyRepository(user, repository); String repoId = user + '/' + repository; return createComment(repoId, issueNumber, comment); } /** * Create comment on specified issue number * * @param repository * @param issueNumber * @param comment * @return created issue * @throws IOException */ public Comment createComment(IRepositoryIdProvider repository, int issueNumber, String comment) throws IOException { return createComment(repository, Integer.toString(issueNumber), comment); } /** * Create comment on specified issue number * * @param repository * @param issueNumber * @param comment * @return created issue * @throws IOException */ public Comment createComment(IRepositoryIdProvider repository, String issueNumber, String comment) throws IOException { String repoId = getId(repository); return createComment(repoId, issueNumber, comment); } /** * Create comment on specified issue number * * @param repoId * @param issueNumber * @param comment * @return created issue * @throws IOException */ private Comment createComment(String repoId, String issueNumber, String comment) throws IOException { if (issueNumber == null) throw new IllegalArgumentException("Issue number cannot be null"); //$NON-NLS-1$ if (issueNumber.length() == 0) throw new IllegalArgumentException("Issue number cannot be empty"); //$NON-NLS-1$ StringBuilder uri = new StringBuilder(SEGMENT_REPOS); uri.append('/').append(repoId); uri.append(SEGMENT_ISSUES); uri.append('/').append(issueNumber); uri.append(SEGMENT_COMMENTS); Map<String, String> params = new HashMap<String, String>(1, 1); params.put(FIELD_BODY, comment); return client.post(uri.toString(), params, Comment.class); } /** * Get issue comment * * @param user * @param repository * @param commentId * @return comment * @throws IOException */ public Comment getComment(String user, String repository, long commentId) throws IOException { verifyRepository(user, repository); GitHubRequest request = createRequest(); StringBuilder uri = new StringBuilder(SEGMENT_REPOS); uri.append('/').append(user).append('/').append(repository); uri.append(SEGMENT_ISSUES).append(SEGMENT_COMMENTS); uri.append('/').append(commentId); request.setUri(uri); request.setType(Comment.class); return (Comment) client.get(request).getBody(); } /** * Edit issue comment * * @param user * @param repository * @param comment * @return edited comment * @throws IOException */ public Comment editComment(String user, String repository, Comment comment) throws IOException { verifyRepository(user, repository); String repoId = user + '/' + repository; return editComment(repoId, comment); } /** * Edit issue comment * * @param repository * @param comment * @return edited comment * @throws IOException */ public Comment editComment(IRepositoryIdProvider repository, Comment comment) throws IOException { String repoId = getId(repository); return editComment(repoId, comment); } /** * Edit issue comment * * @param user * @param repository * @param comment * @return edited comment * @throws IOException */ private Comment editComment(String repoId, Comment comment) throws IOException { if (comment == null) throw new IllegalArgumentException("Comment cannot be null"); //$NON-NLS-1$ StringBuilder uri = new StringBuilder(SEGMENT_REPOS); uri.append('/').append(repoId); uri.append(SEGMENT_ISSUES).append(SEGMENT_COMMENTS); uri.append('/').append(comment.getId()); return client.post(uri.toString(), comment, Comment.class); } /** * Delete the issue comment with the given id * * @param user * @param repository * @param commentId * @throws IOException */ public void deleteComment(String user, String repository, long commentId) throws IOException { deleteComment(user, repository, Long.toString(commentId)); } /** * Delete the issue comment with the given id * * @param user * @param repository * @param commentId * @throws IOException */ public void deleteComment(String user, String repository, String commentId) throws IOException { verifyRepository(user, repository); String repoId = user + '/' + repository; deleteComment(repoId, commentId); } /** * Delete the issue comment with the given id * * @param repository * @param commentId * @throws IOException */ public void deleteComment(IRepositoryIdProvider repository, long commentId) throws IOException { deleteComment(repository, Long.toString(commentId)); } /** * Delete the issue comment with the given id * * @param repository * @param commentId * @throws IOException */ public void deleteComment(IRepositoryIdProvider repository, String commentId) throws IOException { String repoId = getId(repository); deleteComment(repoId, commentId); } /** * Delete the issue comment with the given id * * @param user * @param repository * @param commentId * @throws IOException */ private void deleteComment(String repoId, String commentId) throws IOException { if (commentId == null) throw new IllegalArgumentException("Comment cannot be null"); //$NON-NLS-1$ if (commentId.length() == 0) throw new IllegalArgumentException("Comment cannot be empty"); //$NON-NLS-1$ StringBuilder uri = new StringBuilder(SEGMENT_REPOS); uri.append('/').append(repoId); uri.append(SEGMENT_ISSUES).append(SEGMENT_COMMENTS); uri.append('/').append(commentId); client.delete(uri.toString()); } /** * Page issue events for repository * * @param user * @param repository * @return iterator over issue event pages */ public PageIterator<IssueEvent> pageEvents(String user, String repository) { return pageEvents(user, repository, PAGE_SIZE); } /** * Page issue events for repository * * @param user * @param repository * @param size * @return iterator over issue event pages */ public PageIterator<IssueEvent> pageEvents(String user, String repository, int size) { return pageEvents(user, repository, PAGE_FIRST, size); } /** * Page events for issue in repository * * @param user * @param repository * @param start * @param size * @return iterator over issue event pages */ public PageIterator<IssueEvent> pageEvents(String user, String repository, int start, int size) { verifyRepository(user, repository); PagedRequest<IssueEvent> request = createPagedRequest(start, size); StringBuilder uri = new StringBuilder(SEGMENT_REPOS); uri.append('/').append(user).append('/').append(repository); uri.append(SEGMENT_ISSUES); uri.append(SEGMENT_EVENTS); request.setUri(uri); request.setType(new TypeToken<List<IssueEvent>>() { }.getType()); return createPageIterator(request); } /** * Page events for issue in repository * * @param user * @param repository * @param issueId * @return iterator over issue event pages */ public PageIterator<IssueEvent> pageIssueEvents(String user, String repository, int issueId) { return pageIssueEvents(user, repository, issueId, PAGE_SIZE); } /** * Page events for issue in repository * * @param user * @param repository * @param issueId * @param size * @return iterator over issue event pages */ public PageIterator<IssueEvent> pageIssueEvents(String user, String repository, int issueId, int size) { return pageIssueEvents(user, repository, issueId, PAGE_FIRST, size); } /** * Page issue events for repository * * @param user * @param repository * @param issueId * @param start * @param size * @return iterator over issue event pages */ public PageIterator<IssueEvent> pageIssueEvents(String user, String repository, int issueId, int start, int size) { verifyRepository(user, repository); PagedRequest<IssueEvent> request = createPagedRequest(start, size); StringBuilder uri = new StringBuilder(SEGMENT_REPOS); uri.append('/').append(user).append('/').append(repository); uri.append(SEGMENT_ISSUES); uri.append('/').append(issueId); uri.append(SEGMENT_EVENTS); request.setUri(uri); request.setType(new TypeToken<List<IssueEvent>>() { }.getType()); return createPageIterator(request); } /** * Get issue event for repository * * @param user * @param repository * @param eventId * @return iterator over issue event pages * @throws IOException */ public IssueEvent getIssueEvent(String user, String repository, long eventId) throws IOException { verifyRepository(user, repository); GitHubRequest request = createRequest(); StringBuilder uri = new StringBuilder(SEGMENT_REPOS); uri.append('/').append(user).append('/').append(repository); uri.append(SEGMENT_ISSUES); uri.append(SEGMENT_EVENTS); uri.append('/').append(eventId); request.setUri(uri); request.setType(IssueEvent.class); return (IssueEvent) client.get(request).getBody(); } /** * Search issues in the given repository using the given query * * @param repository * @param state * {@link #STATE_OPEN} or {@link #STATE_CLOSED} * @param query * @return issues matching query * @throws IOException */ public List<SearchIssue> searchIssues(IRepositoryIdProvider repository, String state, String query) throws IOException { String id = getId(repository); if (state == null) throw new IllegalArgumentException("State cannot be null"); //$NON-NLS-1$ if (state.length() == 0) throw new IllegalArgumentException("State cannot be empty"); //$NON-NLS-1$ if (query == null) throw new IllegalArgumentException("Query cannot be null"); //$NON-NLS-1$ if (query.length() == 0) throw new IllegalArgumentException("Query cannot be empty"); //$NON-NLS-1$ StringBuilder uri = new StringBuilder(SEGMENT_LEGACY + SEGMENT_ISSUES + SEGMENT_SEARCH); uri.append('/').append(id); uri.append('/').append(state); final String encodedQuery = URLEncoder.encode(query, CHARSET_UTF8) .replace("+", "%20") //$NON-NLS-1$ //$NON-NLS-2$ .replace(".", "%2E"); //$NON-NLS-1$ //$NON-NLS-2$ uri.append('/').append(encodedQuery); PagedRequest<SearchIssue> request = createPagedRequest(); request.setUri(uri); request.setType(IssueContainer.class); return getAll(request); } }