Skip to content

Commit

Permalink
Merge pull request #101 from zalando/feature/custom-constraint-violat…
Browse files Browse the repository at this point in the history
…ion-type

Made problem type configurable
  • Loading branch information
whiskeysierra authored May 12, 2017
2 parents 408c821 + 010f3bf commit 110c6aa
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ default Violation createViolation(final ObjectError error) {
return new Violation(fieldName, error.getDefaultMessage());
}

default List<Violation> createViolations(BindingResult bindingResult) {
return Stream.concat(
bindingResult.getFieldErrors().stream().map(this::createViolation),
bindingResult.getGlobalErrors().stream().map(this::createViolation)).collect(toList());
default List<Violation> createViolations(final BindingResult result) {
final Stream<Violation> fieldErrors = result.getFieldErrors().stream().map(this::createViolation);
final Stream<Violation> globalErrors = result.getGlobalErrors().stream().map(this::createViolation);
return Stream.concat(fieldErrors, globalErrors).collect(toList());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.zalando.problem.spring.web.advice.AdviceTrait;

import javax.ws.rs.core.Response.StatusType;
import java.net.URI;
import java.util.Collection;
import java.util.List;

Expand All @@ -15,6 +16,10 @@

interface BaseValidationAdviceTrait extends AdviceTrait {

default URI defaultConstraintViolationType() {
return ConstraintViolationProblem.TYPE;
}

default StatusType defaultConstraintViolationStatus() {
return BAD_REQUEST;
}
Expand All @@ -32,13 +37,17 @@ default String formatFieldName(final String fieldName) {
default ResponseEntity<Problem> newConstraintViolationProblem(final Throwable throwable,
final Collection<Violation> stream, final NativeWebRequest request) {

final URI type = defaultConstraintViolationType();
final StatusType status = defaultConstraintViolationStatus();

final List<Violation> violations = stream.stream()
// sorting to make tests deterministic
.sorted(comparing(Violation::getField).thenComparing(Violation::getMessage))
.collect(toList());

return create(throwable, new ConstraintViolationProblem(status, violations), request);
final Problem problem = new ConstraintViolationProblem(type, status, violations);

return create(throwable, problem, request);
}

}
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package org.zalando.problem.spring.web.advice.validation;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonTypeName;
import org.zalando.problem.ThrowableProblem;

import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import javax.ws.rs.core.Response.StatusType;
import java.net.URI;
Expand All @@ -13,30 +10,28 @@
import java.util.List;

@Immutable
@JsonTypeName(ConstraintViolationProblem.TYPE_VALUE)
public final class ConstraintViolationProblem extends ThrowableProblem {

public static final String TYPE_VALUE = "https://zalando.github.io/problem/constraint-violation";
public static final URI TYPE = URI.create(TYPE_VALUE);

private final URI type;
private final StatusType status;
private final String detail;
private final List<Violation> violations;

public ConstraintViolationProblem(final StatusType status, final List<Violation> violations) {
this(status, null, new ArrayList<>(violations));
this(TYPE, status, new ArrayList<>(violations));
}

@JsonCreator
ConstraintViolationProblem(final StatusType status, @Nullable final String detail, final List<Violation> violations) {
ConstraintViolationProblem(final URI type, final StatusType status, final List<Violation> violations) {
this.type = type;
this.status = status;
this.detail = detail;
this.violations = Collections.unmodifiableList(violations);
}

@Override
public URI getType() {
return TYPE;
return type;
}

@Override
Expand All @@ -49,11 +44,6 @@ public StatusType getStatus() {
return status;
}

@Override
public String getDetail() {
return detail;
}

public List<Violation> getViolations() {
return violations;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

import java.util.List;

// TODO package private
// TODO rename to MixIn
public interface ConstraintViolationProblemMixin {

@JsonProperty("violations")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@

/**
* A companion to {@link org.zalando.problem.ProblemModule} to enable serialization
* of {@link ConstraintViolationProblem} and {@link Violation} without relying on autodetection.
* of {@link ConstraintViolationProblem} and {@link Violation} without relying on auto detection.
*/
public class ConstraintViolationProblemModule extends Module {
@Override
public void setupModule(SetupContext context) {
final SimpleModule module = new SimpleModule();
public final class ConstraintViolationProblemModule extends Module {

module.setMixInAnnotation(Violation.class, ViolationMixIn.class);
module.setMixInAnnotation(ConstraintViolationProblem.class, ConstraintViolationProblemMixin.class);
@Override
public void setupModule(final SetupContext context) {
final SimpleModule module = new SimpleModule()
.setMixInAnnotation(ConstraintViolationProblem.class, ConstraintViolationProblemMixin.class)
.setMixInAnnotation(Violation.class, ViolationMixIn.class);

module.setupModule(context);
}
Expand All @@ -27,10 +27,11 @@ public String getModuleName() {
return ConstraintViolationProblemModule.class.getSimpleName();
}

@SuppressWarnings("deprecation")
@Override
@SuppressWarnings("deprecation")
public Version version() {
return VersionUtil.mavenVersionFor(ConstraintViolationProblemModule.class.getClassLoader(),
"org.zalando", "problem-spring-web");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.fasterxml.jackson.annotation.JsonProperty;

// TODO package private
public interface ViolationMixIn {

@JsonProperty("field")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package org.zalando.problem.spring.web.advice.validation;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.zalando.problem.ProblemModule;
import org.zalando.problem.validation.ConstraintViolationProblemModule;

import java.net.URI;

import static com.jayway.jsonassert.JsonAssert.with;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;

@Slf4j
public class ConstraintViolationProblemModuleTest {

@Test
public void shouldSerializeWithoutAutoDetect() throws JsonProcessingException {
final ObjectMapper mapper = new ObjectMapper()
.disable(MapperFeature.AUTO_DETECT_FIELDS)
.disable(MapperFeature.AUTO_DETECT_GETTERS)
.disable(MapperFeature.AUTO_DETECT_IS_GETTERS)
.registerModule(new ProblemModule())
.registerModule(new ConstraintViolationProblemModule());

final Violation violation = new Violation("bob", "was missing");
final ConstraintViolationProblem unit = new ConstraintViolationProblem(BAD_REQUEST, singletonList(violation));

with(mapper.writeValueAsString(unit))
.assertThat("status", is(400))
.assertThat("type", is(ConstraintViolationProblem.TYPE_VALUE))
.assertThat("title", is("Constraint Violation"))
.assertThat("violations", hasSize(1))
.assertThat("violations.*.field", contains("bob"))
.assertThat("violations.*.message", contains("was missing"));
}

@Test
public void shouldSerializeCustomType() throws JsonProcessingException {
final ObjectMapper mapper = new ObjectMapper()
.registerModule(new ProblemModule())
.registerModule(new ConstraintViolationProblemModule());

final URI type = URI.create("foo");
final ConstraintViolationProblem unit = new ConstraintViolationProblem(type, BAD_REQUEST, emptyList());

with(mapper.writeValueAsString(unit))
.assertThat("type", is("foo"));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;

public class ConstraintViolationProblemTest {

Expand All @@ -22,7 +21,6 @@ public void shouldCreateCopyOfViolations(){
violations.clear();

assertThat(problem.getViolations(), hasSize(1));
assertThat(problem.getViolations().get(0).getField(), is("x"));
assertThat(problem.getViolations().get(0).getMessage(), is("y"));
}

}

This file was deleted.

0 comments on commit 110c6aa

Please sign in to comment.