diff --git a/scm-core/src/main/java/sonia/scm/Manager.java b/scm-core/src/main/java/sonia/scm/Manager.java index 600ab268ca..52a3c7f20e 100644 --- a/scm-core/src/main/java/sonia/scm/Manager.java +++ b/scm-core/src/main/java/sonia/scm/Manager.java @@ -35,8 +35,9 @@ package sonia.scm; //~--- JDK imports ------------------------------------------------------------ -import java.io.IOException; +import sonia.scm.util.Util; +import java.io.IOException; import java.util.Collection; import java.util.Comparator; @@ -61,7 +62,7 @@ public interface Manager * @throws E * @throws IOException */ - public void refresh(T object) throws E, IOException; + void refresh(T object) throws E, IOException; //~--- get methods ---------------------------------------------------------- @@ -73,7 +74,7 @@ public interface Manager * * @return object with the given id */ - public T get(String id); + T get(String id); /** * Returns a {@link java.util.Collection} of all objects in the store. @@ -81,7 +82,7 @@ public interface Manager * * @return all object in the store */ - public Collection getAll(); + Collection getAll(); /** * Returns all object of the store sorted by the given {@link java.util.Comparator} @@ -91,7 +92,7 @@ public interface Manager * @since 1.4 * @return all object of the store sorted by the given {@link java.util.Comparator} */ - public Collection getAll(Comparator comparator); + Collection getAll(Comparator comparator); /** * Returns objects from the store which are starts at the given start @@ -105,7 +106,7 @@ public interface Manager * @return objects from the store which are starts at the given * start parameter */ - public Collection getAll(int start, int limit); + Collection getAll(int start, int limit); /** * Returns objects from the store which are starts at the given start @@ -121,5 +122,26 @@ public interface Manager * @return objects from the store which are starts at the given * start parameter */ - public Collection getAll(Comparator comparator, int start, int limit); + Collection getAll(Comparator comparator, int start, int limit); + + /** + * Returns objects from the store divided into pages with the given page + * size for the given page number (zero based) and sorted by the given + * {@link java.util.Comparator}. + * + * @param comparator to sort the returned objects + * @param pageNumber the number of the page to be returned (zero based) + * @param pageSize the size of the pages + * + * @since 2.0 + * @return {@link PageResult} with the objects from the store for the requested + * page. If the requested page number exceeds the existing pages, an + * empty page result is returned. + */ + default PageResult getPage(Comparator comparator, int pageNumber, int pageSize) { + Collection entities = getAll(comparator, pageNumber * pageSize, pageSize + 1); + boolean hasMore = entities.size() > pageSize; + return new PageResult<>(Util.createSubCollection(entities, 0, pageSize), hasMore); + } + } diff --git a/scm-core/src/main/java/sonia/scm/PageResult.java b/scm-core/src/main/java/sonia/scm/PageResult.java new file mode 100644 index 0000000000..34fa37e5ee --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/PageResult.java @@ -0,0 +1,23 @@ +package sonia.scm; + +import java.util.Collection; +import java.util.Collections; + +public class PageResult { + + private final Collection entities; + private final boolean hasMore; + + public PageResult(Collection entities, boolean hasMore) { + this.entities = entities; + this.hasMore = hasMore; + } + + public Collection getEntities() { + return Collections.unmodifiableCollection(entities); + } + + public boolean hasMore() { + return hasMore; + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AbstractManagerResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AbstractManagerResource.java index 62ee9ea5df..62f77f2513 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AbstractManagerResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AbstractManagerResource.java @@ -42,6 +42,7 @@ import org.slf4j.LoggerFactory; import sonia.scm.LastModifiedAware; import sonia.scm.Manager; import sonia.scm.ModelObject; +import sonia.scm.PageResult; import sonia.scm.api.rest.RestExceptionResult; import sonia.scm.util.AssertUtil; import sonia.scm.util.HttpUtil; @@ -588,6 +589,19 @@ public abstract class AbstractManagerResource fetchPage(String sortby, boolean desc, int pageNumber, + int pageSize) { + AssertUtil.assertPositive(pageNumber); + AssertUtil.assertPositive(pageSize); + + if (Util.isEmpty(sortby)) { + // replace with something useful + sortby = "id"; + } + + return manager.getPage(createComparator(sortby, desc), pageNumber, pageSize); + } + //~--- get methods ---------------------------------------------------------- /** diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionDto.java index 2974379220..6bd955c417 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionDto.java @@ -13,11 +13,12 @@ import static de.otto.edison.hal.Links.linkingTo; public class UserCollectionDto extends HalRepresentation { public UserCollectionDto(String baseUrl, NumberedPaging page, List users) { - super(linkingTo() - .with(page.links( - fromTemplate(baseUrl + "{?page,pageSize}"), - EnumSet.allOf(PagingRel.class))) - .build(), + super( + linkingTo() + .with(page.links( + fromTemplate(baseUrl + "{?page,pageSize}"), + EnumSet.allOf(PagingRel.class))) + .build(), embeddedBuilder() .with("users", users) .build() diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionResource.java index bd27b17474..66cdf735bd 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionResource.java @@ -6,6 +6,7 @@ import com.webcohesion.enunciate.metadata.rs.ResponseCode; import com.webcohesion.enunciate.metadata.rs.ResponseHeader; import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; +import sonia.scm.PageResult; import sonia.scm.api.rest.resources.AbstractManagerResource; import sonia.scm.user.User; import sonia.scm.user.UserException; @@ -24,6 +25,7 @@ import static sonia.scm.api.v2.resources.ScmMediaType.USER; @Singleton @Produces(USER) public class UserCollectionResource extends AbstractManagerResource { + public static final int DEFAULT_PAGE_SIZE = 10; private final UserDto2UserMapper dtoToUserMapper; private final User2UserDtoMapper userToDtoMapper; @@ -53,18 +55,18 @@ public class UserCollectionResource extends AbstractManagerResource items = fetchItems(sortby, desc, page * pageSize, pageSize); + PageResult pageResult = fetchPage(sortby, desc, page, pageSize); LinkBuilder collectionLinkBuilder = new LinkBuilder(uriInfo, UserV2Resource.class, UserCollectionResource.class); String baseUrl = collectionLinkBuilder.method("getUserCollectionResource").parameters().method("create").parameters().href(); - List dtos = items.stream().map(user -> userToDtoMapper.userToUserDto(user, uriInfo)).collect(Collectors.toList()); + List dtos = pageResult.getEntities().stream().map(user -> userToDtoMapper.userToUserDto(user, uriInfo)).collect(Collectors.toList()); - return Response.ok(new UserCollectionDto(baseUrl, zeroBasedNumberedPaging(page, pageSize, true), dtos)).build(); + return Response.ok(new UserCollectionDto(baseUrl, zeroBasedNumberedPaging(page, pageSize, pageResult.hasMore()), dtos)).build(); } @POST