From 3822c4b51cda6cf6d08c1b292dffda2b5bb91226 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Thu, 16 Aug 2018 16:53:20 +0200 Subject: [PATCH] Include resteasy javaee 7 validation with hibernate --- scm-webapp/pom.xml | 16 ++++- .../scm/api/v2/ValidationExceptionMapper.java | 58 +++++++++++++++++++ .../RepositoryCollectionResource.java | 17 +++++- .../scm/api/v2/resources/RepositoryDto.java | 2 + 4 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 scm-webapp/src/main/java/sonia/scm/api/v2/ValidationExceptionMapper.java diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index 965bddf2b0..6d3b94412d 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -99,6 +99,16 @@ jackson-datatype-jsr310 ${jackson.version} + + javax + javaee-api + 7.0 + + + org.hibernate.validator + hibernate-validator + 6.0.12.Final + @@ -131,7 +141,11 @@ org.jboss.resteasy resteasy-servlet-initializer - + + org.jboss.resteasy + resteasy-validator-provider-11 + ${resteasy.version} + diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/ValidationExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/ValidationExceptionMapper.java new file mode 100644 index 0000000000..53681bc9af --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/ValidationExceptionMapper.java @@ -0,0 +1,58 @@ +package sonia.scm.api.v2; + +import lombok.Getter; +import org.jboss.resteasy.api.validation.ResteasyViolationException; + +import javax.validation.ConstraintViolation; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.List; +import java.util.stream.Collectors; + +@Provider +public class ValidationExceptionMapper implements ExceptionMapper { + + @Override + public Response toResponse(ResteasyViolationException exception) { + + List violations = + exception.getConstraintViolations() + .stream() + .map(ConstraintViolationBean::new) + .collect(Collectors.toList()); + + return Response + .status(Response.Status.BAD_REQUEST) + .type(MediaType.APPLICATION_JSON_TYPE) + .entity(new ValidationError(violations)) + .build(); + } + + @Getter + public static class ValidationError { + @XmlElement(name = "violation") + @XmlElementWrapper(name = "violations") + private List violoations; + + public ValidationError(List violoations) { + this.violoations = violoations; + } + } + + @XmlRootElement(name = "violation") + @Getter + public static class ConstraintViolationBean { + private String path; + private String message; + + public ConstraintViolationBean(ConstraintViolation violation) { + message = violation.getMessage(); + path = violation.getPropertyPath().toString(); + } + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryCollectionResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryCollectionResource.java index c6d54e7f4e..9fef539756 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryCollectionResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryCollectionResource.java @@ -1,13 +1,24 @@ package sonia.scm.api.v2.resources; -import com.webcohesion.enunciate.metadata.rs.*; +import com.webcohesion.enunciate.metadata.rs.ResponseCode; +import com.webcohesion.enunciate.metadata.rs.ResponseHeader; +import com.webcohesion.enunciate.metadata.rs.ResponseHeaders; +import com.webcohesion.enunciate.metadata.rs.StatusCodes; +import com.webcohesion.enunciate.metadata.rs.TypeHint; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryException; import sonia.scm.repository.RepositoryManager; import sonia.scm.web.VndMediaType; import javax.inject.Inject; -import javax.ws.rs.*; +import javax.validation.Valid; +import javax.ws.rs.Consumes; +import javax.ws.rs.DefaultValue; +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.Response; public class RepositoryCollectionResource { @@ -76,7 +87,7 @@ public class RepositoryCollectionResource { }) @TypeHint(TypeHint.NO_CONTENT.class) @ResponseHeaders(@ResponseHeader(name = "Location", description = "uri to the created repository")) - public Response create(RepositoryDto repositoryDto) throws RepositoryException { + public Response create(@Valid RepositoryDto repositoryDto) throws RepositoryException { return adapter.create(repositoryDto, () -> dtoToRepositoryMapper.map(repositoryDto, null), repository -> resourceLinks.repository().self(repository.getNamespace(), repository.getName())); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryDto.java index bcc8e16ebb..94fdb68cd6 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryDto.java @@ -6,6 +6,7 @@ import de.otto.edison.hal.Links; import lombok.Getter; import lombok.Setter; +import javax.validation.constraints.Pattern; import java.time.Instant; import java.util.List; import java.util.Map; @@ -20,6 +21,7 @@ public class RepositoryDto extends HalRepresentation { @JsonInclude(JsonInclude.Include.NON_NULL) private Instant lastModified; private String namespace; + @Pattern(regexp = "[\\w-]+", message = "The name must be a valid identifyer") private String name; private boolean archived = false; private String type;