Skip to content

Commit

Permalink
Issue #114. breaking change! getIssues(params map) method in IssueMan…
Browse files Browse the repository at this point in the history
…ager

now does NOT handle paging automatically.
this means you can set "limit" and "offset" parameters yourself.
  • Loading branch information
alexeyOnGitHub committed Mar 25, 2015
1 parent 86d4263 commit 82d29bb
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 47 deletions.
10 changes: 5 additions & 5 deletions src/main/java/com/taskadapter/redmineapi/IssueManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,9 @@ public List<Issue> getIssuesBySummary(String projectKey, String summaryField) th
}

/**
* Generic method to search for issues.
* <p>Note that you cannot currently use "offset" and "limit" parameters because
* paging is managed by Transport class internally. This method will always return all found objects
* even if it had to perform multiple requests to the server to load several pages.
* Direct method to search for issues using any Redmine REST API parameters you want.
* <p>Unlike other getXXXObjects() methods in this library, this one does NOT handle paging for you so
* you have to provide "offset" and "limit" parameters if you want to control paging.
*
* @param pParameters the http parameters key/value pairs to append to the rest api request
* @return empty list if no issues found matching given parameters
Expand All @@ -86,7 +85,8 @@ public List<Issue> getIssues(Map<String, String> pParameters) throws RedmineExce
params.add(new BasicNameValuePair(param.getKey(), param.getValue()));
}

return transport.getObjectsList(Issue.class, params);
final Transport.ResultsWrapper<Issue> wrapper = transport.getObjectsListNoPaging(Issue.class, params);
return wrapper.getResults();
}

/**
Expand Down
108 changes: 68 additions & 40 deletions src/main/java/com/taskadapter/redmineapi/internal/Transport.java
Original file line number Diff line number Diff line change
Expand Up @@ -394,63 +394,91 @@ public <T> List<T> getObjectsList(Class<T> objectClass, NameValuePair... params)
}

/**
* Returns an object list.
* Returns all objects found using the provided parameters.
* This method IGNORES "limit" and "offset" parameters and handles paging AUTOMATICALLY for you.
* Please use getObjectsListNoPaging() method if you want to control paging yourself with "limit" and "offset" parameters.
*
* @return objects list, never NULL
*
* @see #getObjectsListNoPaging(Class, Collection)
*/
public <T> List<T> getObjectsList(Class<T> objectClass,
Collection<? extends NameValuePair> params) throws RedmineException {
final EntityConfig<T> config = getConfig(objectClass);
Collection<? extends NameValuePair> params) throws RedmineException {
final List<T> result = new ArrayList<T>();

final List<NameValuePair> newParams = new ArrayList<NameValuePair>(params);

newParams.add(new BasicNameValuePair("limit", String.valueOf(objectsPerPage)));
int offset = 0;

int totalObjectsFoundOnServer;
Integer totalObjectsFoundOnServer;
do {
List<NameValuePair> paramsList = new ArrayList<NameValuePair>(newParams);
paramsList.add(new BasicNameValuePair("offset", String.valueOf(offset)));

final URI uri = getURIConfigurator().getObjectsURI(objectClass, paramsList);

logger.debug(uri.toString());
final HttpGet http = new HttpGet(uri);
final String response = send(http);
logger.debug("received: " + response);

final List<T> foundItems;
try {
final JSONObject responseObject = RedmineJSONParser.getResponse(response);
foundItems = JsonInput.getListOrNull(responseObject,config.multiObjectName, config.parser);
result.addAll(foundItems);

/* Necessary for trackers */
if (!responseObject.has(KEY_TOTAL_COUNT)) {
break;
}
totalObjectsFoundOnServer = JsonInput.getInt(responseObject,KEY_TOTAL_COUNT);
} catch (JSONException e) {
throw new RedmineFormatException(e);
}
final List<NameValuePair> newParams = new ArrayList<NameValuePair>(params);
newParams.add(new BasicNameValuePair("limit", String.valueOf(objectsPerPage)));
newParams.add(new BasicNameValuePair("offset", String.valueOf(offset)));

final ResultsWrapper<T> wrapper = getObjectsListNoPaging(objectClass, newParams);
result.addAll(wrapper.getResults());

if (foundItems.size() == 0) {
totalObjectsFoundOnServer = wrapper.getTotalFoundOnServer();
// Necessary for trackers.
// TODO Alexey: is this still necessary for Redmine 2.x?
if (totalObjectsFoundOnServer == null) {
break;
}

offset += foundItems.size();
if (!wrapper.hasSomeResults()) {
break;
}
offset += wrapper.getResultsNumber();
} while (offset < totalObjectsFoundOnServer);

return result;
}

/**
* This number of objects (tasks, projects, users) will be requested from
* Redmine server in 1 request.
* Returns an object list. Provide your own "limit" and "offset" parameters if you need those, otherwise
* this method will return the first page of some default size only (this default is controlled by
* your Redmine configuration).
*
* @return objects list, never NULL
*/
public int getObjectsPerPage() {
return objectsPerPage;
public <T> ResultsWrapper<T> getObjectsListNoPaging(Class<T> objectClass,
Collection<? extends NameValuePair> params) throws RedmineException {
final EntityConfig<T> config = getConfig(objectClass);
final List<NameValuePair> newParams = new ArrayList<NameValuePair>(params);
List<NameValuePair> paramsList = new ArrayList<NameValuePair>(newParams);
final URI uri = getURIConfigurator().getObjectsURI(objectClass, paramsList);
final HttpGet http = new HttpGet(uri);
final String response = send(http);
try {
final JSONObject responseObject = RedmineJSONParser.getResponse(response);
List<T> results = JsonInput.getListOrNull(responseObject, config.multiObjectName, config.parser);
Integer totalFoundOnServer = JsonInput.getIntOrNull(responseObject, KEY_TOTAL_COUNT);
return new ResultsWrapper<T>(totalFoundOnServer, results);
} catch (JSONException e) {
throw new RedmineFormatException(e);
}
}

public static class ResultsWrapper<T> {
final private Integer totalFoundOnServer;
final private List<T> results;

public ResultsWrapper(Integer totalFoundOnServer, List<T> results) {
this.totalFoundOnServer = totalFoundOnServer;
this.results = results;
}

public boolean hasSomeResults() {
return !results.isEmpty();
}

public List<T> getResults() {
return results;
}

public int getResultsNumber() {
return results.size();
}

public Integer getTotalFoundOnServer() {
return totalFoundOnServer;
}
}

public <T> List<T> getChildEntries(Class<?> parentClass, int parentId, Class<T> classs) throws RedmineException {
Expand Down
20 changes: 18 additions & 2 deletions src/test/java/com/taskadapter/redmineapi/IssueManagerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

Expand Down Expand Up @@ -302,10 +304,24 @@ public void testGetIssuesPaging() throws RedmineException {
// create 27 issues. default page size is 25.
createIssues(issueManager, projectId, 27);
List<Issue> issues = issueManager.getIssues(projectKey, null);
assertTrue(issues.size() > 26);
assertThat(issues.size()).isGreaterThan(26);

// check that there are no duplicates in the list.
Set<Issue> issueSet = new HashSet<Issue>(issues);
assertEquals(issues.size(), issueSet.size());
assertThat(issueSet.size()).isEqualTo(issues.size());
}

@Test
public void canControlLimitAndOffsetDirectly() throws RedmineException {
// create 27 issues. default Redmine page size is usually 25 (unless changed in the server settings).
createIssues(issueManager, projectId, 27);
Map<String, String> params = new HashMap<String, String>();
params.put("limit", "3");
params.put("offset", "0");
params.put("project_id", projectId + "");
List<Issue> issues = issueManager.getIssues(params);
// only the requested number of issues is loaded, not all result pages.
assertThat(issues.size()).isEqualTo(3);
}

@Test(expected = NotFoundException.class)
Expand Down

0 comments on commit 82d29bb

Please sign in to comment.