mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-14 17:26:22 +01:00
Add json enricher and json field filter
This commit is contained in:
1
pom.xml
1
pom.xml
@@ -484,6 +484,7 @@
|
|||||||
|
|
||||||
<jaxrs.version>2.0.1</jaxrs.version>
|
<jaxrs.version>2.0.1</jaxrs.version>
|
||||||
<jersey-client.version>1.19.4</jersey-client.version>
|
<jersey-client.version>1.19.4</jersey-client.version>
|
||||||
|
<jackson.version>2.8.6</jackson.version>
|
||||||
<guice.version>4.0</guice.version>
|
<guice.version>4.0</guice.version>
|
||||||
|
|
||||||
<!-- event bus -->
|
<!-- event bus -->
|
||||||
|
|||||||
@@ -82,7 +82,17 @@
|
|||||||
<artifactId>javax.ws.rs-api</artifactId>
|
<artifactId>javax.ws.rs-api</artifactId>
|
||||||
<version>${jaxrs.version}</version>
|
<version>${jaxrs.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-core</artifactId>
|
||||||
|
<version>${jackson.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<version>${jackson.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- event bus -->
|
<!-- event bus -->
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
10
scm-core/src/main/java/sonia/scm/web/JsonEnricher.java
Normal file
10
scm-core/src/main/java/sonia/scm/web/JsonEnricher.java
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package sonia.scm.web;
|
||||||
|
|
||||||
|
import sonia.scm.plugin.ExtensionPoint;
|
||||||
|
|
||||||
|
@ExtensionPoint
|
||||||
|
public interface JsonEnricher {
|
||||||
|
|
||||||
|
void enrich(JsonEnricherContext context);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package sonia.scm.web;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
public class JsonEnricherContext {
|
||||||
|
|
||||||
|
private URI requestUri;
|
||||||
|
private MediaType responseMediaType;
|
||||||
|
private JsonNode responseEntity;
|
||||||
|
|
||||||
|
public JsonEnricherContext(URI requestUri, MediaType responseMediaType, JsonNode responseEntity) {
|
||||||
|
this.requestUri = requestUri;
|
||||||
|
this.responseMediaType = responseMediaType;
|
||||||
|
this.responseEntity = responseEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public URI getRequestUri() {
|
||||||
|
return requestUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MediaType getResponseMediaType() {
|
||||||
|
return responseMediaType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonNode getResponseEntity() {
|
||||||
|
return responseEntity;
|
||||||
|
}
|
||||||
|
}
|
||||||
28
scm-core/src/main/java/sonia/scm/web/VndMediaType.java
Normal file
28
scm-core/src/main/java/sonia/scm/web/VndMediaType.java
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package sonia.scm.web;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
|
||||||
|
public class VndMediaType {
|
||||||
|
private static final String VERSION = "2";
|
||||||
|
private static final String TYPE = "application";
|
||||||
|
private static final String SUBTYPE_PREFIX = "vnd.scmm-";
|
||||||
|
private static final String PREFIX = TYPE + "/" + SUBTYPE_PREFIX;
|
||||||
|
private static final String SUFFIX = "+json;v=" + VERSION;
|
||||||
|
|
||||||
|
public static final String USER = PREFIX + "user" + SUFFIX;
|
||||||
|
|
||||||
|
private VndMediaType() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MediaType jsonType(String resource) {
|
||||||
|
return MediaType.valueOf(json(resource));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String json(String resource) {
|
||||||
|
return PREFIX + resource + SUFFIX;// ".v2+json";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isVndType(MediaType type) {
|
||||||
|
return type.getType().equals(TYPE) && type.getSubtype().startsWith(SUBTYPE_PREFIX);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -560,7 +560,6 @@
|
|||||||
<wagon.version>1.0</wagon.version>
|
<wagon.version>1.0</wagon.version>
|
||||||
<mustache.version>0.8.17</mustache.version>
|
<mustache.version>0.8.17</mustache.version>
|
||||||
<resteasy.version>3.1.3.Final</resteasy.version>
|
<resteasy.version>3.1.3.Final</resteasy.version>
|
||||||
<jackson.version>2.8.6</jackson.version>
|
|
||||||
<netbeans.hint.deploy.server>Tomcat</netbeans.hint.deploy.server>
|
<netbeans.hint.deploy.server>Tomcat</netbeans.hint.deploy.server>
|
||||||
<sonar.issue.ignore.multicriteria>e1</sonar.issue.ignore.multicriteria>
|
<sonar.issue.ignore.multicriteria>e1</sonar.issue.ignore.multicriteria>
|
||||||
<sonar.issue.ignore.multicriteria.e1.ruleKey>javascript:S3827</sonar.issue.ignore.multicriteria.e1.ruleKey>
|
<sonar.issue.ignore.multicriteria.e1.ruleKey>javascript:S3827</sonar.issue.ignore.multicriteria.e1.ruleKey>
|
||||||
|
|||||||
@@ -35,16 +35,16 @@ package sonia.scm;
|
|||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
//~--- non-JDK imports --------------------------------------------------------
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.multibindings.Multibinder;
|
import com.google.inject.multibindings.Multibinder;
|
||||||
import com.google.inject.name.Names;
|
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.cache.CacheManager;
|
import sonia.scm.cache.CacheManager;
|
||||||
import sonia.scm.cache.GuavaCacheManager;
|
import sonia.scm.cache.GuavaCacheManager;
|
||||||
import sonia.scm.config.ScmConfiguration;
|
import sonia.scm.config.ScmConfiguration;
|
||||||
@@ -56,18 +56,13 @@ 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.DefaultRepositoryManager;
|
import sonia.scm.repository.*;
|
||||||
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;
|
||||||
@@ -76,28 +71,15 @@ 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.security.CipherHandler;
|
import sonia.scm.schedule.QuartzScheduler;
|
||||||
import sonia.scm.security.CipherUtil;
|
import sonia.scm.schedule.Scheduler;
|
||||||
import sonia.scm.security.DefaultKeyGenerator;
|
import sonia.scm.security.*;
|
||||||
import sonia.scm.security.DefaultSecuritySystem;
|
import sonia.scm.store.*;
|
||||||
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.RestJsonUrlProvider;
|
import sonia.scm.url.*;
|
||||||
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;
|
||||||
@@ -105,31 +87,17 @@ 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;
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
import javax.servlet.ServletContext;
|
|
||||||
import sonia.scm.store.ConfigurationStoreFactory;
|
|
||||||
|
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
import sonia.scm.net.SSLContextProvider;
|
import javax.servlet.ServletContext;
|
||||||
import sonia.scm.net.ahc.AdvancedHttpClient;
|
|
||||||
import sonia.scm.net.ahc.ContentTransformer;
|
//~--- JDK imports ------------------------------------------------------------
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -354,6 +322,7 @@ public class ScmServletModule extends ServletModule
|
|||||||
bind(TemplateEngine.class).annotatedWith(Default.class).to(
|
bind(TemplateEngine.class).annotatedWith(Default.class).to(
|
||||||
MustacheTemplateEngine.class);
|
MustacheTemplateEngine.class);
|
||||||
bind(TemplateEngineFactory.class);
|
bind(TemplateEngineFactory.class);
|
||||||
|
bind(ObjectMapper.class).toProvider(ObjectMapperProvider.class);
|
||||||
|
|
||||||
// bind events
|
// bind events
|
||||||
// bind(LastModifiedUpdateListener.class);
|
// bind(LastModifiedUpdateListener.class);
|
||||||
|
|||||||
@@ -30,18 +30,9 @@
|
|||||||
*/
|
*/
|
||||||
package sonia.scm.api.rest;
|
package sonia.scm.api.rest;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.AnnotationIntrospector;
|
|
||||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
|
||||||
import com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair;
|
|
||||||
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
|
|
||||||
import com.fasterxml.jackson.databind.type.TypeFactory;
|
|
||||||
import com.fasterxml.jackson.databind.util.ISO8601DateFormat;
|
|
||||||
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
|
|
||||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
|
||||||
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;
|
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.ext.ContextResolver;
|
import javax.ws.rs.ext.ContextResolver;
|
||||||
import javax.ws.rs.ext.Provider;
|
import javax.ws.rs.ext.Provider;
|
||||||
@@ -58,27 +49,13 @@ public final class JSONContextResolver implements ContextResolver<ObjectMapper>
|
|||||||
|
|
||||||
private final ObjectMapper mapper;
|
private final ObjectMapper mapper;
|
||||||
|
|
||||||
public JSONContextResolver() {
|
@Inject
|
||||||
mapper = new ObjectMapper()
|
public JSONContextResolver(ObjectMapper mapper) {
|
||||||
.registerModule(new Jdk8Module())
|
this.mapper = mapper;
|
||||||
.registerModule(new JavaTimeModule());
|
|
||||||
mapper.setAnnotationIntrospector(createAnnotationIntrospector());
|
|
||||||
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
|
||||||
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
|
|
||||||
mapper.configure(SerializationFeature.WRITE_DATES_WITH_ZONE_ID, true);
|
|
||||||
mapper.setDateFormat(new ISO8601DateFormat());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private AnnotationIntrospector createAnnotationIntrospector() {
|
|
||||||
return new AnnotationIntrospectorPair(
|
|
||||||
new JaxbAnnotationIntrospector(TypeFactory.defaultInstance()),
|
|
||||||
new JacksonAnnotationIntrospector()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ObjectMapper getContext(Class<?> type) {
|
public ObjectMapper getContext(Class<?> type) {
|
||||||
return mapper;
|
return mapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package sonia.scm.api.rest;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.AnnotationIntrospector;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
|
import com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair;
|
||||||
|
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
|
||||||
|
import com.fasterxml.jackson.databind.type.TypeFactory;
|
||||||
|
import com.fasterxml.jackson.databind.util.ISO8601DateFormat;
|
||||||
|
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
|
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;
|
||||||
|
|
||||||
|
import javax.inject.Provider;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class ObjectMapperProvider implements Provider<ObjectMapper> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ObjectMapper get() {
|
||||||
|
ObjectMapper mapper = new ObjectMapper()
|
||||||
|
.registerModule(new Jdk8Module())
|
||||||
|
.registerModule(new JavaTimeModule());
|
||||||
|
mapper.setAnnotationIntrospector(createAnnotationIntrospector());
|
||||||
|
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||||
|
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
|
||||||
|
mapper.configure(SerializationFeature.WRITE_DATES_WITH_ZONE_ID, true);
|
||||||
|
mapper.setDateFormat(new ISO8601DateFormat());
|
||||||
|
return mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AnnotationIntrospector createAnnotationIntrospector() {
|
||||||
|
return new AnnotationIntrospectorPair(
|
||||||
|
new JaxbAnnotationIntrospector(TypeFactory.defaultInstance()),
|
||||||
|
new JacksonAnnotationIntrospector()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package sonia.scm.api.v2;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import javax.annotation.Priority;
|
||||||
|
import javax.ws.rs.Priorities;
|
||||||
|
import javax.ws.rs.container.ContainerRequestContext;
|
||||||
|
import javax.ws.rs.container.ContainerResponseContext;
|
||||||
|
import javax.ws.rs.container.ContainerResponseFilter;
|
||||||
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
|
import javax.ws.rs.ext.Provider;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Provider
|
||||||
|
@Priority(Priorities.USER)
|
||||||
|
public class FieldContainerResponseFilter implements ContainerResponseFilter {
|
||||||
|
|
||||||
|
private static final String PARAMETER_FIELDS = "fields";
|
||||||
|
private static final String FIELD_SEPARATOR = ",";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) {
|
||||||
|
Optional<JsonNode> entity = getJsonEntity(responseContext);
|
||||||
|
if (entity.isPresent()) {
|
||||||
|
List<String> fields = extractFieldsFrom(requestContext);
|
||||||
|
if (!fields.isEmpty()) {
|
||||||
|
JsonFilters.filterByFields(entity.get(), fields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<JsonNode> getJsonEntity(ContainerResponseContext responseContext) {
|
||||||
|
Object entity = responseContext.getEntity();
|
||||||
|
if (isJsonEntity(entity)) {
|
||||||
|
return Optional.of((JsonNode) entity);
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isJsonEntity(Object entity) {
|
||||||
|
return entity instanceof JsonNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> extractFieldsFrom(ContainerRequestContext requestContext) {
|
||||||
|
List<String> fields = Lists.newArrayList();
|
||||||
|
|
||||||
|
List<String> fieldParameters = getFieldParameterFrom(requestContext);
|
||||||
|
if (fieldParameters != null && !fieldParameters.isEmpty()) {
|
||||||
|
for (String fieldParameter : fieldParameters) {
|
||||||
|
appendFieldsFromParameter(fields, fieldParameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getFieldParameterFrom(ContainerRequestContext requestContext) {
|
||||||
|
MultivaluedMap<String, String> queryParameters = requestContext.getUriInfo().getQueryParameters();
|
||||||
|
return queryParameters.get(PARAMETER_FIELDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendFieldsFromParameter(List<String> fields, String fieldParameter) {
|
||||||
|
for (String field : fieldParameter.split(FIELD_SEPARATOR)) {
|
||||||
|
fields.add(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
85
scm-webapp/src/main/java/sonia/scm/api/v2/JsonFilters.java
Normal file
85
scm-webapp/src/main/java/sonia/scm/api/v2/JsonFilters.java
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
package sonia.scm.api.v2;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public final class JsonFilters {
|
||||||
|
|
||||||
|
private JsonFilters() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void filterByFields(JsonNode root, Iterable<String> fields) {
|
||||||
|
filterNode(createJsonFilterNode(fields), root);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JsonFilterNode createJsonFilterNode(Iterable<String> fields) {
|
||||||
|
JsonFilterNode rootFilterNode = new JsonFilterNode();
|
||||||
|
for (String field : fields) {
|
||||||
|
appendFilterNode(rootFilterNode, field);
|
||||||
|
}
|
||||||
|
return rootFilterNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void appendFilterNode(JsonFilterNode rootFilterNode, String field) {
|
||||||
|
JsonFilterNode filterNode = rootFilterNode;
|
||||||
|
for (String part : field.split("\\.")) {
|
||||||
|
filterNode = filterNode.addOrGet(part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void filterNode(JsonFilterNode filter, JsonNode node) {
|
||||||
|
if (node.isObject()) {
|
||||||
|
filterObjectNode(filter, (ObjectNode) node);
|
||||||
|
} else if (node.isArray()) {
|
||||||
|
filterArrayNode(filter, (ArrayNode) node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void filterObjectNode(JsonFilterNode filter, ObjectNode objectNode) {
|
||||||
|
Iterator<Map.Entry<String,JsonNode>> entryIterator = objectNode.fields();
|
||||||
|
while (entryIterator.hasNext()) {
|
||||||
|
Map.Entry<String,JsonNode> entry = entryIterator.next();
|
||||||
|
|
||||||
|
JsonFilterNode childFilter = filter.get(entry.getKey());
|
||||||
|
if (childFilter == null) {
|
||||||
|
entryIterator.remove();
|
||||||
|
} else if (!childFilter.isLeaf()) {
|
||||||
|
filterNode(childFilter, entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void filterArrayNode(JsonFilterNode filter, ArrayNode arrayNode) {
|
||||||
|
for (int i=0; i<arrayNode.size(); i++) {
|
||||||
|
filterNode(filter, arrayNode.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class JsonFilterNode {
|
||||||
|
|
||||||
|
private final Map<String,JsonFilterNode> children = Maps.newHashMap();
|
||||||
|
|
||||||
|
JsonFilterNode addOrGet(String name) {
|
||||||
|
JsonFilterNode child = children.get(name);
|
||||||
|
if (child == null) {
|
||||||
|
child = new JsonFilterNode();
|
||||||
|
children.put(name, child);
|
||||||
|
}
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonFilterNode get(String name) {
|
||||||
|
return children.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isLeaf() {
|
||||||
|
return children.isEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package sonia.scm.api.v2;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import sonia.scm.web.JsonEnricher;
|
||||||
|
import sonia.scm.web.JsonEnricherContext;
|
||||||
|
import sonia.scm.web.VndMediaType;
|
||||||
|
|
||||||
|
import javax.annotation.Priority;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.ws.rs.Priorities;
|
||||||
|
import javax.ws.rs.container.ContainerRequestContext;
|
||||||
|
import javax.ws.rs.container.ContainerResponseContext;
|
||||||
|
import javax.ws.rs.container.ContainerResponseFilter;
|
||||||
|
import javax.ws.rs.ext.Provider;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Provider
|
||||||
|
@Priority(Priorities.USER + 1000)
|
||||||
|
public class JsonMarshallingResponseFilter implements ContainerResponseFilter {
|
||||||
|
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
private final Set<JsonEnricher> enrichers;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public JsonMarshallingResponseFilter(ObjectMapper objectMapper, Set<JsonEnricher> enrichers) {
|
||||||
|
this.objectMapper = objectMapper;
|
||||||
|
this.enrichers = enrichers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) {
|
||||||
|
if (hasVndEntity(responseContext)) {
|
||||||
|
JsonNode node = getJsonEntity(responseContext);
|
||||||
|
callEnrichers(requestContext, responseContext, node);
|
||||||
|
responseContext.setEntity(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void callEnrichers(ContainerRequestContext requestContext, ContainerResponseContext responseContext, JsonNode node) {
|
||||||
|
JsonEnricherContext context = new JsonEnricherContext(
|
||||||
|
requestContext.getUriInfo().getRequestUri(),
|
||||||
|
responseContext.getMediaType(),
|
||||||
|
node
|
||||||
|
);
|
||||||
|
|
||||||
|
for (JsonEnricher enricher : enrichers) {
|
||||||
|
enricher.enrich(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonNode getJsonEntity(ContainerResponseContext responseContext) {
|
||||||
|
Object entity = responseContext.getEntity();
|
||||||
|
return objectMapper.valueToTree(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasVndEntity(ContainerResponseContext responseContext) {
|
||||||
|
return responseContext.hasEntity() && VndMediaType.isVndType(responseContext.getMediaType());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,4 @@
|
|||||||
package sonia.scm.api.v2.resources;
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
public class ScmMediaType {
|
public class ScmMediaType {
|
||||||
private static final String VERSION = "2";
|
|
||||||
private static final String PREFIX = "application/vnd.scmm-";
|
|
||||||
private static final String SUFFIX = "+json;v=" + VERSION;
|
|
||||||
|
|
||||||
public static final String USER = PREFIX + "user" + SUFFIX;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import sonia.scm.api.rest.resources.AbstractManagerResource;
|
|||||||
import sonia.scm.user.User;
|
import sonia.scm.user.User;
|
||||||
import sonia.scm.user.UserException;
|
import sonia.scm.user.UserException;
|
||||||
import sonia.scm.user.UserManager;
|
import sonia.scm.user.UserManager;
|
||||||
|
import sonia.scm.web.VndMediaType;
|
||||||
|
|
||||||
import javax.ws.rs.*;
|
import javax.ws.rs.*;
|
||||||
import javax.ws.rs.core.*;
|
import javax.ws.rs.core.*;
|
||||||
@@ -20,10 +21,9 @@ import java.util.List;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static de.otto.edison.hal.paging.NumberedPaging.zeroBasedNumberedPaging;
|
import static de.otto.edison.hal.paging.NumberedPaging.zeroBasedNumberedPaging;
|
||||||
import static sonia.scm.api.v2.resources.ScmMediaType.USER;
|
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Produces(USER)
|
@Produces(VndMediaType.USER)
|
||||||
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;
|
||||||
|
|||||||
@@ -11,15 +11,15 @@ 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;
|
||||||
import sonia.scm.user.UserManager;
|
import sonia.scm.user.UserManager;
|
||||||
|
import sonia.scm.web.VndMediaType;
|
||||||
|
|
||||||
import javax.ws.rs.*;
|
import javax.ws.rs.*;
|
||||||
import javax.ws.rs.core.*;
|
import javax.ws.rs.core.*;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import static sonia.scm.api.v2.resources.ScmMediaType.USER;
|
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Produces(USER)
|
@Produces(VndMediaType.USER)
|
||||||
public class UserSubResource extends AbstractManagerResource<User, UserException> {
|
public class UserSubResource extends AbstractManagerResource<User, UserException> {
|
||||||
private final UserDto2UserMapper dtoToUserMapper;
|
private final UserDto2UserMapper dtoToUserMapper;
|
||||||
private final User2UserDtoMapper userToDtoMapper;
|
private final User2UserDtoMapper userToDtoMapper;
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ import static org.junit.Assert.*;
|
|||||||
*/
|
*/
|
||||||
public class JSONContextResolverTest {
|
public class JSONContextResolverTest {
|
||||||
|
|
||||||
private final ObjectMapper mapper = new JSONContextResolver().getContext(Object.class);
|
private final ObjectMapper mapper = new ObjectMapperProvider().get();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests json unmarshalling with unknown properties.
|
* Tests json unmarshalling with unknown properties.
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package sonia.scm.api.v2;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.io.Resources;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
import javax.ws.rs.container.ContainerRequestContext;
|
||||||
|
import javax.ws.rs.container.ContainerResponseContext;
|
||||||
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class FieldContainerResponseFilterTest {
|
||||||
|
|
||||||
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ContainerRequestContext requestContext;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ContainerResponseContext responseContext;
|
||||||
|
|
||||||
|
private FieldContainerResponseFilter filter = new FieldContainerResponseFilter();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFilter() throws IOException {
|
||||||
|
applyFields("one");
|
||||||
|
JsonNode node = applyEntity("filter-test-002");
|
||||||
|
|
||||||
|
filter.filter(requestContext, responseContext);
|
||||||
|
|
||||||
|
assertEquals("{\"one\":1}", objectMapper.writeValueAsString(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFilterWithMultiple() throws IOException {
|
||||||
|
applyFields("one", "five");
|
||||||
|
JsonNode node = applyEntity("filter-test-002");
|
||||||
|
|
||||||
|
filter.filter(requestContext, responseContext);
|
||||||
|
|
||||||
|
assertEquals("{\"one\":1,\"five\":5}", objectMapper.writeValueAsString(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFilterCommaSeparated() throws IOException {
|
||||||
|
applyFields("one,five");
|
||||||
|
JsonNode node = applyEntity("filter-test-002");
|
||||||
|
|
||||||
|
filter.filter(requestContext, responseContext);
|
||||||
|
|
||||||
|
assertEquals("{\"one\":1,\"five\":5}", objectMapper.writeValueAsString(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyFields(String... fields) {
|
||||||
|
UriInfo info = mock(UriInfo.class);
|
||||||
|
MultivaluedMap<String,String> queryParameters = mock(MultivaluedMap.class);
|
||||||
|
when(queryParameters.get("fields")).thenReturn(Lists.newArrayList(fields));
|
||||||
|
when(info.getQueryParameters()).thenReturn(queryParameters);
|
||||||
|
when(requestContext.getUriInfo()).thenReturn(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonNode applyEntity(String name) throws IOException {
|
||||||
|
JsonNode node = readJson(name);
|
||||||
|
when(responseContext.hasEntity()).thenReturn(Boolean.TRUE);
|
||||||
|
when(responseContext.getEntity()).thenReturn(node);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonNode readJson(String name) throws IOException {
|
||||||
|
URL resource = Resources.getResource("sonia/scm/api/v2/" + name + ".json");
|
||||||
|
return objectMapper.readTree(resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package sonia.scm.api.v2;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.io.Resources;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
|
||||||
|
public class JsonFiltersTest {
|
||||||
|
|
||||||
|
private ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFilterByFields() throws IOException {
|
||||||
|
JsonNode node = readJson("filter-test-001");
|
||||||
|
|
||||||
|
JsonFilters.filterByFields(node, Lists.newArrayList("one"));
|
||||||
|
|
||||||
|
assertEquals(1, node.get("one").intValue());
|
||||||
|
assertFalse(node.has("two"));
|
||||||
|
assertFalse(node.has("three"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFilterByFieldsWithMultipleFields() throws IOException {
|
||||||
|
JsonNode node = readJson("filter-test-001");
|
||||||
|
|
||||||
|
JsonFilters.filterByFields(node, Lists.newArrayList("one", "three"));
|
||||||
|
|
||||||
|
assertEquals(1, node.get("one").intValue());
|
||||||
|
assertFalse(node.has("two"));
|
||||||
|
assertEquals(3, node.get("three").intValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFilterByFieldsWithNonPrimitive() throws IOException {
|
||||||
|
JsonNode node = readJson("filter-test-002");
|
||||||
|
JsonFilters.filterByFields(node, Lists.newArrayList("two"));
|
||||||
|
assertEquals("{\"two\":{\"three\":3,\"four\":4}}", objectMapper.writeValueAsString(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFilterByFieldsWithDeepField() throws IOException {
|
||||||
|
JsonNode node = readJson("filter-test-002");
|
||||||
|
JsonFilters.filterByFields(node, Lists.newArrayList("two.three"));
|
||||||
|
assertEquals("{\"two\":{\"three\":3}}", objectMapper.writeValueAsString(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFilterByFieldsWithVeryDeepField() throws IOException {
|
||||||
|
JsonNode node = readJson("filter-test-003");
|
||||||
|
JsonFilters.filterByFields(node, Lists.newArrayList("two.three.four.five"));
|
||||||
|
assertFalse(node.has("one"));
|
||||||
|
String json = objectMapper.writeValueAsString(node.get("two").get("three").get("four").get("five"));
|
||||||
|
assertEquals("{\"six\":6,\"seven\":7}", json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFilterByFieldsWithArray() throws IOException {
|
||||||
|
JsonNode node = readJson("filter-test-004");
|
||||||
|
JsonFilters.filterByFields(node, Lists.newArrayList("one.two"));
|
||||||
|
ArrayNode one = (ArrayNode) node.get("one");
|
||||||
|
assertEquals(one.size(), 2);
|
||||||
|
for (int i=0; i<one.size(); i++) {
|
||||||
|
JsonNode childOfOne = one.get(i);
|
||||||
|
assertFalse(childOfOne.has("three"));
|
||||||
|
assertEquals(2, childOfOne.get("two").intValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonNode readJson(String name) throws IOException {
|
||||||
|
URL resource = Resources.getResource("sonia/scm/api/v2/" + name + ".json");
|
||||||
|
return objectMapper.readTree(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
package sonia.scm.api.v2;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
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.Mockito;
|
||||||
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
import sonia.scm.web.JsonEnricher;
|
||||||
|
import sonia.scm.web.VndMediaType;
|
||||||
|
|
||||||
|
import javax.ws.rs.container.ContainerRequestContext;
|
||||||
|
import javax.ws.rs.container.ContainerResponseContext;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class JsonMarshallingResponseFilterTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ContainerRequestContext requestContext;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ContainerResponseContext responseContext;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private UriInfo uriInfo;
|
||||||
|
|
||||||
|
@Captor
|
||||||
|
private ArgumentCaptor<JsonNode> jsonNodeCaptor;
|
||||||
|
|
||||||
|
private final ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
||||||
|
private Set<JsonEnricher> enrichers;
|
||||||
|
|
||||||
|
private JsonMarshallingResponseFilter filter;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUpObjectUnderTest() throws URISyntaxException {
|
||||||
|
this.enrichers = new HashSet<>();
|
||||||
|
filter = new JsonMarshallingResponseFilter(mapper, enrichers);
|
||||||
|
|
||||||
|
when(requestContext.getUriInfo()).thenReturn(uriInfo);
|
||||||
|
when(uriInfo.getRequestUri()).thenReturn(new URI("https://www.scm-manager.org/scm/api/v2/repositories"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFilter() {
|
||||||
|
when(responseContext.hasEntity()).thenReturn(Boolean.TRUE);
|
||||||
|
when(responseContext.getEntity()).thenReturn(new JsonMarshallingResponseFilterTest.Sample("one-two-three"));
|
||||||
|
when(responseContext.getMediaType()).thenReturn(VndMediaType.jsonType("sample"));
|
||||||
|
|
||||||
|
filter.filter(requestContext, responseContext);
|
||||||
|
|
||||||
|
verify(responseContext).setEntity(jsonNodeCaptor.capture());
|
||||||
|
|
||||||
|
JsonNode node = jsonNodeCaptor.getValue();
|
||||||
|
assertEquals("one-two-three", node.get("value").asText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFilterWithEnricher() {
|
||||||
|
enrichers.add(context -> {
|
||||||
|
JsonNode node = context.getResponseEntity();
|
||||||
|
if (node.isObject()) {
|
||||||
|
((ObjectNode)node).put("version", 2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
when(responseContext.hasEntity()).thenReturn(Boolean.TRUE);
|
||||||
|
when(responseContext.getEntity()).thenReturn(new JsonMarshallingResponseFilterTest.Sample("one-two-three"));
|
||||||
|
when(responseContext.getMediaType()).thenReturn(VndMediaType.jsonType("sample"));
|
||||||
|
|
||||||
|
filter.filter(requestContext, responseContext);
|
||||||
|
|
||||||
|
verify(responseContext).setEntity(jsonNodeCaptor.capture());
|
||||||
|
|
||||||
|
JsonNode node = jsonNodeCaptor.getValue();
|
||||||
|
assertEquals(2, node.get("version").asInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFilterWithoutEntity() {
|
||||||
|
filter.filter(requestContext, responseContext);
|
||||||
|
verify(responseContext, never()).setEntity(Mockito.anyObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFilterWithNonVndEntity() {
|
||||||
|
when(responseContext.hasEntity()).thenReturn(Boolean.TRUE);
|
||||||
|
when(responseContext.getEntity()).thenReturn(new JsonMarshallingResponseFilterTest.Sample("one-two-three"));
|
||||||
|
when(responseContext.getMediaType()).thenReturn(MediaType.APPLICATION_JSON_TYPE);
|
||||||
|
|
||||||
|
filter.filter(requestContext, responseContext);
|
||||||
|
verify(responseContext, never()).setEntity(Mockito.anyObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Sample {
|
||||||
|
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
public Sample(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,37 +35,34 @@ package sonia.scm.it;
|
|||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
//~--- non-JDK imports --------------------------------------------------------
|
||||||
|
|
||||||
import sonia.scm.ScmState;
|
|
||||||
import sonia.scm.Type;
|
|
||||||
import sonia.scm.user.User;
|
|
||||||
import sonia.scm.util.IOUtil;
|
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
import com.sun.jersey.api.client.Client;
|
import com.sun.jersey.api.client.Client;
|
||||||
import com.sun.jersey.api.client.ClientResponse;
|
import com.sun.jersey.api.client.ClientResponse;
|
||||||
import com.sun.jersey.api.client.WebResource;
|
import com.sun.jersey.api.client.WebResource;
|
||||||
import com.sun.jersey.api.client.filter.LoggingFilter;
|
|
||||||
import com.sun.jersey.client.apache.ApacheHttpClient;
|
import com.sun.jersey.client.apache.ApacheHttpClient;
|
||||||
import com.sun.jersey.client.apache.config.ApacheHttpClientConfig;
|
import com.sun.jersey.client.apache.config.ApacheHttpClientConfig;
|
||||||
import com.sun.jersey.client.apache.config.DefaultApacheHttpClientConfig;
|
import com.sun.jersey.client.apache.config.DefaultApacheHttpClientConfig;
|
||||||
import com.sun.jersey.core.util.MultivaluedMapImpl;
|
import com.sun.jersey.core.util.MultivaluedMapImpl;
|
||||||
|
import sonia.scm.ScmState;
|
||||||
|
import sonia.scm.Type;
|
||||||
|
import sonia.scm.api.rest.JSONContextResolver;
|
||||||
|
import sonia.scm.api.rest.ObjectMapperProvider;
|
||||||
|
import sonia.scm.repository.Person;
|
||||||
|
import sonia.scm.repository.client.api.ClientCommand;
|
||||||
|
import sonia.scm.repository.client.api.RepositoryClient;
|
||||||
|
import sonia.scm.user.User;
|
||||||
|
import sonia.scm.util.IOUtil;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import javax.ws.rs.core.MultivaluedMap;
|
import static org.junit.Assert.*;
|
||||||
import sonia.scm.api.rest.JSONContextResolver;
|
|
||||||
import sonia.scm.repository.Person;
|
//~--- JDK imports ------------------------------------------------------------
|
||||||
import sonia.scm.repository.client.api.ClientCommand;
|
|
||||||
import sonia.scm.repository.client.api.RepositoryClient;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -175,7 +172,7 @@ public final class IntegrationTestUtil
|
|||||||
public static Client createClient()
|
public static Client createClient()
|
||||||
{
|
{
|
||||||
DefaultApacheHttpClientConfig config = new DefaultApacheHttpClientConfig();
|
DefaultApacheHttpClientConfig config = new DefaultApacheHttpClientConfig();
|
||||||
config.getSingletons().add(new JSONContextResolver());
|
config.getSingletons().add(new JSONContextResolver(new ObjectMapperProvider().get()));
|
||||||
config.getProperties().put(ApacheHttpClientConfig.PROPERTY_HANDLE_COOKIES, true);
|
config.getProperties().put(ApacheHttpClientConfig.PROPERTY_HANDLE_COOKIES, true);
|
||||||
|
|
||||||
return ApacheHttpClient.create(config);
|
return ApacheHttpClient.create(config);
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"one": 1,
|
||||||
|
"two": 2,
|
||||||
|
"three": 3
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"one": 1,
|
||||||
|
"two": {
|
||||||
|
"three": 3,
|
||||||
|
"four": 4
|
||||||
|
},
|
||||||
|
"five": 5
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"one": 1,
|
||||||
|
"two": {
|
||||||
|
"three": {
|
||||||
|
"four": {
|
||||||
|
"five": {
|
||||||
|
"six": 6,
|
||||||
|
"seven": 7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"one": [{
|
||||||
|
"two": 2
|
||||||
|
}, {
|
||||||
|
"two": 2,
|
||||||
|
"three": 3
|
||||||
|
}]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user