Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/feature/#124-i18n' into feature/#…
Browse files Browse the repository at this point in the history
…124-i18n
  • Loading branch information
jflabatBCSS committed Nov 29, 2024
2 parents 84fcb31 + 07b96a1 commit 1a091fa
Show file tree
Hide file tree
Showing 21 changed files with 239 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
<version>2.2.25</version>
<version>2.2.26</version>
</dependency>
<dependency>
<groupId>jakarta.platform</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,24 @@ void issueWithStatusAndInstance() throws JsonProcessingException {
assertSerializationRoundtrip(problem);
}

@Test
void issueWithNullValue() throws JsonProcessingException {
BadRequestProblem problem = new BadRequestProblem(
new InputValidationIssue(InEnum.BODY, "id", null));
String json = mapper.writeValueAsString(problem);
assertThat(json).doesNotContain("null");
assertSerializationRoundtrip(problem);
}

@Test
void issueWithNullInputValue() throws JsonProcessingException {
BadRequestProblem problem = new BadRequestProblem(new InputValidationIssue()
.inputs(Input.body("a", null), Input.body("b", null)));
String json = mapper.writeValueAsString(problem);
assertThat(json).doesNotContain("null");
assertSerializationRoundtrip(problem);
}

void assertSerializationRoundtrip(Problem problem) throws JsonProcessingException {
String json = mapper.writeValueAsString(problem);
print(json);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.20.3</version>
<version>1.20.4</version>
<scope>test</scope>
</dependency>
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.20.3</version>
<version>1.20.4</version>
<scope>test</scope>
</dependency>
<dependency>
Expand Down
5 changes: 5 additions & 0 deletions belgif-rest-problem-spring-boot-2/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.github.belgif.rest.problem.spring;

import java.util.stream.Stream;

import org.springframework.core.ParameterNameDiscoverer;

import io.github.belgif.rest.problem.internal.CachedAnnotationParameterNameSupport;

/**
* ParameterNameDiscoverer that retrieves the parameter name from Spring MVC annotations (if present).
*/
public class AnnotationParameterNameDiscoverer extends CachedAnnotationParameterNameSupport<String[]>
implements ParameterNameDiscoverer {

@Override
protected String[] toResult(Stream<String> parameterNames) {
return parameterNames.toArray(String[]::new);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
import javax.validation.ConstraintViolationException;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
import org.springframework.boot.validation.MessageInterpolatorFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
Expand All @@ -21,6 +23,7 @@
* @see org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration
*/
@Configuration
@AutoConfigureBefore(ValidationAutoConfiguration.class)
public class ProblemValidatorConfiguration {

@Bean
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.github.belgif.rest.problem.spring;

import static org.assertj.core.api.Assertions.*;

import java.util.stream.Stream;

import org.junit.jupiter.api.Test;

class AnnotationParameterNameDiscovererTest {

private final AnnotationParameterNameDiscoverer discoverer = new AnnotationParameterNameDiscoverer();

@Test
void toResult() {
assertThat(discoverer.toResult(Stream.of("A", "B"))).containsExactly("A", "B");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package io.github.belgif.rest.problem.spring;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import jakarta.validation.ParameterNameProvider;

import io.github.belgif.rest.problem.internal.CachedAnnotationParameterNameSupport;

/**
* ParameterNameProvider that retrieves the parameter name from Spring MVC annotations (if present).
*/
public class AnnotationParameterNameProvider extends CachedAnnotationParameterNameSupport<List<String>>
implements ParameterNameProvider {

@Override
protected List<String> toResult(Stream<String> parameterNames) {
return parameterNames.collect(Collectors.toList());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.github.belgif.rest.problem.spring;

import jakarta.validation.Configuration;

import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.validation.ValidationConfigurationCustomizer;
import org.springframework.stereotype.Component;

/**
* ValidationConfigurationCustomizer that registers the AnnotationParameterNameProvider.
*/
@Component
@ConditionalOnClass(Configuration.class)
public class ProblemValidationConfigurationCustomizer implements ValidationConfigurationCustomizer {

@Override
public void customize(Configuration<?> configuration) {
configuration.parameterNameProvider(new AnnotationParameterNameProvider());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.github.belgif.rest.problem.spring;

import static org.assertj.core.api.Assertions.*;

import java.util.stream.Stream;

import org.junit.jupiter.api.Test;

class AnnotationParameterNameProviderTest {

private final AnnotationParameterNameProvider provider = new AnnotationParameterNameProvider();

@Test
void toResult() {
assertThat(provider.toResult(Stream.of("A", "B"))).containsExactly("A", "B");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.github.belgif.rest.problem.spring;

import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;

import jakarta.validation.Configuration;
import jakarta.validation.ParameterNameProvider;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
class ProblemValidationConfigurationCustomizerTest {

private final ProblemValidationConfigurationCustomizer customizer = new ProblemValidationConfigurationCustomizer();

@Mock
private Configuration configuration;

@Captor
private ArgumentCaptor<ParameterNameProvider> parameterNameProviderCaptor;

@Test
void customize() {
when(configuration.parameterNameProvider(parameterNameProviderCaptor.capture())).thenReturn(configuration);
customizer.customize(configuration);
assertThat(parameterNameProviderCaptor.getValue()).isInstanceOf(AnnotationParameterNameProvider.class);
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.github.belgif.rest.problem.spring;
package io.github.belgif.rest.problem.internal;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
Expand All @@ -7,35 +7,35 @@
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;

import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.lang.NonNull;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.MatrixVariable;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;

/**
* ParameterNameDiscoverer that retrieves the parameter name from Spring MVC annotations (if present).
* Helper class for cached retrieval of parameter names from Spring MVC annotations.
*
* @param <T> the result type
*/
public class AnnotationParameterNameDiscoverer implements ParameterNameDiscoverer {
public abstract class CachedAnnotationParameterNameSupport<T> {

private final ConcurrentHashMap<Executable, String[]> parameterNameCache = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Executable, T> parameterNameCache = new ConcurrentHashMap<>();

@Override
public String[] getParameterNames(Constructor<?> constructor) {
public T getParameterNames(@NonNull Constructor<?> constructor) {
return parameterNameCache.computeIfAbsent(constructor, this::getParameterNames);
}

@Override
public String[] getParameterNames(Method method) {
public T getParameterNames(@NonNull Method method) {
return parameterNameCache.computeIfAbsent(method, this::getParameterNames);
}

private String[] getParameterNames(Executable executable) {
return Arrays.stream(executable.getParameters())
.map(this::getParameterName)
.toArray(String[]::new);
private T getParameterNames(Executable executable) {
return toResult(Arrays.stream(executable.getParameters())
.map(this::getParameterName));
}

private String getParameterName(Parameter parameter) {
Expand Down Expand Up @@ -64,4 +64,6 @@ private String getParameterNameFromAnnotations(Parameter parameter) {
return null;
}

protected abstract T toResult(Stream<String> parameterNames);

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package io.github.belgif.rest.problem.spring;
package io.github.belgif.rest.problem.internal;

import static org.assertj.core.api.Assertions.*;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
Expand All @@ -11,28 +15,34 @@
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;

class AnnotationParameterNameDiscovererTest {
class CachedAnnotationParameterNameSupportTest {

private final AnnotationParameterNameDiscoverer discoverer = new AnnotationParameterNameDiscoverer();
private final CachedAnnotationParameterNameSupport<List<String>> support =
new CachedAnnotationParameterNameSupport<List<String>>() {
@Override
protected List<String> toResult(Stream<String> parameterNames) {
return parameterNames.collect(Collectors.toList());
}
};

@Test
void getParameterNameConstructor() throws Exception {
assertThat(discoverer.getParameterNames(Tested.class.getDeclaredConstructor(String.class, Long.class)))
assertThat(support.getParameterNames(Tested.class.getDeclaredConstructor(String.class, Long.class)))
.containsExactly("name", "other");
}

@Test
void getParameterNamesMethod() throws Exception {
assertThat(
discoverer.getParameterNames(Tested.class.getDeclaredMethod("plainMethod", String.class, Long.class)))
support.getParameterNames(Tested.class.getDeclaredMethod("plainMethod", String.class, Long.class)))
.containsExactly("name", "other");
}

@ParameterizedTest
@ValueSource(
strings = { "requestParam", "pathVariable", "requestHeader", "cookieValue", "matrixVariable", "fallback" })
void getParameterNamesMethodWithJaxRsAnnotation(String method) throws Exception {
assertThat(discoverer.getParameterNames(Tested.class.getDeclaredMethod(method, String.class)))
assertThat(support.getParameterNames(Tested.class.getDeclaredMethod(method, String.class)))
.containsExactly("name");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
*
* @param <V> the input value type
*/
@JsonInclude(value = JsonInclude.Include.NON_EMPTY)
@JsonPropertyOrder({ "in", "name", "value" })
public class Input<V> {

Expand Down Expand Up @@ -52,7 +53,6 @@ public void setName(String name) {
this.name = name;
}

@JsonInclude(value = JsonInclude.Include.ALWAYS, content = JsonInclude.Include.ALWAYS)
public V getValue() {
return value;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
*
* @see InputValidationProblem
*/
@JsonInclude(value = Include.NON_DEFAULT)
@JsonInclude(value = Include.NON_EMPTY)
@JsonPropertyOrder(value = { "type", "href", "title", "detail", "in", "name", "value", "inputs" })
public class InputValidationIssue {

Expand Down Expand Up @@ -123,7 +123,6 @@ public void setName(String name) {
this.name = name;
}

@JsonInclude(value = Include.NON_NULL, content = Include.ALWAYS)
public Object getValue() {
return value;
}
Expand Down Expand Up @@ -263,6 +262,14 @@ public InputValidationIssue in(InEnum in, String name, Object value) {
return in(in).name(name).value(value);
}

public InputValidationIssue in(Input<?> input) {
if (input != null) {
return in(input.getIn(), input.getName(), input.getValue());
} else {
return this;
}
}

public InputValidationIssue in(InEnum in) {
setIn(in);
return this;
Expand Down
Loading

0 comments on commit 1a091fa

Please sign in to comment.