diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java index 0a0468de3c..8e22755fe0 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java @@ -16,6 +16,7 @@ public class MapperModule extends AbstractModule { bind(GroupCollectionToDtoMapper.class); bind(RepositoryToRepositoryDtoMapper.class).to(Mappers.getMapper(RepositoryToRepositoryDtoMapper.class).getClass()); + bind(RepositoryDtoToRepositoryMapper.class).to(Mappers.getMapper(RepositoryDtoToRepositoryMapper.class).getClass()); bind(UriInfoStore.class).in(ServletScopes.REQUEST); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryDtoToRepositoryMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryDtoToRepositoryMapper.java new file mode 100644 index 0000000000..9bab9d1d3b --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryDtoToRepositoryMapper.java @@ -0,0 +1,18 @@ +package sonia.scm.api.v2.resources; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import sonia.scm.repository.Repository; + +@Mapper +public abstract class RepositoryDtoToRepositoryMapper { + + @Mapping(target = "creationDate", ignore = true) + @Mapping(target = "lastModified", ignore = true) + @Mapping(target = "id", ignore = true) + @Mapping(target = "publicReadable", ignore = true) + @Mapping(target = "healthCheckFailures", ignore = true) + @Mapping(target = "permissions", ignore = true) + public abstract Repository map(RepositoryDto repositoryDto); + +} 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 0884664891..d1ecadf4dd 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 @@ -21,6 +21,7 @@ import javax.ws.rs.core.Response; public class RepositoryResource { private final RepositoryToRepositoryDtoMapper repositoryToDtoMapper; + private final RepositoryDtoToRepositoryMapper dtoToRepositoryMapper; private final RepositoryManager manager; private final SingleResourceManagerAdapter adapter; @@ -33,11 +34,12 @@ public class RepositoryResource { @Inject public RepositoryResource( RepositoryToRepositoryDtoMapper repositoryToDtoMapper, - RepositoryManager manager, + RepositoryDtoToRepositoryMapper dtoToRepositoryMapper, RepositoryManager manager, Provider tagRootResource, Provider branchRootResource, Provider changesetRootResource, Provider sourceRootResource, Provider permissionRootResource) { + this.dtoToRepositoryMapper = dtoToRepositoryMapper; this.manager = manager; this.repositoryToDtoMapper = repositoryToDtoMapper; this.adapter = new SingleResourceManagerAdapter<>(manager); @@ -65,14 +67,25 @@ public class RepositoryResource { @DELETE @Path("") + @StatusCodes({ + @ResponseCode(code = 204, condition = "delete success or nothing to delete"), + @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), + @ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"repository\" privilege"), + @ResponseCode(code = 500, condition = "internal server error") + }) + @TypeHint(TypeHint.NO_CONTENT.class) public Response delete(@PathParam("namespace") String namespace, @PathParam("name") String name) { - throw new UnsupportedOperationException(); + return adapter.delete(() -> manager.getByNamespace(namespace, name)); } @PUT @Path("") - public Response update(@PathParam("namespace") String namespace, @PathParam("name") String name) { - throw new UnsupportedOperationException(); + public Response update(@PathParam("namespace") String namespace, @PathParam("name") String name, RepositoryDto repositoryDto) { + return adapter.update(() -> manager.getByNamespace(namespace, name), existing -> { + Repository repository = dtoToRepositoryMapper.map(repositoryDto); + repository.setId(existing.getId()); + return repository; + }); } @Path("tags/") 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 a56111d893..d5e7a97c51 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 @@ -60,6 +60,11 @@ class SingleResourceManagerAdapter reader) { + MODEL_OBJECT existingModelObject = reader.get(); + return delete(existingModelObject.getId()); + } + @Override protected GenericEntity> createGenericEntity(Collection modelObjects) { throw new UnsupportedOperationException(); 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 66e8aa0056..a5367971b4 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 @@ -2,6 +2,7 @@ package sonia.scm.api.v2.resources; 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; @@ -14,17 +15,23 @@ import org.mockito.Mock; import sonia.scm.PageResult; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryManager; +import sonia.scm.web.VndMediaType; +import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.net.URL; import static java.util.Collections.singletonList; import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND; +import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT; import static javax.servlet.http.HttpServletResponse.SC_OK; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; @@ -48,11 +55,13 @@ public class RepositoryRootResourceTest { @InjectMocks private RepositoryToRepositoryDtoMapperImpl repositoryToDtoMapper; + @InjectMocks + private RepositoryDtoToRepositoryMapperImpl dtoToRepositoryMapper; @Before public void prepareEnvironment() { initMocks(this); - RepositoryResource repositoryResource = new RepositoryResource(repositoryToDtoMapper, repositoryManager, null, null, null, null, null); + RepositoryResource repositoryResource = new RepositoryResource(repositoryToDtoMapper, dtoToRepositoryMapper, repositoryManager, null, null, null, null, null); RepositoryCollectionToDtoMapper repositoryCollectionToDtoMapper = new RepositoryCollectionToDtoMapper(repositoryToDtoMapper, resourceLinks); RepositoryCollectionResource repositoryCollectionResource = new RepositoryCollectionResource(repositoryManager, repositoryCollectionToDtoMapper); RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider.of(repositoryResource), MockProvider.of(repositoryCollectionResource)); @@ -98,6 +107,57 @@ public class RepositoryRootResourceTest { assertTrue(response.getContentAsString().contains("\"name\":\"repo\"")); } + @Test + public void shouldHandleUpdateForNotExistingRepository() throws URISyntaxException, IOException { + URL url = Resources.getResource("sonia/scm/api/v2/repository-test-update.json"); + byte[] repository = Resources.toByteArray(url); + + MockHttpRequest request = MockHttpRequest + .put("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo") + .contentType(VndMediaType.REPOSITORY) + .content(repository); + MockHttpResponse response = new MockHttpResponse(); + + dispatcher.invoke(request, response); + + assertEquals(SC_NOT_FOUND, response.getStatus()); + } + + @Test + public void shouldHandleUpdateForExistingRepository() throws Exception { + mockRepository("space", "repo"); + + URL url = Resources.getResource("sonia/scm/api/v2/repository-test-update.json"); + byte[] repository = Resources.toByteArray(url); + + MockHttpRequest request = MockHttpRequest + .put("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo") + .contentType(VndMediaType.REPOSITORY) + .content(repository); + MockHttpResponse response = new MockHttpResponse(); + + dispatcher.invoke(request, response); + + assertEquals(SC_NO_CONTENT, response.getStatus()); + verify(repositoryManager).modify(anyObject()); + } + + @Test + public void shouldHandleDeleteForExistingRepository() throws Exception { + mockRepository("space", "repo"); + + URL url = Resources.getResource("sonia/scm/api/v2/repository-test-update.json"); + byte[] repository = Resources.toByteArray(url); + + MockHttpRequest request = MockHttpRequest.delete("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo"); + MockHttpResponse response = new MockHttpResponse(); + + dispatcher.invoke(request, response); + + assertEquals(SC_NO_CONTENT, response.getStatus()); + verify(repositoryManager).delete(anyObject()); + } + private PageResult createSingletonPageResult(Repository repository) { return new PageResult<>(singletonList(repository), 0); } @@ -106,8 +166,10 @@ public class RepositoryRootResourceTest { Repository repository = new Repository(); repository.setNamespace(namespace); repository.setName(name); - repository.setId("id"); + String id = namespace + "-" + name; + repository.setId(id); when(repositoryManager.getByNamespace(namespace, name)).thenReturn(repository); + when(repositoryManager.get(id)).thenReturn(repository); return repository; } } diff --git a/scm-webapp/src/test/resources/sonia/scm/api/v2/repository-test-update.json b/scm-webapp/src/test/resources/sonia/scm/api/v2/repository-test-update.json new file mode 100644 index 0000000000..660fa256bf --- /dev/null +++ b/scm-webapp/src/test/resources/sonia/scm/api/v2/repository-test-update.json @@ -0,0 +1,8 @@ +{ + "contact": "none@example.com", + "description": "Test repository", + "namespace": "space", + "name": "repo", + "archived": false, + "type": "git" +}