diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupResource.java index c0bb7e5bc5..e1e8e0ccdb 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupResource.java @@ -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)); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IdResourceManagerAdapter.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IdResourceManagerAdapter.java index e1f9be1ead..a089f5ba49 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IdResourceManagerAdapter.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IdResourceManagerAdapter.java @@ -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(manager, type); } - Response get(String id, Function mapToDto) { + Response get(String id, Function mapToDto) throws NotFoundException { return singleAdapter.get(loadBy(id), mapToDto); } - public Response update(String id, Function applyChanges) { + public Response update(String id, Function applyChanges) throws NotFoundException { return singleAdapter.update( loadBy(id), applyChanges, diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeResource.java index 9421c3ca14..f016f4604c 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeResource.java @@ -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); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryResource.java index 703e6c4403..2d4ee9f600 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryResource.java @@ -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()), diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SingleResourceManagerAdapter.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SingleResourceManagerAdapter.java index bc84ca7c33..2fa8466ab6 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SingleResourceManagerAdapter.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SingleResourceManagerAdapter.java @@ -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> reader, Function mapToDto) { + Response get(Supplier> reader, Function 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> reader, Function applyChanges, Predicate hasSameKey) { - Optional 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> reader, Function applyChanges, Predicate 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> reader) { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserResource.java index a4e07382dd..687c94e4ff 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserResource.java @@ -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())); } } diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/DispatcherMock.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/DispatcherMock.java new file mode 100644 index 0000000000..4ff0d3d475 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/DispatcherMock.java @@ -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; + } +} diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupRootResourceTest.java index 6a463b3baa..b689d58058 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupRootResourceTest.java @@ -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 diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PermissionRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PermissionRootResourceTest.java index 33dda0b556..b72773e01f 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PermissionRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PermissionRootResourceTest.java @@ -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 diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryRootResourceTest.java index 35de1ca80b..a5ea8ffb9b 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryRootResourceTest.java @@ -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 diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java index 3ebe48ca1f..5004eb7665 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java @@ -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