Migrate not found errors to NotFoundException

This commit is contained in:
René Pfeuffer
2018-08-22 09:35:13 +02:00
parent 5ed6cc4a53
commit 2761edb9f6
11 changed files with 50 additions and 36 deletions

View File

@@ -3,6 +3,7 @@ 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.NotFoundException;
import sonia.scm.group.Group;
import sonia.scm.group.GroupManager;
import sonia.scm.web.VndMediaType;
@@ -50,7 +51,7 @@ public class GroupResource {
@ResponseCode(code = 404, condition = "not found, no group with the specified id/name available"),
@ResponseCode(code = 500, condition = "internal server error")
})
public Response get(@PathParam("id") String id) {
public Response get(@PathParam("id") String id) throws NotFoundException {
return adapter.get(id, groupToGroupDtoMapper::map);
}
@@ -95,7 +96,7 @@ public class GroupResource {
@ResponseCode(code = 500, condition = "internal server error")
})
@TypeHint(TypeHint.NO_CONTENT.class)
public Response update(@PathParam("id") String name, GroupDto groupDto) {
public Response update(@PathParam("id") String name, GroupDto groupDto) throws NotFoundException {
return adapter.update(name, existing -> dtoToGroupMapper.map(groupDto));
}
}

View File

@@ -4,6 +4,7 @@ import de.otto.edison.hal.HalRepresentation;
import sonia.scm.AlreadyExistsException;
import sonia.scm.Manager;
import sonia.scm.ModelObject;
import sonia.scm.NotFoundException;
import sonia.scm.PageResult;
import javax.ws.rs.core.Response;
@@ -31,11 +32,11 @@ class IdResourceManagerAdapter<MODEL_OBJECT extends ModelObject,
collectionAdapter = new CollectionResourceManagerAdapter<>(manager, type);
}
Response get(String id, Function<MODEL_OBJECT, DTO> mapToDto) {
Response get(String id, Function<MODEL_OBJECT, DTO> mapToDto) throws NotFoundException {
return singleAdapter.get(loadBy(id), mapToDto);
}
public Response update(String id, Function<MODEL_OBJECT, MODEL_OBJECT> applyChanges) {
public Response update(String id, Function<MODEL_OBJECT, MODEL_OBJECT> applyChanges) throws NotFoundException {
return singleAdapter.update(
loadBy(id),
applyChanges,

View File

@@ -4,6 +4,7 @@ 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.SecurityUtils;
import sonia.scm.NotFoundException;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import sonia.scm.web.VndMediaType;
@@ -46,7 +47,7 @@ public class MeResource {
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
@ResponseCode(code = 500, condition = "internal server error")
})
public Response get(@Context Request request, @Context UriInfo uriInfo) {
public Response get(@Context Request request, @Context UriInfo uriInfo) throws NotFoundException {
String id = (String) SecurityUtils.getSubject().getPrincipals().getPrimaryPrincipal();
return adapter.get(id, userToDtoMapper::map);

View File

@@ -3,6 +3,7 @@ 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.NotFoundException;
import sonia.scm.repository.NamespaceAndName;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryIsNotArchivedException;
@@ -77,7 +78,7 @@ public class RepositoryResource {
@ResponseCode(code = 404, condition = "not found, no repository with the specified name available in the namespace"),
@ResponseCode(code = 500, condition = "internal server error")
})
public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name) {
public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name) throws NotFoundException {
return adapter.get(loadBy(namespace, name), repositoryToDtoMapper::map);
}
@@ -124,7 +125,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, RepositoryDto repositoryDto) {
public Response update(@PathParam("namespace") String namespace, @PathParam("name") String name, RepositoryDto repositoryDto) throws NotFoundException {
return adapter.update(
loadBy(namespace, name),
existing -> dtoToRepositoryMapper.map(repositoryDto, existing.getId()),

View File

@@ -3,6 +3,7 @@ package sonia.scm.api.v2.resources;
import de.otto.edison.hal.HalRepresentation;
import sonia.scm.Manager;
import sonia.scm.ModelObject;
import sonia.scm.NotFoundException;
import sonia.scm.api.rest.resources.AbstractManagerResource;
import javax.ws.rs.core.GenericEntity;
@@ -44,28 +45,25 @@ class SingleResourceManagerAdapter<MODEL_OBJECT extends ModelObject,
* Reads the model object for the given id, transforms it to a dto and returns a corresponding http response.
* This handles all corner cases, eg. no matching object for the id or missing privileges.
*/
Response get(Supplier<Optional<MODEL_OBJECT>> reader, Function<MODEL_OBJECT, DTO> mapToDto) {
Response get(Supplier<Optional<MODEL_OBJECT>> reader, Function<MODEL_OBJECT, DTO> mapToDto) throws NotFoundException {
return reader.get()
.map(mapToDto)
.map(Response::ok)
.map(Response.ResponseBuilder::build)
.orElse(Response.status(Response.Status.NOT_FOUND).build());
.orElseThrow(NotFoundException::new);
}
/**
* 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.
*/
public Response update(Supplier<Optional<MODEL_OBJECT>> reader, Function<MODEL_OBJECT, MODEL_OBJECT> applyChanges, Predicate<MODEL_OBJECT> hasSameKey) {
Optional<MODEL_OBJECT> existingModelObject = reader.get();
if (!existingModelObject.isPresent()) {
return Response.status(Response.Status.NOT_FOUND).build();
}
MODEL_OBJECT changedModelObject = applyChanges.apply(existingModelObject.get());
public Response update(Supplier<Optional<MODEL_OBJECT>> reader, Function<MODEL_OBJECT, MODEL_OBJECT> applyChanges, Predicate<MODEL_OBJECT> hasSameKey) throws NotFoundException {
MODEL_OBJECT existingModelObject = reader.get().orElseThrow(NotFoundException::new);
MODEL_OBJECT changedModelObject = applyChanges.apply(existingModelObject);
if (!hasSameKey.test(changedModelObject)) {
return Response.status(BAD_REQUEST).entity("illegal change of id").build();
}
return update(getId(existingModelObject.get()), changedModelObject);
return update(getId(existingModelObject), changedModelObject);
}
public Response delete(Supplier<Optional<MODEL_OBJECT>> reader) {

View File

@@ -3,6 +3,7 @@ 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.NotFoundException;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import sonia.scm.web.VndMediaType;
@@ -50,7 +51,7 @@ public class UserResource {
@ResponseCode(code = 404, condition = "not found, no user with the specified id/name available"),
@ResponseCode(code = 500, condition = "internal server error")
})
public Response get(@PathParam("id") String id) {
public Response get(@PathParam("id") String id) throws NotFoundException {
return adapter.get(id, userToDtoMapper::map);
}
@@ -95,7 +96,7 @@ public class UserResource {
@ResponseCode(code = 500, condition = "internal server error")
})
@TypeHint(TypeHint.NO_CONTENT.class)
public Response update(@PathParam("id") String name, UserDto userDto) {
public Response update(@PathParam("id") String name, UserDto userDto) throws NotFoundException {
return adapter.update(name, existing -> dtoToUserMapper.map(userDto, existing.getPassword()));
}
}

View File

@@ -0,0 +1,17 @@
package sonia.scm.api.v2.resources;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.mock.MockDispatcherFactory;
import sonia.scm.api.rest.AlreadyExistsExceptionMapper;
import sonia.scm.api.rest.AuthorizationExceptionMapper;
public class DispatcherMock {
public static Dispatcher createDispatcher(Object resource) {
Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
dispatcher.getRegistry().addSingletonResource(resource);
dispatcher.getProviderFactory().registerProvider(NotFoundExceptionMapper.class);
dispatcher.getProviderFactory().registerProvider(AlreadyExistsExceptionMapper.class);
dispatcher.getProviderFactory().registerProvider(AuthorizationExceptionMapper.class);
return dispatcher;
}
}

View File

@@ -4,7 +4,6 @@ import com.github.sdorra.shiro.ShiroRule;
import com.github.sdorra.shiro.SubjectAware;
import com.google.common.io.Resources;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.mock.MockDispatcherFactory;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
import org.junit.Before;
@@ -34,6 +33,7 @@ import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
import static sonia.scm.api.v2.resources.DispatcherMock.createDispatcher;
@SubjectAware(
username = "trillian",
@@ -45,7 +45,7 @@ public class GroupRootResourceTest {
@Rule
public ShiroRule shiro = new ShiroRule();
private Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
private Dispatcher dispatcher;
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(URI.create("/"));
@@ -73,7 +73,7 @@ public class GroupRootResourceTest {
GroupResource groupResource = new GroupResource(groupManager, groupToDtoMapper, dtoToGroupMapper);
GroupRootResource groupRootResource = new GroupRootResource(MockProvider.of(groupCollectionResource), MockProvider.of(groupResource));
dispatcher.getRegistry().addSingletonResource(groupRootResource);
dispatcher = createDispatcher(groupRootResource);
}
@Test

View File

@@ -8,7 +8,6 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.AuthorizationException;
import org.assertj.core.util.Lists;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.mock.MockDispatcherFactory;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
import org.jboss.resteasy.spi.HttpRequest;
@@ -20,8 +19,6 @@ import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import sonia.scm.api.rest.AlreadyExistsExceptionMapper;
import sonia.scm.api.rest.AuthorizationExceptionMapper;
import sonia.scm.repository.NamespaceAndName;
import sonia.scm.repository.Permission;
import sonia.scm.repository.PermissionType;
@@ -47,6 +44,7 @@ import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
import static sonia.scm.api.v2.resources.DispatcherMock.createDispatcher;
@Slf4j
public class PermissionRootResourceTest {
@@ -88,7 +86,7 @@ public class PermissionRootResourceTest {
.content(PERMISSION_TEST_PAYLOAD)
.path(PATH_OF_ONE_PERMISSION);
private final Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
private Dispatcher dispatcher;
@Mock
private RepositoryManager repositoryManager;
@@ -111,11 +109,7 @@ public class PermissionRootResourceTest {
permissionRootResource = new PermissionRootResource(permissionDtoToPermissionMapper, permissionToPermissionDtoMapper, resourceLinks, repositoryManager);
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider
.of(new RepositoryResource(null, null, null, null, null, null, null,null, MockProvider.of(permissionRootResource))), null);
dispatcher.getRegistry().addSingletonResource(repositoryRootResource);
dispatcher.getProviderFactory().registerProvider(NotFoundExceptionMapper.class);
dispatcher.getProviderFactory().registerProvider(NotFoundExceptionMapper.class);
dispatcher.getProviderFactory().registerProvider(AlreadyExistsExceptionMapper.class);
dispatcher.getProviderFactory().registerProvider(AuthorizationExceptionMapper.class);
dispatcher = createDispatcher(repositoryRootResource);
}
@TestFactory

View File

@@ -4,7 +4,6 @@ import com.github.sdorra.shiro.ShiroRule;
import com.github.sdorra.shiro.SubjectAware;
import com.google.common.io.Resources;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.mock.MockDispatcherFactory;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
import org.junit.Before;
@@ -43,6 +42,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
import static sonia.scm.api.v2.resources.DispatcherMock.createDispatcher;
@SubjectAware(
username = "trillian",
@@ -51,7 +51,7 @@ import static org.mockito.MockitoAnnotations.initMocks;
)
public class RepositoryRootResourceTest {
private final Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
private Dispatcher dispatcher;
@Rule
public ShiroRule shiro = new ShiroRule();
@@ -77,7 +77,7 @@ public class RepositoryRootResourceTest {
RepositoryCollectionToDtoMapper repositoryCollectionToDtoMapper = new RepositoryCollectionToDtoMapper(repositoryToDtoMapper, resourceLinks);
RepositoryCollectionResource repositoryCollectionResource = new RepositoryCollectionResource(repositoryManager, repositoryCollectionToDtoMapper, dtoToRepositoryMapper, resourceLinks);
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider.of(repositoryResource), MockProvider.of(repositoryCollectionResource));
dispatcher.getRegistry().addSingletonResource(repositoryRootResource);
dispatcher = createDispatcher(repositoryRootResource);
}
@Test

View File

@@ -5,7 +5,6 @@ import com.github.sdorra.shiro.SubjectAware;
import com.google.common.io.Resources;
import org.apache.shiro.authc.credential.PasswordService;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.mock.MockDispatcherFactory;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
import org.junit.Before;
@@ -35,6 +34,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
import static sonia.scm.api.v2.resources.DispatcherMock.createDispatcher;
@SubjectAware(
username = "trillian",
@@ -46,7 +46,7 @@ public class UserRootResourceTest {
@Rule
public ShiroRule shiro = new ShiroRule();
private Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
private Dispatcher dispatcher;
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(URI.create("/"));
@@ -76,7 +76,7 @@ public class UserRootResourceTest {
UserRootResource userRootResource = new UserRootResource(MockProvider.of(userCollectionResource),
MockProvider.of(userResource));
dispatcher.getRegistry().addSingletonResource(userRootResource);
dispatcher = createDispatcher(userRootResource);
}
@Test