Create error response for already existing entities

This commit is contained in:
René Pfeuffer
2018-10-25 15:31:42 +02:00
parent d185743ef0
commit 6eb3c38655
58 changed files with 249 additions and 226 deletions

View File

@@ -34,15 +34,15 @@ public class ManagerDaoAdapter<T extends ModelObject> {
}
}
public T create(T newObject, Supplier<PermissionCheck> permissionCheck, AroundHandler<T> beforeCreate, AroundHandler<T> afterCreate) throws AlreadyExistsException {
public T create(T newObject, Supplier<PermissionCheck> permissionCheck, AroundHandler<T> beforeCreate, AroundHandler<T> afterCreate) {
return create(newObject, permissionCheck, beforeCreate, afterCreate, dao::contains);
}
public T create(T newObject, Supplier<PermissionCheck> permissionCheck, AroundHandler<T> beforeCreate, AroundHandler<T> afterCreate, Predicate<T> existsCheck) throws AlreadyExistsException {
public T create(T newObject, Supplier<PermissionCheck> permissionCheck, AroundHandler<T> beforeCreate, AroundHandler<T> afterCreate, Predicate<T> existsCheck) {
permissionCheck.get().check();
AssertUtil.assertIsValid(newObject);
if (existsCheck.test(newObject)) {
throw new AlreadyExistsException();
throw new AlreadyExistsException(newObject);
}
newObject.setCreationDate(System.currentTimeMillis());
beforeCreate.handle(newObject);

View File

@@ -1,6 +1,8 @@
package sonia.scm.api.rest;
import sonia.scm.AlreadyExistsException;
import sonia.scm.api.v2.resources.ErrorDto;
import sonia.scm.web.VndMediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
@@ -12,7 +14,8 @@ public class AlreadyExistsExceptionMapper implements ExceptionMapper<AlreadyExis
@Override
public Response toResponse(AlreadyExistsException exception) {
return Response.status(Status.CONFLICT)
.entity(exception.getMessage())
.entity(ErrorDto.from(exception))
.type(VndMediaType.ERROR_TYPE)
.build();
}
}

View File

@@ -44,8 +44,6 @@ import org.apache.shiro.authc.credential.PasswordService;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.ConcurrentModificationException;
import sonia.scm.NotFoundException;
import sonia.scm.api.rest.RestActionResult;
import sonia.scm.security.Role;
import sonia.scm.security.ScmSecurityException;

View File

@@ -45,7 +45,6 @@ import com.webcohesion.enunciate.metadata.rs.TypeHint;
import org.apache.shiro.SecurityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.AlreadyExistsException;
import sonia.scm.NotFoundException;
import sonia.scm.NotSupportedFeatuerException;
import sonia.scm.Type;
@@ -516,13 +515,6 @@ public class RepositoryImportResource
// repository = new Repository(null, type, name);
manager.create(repository);
}
catch (AlreadyExistsException ex)
{
logger.warn("a {} repository with the name {} already exists", type,
name);
throw new WebApplicationException(Response.Status.CONFLICT);
}
catch (InternalRepositoryException ex)
{
handleGenericCreationFailure(ex, type, name);

View File

@@ -28,13 +28,13 @@
*/
package sonia.scm.api.v2.resources;
package sonia.scm.api.v2;
import sonia.scm.NotFoundException;
import sonia.scm.api.v2.resources.ErrorDto;
import sonia.scm.web.VndMediaType;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

View File

@@ -27,6 +27,9 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import java.io.IOException;
import static sonia.scm.ContextEntry.ContextBuilder.entity;
import static sonia.scm.NotFoundException.notFound;
public class BranchRootResource {
private final RepositoryServiceFactory serviceFactory;
@@ -105,7 +108,7 @@ public class BranchRootResource {
.stream()
.anyMatch(branch -> branchName.equals(branch.getName()));
if (!branchExists){
throw NotFoundException.notFound(Branch.class, branchName).in(Repository.class, namespace + "/" + name).build();
throw notFound(entity(Branch.class, branchName).in(Repository.class, namespace + "/" + name));
}
Repository repository = repositoryService.getRepository();
RepositoryPermissions.read(repository).check();

View File

@@ -1,7 +1,6 @@
package sonia.scm.api.v2.resources;
import de.otto.edison.hal.HalRepresentation;
import sonia.scm.AlreadyExistsException;
import sonia.scm.Manager;
import sonia.scm.ModelObject;
import sonia.scm.PageResult;
@@ -47,7 +46,7 @@ class CollectionResourceManagerAdapter<MODEL_OBJECT extends ModelObject,
* Creates a model object for the given dto and returns a corresponding http response.
* This handles all corner cases, eg. no conflicts or missing privileges.
*/
public Response create(DTO dto, Supplier<MODEL_OBJECT> modelObjectSupplier, Function<MODEL_OBJECT, String> uriCreator) throws AlreadyExistsException {
public Response create(DTO dto, Supplier<MODEL_OBJECT> modelObjectSupplier, Function<MODEL_OBJECT, String> uriCreator) {
if (dto == null) {
return Response.status(BAD_REQUEST).build();
}

View File

@@ -3,9 +3,8 @@ 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;
import sonia.scm.ExceptionWithContext;
import java.util.List;
@@ -30,11 +29,7 @@ public class ErrorDto {
this.url = url;
}
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());
public static ErrorDto from(ExceptionWithContext exception) {
return new ErrorDto(MDC.get("transaction_id"), "todo", exception.getContext(), exception.getMessage());
}
}

View File

@@ -5,7 +5,6 @@ import com.webcohesion.enunciate.metadata.rs.ResponseHeader;
import com.webcohesion.enunciate.metadata.rs.ResponseHeaders;
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import com.webcohesion.enunciate.metadata.rs.TypeHint;
import sonia.scm.AlreadyExistsException;
import sonia.scm.group.Group;
import sonia.scm.group.GroupManager;
import sonia.scm.web.VndMediaType;
@@ -86,7 +85,7 @@ public class GroupCollectionResource {
})
@TypeHint(TypeHint.NO_CONTENT.class)
@ResponseHeaders(@ResponseHeader(name = "Location", description = "uri to the created group"))
public Response create(@Valid GroupDto groupDto) throws AlreadyExistsException {
public Response create(@Valid GroupDto groupDto) {
return adapter.create(groupDto,
() -> dtoToGroupMapper.map(groupDto),
group -> resourceLinks.group().self(group.getName()));

View File

@@ -3,8 +3,6 @@ package sonia.scm.api.v2.resources;
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import com.webcohesion.enunciate.metadata.rs.TypeHint;
import sonia.scm.ConcurrentModificationException;
import sonia.scm.NotFoundException;
import sonia.scm.group.Group;
import sonia.scm.group.GroupManager;
import sonia.scm.web.VndMediaType;

View File

@@ -1,8 +1,6 @@
package sonia.scm.api.v2.resources;
import de.otto.edison.hal.HalRepresentation;
import sonia.scm.AlreadyExistsException;
import sonia.scm.ConcurrentModificationException;
import sonia.scm.Manager;
import sonia.scm.ModelObject;
import sonia.scm.NotFoundException;
@@ -51,7 +49,7 @@ class IdResourceManagerAdapter<MODEL_OBJECT extends ModelObject,
return collectionAdapter.getAll(page, pageSize, sortBy, desc, mapToDto);
}
public Response create(DTO dto, Supplier<MODEL_OBJECT> modelObjectSupplier, Function<MODEL_OBJECT, String> uriCreator) throws AlreadyExistsException {
public Response create(DTO dto, Supplier<MODEL_OBJECT> modelObjectSupplier, Function<MODEL_OBJECT, String> uriCreator) {
return collectionAdapter.create(dto, modelObjectSupplier, uriCreator);
}

View File

@@ -29,6 +29,9 @@ import java.net.URI;
import java.util.Optional;
import java.util.function.Predicate;
import static sonia.scm.AlreadyExistsException.alreadyExists;
import static sonia.scm.ContextEntry.ContextBuilder.entity;
import static sonia.scm.NotFoundException.notFound;
import static sonia.scm.api.v2.resources.PermissionDto.GROUP_PREFIX;
@Slf4j
@@ -70,7 +73,7 @@ public class PermissionRootResource {
@TypeHint(TypeHint.NO_CONTENT.class)
@Consumes(VndMediaType.PERMISSION)
@Path("")
public Response create(@PathParam("namespace") String namespace, @PathParam("name") String name,@Valid PermissionDto permission) throws AlreadyExistsException, NotFoundException {
public Response create(@PathParam("namespace") String namespace, @PathParam("name") String name,@Valid PermissionDto permission) {
log.info("try to add new permission: {}", permission);
Repository repository = load(namespace, name);
RepositoryPermissions.permissionWrite(repository).check();
@@ -108,7 +111,7 @@ public class PermissionRootResource {
.filter(filterPermission(permissionName))
.map(permission -> modelToDtoMapper.map(permission, repository))
.findFirst()
.orElseThrow(() -> NotFoundException.notFound(Permission.class, namespace).in(Repository.class, namespace + "/" + name).build())
.orElseThrow(() -> notFound(entity(Permission.class, namespace).in(Repository.class, namespace + "/" + name)))
).build();
}
@@ -157,23 +160,23 @@ public class PermissionRootResource {
public Response update(@PathParam("namespace") String namespace,
@PathParam("name") String name,
@PathParam("permission-name") String permissionName,
@Valid PermissionDto permission) throws AlreadyExistsException {
@Valid PermissionDto permission) {
log.info("try to update the permission with name: {}. the modified permission is: {}", permissionName, permission);
Repository repository = load(namespace, name);
RepositoryPermissions.permissionWrite(repository).check();
String extractedPermissionName = getPermissionName(permissionName);
if (!isPermissionExist(new PermissionDto(extractedPermissionName, isGroupPermission(permissionName)), repository)) {
throw NotFoundException.notFound(Permission.class, namespace).in(Repository.class, namespace + "/" + name).build();
throw notFound(entity(Permission.class, namespace).in(Repository.class, namespace + "/" + name));
}
permission.setGroupPermission(isGroupPermission(permissionName));
if (!extractedPermissionName.equals(permission.getName())) {
checkPermissionAlreadyExists(permission, repository, "target permission " + permission.getName() + " already exists");
checkPermissionAlreadyExists(permission, repository);
}
Permission existingPermission = repository.getPermissions()
.stream()
.filter(filterPermission(permissionName))
.findFirst()
.orElseThrow(() -> NotFoundException.notFound(Permission.class, namespace).in(Repository.class, namespace + "/" + name).build());
.orElseThrow(() -> notFound(entity(Permission.class, namespace).in(Repository.class, namespace + "/" + name)));
dtoToModelMapper.modify(existingPermission, permission);
manager.modify(repository);
log.info("the permission with name: {} is updated.", permissionName);
@@ -241,7 +244,7 @@ public class PermissionRootResource {
private Repository load(String namespace, String name) {
NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, name);
return Optional.ofNullable(manager.get(namespaceAndName))
.orElseThrow(() -> NotFoundException.notFound(namespaceAndName).build());
.orElseThrow(() -> notFound(entity(namespaceAndName)));
}
/**
@@ -249,12 +252,11 @@ public class PermissionRootResource {
*
* @param permission the searched permission
* @param repository the repository to be inspected
* @param errorMessage error message
* @throws AlreadyExistsException if the permission already exists in the repository
*/
private void checkPermissionAlreadyExists(PermissionDto permission, Repository repository, String errorMessage) throws AlreadyExistsException {
private void checkPermissionAlreadyExists(PermissionDto permission, Repository repository) {
if (isPermissionExist(permission, repository)) {
throw new AlreadyExistsException(errorMessage);
throw alreadyExists(entity("permission", permission.getName()).in(repository));
}
}
@@ -263,10 +265,6 @@ public class PermissionRootResource {
.stream()
.anyMatch(p -> p.getName().equals(permission.getName()) && p.isGroupPermission() == permission.isGroupPermission());
}
private void checkPermissionAlreadyExists(PermissionDto permission, Repository repository) throws AlreadyExistsException {
checkPermissionAlreadyExists(permission, repository, "the permission " + permission.getName() + " already exist.");
}
}

View File

@@ -5,7 +5,6 @@ import com.webcohesion.enunciate.metadata.rs.ResponseHeader;
import com.webcohesion.enunciate.metadata.rs.ResponseHeaders;
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import com.webcohesion.enunciate.metadata.rs.TypeHint;
import sonia.scm.AlreadyExistsException;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryManager;
import sonia.scm.web.VndMediaType;
@@ -87,7 +86,7 @@ public class RepositoryCollectionResource {
})
@TypeHint(TypeHint.NO_CONTENT.class)
@ResponseHeaders(@ResponseHeader(name = "Location", description = "uri to the created repository"))
public Response create(@Valid RepositoryDto repositoryDto) throws AlreadyExistsException {
public Response create(@Valid RepositoryDto repositoryDto) {
return adapter.create(repositoryDto,
() -> dtoToRepositoryMapper.map(repositoryDto, null),
repository -> resourceLinks.repository().self(repository.getNamespace(), repository.getName()));

View File

@@ -3,8 +3,6 @@ package sonia.scm.api.v2.resources;
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import com.webcohesion.enunciate.metadata.rs.TypeHint;
import sonia.scm.ConcurrentModificationException;
import sonia.scm.NotFoundException;
import sonia.scm.repository.NamespaceAndName;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryIsNotArchivedException;
@@ -26,6 +24,9 @@ import java.util.Optional;
import java.util.function.Predicate;
import java.util.function.Supplier;
import static sonia.scm.ContextEntry.ContextBuilder.entity;
import static sonia.scm.NotFoundException.notFound;
public class RepositoryResource {
private final RepositoryToRepositoryDtoMapper repositoryToDtoMapper;
@@ -205,7 +206,7 @@ public class RepositoryResource {
private Supplier<Repository> loadBy(String namespace, String name) {
NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, name);
return () -> Optional.ofNullable(manager.get(namespaceAndName)).orElseThrow(() -> NotFoundException.notFound(namespaceAndName).build());
return () -> Optional.ofNullable(manager.get(namespaceAndName)).orElseThrow(() -> notFound(entity(namespaceAndName)));
}
private Predicate<Repository> nameAndNamespaceStaysTheSame(String namespace, String name) {

View File

@@ -11,7 +11,6 @@ import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.Response;
import java.util.Collection;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

View File

@@ -21,6 +21,9 @@ import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
import java.io.IOException;
import static sonia.scm.ContextEntry.ContextBuilder.entity;
import static sonia.scm.NotFoundException.notFound;
public class TagRootResource {
private final RepositoryServiceFactory serviceFactory;
@@ -91,7 +94,7 @@ public class TagRootResource {
}
private NotFoundException createNotFoundException(String namespace, String name, String tagName) {
return NotFoundException.notFound("Tag", tagName).in("Repository", namespace + "/" + name).build();
return notFound(entity("Tag", tagName).in("Repository", namespace + "/" + name));
}
private Tags getTags(RepositoryService repositoryService) throws IOException {

View File

@@ -6,7 +6,6 @@ import com.webcohesion.enunciate.metadata.rs.ResponseHeaders;
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import com.webcohesion.enunciate.metadata.rs.TypeHint;
import org.apache.shiro.authc.credential.PasswordService;
import sonia.scm.AlreadyExistsException;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import sonia.scm.web.VndMediaType;
@@ -91,7 +90,7 @@ public class UserCollectionResource {
})
@TypeHint(TypeHint.NO_CONTENT.class)
@ResponseHeaders(@ResponseHeader(name = "Location", description = "uri to the created user"))
public Response create(@Valid UserDto userDto) throws AlreadyExistsException {
public Response create(@Valid UserDto userDto) {
return adapter.create(userDto, () -> dtoToUserMapper.map(userDto, passwordService.encryptPassword(userDto.getPassword())), user -> resourceLinks.user().self(user.getName()));
}
}

View File

@@ -4,7 +4,6 @@ import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import com.webcohesion.enunciate.metadata.rs.TypeHint;
import org.apache.shiro.authc.credential.PasswordService;
import sonia.scm.ConcurrentModificationException;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import sonia.scm.web.VndMediaType;

View File

@@ -42,7 +42,6 @@ import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.AlreadyExistsException;
import sonia.scm.HandlerEventType;
import sonia.scm.ManagerDaoAdapter;
import sonia.scm.NotFoundException;
@@ -106,7 +105,7 @@ public class DefaultGroupManager extends AbstractGroupManager
}
@Override
public Group create(Group group) throws AlreadyExistsException {
public Group create(Group group) {
String type = group.getType();
if (Util.isEmpty(type)) {
group.setType(groupDAO.getType());

View File

@@ -65,6 +65,9 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import static sonia.scm.ContextEntry.ContextBuilder.entity;
import static sonia.scm.NotFoundException.notFound;
/**
* Default implementation of {@link RepositoryManager}.
*
@@ -122,11 +125,11 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager {
}
@Override
public Repository create(Repository repository) throws AlreadyExistsException {
public Repository create(Repository repository) {
return create(repository, true);
}
public Repository create(Repository repository, boolean initRepository) throws AlreadyExistsException {
public Repository create(Repository repository, boolean initRepository) {
repository.setId(keyGenerator.createKey());
repository.setNamespace(namespaceStrategy.createNamespace(repository));
@@ -170,7 +173,7 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager {
}
@Override
public void importRepository(Repository repository) throws AlreadyExistsException {
public void importRepository(Repository repository) {
create(repository, false);
}
@@ -207,7 +210,7 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager {
if (fresh != null) {
fresh.copyProperties(repository);
} else {
throw NotFoundException.notFound(repository).build();
throw notFound(entity(repository));
}
}

View File

@@ -33,7 +33,6 @@ import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.ConcurrentModificationException;
import sonia.scm.NotFoundException;
import java.util.Set;

View File

@@ -39,7 +39,6 @@ import com.google.inject.Singleton;
import org.apache.shiro.SecurityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.AlreadyExistsException;
import sonia.scm.EagerSingleton;
import sonia.scm.HandlerEventType;
import sonia.scm.ManagerDaoAdapter;
@@ -137,7 +136,7 @@ public class DefaultUserManager extends AbstractUserManager
* @throws IOException
*/
@Override
public User create(User user) throws AlreadyExistsException {
public User create(User user) {
String type = user.getType();
if (Util.isEmpty(type)) {
user.setType(userDAO.getType());

View File

@@ -30,6 +30,9 @@ import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Function;
import static sonia.scm.ContextEntry.ContextBuilder.entity;
import static sonia.scm.NotFoundException.notFound;
/**
* Collect the plugin translations.
@@ -69,7 +72,7 @@ public class I18nServlet extends HttpServlet {
createdFile.ifPresent(map -> createdJsonFileConsumer.accept(path, map));
return createdFile.orElse(null);
}
)).orElseThrow(() -> NotFoundException.notFound("jsonprovider", path).build());
)).orElseThrow(() -> notFound(entity("jsonprovider", path)));
}
@VisibleForTesting