mirror of
				https://github.com/scm-manager/scm-manager.git
				synced 2025-10-31 10:35:56 +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; | package sonia.scm.api.v2; | ||||||
| 
 | 
 | ||||||
| import org.jboss.resteasy.api.validation.ResteasyViolationException; | 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.inject.Inject; | ||||||
| import javax.ws.rs.core.MediaType; | import javax.ws.rs.core.MediaType; | ||||||
| @@ -10,12 +10,12 @@ import javax.ws.rs.ext.ExceptionMapper; | |||||||
| import javax.ws.rs.ext.Provider; | import javax.ws.rs.ext.Provider; | ||||||
| 
 | 
 | ||||||
| @Provider | @Provider | ||||||
| public class ValidationExceptionMapper implements ExceptionMapper<ResteasyViolationException> { | public class ResteasyValidationExceptionMapper implements ExceptionMapper<ResteasyViolationException> { | ||||||
| 
 | 
 | ||||||
|   private final ViolationExceptionToErrorDtoMapper mapper; |   private final ResteasyViolationExceptionToErrorDtoMapper mapper; | ||||||
| 
 | 
 | ||||||
|   @Inject |   @Inject | ||||||
|   public ValidationExceptionMapper(ViolationExceptionToErrorDtoMapper mapper) { |   public ResteasyValidationExceptionMapper(ResteasyViolationExceptionToErrorDtoMapper mapper) { | ||||||
|     this.mapper = 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(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()); |     bind(ExceptionWithContextToErrorDtoMapper.class).to(Mappers.getMapper(ExceptionWithContextToErrorDtoMapper.class).getClass()); | ||||||
|  |  | ||||||
|     // no mapstruct required |     // no mapstruct required | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ import java.util.List; | |||||||
| import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||||
| 
 | 
 | ||||||
| @Mapper | @Mapper | ||||||
| public abstract class ViolationExceptionToErrorDtoMapper { | public abstract class ResteasyViolationExceptionToErrorDtoMapper { | ||||||
| 
 | 
 | ||||||
|   @Mapping(target = "errorCode", ignore = true) |   @Mapping(target = "errorCode", ignore = true) | ||||||
|   @Mapping(target = "transactionId", 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