diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitRepositoryConfigToGitRepositoryConfigDtoMapper.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitRepositoryConfigMapper.java similarity index 86% rename from scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitRepositoryConfigToGitRepositoryConfigDtoMapper.java rename to scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitRepositoryConfigMapper.java index 07fd83b700..6480e526b1 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitRepositoryConfigToGitRepositoryConfigDtoMapper.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitRepositoryConfigMapper.java @@ -17,15 +17,16 @@ import static de.otto.edison.hal.Links.linkingTo; // Mapstruct does not support parameterized (i.e. non-default) constructors. Thus, we need to use field injection. @SuppressWarnings("squid:S3306") @Mapper -public abstract class GitRepositoryConfigToGitRepositoryConfigDtoMapper { +public abstract class GitRepositoryConfigMapper { @Inject private ScmPathInfoStore scmPathInfoStore; public abstract GitRepositoryConfigDto map(GitRepositoryConfig config, @Context Repository repository); + public abstract GitRepositoryConfig map(GitRepositoryConfigDto dto); @AfterMapping - void appendLinks(GitRepositoryConfig config, @MappingTarget GitRepositoryConfigDto target, @Context Repository repository) { + void appendLinks(@MappingTarget GitRepositoryConfigDto target, @Context Repository repository) { Links.Builder linksBuilder = linkingTo().self(self()); if (RepositoryPermissions.modify(repository).isPermitted()) { linksBuilder.single(link("update", update())); diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitRepositoryConfigResource.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitRepositoryConfigResource.java index 2ff637031b..af3501ae9a 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitRepositoryConfigResource.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitRepositoryConfigResource.java @@ -9,7 +9,9 @@ import sonia.scm.store.ConfigurationStoreFactory; import sonia.scm.web.GitVndMediaType; import javax.inject.Inject; +import javax.ws.rs.Consumes; import javax.ws.rs.GET; +import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; @@ -20,13 +22,13 @@ import static sonia.scm.NotFoundException.notFound; public class GitRepositoryConfigResource { - private final GitRepositoryConfigToGitRepositoryConfigDtoMapper repositoryConfigToDtoMapper; + private final GitRepositoryConfigMapper repositoryConfigMapper; private final RepositoryManager repositoryManager; private final ConfigurationStoreFactory configurationStoreFactory; @Inject - public GitRepositoryConfigResource(GitRepositoryConfigToGitRepositoryConfigDtoMapper repositoryConfigToDtoMapper, RepositoryManager repositoryManager, ConfigurationStoreFactory configurationStoreFactory) { - this.repositoryConfigToDtoMapper = repositoryConfigToDtoMapper; + public GitRepositoryConfigResource(GitRepositoryConfigMapper repositoryConfigMapper, RepositoryManager repositoryManager, ConfigurationStoreFactory configurationStoreFactory) { + this.repositoryConfigMapper = repositoryConfigMapper; this.repositoryManager = repositoryManager; this.configurationStoreFactory = configurationStoreFactory; } @@ -35,18 +37,37 @@ public class GitRepositoryConfigResource { @Path("/") @Produces(GitVndMediaType.GIT_REPOSITORY_CONFIG) public Response getRepositoryConfig(@PathParam("namespace") String namespace, @PathParam("name") String name) { + Repository repository = getRepository(namespace, name); + ConfigurationStore repositoryConfigStore = getStore(repository); + GitRepositoryConfig config = repositoryConfigStore.get(); + if (config == null) { + config = new GitRepositoryConfig(); + } + GitRepositoryConfigDto dto = repositoryConfigMapper.map(config, repository); + return Response.ok(dto).build(); + } + + @PUT + @Path("/") + @Consumes(GitVndMediaType.GIT_REPOSITORY_CONFIG) + public Response setRepositoryConfig(@PathParam("namespace") String namespace, @PathParam("name") String name, GitRepositoryConfigDto dto) { + Repository repository = getRepository(namespace, name); + ConfigurationStore repositoryConfigStore = getStore(repository); + GitRepositoryConfig config = repositoryConfigMapper.map(dto); + repositoryConfigStore.set(config); + return Response.noContent().build(); + } + + private Repository getRepository(@PathParam("namespace") String namespace, @PathParam("name") String name) { NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, name); Repository repository = repositoryManager.get(namespaceAndName); if (repository == null) { throw notFound(entity(namespaceAndName)); } + return repository; + } - ConfigurationStore repositoryConfigStore = configurationStoreFactory.withType(GitRepositoryConfig.class).withName("gitConfig").forRepository(repository).build(); - GitRepositoryConfig config = repositoryConfigStore.get(); - if (config == null) { - config = new GitRepositoryConfig(); - } - GitRepositoryConfigDto dto = repositoryConfigToDtoMapper.map(config, repository); - return Response.ok(dto).build(); + private ConfigurationStore getStore(Repository repository) { + return configurationStoreFactory.withType(GitRepositoryConfig.class).withName("gitConfig").forRepository(repository).build(); } } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryConfig.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryConfig.java index ae41987247..a548d82153 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryConfig.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryConfig.java @@ -1,5 +1,11 @@ package sonia.scm.repository; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement(name = "config") +@XmlAccessorType(XmlAccessType.FIELD) public class GitRepositoryConfig { private String defaultBranch; diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitServletModule.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitServletModule.java index 609ee6cb0b..0bbb993cc6 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitServletModule.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitServletModule.java @@ -40,7 +40,7 @@ import org.eclipse.jgit.transport.ScmTransportProtocol; import org.mapstruct.factory.Mappers; import sonia.scm.api.v2.resources.GitConfigDtoToGitConfigMapper; import sonia.scm.api.v2.resources.GitConfigToGitConfigDtoMapper; -import sonia.scm.api.v2.resources.GitRepositoryConfigToGitRepositoryConfigDtoMapper; +import sonia.scm.api.v2.resources.GitRepositoryConfigMapper; import sonia.scm.plugin.Extension; import sonia.scm.repository.GitWorkdirFactory; import sonia.scm.repository.spi.SimpleGitWorkdirFactory; @@ -66,7 +66,7 @@ public class GitServletModule extends ServletModule bind(GitConfigDtoToGitConfigMapper.class).to(Mappers.getMapper(GitConfigDtoToGitConfigMapper.class).getClass()); bind(GitConfigToGitConfigDtoMapper.class).to(Mappers.getMapper(GitConfigToGitConfigDtoMapper.class).getClass()); - bind(GitRepositoryConfigToGitRepositoryConfigDtoMapper.class).to(Mappers.getMapper(GitRepositoryConfigToGitRepositoryConfigDtoMapper.class).getClass()); + bind(GitRepositoryConfigMapper.class).to(Mappers.getMapper(GitRepositoryConfigMapper.class).getClass()); bind(GitWorkdirFactory.class).to(SimpleGitWorkdirFactory.class); } diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigResourceTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigResourceTest.java index 7bc0e5fe02..f4de22b2b5 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigResourceTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigResourceTest.java @@ -12,8 +12,11 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Answers; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Spy; import org.mockito.runners.MockitoJUnitRunner; import sonia.scm.repository.GitConfig; import sonia.scm.repository.GitRepositoryConfig; @@ -35,6 +38,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.when; @SubjectAware( @@ -66,21 +70,23 @@ public class GitConfigResourceTest { @InjectMocks private GitConfigToGitConfigDtoMapperImpl configToDtoMapper; @InjectMocks - private GitRepositoryConfigToGitRepositoryConfigDtoMapperImpl repositoryConfigToDtoMapper; + private GitRepositoryConfigMapperImpl repositoryConfigMapper; @Mock private GitRepositoryHandler repositoryHandler; @Mock(answer = Answers.CALLS_REAL_METHODS) private ConfigurationStoreFactory configurationStoreFactory; - @Mock - private ConfigurationStore configurationStore; + @Spy + private ConfigurationStore configurationStore; + @Captor + private ArgumentCaptor configurationStoreCaptor; @Before public void prepareEnvironment() { GitConfig gitConfig = createConfiguration(); when(repositoryHandler.getConfig()).thenReturn(gitConfig); - GitRepositoryConfigResource gitRepositoryConfigResource = new GitRepositoryConfigResource(repositoryConfigToDtoMapper, repositoryManager, configurationStoreFactory); + GitRepositoryConfigResource gitRepositoryConfigResource = new GitRepositoryConfigResource(repositoryConfigMapper, repositoryManager, configurationStoreFactory); GitConfigResource gitConfigResource = new GitConfigResource(dtoToConfigMapper, configToDtoMapper, repositoryHandler, of(gitRepositoryConfigResource)); dispatcher.getRegistry().addSingletonResource(gitConfigResource); when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri); @@ -89,6 +95,7 @@ public class GitConfigResourceTest { @Before public void initConfigStore() { when(configurationStoreFactory.getStore(any())).thenReturn(configurationStore); + doNothing().when(configurationStore).set(configurationStoreCaptor.capture()); } @Test @@ -202,6 +209,26 @@ public class GitConfigResourceTest { .contains("\"defaultBranch\":\"test\""); } + @Test + @SubjectAware(username = "writeOnly") + public void shouldStoreChangedRepositoryConfig() throws URISyntaxException { + when(repositoryManager.get(new NamespaceAndName("space", "X"))).thenReturn(new Repository("id", "git", "space", "X")); + + MockHttpRequest request = MockHttpRequest + .put("/" + GitConfigResource.GIT_CONFIG_PATH_V2 + "/space/X") + .contentType(GitVndMediaType.GIT_REPOSITORY_CONFIG) + .content("{\"defaultBranch\": \"new\"}".getBytes()); + MockHttpResponse response = new MockHttpResponse(); + + dispatcher.invoke(request, response); + + assertEquals(HttpServletResponse.SC_NO_CONTENT, response.getStatus()); + assertThat(configurationStoreCaptor.getValue()) + .isInstanceOfSatisfying(GitRepositoryConfig.class, x -> { }) + .extracting("defaultBranch") + .containsExactly("new"); + } + private MockHttpResponse get() throws URISyntaxException { MockHttpRequest request = MockHttpRequest.get("/" + GitConfigResource.GIT_CONFIG_PATH_V2); MockHttpResponse response = new MockHttpResponse();