Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

SO- 4890: support includeNull and pretty query parameters #857

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.BitSet;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.function.Predicate;
import java.util.stream.Stream;

import javax.servlet.http.HttpServletRequest;

Expand Down Expand Up @@ -77,6 +79,9 @@
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.google.common.base.Charsets;
import com.google.common.base.Strings;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
import com.google.inject.Provider;

Expand All @@ -94,20 +99,34 @@
@PropertySource("classpath:com/b2international/snowowl/core/rest/service_configuration.properties")
public class SnowOwlApiConfig extends WebMvcConfigurationSupport {

private static final String INCLUDE_NULL = "includeNull";
private static final int INCLUDE_NULL_IDX = 0;
private static final String PRETTY = "pretty";
private static final int PRETTY_IDX = 1;

static {
SpringDocUtils.getConfig().addResponseWrapperToIgnore(Promise.class);
}

@Autowired
private org.springframework.context.ApplicationContext ctx;

private final LoadingCache<BitSet, ObjectMapper> objectMappers = CacheBuilder.newBuilder().build(new CacheLoader<BitSet, ObjectMapper>() {
@Override
public ObjectMapper load(BitSet configuration) throws Exception {
ObjectMapper mapper = createObjectMapper();
mapper.setSerializationInclusion(configuration.get(INCLUDE_NULL_IDX) ? Include.ALWAYS : Include.NON_NULL);
mapper.configure(SerializationFeature.INDENT_OUTPUT, configuration.get(PRETTY_IDX));
return mapper;
}
});

@Bean
public OpenAPI openAPI() {
return new OpenAPI();
}

@Bean
public ObjectMapper objectMapper() {
public static ObjectMapper createObjectMapper() {
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.setSerializationInclusion(Include.NON_NULL);
Expand All @@ -120,6 +139,42 @@ public ObjectMapper objectMapper() {
return objectMapper;
}

@Bean
@Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public ObjectMapper objectMapper(@Autowired HttpServletRequest request) {
return objectMappers.getUnchecked(toConfig(
extractBooleanQueryParameterValue(request, INCLUDE_NULL),
extractBooleanQueryParameterValue(request, PRETTY)
));
}

private BitSet toConfig(boolean...serializationFeatureValuesInOrder) {
if (serializationFeatureValuesInOrder == null) {
return new BitSet(0);
}
BitSet config = new BitSet(serializationFeatureValuesInOrder.length);
for (int i = 0; i < serializationFeatureValuesInOrder.length; i++) {
config.set(i, serializationFeatureValuesInOrder[i]);
}
return config;
}

private boolean extractBooleanQueryParameterValue(HttpServletRequest request, String queryParameterKey) {
String[] values = request.getParameterMap().containsKey(queryParameterKey) ? request.getParameterMap().getOrDefault(queryParameterKey, null) : null;
if (values == null) {
// query parameter not present, means disable feature
return false;
} else if (values.length == 0) {
// no values present, but the key is present, enable feature
return true;
} else {
// XXX due to a bug in jetty-server v9.x, query parameters are duplicated in the low-level request object
// allowing multiple values for now with empty or valid (true in this case) values here
// see this bug report for details https://github.com/eclipse/jetty.project/issues/2074
return Stream.of(values).allMatch(value -> value.isBlank() || "true".equals(value));
}
}

@Override
protected void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
returnValueHandlers.add(new PromiseMethodReturnValueHandler());
Expand Down Expand Up @@ -204,10 +259,13 @@ public void configureMessageConverters(final List<HttpMessageConverter<?>> conve
converters.add(new ByteArrayHttpMessageConverter());
converters.add(new ResourceHttpMessageConverter());
converters.add(new CsvMessageConverter());
// XXX using null value here as Spring calls a proxied method anyway which returns an already configured instance, see mapping2JacksonHttpMessageConverter Bean method
converters.add(mapping2JacksonHttpMessageConverter(null));
}

final MappingJackson2HttpMessageConverter jacksonConverter = new MappingJackson2HttpMessageConverter();
jacksonConverter.setObjectMapper(objectMapper());
converters.add(jacksonConverter);
@Bean
public MappingJackson2HttpMessageConverter mapping2JacksonHttpMessageConverter(ObjectMapper mapper) {
return new MappingJackson2HttpMessageConverter(mapper);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public class FhirTest {

protected static final String TEST_DATE_STRING = "2018-03-23T07:49:40.000+0000"; //$NON-NLS-N$

protected static final ObjectMapper objectMapper = new SnowOwlApiConfig().objectMapper();
protected static final ObjectMapper objectMapper = SnowOwlApiConfig.createObjectMapper();

@Rule
public ExpectedException exception = ExpectedException.none();
Expand Down