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; | ||||
| 
 | ||||
| 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