mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-10-30 18:15:52 +01:00
Add support for custom violation exceptions
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
package sonia.scm;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import static java.util.Collections.unmodifiableCollection;
|
||||
|
||||
public class ScmConstraintViolationException extends RuntimeException {
|
||||
|
||||
private final Collection<ScmConstraintViolation> violations;
|
||||
|
||||
private final String furtherInformations;
|
||||
|
||||
private ScmConstraintViolationException(Collection<ScmConstraintViolation> violations, String furtherInformations) {
|
||||
this.violations = violations;
|
||||
this.furtherInformations = furtherInformations;
|
||||
}
|
||||
|
||||
public Collection<ScmConstraintViolation> getViolations() {
|
||||
return unmodifiableCollection(violations);
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return furtherInformations;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private final Collection<ScmConstraintViolation> violations = new ArrayList<>();
|
||||
private String furtherInformations;
|
||||
|
||||
public static Builder doThrow() {
|
||||
Builder builder = new Builder();
|
||||
return builder;
|
||||
}
|
||||
|
||||
public Builder andThrow() {
|
||||
this.violations.clear();
|
||||
this.furtherInformations = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder violation(String message, String... pathElements) {
|
||||
this.violations.add(new ScmConstraintViolation(message, pathElements));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withFurtherInformations(String furtherInformations) {
|
||||
this.furtherInformations = furtherInformations;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void when(boolean condition) {
|
||||
if (condition && !this.violations.isEmpty()) {
|
||||
throw new ScmConstraintViolationException(violations, furtherInformations);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ScmConstraintViolation {
|
||||
private final String message;
|
||||
private final String path;
|
||||
|
||||
private ScmConstraintViolation(String message, String... pathElements) {
|
||||
this.message = message;
|
||||
this.path = String.join(".", pathElements);
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public String getPropertyPath() {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package sonia.scm.api.v2;
|
||||
|
||||
import org.jboss.resteasy.api.validation.ResteasyViolationException;
|
||||
import sonia.scm.api.v2.resources.ViolationExceptionToErrorDtoMapper;
|
||||
import sonia.scm.api.v2.resources.ResteasyViolationExceptionToErrorDtoMapper;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
@@ -10,12 +10,12 @@ import javax.ws.rs.ext.ExceptionMapper;
|
||||
import javax.ws.rs.ext.Provider;
|
||||
|
||||
@Provider
|
||||
public class ValidationExceptionMapper implements ExceptionMapper<ResteasyViolationException> {
|
||||
public class ResteasyValidationExceptionMapper implements ExceptionMapper<ResteasyViolationException> {
|
||||
|
||||
private final ViolationExceptionToErrorDtoMapper mapper;
|
||||
private final ResteasyViolationExceptionToErrorDtoMapper mapper;
|
||||
|
||||
@Inject
|
||||
public ValidationExceptionMapper(ViolationExceptionToErrorDtoMapper mapper) {
|
||||
public ResteasyValidationExceptionMapper(ResteasyViolationExceptionToErrorDtoMapper mapper) {
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package sonia.scm.api.v2;
|
||||
|
||||
import sonia.scm.ScmConstraintViolationException;
|
||||
import sonia.scm.api.v2.resources.ScmViolationExceptionToErrorDtoMapper;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.ext.ExceptionMapper;
|
||||
import javax.ws.rs.ext.Provider;
|
||||
|
||||
@Provider
|
||||
public class ScmConstraintValidationExceptionMapper implements ExceptionMapper<ScmConstraintViolationException> {
|
||||
|
||||
private final ScmViolationExceptionToErrorDtoMapper mapper;
|
||||
|
||||
@Inject
|
||||
public ScmConstraintValidationExceptionMapper(ScmViolationExceptionToErrorDtoMapper mapper) {
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response toResponse(ScmConstraintViolationException exception) {
|
||||
return Response
|
||||
.status(Response.Status.BAD_REQUEST)
|
||||
.type(MediaType.APPLICATION_JSON_TYPE)
|
||||
.entity(mapper.map(exception))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,8 @@ public class MapperModule extends AbstractModule {
|
||||
|
||||
bind(ReducedObjectModelToDtoMapper.class).to(Mappers.getMapper(ReducedObjectModelToDtoMapper.class).getClass());
|
||||
|
||||
bind(ViolationExceptionToErrorDtoMapper.class).to(Mappers.getMapper(ViolationExceptionToErrorDtoMapper.class).getClass());
|
||||
bind(ResteasyViolationExceptionToErrorDtoMapper.class).to(Mappers.getMapper(ResteasyViolationExceptionToErrorDtoMapper.class).getClass());
|
||||
bind(ScmViolationExceptionToErrorDtoMapper.class).to(Mappers.getMapper(ScmViolationExceptionToErrorDtoMapper.class).getClass());
|
||||
bind(ExceptionWithContextToErrorDtoMapper.class).to(Mappers.getMapper(ExceptionWithContextToErrorDtoMapper.class).getClass());
|
||||
|
||||
// no mapstruct required
|
||||
|
||||
@@ -12,7 +12,7 @@ import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Mapper
|
||||
public abstract class ViolationExceptionToErrorDtoMapper {
|
||||
public abstract class ResteasyViolationExceptionToErrorDtoMapper {
|
||||
|
||||
@Mapping(target = "errorCode", ignore = true)
|
||||
@Mapping(target = "transactionId", ignore = true)
|
||||
@@ -0,0 +1,53 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import org.mapstruct.AfterMapping;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import org.slf4j.MDC;
|
||||
import sonia.scm.ScmConstraintViolationException;
|
||||
import sonia.scm.ScmConstraintViolationException.ScmConstraintViolation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Mapper
|
||||
public abstract class ScmViolationExceptionToErrorDtoMapper {
|
||||
|
||||
@Mapping(target = "errorCode", ignore = true)
|
||||
@Mapping(target = "transactionId", ignore = true)
|
||||
@Mapping(target = "context", ignore = true)
|
||||
public abstract ErrorDto map(ScmConstraintViolationException exception);
|
||||
|
||||
@AfterMapping
|
||||
void setTransactionId(@MappingTarget ErrorDto dto) {
|
||||
dto.setTransactionId(MDC.get("transaction_id"));
|
||||
}
|
||||
|
||||
@AfterMapping
|
||||
void mapViolations(ScmConstraintViolationException exception, @MappingTarget ErrorDto dto) {
|
||||
List<ErrorDto.ConstraintViolationDto> violations =
|
||||
exception.getViolations()
|
||||
.stream()
|
||||
.map(this::createViolationDto)
|
||||
.collect(Collectors.toList());
|
||||
dto.setViolations(violations);
|
||||
}
|
||||
|
||||
private ErrorDto.ConstraintViolationDto createViolationDto(ScmConstraintViolation violation) {
|
||||
ErrorDto.ConstraintViolationDto constraintViolationDto = new ErrorDto.ConstraintViolationDto();
|
||||
constraintViolationDto.setMessage(violation.getMessage());
|
||||
constraintViolationDto.setPath(violation.getPropertyPath());
|
||||
return constraintViolationDto;
|
||||
}
|
||||
|
||||
@AfterMapping
|
||||
void setErrorCode(@MappingTarget ErrorDto dto) {
|
||||
dto.setErrorCode("3zR9vPNIE1");
|
||||
}
|
||||
|
||||
@AfterMapping
|
||||
void setMessage(@MappingTarget ErrorDto dto) {
|
||||
dto.setMessage("input violates conditions (see violation list)");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user