Cleanup and documentation

This commit is contained in:
René Pfeuffer
2018-06-06 16:24:59 +02:00
parent ff8c6ea014
commit a8c61a9bfd
20 changed files with 244 additions and 64 deletions

View File

@@ -3,6 +3,10 @@ package sonia.scm;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
/**
* This represents the result of a page request. Contains the results for
* the page and a flag whether there are more pages or not.
*/
public class PageResult<T extends ModelObject> { public class PageResult<T extends ModelObject> {
private final Collection<T> entities; private final Collection<T> entities;
@@ -13,10 +17,16 @@ public class PageResult<T extends ModelObject> {
this.hasMore = hasMore; this.hasMore = hasMore;
} }
/**
* The entities for the current page.
*/
public Collection<T> getEntities() { public Collection<T> getEntities() {
return Collections.unmodifiableCollection(entities); return Collections.unmodifiableCollection(entities);
} }
/**
* If this is <code>true</code>, there are more pages (that is, mor entities).
*/
public boolean hasMore() { public boolean hasMore() {
return hasMore; return hasMore;
} }

View File

@@ -2,6 +2,11 @@ package sonia.scm.web;
import sonia.scm.plugin.ExtensionPoint; import sonia.scm.plugin.ExtensionPoint;
/**
* Implementing this extension point you can post process json response objects.
* To do so, you get a {@link JsonEnricherContext} with the complete json tree,
* that can be modified.
*/
@ExtensionPoint @ExtensionPoint
public interface JsonEnricher { public interface JsonEnricher {

View File

@@ -5,6 +5,10 @@ import com.fasterxml.jackson.databind.JsonNode;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import java.net.URI; import java.net.URI;
/**
* Process data for the {@link JsonEnricher} extension point giving context for
* post processing json results.
*/
public class JsonEnricherContext { public class JsonEnricherContext {
private URI requestUri; private URI requestUri;
@@ -17,14 +21,24 @@ public class JsonEnricherContext {
this.responseEntity = responseEntity; this.responseEntity = responseEntity;
} }
/**
* The URI of the originating request.
*/
public URI getRequestUri() { public URI getRequestUri() {
return requestUri; return requestUri;
} }
/**
* The media type of the response. Using this you can determine the content of the result.
* @see VndMediaType
*/
public MediaType getResponseMediaType() { public MediaType getResponseMediaType() {
return responseMediaType; return responseMediaType;
} }
/**
* The json result represented by nodes, that can be modified.
*/
public JsonNode getResponseEntity() { public JsonNode getResponseEntity() {
return responseEntity; return responseEntity;
} }

View File

@@ -2,6 +2,9 @@ package sonia.scm.web;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
/**
* Vendor media types used by SCMM.
*/
public class VndMediaType { public class VndMediaType {
private static final String VERSION = "2"; private static final String VERSION = "2";
private static final String TYPE = "application"; private static final String TYPE = "application";
@@ -10,18 +13,14 @@ public class VndMediaType {
private static final String SUFFIX = "+json;v=" + VERSION; private static final String SUFFIX = "+json;v=" + VERSION;
public static final String USER = PREFIX + "user" + SUFFIX; public static final String USER = PREFIX + "user" + SUFFIX;
public static final String USER_COLLECTION = PREFIX + "userCollection" + SUFFIX;
private VndMediaType() { private VndMediaType() {
} }
public static MediaType jsonType(String resource) { /**
return MediaType.valueOf(json(resource)); * Checks whether the given media type is a media type used by SCMM.
} */
public static String json(String resource) {
return PREFIX + resource + SUFFIX;// ".v2+json";
}
public static boolean isVndType(MediaType type) { public static boolean isVndType(MediaType type) {
return type.getType().equals(TYPE) && type.getSubtype().startsWith(SUBTYPE_PREFIX); return type.getType().equals(TYPE) && type.getSubtype().startsWith(SUBTYPE_PREFIX);
} }

View File

@@ -23,7 +23,7 @@
<groupId>sonia.scm</groupId> <groupId>sonia.scm</groupId>
<artifactId>scm-annotation-processor</artifactId> <artifactId>scm-annotation-processor</artifactId>
<version>2.0.0-SNAPSHOT</version> <version>2.0.0-SNAPSHOT</version>
<optional>true</optional> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@@ -39,10 +39,14 @@ import com.google.common.base.Throwables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.inject.Injector; import com.google.inject.Injector;
import com.google.inject.Module; import com.google.inject.Module;
import org.apache.shiro.guice.web.ShiroWebModule; import org.apache.shiro.guice.web.ShiroWebModule;
import org.jboss.resteasy.plugins.guice.GuiceResteasyBootstrapServletContextListener;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.jboss.resteasy.plugins.guice.GuiceResteasyBootstrapServletContextListener;
import sonia.scm.api.v2.resources.MapperModule; import sonia.scm.api.v2.resources.MapperModule;
import sonia.scm.cache.CacheManager; import sonia.scm.cache.CacheManager;
import sonia.scm.debug.DebugModule; import sonia.scm.debug.DebugModule;
@@ -57,13 +61,14 @@ import sonia.scm.upgrade.UpgradeManager;
import sonia.scm.user.UserManager; import sonia.scm.user.UserManager;
import sonia.scm.util.IOUtil; import sonia.scm.util.IOUtil;
import javax.servlet.ServletContext; //~--- JDK imports ------------------------------------------------------------
import javax.servlet.ServletContextEvent;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.Collections;
//~--- JDK imports ------------------------------------------------------------ import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
/** /**
* *

View File

@@ -42,8 +42,10 @@ import com.google.inject.name.Names;
import com.google.inject.servlet.RequestScoped; import com.google.inject.servlet.RequestScoped;
import com.google.inject.servlet.ServletModule; import com.google.inject.servlet.ServletModule;
import com.google.inject.throwingproviders.ThrowingProviderBinder; import com.google.inject.throwingproviders.ThrowingProviderBinder;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import sonia.scm.api.rest.ObjectMapperProvider; import sonia.scm.api.rest.ObjectMapperProvider;
import sonia.scm.cache.CacheManager; import sonia.scm.cache.CacheManager;
import sonia.scm.cache.GuavaCacheManager; import sonia.scm.cache.GuavaCacheManager;
@@ -56,13 +58,18 @@ import sonia.scm.group.GroupManagerProvider;
import sonia.scm.group.xml.XmlGroupDAO; import sonia.scm.group.xml.XmlGroupDAO;
import sonia.scm.io.DefaultFileSystem; import sonia.scm.io.DefaultFileSystem;
import sonia.scm.io.FileSystem; import sonia.scm.io.FileSystem;
import sonia.scm.net.SSLContextProvider;
import sonia.scm.net.ahc.*;
import sonia.scm.plugin.DefaultPluginLoader; import sonia.scm.plugin.DefaultPluginLoader;
import sonia.scm.plugin.DefaultPluginManager; import sonia.scm.plugin.DefaultPluginManager;
import sonia.scm.plugin.PluginLoader; import sonia.scm.plugin.PluginLoader;
import sonia.scm.plugin.PluginManager; import sonia.scm.plugin.PluginManager;
import sonia.scm.repository.*; import sonia.scm.repository.DefaultRepositoryManager;
import sonia.scm.repository.DefaultRepositoryProvider;
import sonia.scm.repository.HealthCheckContextListener;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryDAO;
import sonia.scm.repository.RepositoryManager;
import sonia.scm.repository.RepositoryManagerProvider;
import sonia.scm.repository.RepositoryProvider;
import sonia.scm.repository.api.HookContextFactory; import sonia.scm.repository.api.HookContextFactory;
import sonia.scm.repository.api.RepositoryServiceFactory; import sonia.scm.repository.api.RepositoryServiceFactory;
import sonia.scm.repository.spi.HookEventFacade; import sonia.scm.repository.spi.HookEventFacade;
@@ -71,15 +78,28 @@ import sonia.scm.resources.DefaultResourceManager;
import sonia.scm.resources.DevelopmentResourceManager; import sonia.scm.resources.DevelopmentResourceManager;
import sonia.scm.resources.ResourceManager; import sonia.scm.resources.ResourceManager;
import sonia.scm.resources.ScriptResourceServlet; import sonia.scm.resources.ScriptResourceServlet;
import sonia.scm.schedule.QuartzScheduler; import sonia.scm.security.CipherHandler;
import sonia.scm.schedule.Scheduler; import sonia.scm.security.CipherUtil;
import sonia.scm.security.*; import sonia.scm.security.DefaultKeyGenerator;
import sonia.scm.store.*; import sonia.scm.security.DefaultSecuritySystem;
import sonia.scm.security.KeyGenerator;
import sonia.scm.security.SecuritySystem;
import sonia.scm.store.BlobStoreFactory;
import sonia.scm.store.ConfigurationEntryStoreFactory;
import sonia.scm.store.DataStoreFactory;
import sonia.scm.store.FileBlobStoreFactory;
import sonia.scm.store.JAXBConfigurationEntryStoreFactory;
import sonia.scm.store.JAXBDataStoreFactory;
import sonia.scm.store.JAXBConfigurationStoreFactory;
import sonia.scm.template.MustacheTemplateEngine; import sonia.scm.template.MustacheTemplateEngine;
import sonia.scm.template.TemplateEngine; import sonia.scm.template.TemplateEngine;
import sonia.scm.template.TemplateEngineFactory; import sonia.scm.template.TemplateEngineFactory;
import sonia.scm.template.TemplateServlet; import sonia.scm.template.TemplateServlet;
import sonia.scm.url.*; import sonia.scm.url.RestJsonUrlProvider;
import sonia.scm.url.RestXmlUrlProvider;
import sonia.scm.url.UrlProvider;
import sonia.scm.url.UrlProviderFactory;
import sonia.scm.url.WebUIUrlProvider;
import sonia.scm.user.DefaultUserManager; import sonia.scm.user.DefaultUserManager;
import sonia.scm.user.UserDAO; import sonia.scm.user.UserDAO;
import sonia.scm.user.UserManager; import sonia.scm.user.UserManager;
@@ -87,18 +107,32 @@ import sonia.scm.user.UserManagerProvider;
import sonia.scm.user.xml.XmlUserDAO; import sonia.scm.user.xml.XmlUserDAO;
import sonia.scm.util.DebugServlet; import sonia.scm.util.DebugServlet;
import sonia.scm.util.ScmConfigurationUtil; import sonia.scm.util.ScmConfigurationUtil;
import sonia.scm.web.UserAgentParser;
import sonia.scm.web.cgi.CGIExecutorFactory; import sonia.scm.web.cgi.CGIExecutorFactory;
import sonia.scm.web.cgi.DefaultCGIExecutorFactory; import sonia.scm.web.cgi.DefaultCGIExecutorFactory;
import sonia.scm.web.filter.LoggingFilter; import sonia.scm.web.filter.LoggingFilter;
import sonia.scm.web.security.AdministrationContext; import sonia.scm.web.security.AdministrationContext;
import sonia.scm.web.security.DefaultAdministrationContext; import sonia.scm.web.security.DefaultAdministrationContext;
import javax.net.ssl.SSLContext;
import javax.servlet.ServletContext;
//~--- JDK imports ------------------------------------------------------------ //~--- JDK imports ------------------------------------------------------------
import javax.servlet.ServletContext;
import sonia.scm.store.ConfigurationStoreFactory;
import javax.net.ssl.SSLContext;
import sonia.scm.net.SSLContextProvider;
import sonia.scm.net.ahc.AdvancedHttpClient;
import sonia.scm.net.ahc.ContentTransformer;
import sonia.scm.net.ahc.DefaultAdvancedHttpClient;
import sonia.scm.net.ahc.JsonContentTransformer;
import sonia.scm.net.ahc.XmlContentTransformer;
import sonia.scm.schedule.QuartzScheduler;
import sonia.scm.schedule.Scheduler;
import sonia.scm.security.ConfigurableLoginAttemptHandler;
import sonia.scm.security.LoginAttemptHandler;
import sonia.scm.security.AuthorizationChangedEventProducer;
import sonia.scm.web.UserAgentParser;
/** /**
* *
* @author Sebastian Sdorra * @author Sebastian Sdorra

View File

@@ -37,8 +37,10 @@ package sonia.scm.api.rest.resources;
import org.apache.commons.beanutils.BeanComparator; import org.apache.commons.beanutils.BeanComparator;
import org.apache.shiro.authz.AuthorizationException; import org.apache.shiro.authz.AuthorizationException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import sonia.scm.LastModifiedAware; import sonia.scm.LastModifiedAware;
import sonia.scm.Manager; import sonia.scm.Manager;
import sonia.scm.ModelObject; import sonia.scm.ModelObject;
@@ -48,13 +50,19 @@ import sonia.scm.util.AssertUtil;
import sonia.scm.util.HttpUtil; import sonia.scm.util.HttpUtil;
import sonia.scm.util.Util; import sonia.scm.util.Util;
import javax.ws.rs.core.*; //~--- JDK imports ------------------------------------------------------------
import javax.ws.rs.core.Response.Status;
import java.util.Collection; import java.util.Collection;
import java.util.Comparator; import java.util.Comparator;
import java.util.Date; import java.util.Date;
//~--- JDK imports ------------------------------------------------------------ import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.EntityTag;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;
/** /**
* *
@@ -559,7 +567,7 @@ public abstract class AbstractManagerResource<T extends ModelObject,
* *
* @return * @return
*/ */
protected Collection<T> fetchItems(String sortby, boolean desc, int start, private Collection<T> fetchItems(String sortby, boolean desc, int start,
int limit) int limit)
{ {
AssertUtil.assertPositive(start); AssertUtil.assertPositive(start);

View File

@@ -41,17 +41,34 @@ import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.ResponseHeader; import com.webcohesion.enunciate.metadata.rs.ResponseHeader;
import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import com.webcohesion.enunciate.metadata.rs.TypeHint; import com.webcohesion.enunciate.metadata.rs.TypeHint;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.SecurityUtils;
import sonia.scm.group.Group; import sonia.scm.group.Group;
import sonia.scm.group.GroupException; import sonia.scm.group.GroupException;
import sonia.scm.group.GroupManager; import sonia.scm.group.GroupManager;
import sonia.scm.security.Role; import sonia.scm.security.Role;
import javax.ws.rs.*; //~--- JDK imports ------------------------------------------------------------
import javax.ws.rs.core.*;
import java.util.Collection; import java.util.Collection;
//~--- JDK imports ------------------------------------------------------------ import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
/** /**
* RESTful Web Service Resource to manage groups and their members. * RESTful Web Service Resource to manage groups and their members.

View File

@@ -42,26 +42,66 @@ import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.ResponseHeader; import com.webcohesion.enunciate.metadata.rs.ResponseHeader;
import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import com.webcohesion.enunciate.metadata.rs.TypeHint; import com.webcohesion.enunciate.metadata.rs.TypeHint;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.AuthorizationException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import sonia.scm.config.ScmConfiguration; import sonia.scm.config.ScmConfiguration;
import sonia.scm.repository.*; import sonia.scm.repository.BlameResult;
import sonia.scm.repository.api.*; import sonia.scm.repository.Branches;
import sonia.scm.repository.BrowserResult;
import sonia.scm.repository.Changeset;
import sonia.scm.repository.ChangesetPagingResult;
import sonia.scm.repository.HealthChecker;
import sonia.scm.repository.Permission;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryException;
import sonia.scm.repository.RepositoryIsNotArchivedException;
import sonia.scm.repository.RepositoryManager;
import sonia.scm.repository.RepositoryNotFoundException;
import sonia.scm.repository.Tags;
import sonia.scm.repository.api.BlameCommandBuilder;
import sonia.scm.repository.api.BrowseCommandBuilder;
import sonia.scm.repository.api.CatCommandBuilder;
import sonia.scm.repository.api.CommandNotSupportedException;
import sonia.scm.repository.api.DiffCommandBuilder;
import sonia.scm.repository.api.DiffFormat;
import sonia.scm.repository.api.LogCommandBuilder;
import sonia.scm.repository.api.RepositoryService;
import sonia.scm.repository.api.RepositoryServiceFactory;
import sonia.scm.util.AssertUtil; import sonia.scm.util.AssertUtil;
import sonia.scm.util.HttpUtil; import sonia.scm.util.HttpUtil;
import sonia.scm.util.IOUtil; import sonia.scm.util.IOUtil;
import sonia.scm.util.Util; import sonia.scm.util.Util;
import javax.ws.rs.*; //~--- JDK imports ------------------------------------------------------------
import javax.ws.rs.core.*;
import javax.ws.rs.core.Response.Status;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
//~--- JDK imports ------------------------------------------------------------ import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.StreamingOutput;
import javax.ws.rs.core.UriInfo;
import org.apache.shiro.authz.AuthorizationException;
/** /**
* Repository related RESTful Web Service Endpoint. * Repository related RESTful Web Service Endpoint.

View File

@@ -41,8 +41,10 @@ import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.ResponseHeader; import com.webcohesion.enunciate.metadata.rs.ResponseHeader;
import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import com.webcohesion.enunciate.metadata.rs.TypeHint; import com.webcohesion.enunciate.metadata.rs.TypeHint;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.credential.PasswordService; import org.apache.shiro.authc.credential.PasswordService;
import sonia.scm.security.Role; import sonia.scm.security.Role;
import sonia.scm.user.User; import sonia.scm.user.User;
import sonia.scm.user.UserException; import sonia.scm.user.UserException;
@@ -50,11 +52,26 @@ import sonia.scm.user.UserManager;
import sonia.scm.util.AssertUtil; import sonia.scm.util.AssertUtil;
import sonia.scm.util.Util; import sonia.scm.util.Util;
import javax.ws.rs.*; //~--- JDK imports ------------------------------------------------------------
import javax.ws.rs.core.*;
import java.util.Collection; import java.util.Collection;
//~--- JDK imports ------------------------------------------------------------ import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
/** /**
* RESTful Web Service Resource to manage users. * RESTful Web Service Resource to manage users.

View File

@@ -13,6 +13,13 @@ import javax.ws.rs.ext.Provider;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
/**
* <p>Post processor for rest requests filtering json responses when a {@value PARAMETER_FIELDS} query
* parameter is provided. In this case, only the given fields will returned. It is possible, to specify
* paths for nested fields. Multiple fields have to be devided using {@value FIELD_SEPARATOR}.</p>
* <p>This requires the {@link JsonMarshallingResponseFilter} to be processed first to create
* the {@link JsonNode} tree.</p>
*/
@Provider @Provider
@Priority(Priorities.USER) @Priority(Priorities.USER)
public class FieldContainerResponseFilter implements ContainerResponseFilter { public class FieldContainerResponseFilter implements ContainerResponseFilter {

View File

@@ -15,6 +15,10 @@ import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider; import javax.ws.rs.ext.Provider;
import java.util.Set; import java.util.Set;
/**
* Transforms json rest responses to {@link JsonNode} trees to support further post processing
* and processes all registered plugins for the {@link JsonEnricher} extension point.
*/
@Provider @Provider
@Priority(Priorities.USER + 1000) @Priority(Priorities.USER + 1000)
public class JsonMarshallingResponseFilter implements ContainerResponseFilter { public class JsonMarshallingResponseFilter implements ContainerResponseFilter {

View File

@@ -1,4 +0,0 @@
package sonia.scm.api.v2.resources;
public class ScmMediaType {
}

View File

@@ -5,6 +5,7 @@ import org.mapstruct.*;
import sonia.scm.api.rest.resources.UserResource; import sonia.scm.api.rest.resources.UserResource;
import sonia.scm.user.User; import sonia.scm.user.User;
import sonia.scm.user.UserPermissions; import sonia.scm.user.UserPermissions;
import sonia.scm.util.AssertUtil;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import java.time.Instant; import java.time.Instant;
@@ -43,7 +44,7 @@ public abstract class User2UserDtoMapper {
@Mapping(target = "creationDate") @Mapping(target = "creationDate")
Instant mapTime(Long epochMilli) { Instant mapTime(Long epochMilli) {
// TODO assert parameter not null AssertUtil.assertIsNotNull(epochMilli);
return Instant.ofEpochMilli(epochMilli); return Instant.ofEpochMilli(epochMilli);
} }

View File

@@ -29,7 +29,7 @@ public class UserCollection2DtoMapper {
this.userToDtoMapper = userToDtoMapper; this.userToDtoMapper = userToDtoMapper;
} }
public UserCollectionDto userCollectionToUserDto(UriInfo uriInfo, int pageNumber, int pageSize, PageResult<User> pageResult) { public UserCollectionDto userCollectionToDto(UriInfo uriInfo, int pageNumber, int pageSize, PageResult<User> pageResult) {
NumberedPaging paging = zeroBasedNumberedPaging(pageNumber, pageSize, pageResult.hasMore()); NumberedPaging paging = zeroBasedNumberedPaging(pageNumber, pageSize, pageResult.hasMore());
List<UserDto> dtos = pageResult.getEntities().stream().map(user -> userToDtoMapper.userToUserDto(user, uriInfo)).collect(Collectors.toList()); List<UserDto> dtos = pageResult.getEntities().stream().map(user -> userToDtoMapper.userToUserDto(user, uriInfo)).collect(Collectors.toList());

View File

@@ -12,12 +12,21 @@ import sonia.scm.user.UserException;
import sonia.scm.user.UserManager; import sonia.scm.user.UserManager;
import sonia.scm.web.VndMediaType; import sonia.scm.web.VndMediaType;
import javax.ws.rs.*; import javax.ws.rs.DefaultValue;
import javax.ws.rs.core.*; 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.Context;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.io.IOException; import java.io.IOException;
import java.util.Collection; import java.util.Collection;
@Produces(VndMediaType.USER) @Produces(VndMediaType.USER_COLLECTION)
public class UserCollectionResource extends AbstractManagerResource<User, UserException> { public class UserCollectionResource extends AbstractManagerResource<User, UserException> {
public static final int DEFAULT_PAGE_SIZE = 10; public static final int DEFAULT_PAGE_SIZE = 10;
private final UserDto2UserMapper dtoToUserMapper; private final UserDto2UserMapper dtoToUserMapper;
@@ -57,9 +66,14 @@ public class UserCollectionResource extends AbstractManagerResource<User, UserEx
@QueryParam("desc") boolean desc) { @QueryParam("desc") boolean desc) {
PageResult<User> pageResult = fetchPage(sortby, desc, page, pageSize); PageResult<User> pageResult = fetchPage(sortby, desc, page, pageSize);
return Response.ok(new UserCollection2DtoMapper(userToDtoMapper).userCollectionToUserDto(uriInfo, page, pageSize, pageResult)).build(); return Response.ok(new UserCollection2DtoMapper(userToDtoMapper).userCollectionToDto(uriInfo, page, pageSize, pageResult)).build();
} }
/**
* Creates a new user.
* @param userDto The user to be created.
* @return A response with the link to the new user (if created successfully).
*/
@POST @POST
@Path("") @Path("")
@StatusCodes({ @StatusCodes({

View File

@@ -12,8 +12,17 @@ import sonia.scm.user.UserException;
import sonia.scm.user.UserManager; import sonia.scm.user.UserManager;
import sonia.scm.web.VndMediaType; import sonia.scm.web.VndMediaType;
import javax.ws.rs.*; import javax.ws.rs.DELETE;
import javax.ws.rs.core.*; import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.util.Collection; import java.util.Collection;

View File

@@ -60,7 +60,7 @@ public class JsonMarshallingResponseFilterTest {
public void testFilter() { public void testFilter() {
when(responseContext.hasEntity()).thenReturn(Boolean.TRUE); when(responseContext.hasEntity()).thenReturn(Boolean.TRUE);
when(responseContext.getEntity()).thenReturn(new JsonMarshallingResponseFilterTest.Sample("one-two-three")); when(responseContext.getEntity()).thenReturn(new JsonMarshallingResponseFilterTest.Sample("one-two-three"));
when(responseContext.getMediaType()).thenReturn(VndMediaType.jsonType("sample")); when(responseContext.getMediaType()).thenReturn(MediaType.valueOf(VndMediaType.USER));
filter.filter(requestContext, responseContext); filter.filter(requestContext, responseContext);
@@ -81,7 +81,7 @@ public class JsonMarshallingResponseFilterTest {
when(responseContext.hasEntity()).thenReturn(Boolean.TRUE); when(responseContext.hasEntity()).thenReturn(Boolean.TRUE);
when(responseContext.getEntity()).thenReturn(new JsonMarshallingResponseFilterTest.Sample("one-two-three")); when(responseContext.getEntity()).thenReturn(new JsonMarshallingResponseFilterTest.Sample("one-two-three"));
when(responseContext.getMediaType()).thenReturn(VndMediaType.jsonType("sample")); when(responseContext.getMediaType()).thenReturn(MediaType.valueOf(VndMediaType.USER));
filter.filter(requestContext, responseContext); filter.filter(requestContext, responseContext);

View File

@@ -45,28 +45,28 @@ public class UserCollection2DtoMapperTest {
@Test @Test
public void shouldSetPageNumber() { public void shouldSetPageNumber() {
PageResult<User> pageResult = mockPageResult(true, "Hannes"); PageResult<User> pageResult = mockPageResult(true, "Hannes");
UserCollectionDto userCollectionDto = mapper.userCollectionToUserDto(uriInfo, 1, 1, pageResult); UserCollectionDto userCollectionDto = mapper.userCollectionToDto(uriInfo, 1, 1, pageResult);
assertEquals(1, userCollectionDto.getPage()); assertEquals(1, userCollectionDto.getPage());
} }
@Test @Test
public void shouldHaveSelfLink() { public void shouldHaveSelfLink() {
PageResult<User> pageResult = mockPageResult(true, "Hannes"); PageResult<User> pageResult = mockPageResult(true, "Hannes");
UserCollectionDto userCollectionDto = mapper.userCollectionToUserDto(uriInfo, 1, 1, pageResult); UserCollectionDto userCollectionDto = mapper.userCollectionToDto(uriInfo, 1, 1, pageResult);
assertTrue(userCollectionDto.getLinks().getLinkBy("self").get().getHref().startsWith(expectedBaseUri.toString())); assertTrue(userCollectionDto.getLinks().getLinkBy("self").get().getHref().startsWith(expectedBaseUri.toString()));
} }
@Test @Test
public void shouldCreateNextPageLink_whenHasMore() { public void shouldCreateNextPageLink_whenHasMore() {
PageResult<User> pageResult = mockPageResult(true, "Hannes"); PageResult<User> pageResult = mockPageResult(true, "Hannes");
UserCollectionDto userCollectionDto = mapper.userCollectionToUserDto(uriInfo, 1, 1, pageResult); UserCollectionDto userCollectionDto = mapper.userCollectionToDto(uriInfo, 1, 1, pageResult);
assertTrue(userCollectionDto.getLinks().getLinkBy("next").get().getHref().contains("page=2")); assertTrue(userCollectionDto.getLinks().getLinkBy("next").get().getHref().contains("page=2"));
} }
@Test @Test
public void shouldNotCreateNextPageLink_whenNoMore() { public void shouldNotCreateNextPageLink_whenNoMore() {
PageResult<User> pageResult = mockPageResult(false, "Hannes"); PageResult<User> pageResult = mockPageResult(false, "Hannes");
UserCollectionDto userCollectionDto = mapper.userCollectionToUserDto(uriInfo, 1, 1, pageResult); UserCollectionDto userCollectionDto = mapper.userCollectionToDto(uriInfo, 1, 1, pageResult);
assertFalse(userCollectionDto.getLinks().stream().anyMatch(link -> link.getHref().contains("page=2"))); assertFalse(userCollectionDto.getLinks().stream().anyMatch(link -> link.getHref().contains("page=2")));
} }
@@ -75,7 +75,7 @@ public class UserCollection2DtoMapperTest {
PageResult<User> pageResult = mockPageResult(false, "Hannes"); PageResult<User> pageResult = mockPageResult(false, "Hannes");
when(subject.isPermitted("user:create")).thenReturn(true); when(subject.isPermitted("user:create")).thenReturn(true);
UserCollectionDto userCollectionDto = mapper.userCollectionToUserDto(uriInfo, 1, 1, pageResult); UserCollectionDto userCollectionDto = mapper.userCollectionToDto(uriInfo, 1, 1, pageResult);
assertTrue(userCollectionDto.getLinks().getLinkBy("create").isPresent()); assertTrue(userCollectionDto.getLinks().getLinkBy("create").isPresent());
} }
@@ -85,7 +85,7 @@ public class UserCollection2DtoMapperTest {
PageResult<User> pageResult = mockPageResult(false, "Hannes"); PageResult<User> pageResult = mockPageResult(false, "Hannes");
when(subject.isPermitted("user:create")).thenReturn(false); when(subject.isPermitted("user:create")).thenReturn(false);
UserCollectionDto userCollectionDto = mapper.userCollectionToUserDto(uriInfo, 1, 1, pageResult); UserCollectionDto userCollectionDto = mapper.userCollectionToDto(uriInfo, 1, 1, pageResult);
assertFalse(userCollectionDto.getLinks().getLinkBy("create").isPresent()); assertFalse(userCollectionDto.getLinks().getLinkBy("create").isPresent());
} }
@@ -93,7 +93,7 @@ public class UserCollection2DtoMapperTest {
@Test @Test
public void shouldMapUsers() { public void shouldMapUsers() {
PageResult<User> pageResult = mockPageResult(false, "Hannes", "Wurst"); PageResult<User> pageResult = mockPageResult(false, "Hannes", "Wurst");
UserCollectionDto userCollectionDto = mapper.userCollectionToUserDto(uriInfo, 1, 2, pageResult); UserCollectionDto userCollectionDto = mapper.userCollectionToDto(uriInfo, 1, 2, pageResult);
List<HalRepresentation> users = userCollectionDto.getEmbedded().getItemsBy("users"); List<HalRepresentation> users = userCollectionDto.getEmbedded().getItemsBy("users");
assertEquals(2, users.size()); assertEquals(2, users.size());
assertEquals("Hannes", ((UserDto) users.get(0)).getName()); assertEquals("Hannes", ((UserDto) users.get(0)).getName());