mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-12 08:25:44 +01:00
Create error response for concurrent modifications
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
package sonia.scm.api.rest;
|
||||
|
||||
import sonia.scm.ConcurrentModificationException;
|
||||
import sonia.scm.api.v2.resources.ErrorDto;
|
||||
import sonia.scm.web.VndMediaType;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.ext.ExceptionMapper;
|
||||
@@ -10,6 +12,6 @@ import javax.ws.rs.ext.Provider;
|
||||
public class ConcurrentModificationExceptionMapper implements ExceptionMapper<ConcurrentModificationException> {
|
||||
@Override
|
||||
public Response toResponse(ConcurrentModificationException exception) {
|
||||
return Response.status(Response.Status.CONFLICT).build();
|
||||
return Response.status(Response.Status.CONFLICT).entity(ErrorDto.from(exception)).type(VndMediaType.ERROR_TYPE).build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package sonia.scm.api.v2.resources;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.Getter;
|
||||
import org.slf4j.MDC;
|
||||
import sonia.scm.ConcurrentModificationException;
|
||||
import sonia.scm.ContextEntry;
|
||||
import sonia.scm.NotFoundException;
|
||||
|
||||
@@ -32,4 +33,8 @@ public class ErrorDto {
|
||||
static ErrorDto from(NotFoundException notFoundException) {
|
||||
return new ErrorDto(MDC.get("transaction_id"), "todo", notFoundException.getContext(), notFoundException.getMessage());
|
||||
}
|
||||
|
||||
public static ErrorDto from(ConcurrentModificationException concurrentModificationException) {
|
||||
return new ErrorDto(MDC.get("transaction_id"), "todo", concurrentModificationException.getContext(), concurrentModificationException.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ public class GroupResource {
|
||||
@ResponseCode(code = 500, condition = "internal server error")
|
||||
})
|
||||
@TypeHint(TypeHint.NO_CONTENT.class)
|
||||
public Response update(@PathParam("id") String name, @Valid GroupDto groupDto) throws ConcurrentModificationException {
|
||||
public Response update(@PathParam("id") String name, @Valid GroupDto groupDto) {
|
||||
return adapter.update(name, existing -> dtoToGroupMapper.map(groupDto));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ class IdResourceManagerAdapter<MODEL_OBJECT extends ModelObject,
|
||||
return singleAdapter.get(loadBy(id), mapToDto);
|
||||
}
|
||||
|
||||
public Response update(String id, Function<MODEL_OBJECT, MODEL_OBJECT> applyChanges) throws ConcurrentModificationException {
|
||||
public Response update(String id, Function<MODEL_OBJECT, MODEL_OBJECT> applyChanges) {
|
||||
return singleAdapter.update(
|
||||
loadBy(id),
|
||||
applyChanges,
|
||||
|
||||
@@ -138,7 +138,7 @@ public class RepositoryResource {
|
||||
@ResponseCode(code = 500, condition = "internal server error")
|
||||
})
|
||||
@TypeHint(TypeHint.NO_CONTENT.class)
|
||||
public Response update(@PathParam("namespace") String namespace, @PathParam("name") String name, @Valid RepositoryDto repositoryDto) throws ConcurrentModificationException {
|
||||
public Response update(@PathParam("namespace") String namespace, @PathParam("name") String name, @Valid RepositoryDto repositoryDto) {
|
||||
return adapter.update(
|
||||
loadBy(namespace, name),
|
||||
existing -> processUpdate(repositoryDto, existing),
|
||||
@@ -204,7 +204,8 @@ public class RepositoryResource {
|
||||
}
|
||||
|
||||
private Supplier<Repository> loadBy(String namespace, String name) {
|
||||
return () -> Optional.ofNullable(manager.get(new NamespaceAndName(namespace, name))).orElseThrow(() -> new NotFoundException(Repository.class, namespace + "/" + name));
|
||||
NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, name);
|
||||
return () -> Optional.ofNullable(manager.get(namespaceAndName)).orElseThrow(() -> NotFoundException.notFound(namespaceAndName).build());
|
||||
}
|
||||
|
||||
private Predicate<Repository> nameAndNamespaceStaysTheSame(String namespace, String name) {
|
||||
|
||||
@@ -33,6 +33,7 @@ class SingleResourceManagerAdapter<MODEL_OBJECT extends ModelObject,
|
||||
DTO extends HalRepresentation> extends AbstractManagerResource<MODEL_OBJECT> {
|
||||
|
||||
private final Function<Throwable, Optional<Response>> errorHandler;
|
||||
private final Class<MODEL_OBJECT> type;
|
||||
|
||||
SingleResourceManagerAdapter(Manager<MODEL_OBJECT> manager, Class<MODEL_OBJECT> type) {
|
||||
this(manager, type, e -> Optional.empty());
|
||||
@@ -44,6 +45,7 @@ class SingleResourceManagerAdapter<MODEL_OBJECT extends ModelObject,
|
||||
Function<Throwable, Optional<Response>> errorHandler) {
|
||||
super(manager, type);
|
||||
this.errorHandler = errorHandler;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,14 +60,14 @@ class SingleResourceManagerAdapter<MODEL_OBJECT extends ModelObject,
|
||||
* Update the model object for the given id according to the given function and returns a corresponding http response.
|
||||
* This handles all corner cases, eg. no matching object for the id or missing privileges.
|
||||
*/
|
||||
Response update(Supplier<MODEL_OBJECT> reader, Function<MODEL_OBJECT, MODEL_OBJECT> applyChanges, Predicate<MODEL_OBJECT> hasSameKey) throws ConcurrentModificationException {
|
||||
Response update(Supplier<MODEL_OBJECT> reader, Function<MODEL_OBJECT, MODEL_OBJECT> applyChanges, Predicate<MODEL_OBJECT> hasSameKey) {
|
||||
MODEL_OBJECT existingModelObject = reader.get();
|
||||
MODEL_OBJECT changedModelObject = applyChanges.apply(existingModelObject);
|
||||
if (!hasSameKey.test(changedModelObject)) {
|
||||
return Response.status(BAD_REQUEST).entity("illegal change of id").build();
|
||||
}
|
||||
else if (modelObjectWasModifiedConcurrently(existingModelObject, changedModelObject)) {
|
||||
throw new ConcurrentModificationException();
|
||||
throw new ConcurrentModificationException(type, existingModelObject.getId());
|
||||
}
|
||||
return update(getId(existingModelObject), changedModelObject);
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ public class UserResource {
|
||||
@ResponseCode(code = 500, condition = "internal server error")
|
||||
})
|
||||
@TypeHint(TypeHint.NO_CONTENT.class)
|
||||
public Response update(@PathParam("id") String name, @Valid UserDto userDto) throws ConcurrentModificationException {
|
||||
public Response update(@PathParam("id") String name, @Valid UserDto userDto) {
|
||||
return adapter.update(name, existing -> dtoToUserMapper.map(userDto, existing.getPassword()));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user