diff --git a/scm-core/src/main/java/sonia/scm/HandlerBase.java b/scm-core/src/main/java/sonia/scm/HandlerBase.java index 0baea8d929..8e6b12ba79 100644 --- a/scm-core/src/main/java/sonia/scm/HandlerBase.java +++ b/scm-core/src/main/java/sonia/scm/HandlerBase.java @@ -59,7 +59,7 @@ public interface HandlerBase * @throws E * @throws IOException */ - public void create(T object) throws E, IOException; + public void create(T object) throws E; /** * Removes a persistent object. @@ -70,7 +70,7 @@ public interface HandlerBase * @throws E * @throws IOException */ - public void delete(T object) throws E, IOException; + public void delete(T object) throws E; /** * Modifies a persistent object. @@ -81,5 +81,5 @@ public interface HandlerBase * @throws E * @throws IOException */ - public void modify(T object) throws E, IOException; + public void modify(T object) throws E; } diff --git a/scm-core/src/main/java/sonia/scm/Manager.java b/scm-core/src/main/java/sonia/scm/Manager.java index 79155b56a2..c0d074520a 100644 --- a/scm-core/src/main/java/sonia/scm/Manager.java +++ b/scm-core/src/main/java/sonia/scm/Manager.java @@ -33,7 +33,6 @@ package sonia.scm; -import java.io.IOException; import java.util.Collection; import java.util.Comparator; @@ -56,9 +55,8 @@ public interface Manager * @param object to refresh * * @throws E - * @throws IOException */ - void refresh(T object) throws E, IOException; + void refresh(T object) throws E; //~--- get methods ---------------------------------------------------------- diff --git a/scm-core/src/main/java/sonia/scm/ManagerDecorator.java b/scm-core/src/main/java/sonia/scm/ManagerDecorator.java index af0215202c..99707c4c4d 100644 --- a/scm-core/src/main/java/sonia/scm/ManagerDecorator.java +++ b/scm-core/src/main/java/sonia/scm/ManagerDecorator.java @@ -35,7 +35,6 @@ package sonia.scm; //~--- JDK imports ------------------------------------------------------------ import java.io.IOException; - import java.util.Collection; import java.util.Comparator; @@ -78,7 +77,7 @@ public class ManagerDecorator * {@inheritDoc} */ @Override - public void create(T object) throws E, IOException + public void create(T object) throws E { decorated.create(object); } @@ -87,7 +86,7 @@ public class ManagerDecorator * {@inheritDoc} */ @Override - public void delete(T object) throws E, IOException + public void delete(T object) throws E { decorated.delete(object); } @@ -105,7 +104,7 @@ public class ManagerDecorator * {@inheritDoc} */ @Override - public void modify(T object) throws E, IOException + public void modify(T object) throws E { decorated.modify(object); } @@ -114,7 +113,7 @@ public class ManagerDecorator * {@inheritDoc} */ @Override - public void refresh(T object) throws E, IOException + public void refresh(T object) throws E { decorated.refresh(object); } diff --git a/scm-core/src/main/java/sonia/scm/group/GroupAlreadyExistsException.java b/scm-core/src/main/java/sonia/scm/group/GroupAlreadyExistsException.java index 8389900098..e63ee1d78b 100644 --- a/scm-core/src/main/java/sonia/scm/group/GroupAlreadyExistsException.java +++ b/scm-core/src/main/java/sonia/scm/group/GroupAlreadyExistsException.java @@ -48,9 +48,9 @@ public class GroupAlreadyExistsException extends GroupException /** * Constructs a new instance. * - * @param message exception message + * @param name The name (aka id) of the group */ - public GroupAlreadyExistsException(String message) { - super(message); + public GroupAlreadyExistsException(String name) { + super(name + " group already exists"); } } diff --git a/scm-core/src/main/java/sonia/scm/group/GroupNotFoundException.java b/scm-core/src/main/java/sonia/scm/group/GroupNotFoundException.java index f4b9934128..d4c06a1559 100644 --- a/scm-core/src/main/java/sonia/scm/group/GroupNotFoundException.java +++ b/scm-core/src/main/java/sonia/scm/group/GroupNotFoundException.java @@ -52,39 +52,7 @@ public class GroupNotFoundException extends GroupException * Constructs a new GroupNotFoundException. * */ - public GroupNotFoundException() {} - - /** - * Constructs a new GroupNotFoundException. - * - * - * @param message message for the exception - */ - public GroupNotFoundException(String message) - { - super(message); - } - - /** - * Constructs a new GroupNotFoundException. - * - * - * @param throwable root cause - */ - public GroupNotFoundException(Throwable throwable) - { - super(throwable); - } - - /** - * Constructs a new GroupNotFoundException. - * - * - * @param message message for the exception - * @param throwable root cause - */ - public GroupNotFoundException(String message, Throwable throwable) - { - super(message, throwable); + public GroupNotFoundException() { + super("group does not exists"); } } diff --git a/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java b/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java index b61a4dce19..0e0e05f74b 100644 --- a/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java +++ b/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java @@ -81,7 +81,8 @@ public abstract class AbstractSimpleRepositoryHandler manager; + private final Class type; - /** - * Constructs ... - * - * - * @param manager - */ - public AbstractManagerResource(Manager manager) - { + protected int cacheMaxAge = 0; + protected boolean disableCache = false; + + public AbstractManagerResource(Manager manager, Class type) { this.manager = manager; + this.type = type; } //~--- methods -------------------------------------------------------------- @@ -526,45 +529,25 @@ public abstract class AbstractManagerResource createComparator(String sortby, boolean desc) + private Comparator createComparator(String sortBy, boolean desc) { + checkSortByField(sortBy); Comparator comparator; if (desc) { - comparator = new BeanReverseComparator(sortby); + comparator = new BeanReverseComparator(sortBy); } else { - comparator = new BeanComparator(sortby); + comparator = new BeanComparator(sortBy); } return comparator; } - /** - * Method description - * - * - * - * @param sortby - * @param desc - * @param start - * @param limit - * - * @return - */ - private Collection fetchItems(String sortby, boolean desc, int start, + private Collection fetchItems(String sortBy, boolean desc, int start, int limit) { AssertUtil.assertPositive(start); @@ -573,18 +556,18 @@ public abstract class AbstractManagerResource 0) { - if (Util.isEmpty(sortby)) + if (Util.isEmpty(sortBy)) { // replace with something useful - sortby = "id"; + sortBy = "id"; } - items = manager.getAll(createComparator(sortby, desc), start, limit); + items = manager.getAll(createComparator(sortBy, desc), start, limit); } - else if (Util.isNotEmpty(sortby)) + else if (Util.isNotEmpty(sortBy)) { - items = manager.getAll(createComparator(sortby, desc)); + items = manager.getAll(createComparator(sortBy, desc)); } else { @@ -594,17 +577,32 @@ public abstract class AbstractManagerResource fetchPage(String sortby, boolean desc, int pageNumber, + // We have to handle IntrospectionException here, because it's a checked exception + // It shouldn't occur really - so creating a new unchecked exception would be over-engineered here + @SuppressWarnings("squid:S00112") + private void checkSortByField(String sortBy) { + try { + BeanInfo info = Introspector.getBeanInfo(type); + PropertyDescriptor[] pds = info.getPropertyDescriptors(); + if (Arrays.stream(pds).noneMatch(p -> p.getName().equals(sortBy))) { + throw new IllegalArgumentException("sortBy"); + } + } catch (IntrospectionException e) { + throw new RuntimeException("error introspecting model type " + type.getName(), e); + } + } + + protected PageResult fetchPage(String sortBy, boolean desc, int pageNumber, int pageSize) { AssertUtil.assertPositive(pageNumber); AssertUtil.assertPositive(pageSize); - if (Util.isEmpty(sortby)) { + if (Util.isEmpty(sortBy)) { // replace with something useful - sortby = "id"; + sortBy = "id"; } - return manager.getPage(createComparator(sortby, desc), pageNumber, pageSize); + return manager.getPage(createComparator(sortBy, desc), pageNumber, pageSize); } //~--- get methods ---------------------------------------------------------- @@ -676,16 +674,4 @@ public abstract class AbstractManagerResource manager; } diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/GroupResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/GroupResource.java index 364a1b200e..b9701f7e38 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/GroupResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/GroupResource.java @@ -41,18 +41,12 @@ 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 org.apache.shiro.SecurityUtils; - import sonia.scm.group.Group; import sonia.scm.group.GroupException; import sonia.scm.group.GroupManager; import sonia.scm.security.Role; -//~--- JDK imports ------------------------------------------------------------ - -import java.util.Collection; - import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; @@ -69,6 +63,9 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Request; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; +import java.util.Collection; + +//~--- JDK imports ------------------------------------------------------------ /** * RESTful Web Service Resource to manage groups and their members. @@ -97,7 +94,7 @@ public class GroupResource @Inject public GroupResource(GroupManager groupManager) { - super(groupManager); + super(groupManager, Group.class); } //~--- methods -------------------------------------------------------------- diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java index 0fe6f8df1d..f336bbb6e7 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java @@ -40,12 +40,13 @@ import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.io.Files; import com.google.inject.Inject; - +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 org.apache.shiro.SecurityUtils; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import sonia.scm.NotSupportedFeatuerException; import sonia.scm.Type; import sonia.scm.api.rest.RestActionUploadResult; @@ -65,26 +66,6 @@ import sonia.scm.repository.api.UnbundleCommandBuilder; import sonia.scm.security.Role; import sonia.scm.util.IOUtil; -import static com.google.common.base.Preconditions.*; - -//~--- JDK imports ------------------------------------------------------------ - -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 java.io.File; -import java.io.IOException; -import java.io.InputStream; - -import java.net.URI; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Set; - import javax.ws.rs.Consumes; import javax.ws.rs.DefaultValue; import javax.ws.rs.FormParam; @@ -100,10 +81,22 @@ import javax.ws.rs.core.GenericEntity; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; - import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlRootElement; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +//~--- JDK imports ------------------------------------------------------------ /** * Rest resource for importing repositories. @@ -564,10 +557,6 @@ public class RepositoryImportResource { handleGenericCreationFailure(ex, type, name); } - catch (IOException ex) - { - handleGenericCreationFailure(ex, type, name); - } return repository; } @@ -716,10 +705,6 @@ public class RepositoryImportResource { manager.delete(repository); } - catch (IOException e) - { - logger.error("can not delete repository", e); - } catch (RepositoryException e) { logger.error("can not delete repository", e); diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java index 0637f3fc8f..bf974b2fa4 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java @@ -131,7 +131,7 @@ public class RepositoryResource extends AbstractManagerResource @Inject public UserResource(UserManager userManager, PasswordService passwordService) { - super(userManager); + super(userManager, User.class); this.passwordService = passwordService; } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BaseMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BaseMapper.java index f92d0d6e99..bae1a89308 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BaseMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BaseMapper.java @@ -3,25 +3,15 @@ package sonia.scm.api.v2.resources; import de.otto.edison.hal.HalRepresentation; import org.mapstruct.Mapping; import sonia.scm.ModelObject; -import sonia.scm.util.AssertUtil; import java.time.Instant; -import java.util.Optional; abstract class BaseMapper { - @Mapping(target = "attributes", ignore = true) // We do not map HAL attributes public abstract D map(T user); Instant mapTime(Long epochMilli) { - AssertUtil.assertIsNotNull(epochMilli); - return Instant.ofEpochMilli(epochMilli); - } - - Optional mapOptionalTime(Long epochMilli) { - return Optional - .ofNullable(epochMilli) - .map(Instant::ofEpochMilli); + return epochMilli == null? null: Instant.ofEpochMilli(epochMilli); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupCollectionResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupCollectionResource.java index 2d777c7487..912d890fe8 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupCollectionResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupCollectionResource.java @@ -1,13 +1,23 @@ package sonia.scm.api.v2.resources; -import com.webcohesion.enunciate.metadata.rs.*; +import com.webcohesion.enunciate.metadata.rs.ResponseCode; +import com.webcohesion.enunciate.metadata.rs.ResponseHeader; +import com.webcohesion.enunciate.metadata.rs.ResponseHeaders; +import com.webcohesion.enunciate.metadata.rs.StatusCodes; +import com.webcohesion.enunciate.metadata.rs.TypeHint; import sonia.scm.group.Group; import sonia.scm.group.GroupException; import sonia.scm.group.GroupManager; import sonia.scm.web.VndMediaType; import javax.inject.Inject; -import javax.ws.rs.*; +import javax.ws.rs.Consumes; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; import java.io.IOException; @@ -26,16 +36,17 @@ public class GroupCollectionResource { this.dtoToGroupMapper = dtoToGroupMapper; this.groupCollectionToDtoMapper = groupCollectionToDtoMapper; this.resourceLinks = resourceLinks; - this.adapter = new ResourceManagerAdapter<>(manager); + this.adapter = new ResourceManagerAdapter<>(manager, Group.class); } /** * Returns all groups for a given page number with a given page size (default page size is {@value DEFAULT_PAGE_SIZE}). - * + * * Note: This method requires "group" privilege. - * @param page the number of the requested page + * + * @param page the number of the requested page * @param pageSize the page size (default page size is {@value DEFAULT_PAGE_SIZE}) - * @param sortBy sort parameter + * @param sortBy sort parameter (if empty - undefined sorting) * @param desc sort direction desc or aesc */ @GET @@ -44,6 +55,7 @@ public class GroupCollectionResource { @TypeHint(GroupDto[].class) @StatusCodes({ @ResponseCode(code = 200, condition = "success"), + @ResponseCode(code = 400, condition = "\"sortBy\" field unknown"), @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), @ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"group\" privilege"), @ResponseCode(code = 500, condition = "internal server error") diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupDto.java index 081ae817ae..3944be81b8 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupDto.java @@ -10,15 +10,14 @@ import lombok.Setter; import java.time.Instant; import java.util.List; import java.util.Map; -import java.util.Optional; @Getter @Setter @NoArgsConstructor public class GroupDto extends HalRepresentation { private Instant creationDate; private String description; - @JsonInclude(JsonInclude.Include.NON_EMPTY) - private Optional lastModified; + @JsonInclude(JsonInclude.Include.NON_NULL) + private Instant lastModified; private String name; private String type; private Map properties; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupResource.java index 3c1f2aeb64..62bdaad38f 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupResource.java @@ -29,7 +29,7 @@ public class GroupResource { GroupDtoToGroupMapper groupDtoToGroupMapper) { this.groupToGroupDtoMapper = groupToGroupDtoMapper; this.dtoToGroupMapper = groupDtoToGroupMapper; - this.adapter = new ResourceManagerAdapter<>(manager); + this.adapter = new ResourceManagerAdapter<>(manager, Group.class); } /** diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ResourceManagerAdapter.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ResourceManagerAdapter.java index c2aab1a058..bc35942900 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ResourceManagerAdapter.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ResourceManagerAdapter.java @@ -30,8 +30,8 @@ class ResourceManagerAdapter extends AbstractManagerResource { - ResourceManagerAdapter(Manager manager) { - super(manager); + ResourceManagerAdapter(Manager manager, Class type) { + super(manager, type); } /** 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 4b88ea48bf..c269cd9f90 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 @@ -1,13 +1,23 @@ package sonia.scm.api.v2.resources; -import com.webcohesion.enunciate.metadata.rs.*; +import com.webcohesion.enunciate.metadata.rs.ResponseCode; +import com.webcohesion.enunciate.metadata.rs.ResponseHeader; +import com.webcohesion.enunciate.metadata.rs.ResponseHeaders; +import com.webcohesion.enunciate.metadata.rs.StatusCodes; +import com.webcohesion.enunciate.metadata.rs.TypeHint; import sonia.scm.user.User; import sonia.scm.user.UserException; import sonia.scm.user.UserManager; import sonia.scm.web.VndMediaType; import javax.inject.Inject; -import javax.ws.rs.*; +import javax.ws.rs.Consumes; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; import java.io.IOException; @@ -25,7 +35,7 @@ public class UserCollectionResource { UserCollectionToDtoMapper userCollectionToDtoMapper, ResourceLinks resourceLinks) { this.dtoToUserMapper = dtoToUserMapper; this.userCollectionToDtoMapper = userCollectionToDtoMapper; - this.adapter = new ResourceManagerAdapter<>(manager); + this.adapter = new ResourceManagerAdapter<>(manager, User.class); this.resourceLinks = resourceLinks; } @@ -33,9 +43,10 @@ public class UserCollectionResource { * Returns all users for a given page number with a given page size (default page size is {@value DEFAULT_PAGE_SIZE}). * * Note: This method requires "user" privilege. - * @param page the number of the requested page + * + * @param page the number of the requested page * @param pageSize the page size (default page size is {@value DEFAULT_PAGE_SIZE}) - * @param sortBy sort parameter + * @param sortBy sort parameter (if empty - undefined sorting) * @param desc sort direction desc or asc */ @GET @@ -44,6 +55,7 @@ public class UserCollectionResource { @TypeHint(UserDto[].class) @StatusCodes({ @ResponseCode(code = 200, condition = "success"), + @ResponseCode(code = 400, condition = "\"sortBy\" field unknown"), @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), @ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"user\" privilege"), @ResponseCode(code = 500, condition = "internal server error") diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserDto.java index d87f4a74f1..cf7bf90504 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserDto.java @@ -9,7 +9,6 @@ import lombok.Setter; import java.time.Instant; import java.util.Map; -import java.util.Optional; @NoArgsConstructor @Getter @Setter public class UserDto extends HalRepresentation { @@ -17,8 +16,8 @@ public class UserDto extends HalRepresentation { private boolean admin; private Instant creationDate; private String displayName; - @JsonInclude(JsonInclude.Include.NON_EMPTY) - private Optional lastModified; + @JsonInclude(JsonInclude.Include.NON_NULL) + private Instant lastModified; private String mail; private String name; private String password; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserResource.java index bf747bab4d..7805ba3440 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserResource.java @@ -29,7 +29,7 @@ public class UserResource { public UserResource(UserDtoToUserMapper dtoToUserMapper, UserToUserDtoMapper userToDtoMapper, UserManager manager) { this.dtoToUserMapper = dtoToUserMapper; this.userToDtoMapper = userToDtoMapper; - this.adapter = new ResourceManagerAdapter<>(manager); + this.adapter = new ResourceManagerAdapter<>(manager, User.class); } /** diff --git a/scm-webapp/src/main/java/sonia/scm/group/DefaultGroupManager.java b/scm-webapp/src/main/java/sonia/scm/group/DefaultGroupManager.java index b4e9df2430..0646b109d8 100644 --- a/scm-webapp/src/main/java/sonia/scm/group/DefaultGroupManager.java +++ b/scm-webapp/src/main/java/sonia/scm/group/DefaultGroupManager.java @@ -51,7 +51,12 @@ import sonia.scm.util.CollectionAppender; import sonia.scm.util.Util; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedList; +import java.util.List; //~--- JDK imports ------------------------------------------------------------ @@ -106,7 +111,7 @@ public class DefaultGroupManager extends AbstractGroupManager * @throws IOException */ @Override - public void create(Group group) throws GroupException, IOException + public void create(Group group) throws GroupException { String type = group.getType(); @@ -127,7 +132,7 @@ public class DefaultGroupManager extends AbstractGroupManager if (groupDAO.contains(name)) { - throw new GroupAlreadyExistsException(name.concat(" group already exists")); + throw new GroupAlreadyExistsException(name); } removeDuplicateMembers(group); @@ -147,7 +152,7 @@ public class DefaultGroupManager extends AbstractGroupManager * @throws IOException */ @Override - public void delete(Group group) throws GroupException, IOException + public void delete(Group group) throws GroupException { if (logger.isInfoEnabled()) { @@ -166,7 +171,7 @@ public class DefaultGroupManager extends AbstractGroupManager } else { - throw new GroupNotFoundException("user does not exists"); + throw new GroupNotFoundException(); } } @@ -189,7 +194,7 @@ public class DefaultGroupManager extends AbstractGroupManager * @throws IOException */ @Override - public void modify(Group group) throws GroupException, IOException + public void modify(Group group) throws GroupException { if (logger.isInfoEnabled()) { @@ -212,7 +217,7 @@ public class DefaultGroupManager extends AbstractGroupManager } else { - throw new GroupNotFoundException("group does not exists"); + throw new GroupNotFoundException(); } } @@ -226,7 +231,7 @@ public class DefaultGroupManager extends AbstractGroupManager * @throws IOException */ @Override - public void refresh(Group group) throws GroupException, IOException + public void refresh(Group group) throws GroupException { String name = group.getName(); if (logger.isInfoEnabled()) @@ -239,7 +244,7 @@ public class DefaultGroupManager extends AbstractGroupManager if (fresh == null) { - throw new GroupNotFoundException("group does not exists"); + throw new GroupNotFoundException(); } fresh.copyProperties(group); diff --git a/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java b/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java index 363cc4c0bb..1fee9e2460 100644 --- a/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java +++ b/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java @@ -114,7 +114,7 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager { } public void create(Repository repository, boolean initRepository) - throws RepositoryException, IOException { + throws RepositoryException { logger.info("create repository {} of type {}", repository.getName(), repository.getType()); @@ -140,13 +140,13 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager { @Override public void create(Repository repository) - throws RepositoryException, IOException { + throws RepositoryException { create(repository, true); } @Override public void delete(Repository repository) - throws RepositoryException, IOException { + throws RepositoryException { if (logger.isInfoEnabled()) { logger.info("delete repository {} of type {}", repository.getName(), repository.getType()); @@ -182,7 +182,7 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager { @Override public void modify(Repository repository) - throws RepositoryException, IOException { + throws RepositoryException { if (logger.isInfoEnabled()) { logger.info("modify repository {} of type {}", repository.getName(), repository.getType()); @@ -208,7 +208,7 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager { @Override public void refresh(Repository repository) - throws RepositoryException, IOException { + throws RepositoryException { AssertUtil.assertIsNotNull(repository); RepositoryPermissions.read(repository).check(); diff --git a/scm-webapp/src/main/java/sonia/scm/repository/LastModifiedUpdateListener.java b/scm-webapp/src/main/java/sonia/scm/repository/LastModifiedUpdateListener.java index f8afe61200..bf868b2027 100644 --- a/scm-webapp/src/main/java/sonia/scm/repository/LastModifiedUpdateListener.java +++ b/scm-webapp/src/main/java/sonia/scm/repository/LastModifiedUpdateListener.java @@ -35,10 +35,8 @@ package sonia.scm.repository; import com.google.common.eventbus.Subscribe; import com.google.inject.Inject; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import sonia.scm.EagerSingleton; import sonia.scm.plugin.Extension; import sonia.scm.web.security.AdministrationContext; @@ -46,8 +44,6 @@ import sonia.scm.web.security.PrivilegedAction; //~--- JDK imports ------------------------------------------------------------ -import java.io.IOException; - /** * * @author Sebastian Sdorra @@ -154,7 +150,7 @@ public final class LastModifiedUpdateListener { repositoryManager.modify(dbr); } - catch (RepositoryException | IOException ex) + catch (RepositoryException ex) { logger.error("could not modify repository", ex); } diff --git a/scm-webapp/src/main/java/sonia/scm/user/DefaultUserManager.java b/scm-webapp/src/main/java/sonia/scm/user/DefaultUserManager.java index 72ead40c58..7d6a45b790 100644 --- a/scm-webapp/src/main/java/sonia/scm/user/DefaultUserManager.java +++ b/scm-webapp/src/main/java/sonia/scm/user/DefaultUserManager.java @@ -137,7 +137,7 @@ public class DefaultUserManager extends AbstractUserManager * @throws UserException */ @Override - public void create(User user) throws UserException, IOException + public void create(User user) throws UserException { String type = user.getType(); @@ -155,7 +155,7 @@ public class DefaultUserManager extends AbstractUserManager if (userDAO.contains(user.getName())) { - throw new UserAlreadyExistsException(user.getName().concat(" user already exists")); + throw new UserAlreadyExistsException(user.getName()); } AssertUtil.assertIsValid(user); @@ -175,7 +175,7 @@ public class DefaultUserManager extends AbstractUserManager * @throws UserException */ @Override - public void delete(User user) throws UserException, IOException + public void delete(User user) throws UserException { if (logger.isInfoEnabled()) { @@ -193,7 +193,7 @@ public class DefaultUserManager extends AbstractUserManager } else { - throw new UserNotFoundException("user does not exists"); + throw new UserNotFoundException(); } } @@ -224,7 +224,7 @@ public class DefaultUserManager extends AbstractUserManager * @throws UserException */ @Override - public void modify(User user) throws UserException, IOException + public void modify(User user) throws UserException { String name = user.getName(); if (logger.isInfoEnabled()) @@ -245,7 +245,7 @@ public class DefaultUserManager extends AbstractUserManager } else { - throw new UserNotFoundException("user does not exists"); + throw new UserNotFoundException(); } } @@ -259,7 +259,7 @@ public class DefaultUserManager extends AbstractUserManager * @throws UserException */ @Override - public void refresh(User user) throws UserException, IOException + public void refresh(User user) throws UserException { if (logger.isInfoEnabled()) { @@ -271,7 +271,7 @@ public class DefaultUserManager extends AbstractUserManager if (fresh == null) { - throw new UserNotFoundException("user does not exists"); + throw new UserNotFoundException(); } fresh.copyProperties(user); diff --git a/scm-webapp/src/test/java/sonia/scm/api/rest/resources/AbstractManagerResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/rest/resources/AbstractManagerResourceTest.java new file mode 100644 index 0000000000..e8421bc417 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/api/rest/resources/AbstractManagerResourceTest.java @@ -0,0 +1,122 @@ +package sonia.scm.api.rest.resources; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import sonia.scm.Manager; +import sonia.scm.ModelObject; + +import javax.ws.rs.core.GenericEntity; +import javax.ws.rs.core.Request; +import java.util.Collection; +import java.util.Comparator; + +import static java.util.Collections.emptyList; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class AbstractManagerResourceTest { + + @Mock + private Manager manager; + @Mock + private Request request; + @Captor + private ArgumentCaptor> comparatorCaptor; + + private AbstractManagerResource abstractManagerResource; + + @Before + public void captureComparator() { + when(manager.getAll(comparatorCaptor.capture(), eq(0), eq(1))).thenReturn(emptyList()); + abstractManagerResource = new SimpleManagerResource(); + } + + @Test + public void shouldAcceptDefaultSortByParameter() { + abstractManagerResource.getAll(request, 0, 1, null, true); + + Comparator comparator = comparatorCaptor.getValue(); + assertTrue(comparator.compare(new Simple("1", null), new Simple("2", null)) > 0); + } + + @Test + public void shouldAcceptValidSortByParameter() { + abstractManagerResource.getAll(request, 0, 1, "data", true); + + Comparator comparator = comparatorCaptor.getValue(); + assertTrue(comparator.compare(new Simple("", "1"), new Simple("", "2")) > 0); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldFailForIllegalSortByParameter() { + abstractManagerResource.getAll(request, 0, 1, "x", true); + } + + + private class SimpleManagerResource extends AbstractManagerResource { + + { + disableCache = true; + } + + private SimpleManagerResource() { + super(AbstractManagerResourceTest.this.manager, Simple.class); + } + + @Override + protected GenericEntity> createGenericEntity(Collection items) { + return null; + } + + @Override + protected String getId(Simple item) { + return null; + } + + @Override + protected String getPathPart() { + return null; + } + } + + public static class Simple implements ModelObject { + + private String id; + private String data; + + Simple(String id, String data) { + this.id = id; + this.data = data; + } + + public String getData() { + return data; + } + + @Override + public String getId() { + return id; + } + + @Override + public Long getLastModified() { + return null; + } + + @Override + public String getType() { + return null; + } + @Override + public boolean isValid() { + return false; + } + } +} diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserDtoToUserMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserDtoToUserMapperTest.java index e76cee1ef7..f97fd91c3b 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserDtoToUserMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserDtoToUserMapperTest.java @@ -8,7 +8,6 @@ import org.mockito.Mock; import sonia.scm.user.User; import java.time.Instant; -import java.util.Optional; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.when; @@ -47,7 +46,7 @@ public class UserDtoToUserMapperTest { UserDto dto = new UserDto(); dto.setName("abc"); dto.setCreationDate(Instant.now()); - dto.setLastModified(Optional.empty()); + dto.setLastModified(null); return dto; } } diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserToUserDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserToUserDtoMapperTest.java index 684ccdb5f6..ae3168cc07 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserToUserDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserToUserDtoMapperTest.java @@ -122,6 +122,6 @@ public class UserToUserDtoMapperTest { UserDto userDto = mapper.map(user); assertEquals(expectedCreationDate, userDto.getCreationDate()); - assertEquals(expectedModificationDate, userDto.getLastModified().get()); + assertEquals(expectedModificationDate, userDto.getLastModified()); } }