mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-14 01:15:44 +01:00
Merge 2.0.0-m3
This commit is contained in:
@@ -6,6 +6,8 @@ import static java.util.Collections.unmodifiableList;
|
|||||||
|
|
||||||
public abstract class ExceptionWithContext extends RuntimeException {
|
public abstract class ExceptionWithContext extends RuntimeException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 4327413456580409224L;
|
||||||
|
|
||||||
private final List<ContextEntry> context;
|
private final List<ContextEntry> context;
|
||||||
|
|
||||||
public ExceptionWithContext(List<ContextEntry> context, String message) {
|
public ExceptionWithContext(List<ContextEntry> context, String message) {
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import static java.util.stream.Collectors.joining;
|
|||||||
|
|
||||||
public class NotFoundException extends ExceptionWithContext {
|
public class NotFoundException extends ExceptionWithContext {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1710455380886499111L;
|
||||||
|
|
||||||
private static final String CODE = "AGR7UzkhA1";
|
private static final String CODE = "AGR7UzkhA1";
|
||||||
|
|
||||||
public NotFoundException(Class type, String id) {
|
public NotFoundException(Class type, String id) {
|
||||||
|
|||||||
@@ -0,0 +1,136 @@
|
|||||||
|
package sonia.scm;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import static java.util.Collections.unmodifiableCollection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this exception to handle invalid input values that cannot be handled using
|
||||||
|
* <a href="https://docs.oracle.com/javaee/7/tutorial/bean-validation001.htm#GIRCZ">JEE bean validation</a>.
|
||||||
|
* Use the {@link Builder} to conditionally create a new exception:
|
||||||
|
* <pre>
|
||||||
|
* Builder
|
||||||
|
* .doThrow()
|
||||||
|
* .violation("name or alias must not be empty if not anonymous", "myParameter", "name")
|
||||||
|
* .violation("name or alias must not be empty if not anonymous", "myParameter", "alias")
|
||||||
|
* .when(myParameter.getName() == null && myParameter.getAlias() == null && !myParameter.isAnonymous())
|
||||||
|
* .andThrow()
|
||||||
|
* .violation("name must be empty if anonymous", "myParameter", "name")
|
||||||
|
* .when(myParameter.getName() != null && myParameter.isAnonymous());
|
||||||
|
* </pre>
|
||||||
|
* Mind that using this way you do not have to use if-else constructs.
|
||||||
|
*/
|
||||||
|
public class ScmConstraintViolationException extends RuntimeException implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 6904534307450229887L;
|
||||||
|
|
||||||
|
private final Collection<ScmConstraintViolation> violations;
|
||||||
|
|
||||||
|
private final String furtherInformation;
|
||||||
|
|
||||||
|
private ScmConstraintViolationException(Collection<ScmConstraintViolation> violations, String furtherInformation) {
|
||||||
|
this.violations = violations;
|
||||||
|
this.furtherInformation = furtherInformation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The violations that caused this exception.
|
||||||
|
*/
|
||||||
|
public Collection<ScmConstraintViolation> getViolations() {
|
||||||
|
return unmodifiableCollection(violations);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An optional URL for more informations about this constraint violation.
|
||||||
|
*/
|
||||||
|
public String getUrl() {
|
||||||
|
return furtherInformation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder to conditionally create constraint violations.
|
||||||
|
*/
|
||||||
|
public static class Builder {
|
||||||
|
private final Collection<ScmConstraintViolation> violations = new ArrayList<>();
|
||||||
|
private String furtherInformation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this to create a new builder instance.
|
||||||
|
*/
|
||||||
|
public static Builder doThrow() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets this builder to check for further violations.
|
||||||
|
* @return this builder instance.
|
||||||
|
*/
|
||||||
|
public Builder andThrow() {
|
||||||
|
this.violations.clear();
|
||||||
|
this.furtherInformation = null;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the violation with a custom message and the affected property. When more than one property is affected,
|
||||||
|
* you can call this method multiple times.
|
||||||
|
* @param message The message describing the violation.
|
||||||
|
* @param pathElements The affected property denoted by the path to reach this property,
|
||||||
|
* eg. "someParameter", "complexProperty", "attribute"
|
||||||
|
* @return this builder instance.
|
||||||
|
*/
|
||||||
|
public Builder violation(String message, String... pathElements) {
|
||||||
|
this.violations.add(new ScmConstraintViolation(message, pathElements));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this to specify a URL with further information about this violation and hints how to solve this.
|
||||||
|
* This is optional.
|
||||||
|
* @return this builder instance.
|
||||||
|
*/
|
||||||
|
public Builder withFurtherInformation(String furtherInformation) {
|
||||||
|
this.furtherInformation = furtherInformation;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the given condition is <code>true</code>, a exception will be thrown. Otherwise this simply resets this
|
||||||
|
* builder and does nothing else.
|
||||||
|
* @param condition The condition that indicates a violation of this constraint.
|
||||||
|
* @return this builder instance.
|
||||||
|
*/
|
||||||
|
public Builder when(boolean condition) {
|
||||||
|
if (condition && !this.violations.isEmpty()) {
|
||||||
|
throw new ScmConstraintViolationException(violations, furtherInformation);
|
||||||
|
}
|
||||||
|
return andThrow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A single constraint violation.
|
||||||
|
*/
|
||||||
|
public static class ScmConstraintViolation implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -6900317468157084538L;
|
||||||
|
|
||||||
|
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