From b339dd068f11dfbee8c592a6ba21ab418f9ebecb Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Mon, 15 Mar 2021 08:40:05 +0100 Subject: [PATCH] Repository specific mercurial encoding (#1583) * Use HgRepositoryConfig instead of HgConfig for hg commands * Rename HgConfig to HgGlobalConfig * Resolve encoding from repository specific configuration * Add ui for repository specific configuration * Validate encoding on dto --- gradle/changelog/hg_repository_encoding.yaml | 2 + scm-plugins/scm-hg-plugin/build.gradle | 6 + .../sonia/scm/api/v2/resources/Encoding.java | 46 +++++ .../api/v2/resources/EncodingValidator.java | 53 ++++++ .../scm/api/v2/resources/HgConfigLinks.java | 90 ++++++++++ .../api/v2/resources/HgConfigResource.java | 40 +++-- ...lobalConfigAutoConfigurationResource.java} | 20 +-- ...=> HgGlobalConfigDtoToHgConfigMapper.java} | 8 +- ...ava => HgGlobalConfigInIndexResource.java} | 10 +- ...lobalConfigToHgGlobalConfigDtoMapper.java} | 31 ++-- ...gDto.java => HgGlobalGlobalConfigDto.java} | 5 +- .../v2/resources/HgRepositoryConfigDto.java | 45 +++++ .../resources/HgRepositoryConfigEnricher.java | 55 ++++++ .../resources/HgRepositoryConfigMapper.java | 66 +++++++ .../resources/HgRepositoryConfigResource.java | 149 ++++++++++++++++ ...gDto.java => UpdateHgGlobalConfigDto.java} | 2 +- .../scm/autoconfig/AutoConfigurator.java | 4 +- .../scm/autoconfig/NoOpAutoConfigurator.java | 4 +- .../scm/autoconfig/PosixAutoConfigurator.java | 4 +- .../autoconfig/WindowsAutoConfigurator.java | 4 +- .../DefaultHgEnvironmentBuilder.java | 11 +- .../java/sonia/scm/repository/HgConfig.java | 129 +------------- .../scm/repository/HgConfigResolver.java | 86 +++++++++ .../sonia/scm/repository/HgGlobalConfig.java | 151 ++++++++++++++++ .../scm/repository/HgRepositoryConfig.java | 35 ++++ .../repository/HgRepositoryConfigStore.java | 59 +++++++ .../scm/repository/HgRepositoryFactory.java | 29 +-- .../scm/repository/HgRepositoryHandler.java | 12 +- .../java/sonia/scm/repository/HgVerifier.java | 2 +- .../hooks/HookContextProviderFactory.java | 10 +- .../scm/repository/spi/HgCommandContext.java | 20 ++- .../spi/HgHookChangesetProvider.java | 12 +- .../repository/spi/HgHookContextProvider.java | 6 +- .../spi/HgRepositoryServiceProvider.java | 4 +- .../spi/HgRepositoryServiceResolver.java | 6 +- .../scm/repository/spi/HgVersionCommand.java | 8 +- .../javahg/HgIncomingChangesetCommand.java | 6 +- .../HgIncomingOutgoingChangesetCommand.java | 6 +- .../javahg/HgOutgoingChangesetCommand.java | 3 +- .../main/java/sonia/scm/web/HgCGIServlet.java | 48 ++--- .../java/sonia/scm/web/HgServletModule.java | 10 +- .../java/sonia/scm/web/HgVndMediaType.java | 3 +- .../main/js/HgRepositoryConfigurationForm.tsx | 85 +++++++++ .../scm-hg-plugin/src/main/js/hooks.ts | 86 +++++++++ .../scm-hg-plugin/src/main/js/index.ts | 5 + .../main/resources/locales/de/plugins.json | 4 +- .../main/resources/locales/en/plugins.json | 4 +- .../v2/resources/HgConfigResourceTest.java | 45 +++-- .../HgConfigToHgConfigDtoMapperTest.java | 102 ----------- ...lConfigAutoConfigurationResourceTest.java} | 31 ++-- ...lConfigDtoToHgGlobalConfigMapperTest.java} | 14 +- ...=> HgGlobalConfigInIndexResourceTest.java} | 16 +- ...Tests.java => HgGlobalConfigTestUtil.java} | 12 +- ...alConfigToHgGlobalConfigDtoMapperTest.java | 101 +++++++++++ .../HgRepositoryConfigEnricherTest.java | 77 ++++++++ .../HgRepositoryConfigMapperTest.java | 95 ++++++++++ .../HgRepositoryConfigResourceTest.java | 165 ++++++++++++++++++ .../autoconfig/PosixAutoConfiguratorTest.java | 10 +- .../WindowsAutoConfiguratorTest.java | 4 +- .../DefaultHgEnvironmentBuilderTest.java | 14 +- .../scm/repository/HgConfigResolverTest.java | 85 +++++++++ .../HgRepositoryConfigStoreTest.java | 84 +++++++++ .../repository/HgRepositoryFactoryTest.java | 3 +- .../repository/HgRepositoryHandlerTest.java | 6 +- .../java/sonia/scm/repository/HgTestUtil.java | 21 +-- .../sonia/scm/repository/HgVerifierTest.java | 2 +- .../spi/AbstractHgCommandTestBase.java | 7 +- .../repository/spi/HgIncomingCommandTest.java | 4 +- .../spi/HgModificationsCommandTest.java | 4 +- .../repository/spi/HgOutgoingCommandTest.java | 4 +- .../repository/spi/HgVersionCommandTest.java | 4 +- .../spi/IncomingOutgoingTestBase.java | 4 +- .../sonia/scm/web/HgPermissionFilterTest.java | 14 +- 73 files changed, 1914 insertions(+), 498 deletions(-) create mode 100644 gradle/changelog/hg_repository_encoding.yaml create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/Encoding.java create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/EncodingValidator.java create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigLinks.java rename scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/{HgConfigAutoConfigurationResource.java => HgGlobalConfigAutoConfigurationResource.java} (89%) rename scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/{HgConfigDtoToHgConfigMapper.java => HgGlobalConfigDtoToHgConfigMapper.java} (89%) rename scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/{HgConfigInIndexResource.java => HgGlobalConfigInIndexResource.java} (88%) rename scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/{HgConfigToHgConfigDtoMapper.java => HgGlobalConfigToHgGlobalConfigDtoMapper.java} (71%) rename scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/{HgConfigDto.java => HgGlobalGlobalConfigDto.java} (94%) create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgRepositoryConfigDto.java create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgRepositoryConfigEnricher.java create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgRepositoryConfigMapper.java create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgRepositoryConfigResource.java rename scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/{UpdateHgConfigDto.java => UpdateHgGlobalConfigDto.java} (97%) create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgConfigResolver.java create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgGlobalConfig.java create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryConfig.java create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryConfigStore.java create mode 100644 scm-plugins/scm-hg-plugin/src/main/js/HgRepositoryConfigurationForm.tsx create mode 100644 scm-plugins/scm-hg-plugin/src/main/js/hooks.ts delete mode 100644 scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigToHgConfigDtoMapperTest.java rename scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/{HgConfigAutoConfigurationResourceTest.java => HgGlobalConfigAutoConfigurationResourceTest.java} (82%) rename scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/{HgConfigDtoToHgConfigMapperTest.java => HgGlobalConfigDtoToHgGlobalConfigMapperTest.java} (83%) rename scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/{HgConfigInIndexResourceTest.java => HgGlobalConfigInIndexResourceTest.java} (87%) rename scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/{HgConfigTests.java => HgGlobalConfigTestUtil.java} (85%) create mode 100644 scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgGlobalConfigToHgGlobalConfigDtoMapperTest.java create mode 100644 scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgRepositoryConfigEnricherTest.java create mode 100644 scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgRepositoryConfigMapperTest.java create mode 100644 scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgRepositoryConfigResourceTest.java create mode 100644 scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgConfigResolverTest.java create mode 100644 scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryConfigStoreTest.java diff --git a/gradle/changelog/hg_repository_encoding.yaml b/gradle/changelog/hg_repository_encoding.yaml new file mode 100644 index 0000000000..aab4ea0788 --- /dev/null +++ b/gradle/changelog/hg_repository_encoding.yaml @@ -0,0 +1,2 @@ +- type: added + description: Mercurial encoding configuration per repository ([#1577](https://github.com/scm-manager/scm-manager/issues/1577), [#1583](https://github.com/scm-manager/scm-manager/issues/1583)) diff --git a/scm-plugins/scm-hg-plugin/build.gradle b/scm-plugins/scm-hg-plugin/build.gradle index b1294f1dec..d6a1116b5b 100644 --- a/scm-plugins/scm-hg-plugin/build.gradle +++ b/scm-plugins/scm-hg-plugin/build.gradle @@ -35,6 +35,12 @@ dependencies { implementation libraries.commonsCompress testImplementation libraries.shiroUnit testImplementation libraries.logback + + // validation api + testImplementation libraries.validator + testImplementation libraries.elApi + testImplementation libraries.elRuntime + testImplementation libraries.resteasyValidatorProvider } scmPlugin { diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/Encoding.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/Encoding.java new file mode 100644 index 0000000000..98d96824c8 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/Encoding.java @@ -0,0 +1,46 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.api.v2.resources; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Documented +@Retention(RUNTIME) +@Target({ FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE }) +@Constraint(validatedBy = EncodingValidator.class) +public @interface Encoding { + String message() default "Invalid encoding"; + Class[] groups() default { }; + Class[] payload() default { }; +} + + diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/EncodingValidator.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/EncodingValidator.java new file mode 100644 index 0000000000..07e55d5b18 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/EncodingValidator.java @@ -0,0 +1,53 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.api.v2.resources; + +import com.google.common.base.Strings; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.nio.charset.Charset; +import java.nio.charset.UnsupportedCharsetException; + +public class EncodingValidator implements ConstraintValidator { + + @Override + public void initialize(Encoding constraintAnnotation) { + // do nothing + } + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + if (Strings.isNullOrEmpty(value)) { + return true; + } + try { + Charset.forName(value); + return true; + } catch (UnsupportedCharsetException ex) { + return false; + } + } +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigLinks.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigLinks.java new file mode 100644 index 0000000000..70dc5c3c86 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigLinks.java @@ -0,0 +1,90 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.api.v2.resources; + +import com.google.common.annotations.VisibleForTesting; +import com.google.inject.util.Providers; +import sonia.scm.repository.Repository; + +import javax.inject.Inject; +import javax.inject.Provider; + +public class HgConfigLinks { + + private final Provider pathInfoStore; + + @Inject + public HgConfigLinks(Provider pathInfoStore) { + this.pathInfoStore = pathInfoStore; + } + + @VisibleForTesting + public HgConfigLinks(ScmPathInfoStore pathInfoStore) { + this.pathInfoStore = Providers.of(pathInfoStore); + } + + + public ConfigLinks global() { + LinkBuilder linkBuilder = new LinkBuilder(pathInfoStore.get().get(), HgConfigResource.class); + return new ConfigLinks() { + @Override + public String get() { + return linkBuilder.method("get").parameters().href(); + } + + @Override + public String update() { + return linkBuilder.method("update").parameters().href(); + } + }; + } + + public ConfigLinks repository(Repository repository) { + return repository(repository.getNamespace(), repository.getName()); + } + + public ConfigLinks repository(String namespace, String name) { + LinkBuilder linkBuilder = new LinkBuilder(pathInfoStore.get().get(), HgConfigResource.class, HgRepositoryConfigResource.class) + .method("getRepositoryConfigResource") + .parameters(); + + return new ConfigLinks() { + @Override + public String get() { + return linkBuilder.method("getHgRepositoryConfig").parameters(namespace, name).href(); + } + + @Override + public String update() { + return linkBuilder.method("updateHgRepositoryConfig").parameters(namespace, name).href(); + } + }; + } + + public interface ConfigLinks { + String get(); + String update(); + } +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigResource.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigResource.java index 00ef38ade1..41764948df 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigResource.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigResource.java @@ -33,13 +33,14 @@ import io.swagger.v3.oas.annotations.parameters.RequestBody; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; import sonia.scm.config.ConfigurationPermissions; -import sonia.scm.repository.HgConfig; +import sonia.scm.repository.HgGlobalConfig; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.web.HgVndMediaType; import sonia.scm.web.VndMediaType; import javax.inject.Inject; import javax.inject.Provider; +import javax.validation.Valid; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.PUT; @@ -57,20 +58,23 @@ import javax.ws.rs.core.Response; public class HgConfigResource { static final String HG_CONFIG_PATH_V2 = "v2/config/hg"; - private final HgConfigDtoToHgConfigMapper dtoToConfigMapper; - private final HgConfigToHgConfigDtoMapper configToDtoMapper; + private final HgGlobalConfigDtoToHgConfigMapper dtoToConfigMapper; + private final HgGlobalConfigToHgGlobalConfigDtoMapper configToDtoMapper; private final HgRepositoryHandler repositoryHandler; - private final Provider autoconfigResource; + private final Provider autoconfigResource; + private final Provider repositoryConfigResource; @Inject - public HgConfigResource(HgConfigDtoToHgConfigMapper dtoToConfigMapper, - HgConfigToHgConfigDtoMapper configToDtoMapper, + public HgConfigResource(HgGlobalConfigDtoToHgConfigMapper dtoToConfigMapper, + HgGlobalConfigToHgGlobalConfigDtoMapper configToDtoMapper, HgRepositoryHandler repositoryHandler, - Provider autoconfigResource) { + Provider autoconfigResource, + Provider repositoryConfigResource) { this.dtoToConfigMapper = dtoToConfigMapper; this.configToDtoMapper = configToDtoMapper; this.repositoryHandler = repositoryHandler; this.autoconfigResource = autoconfigResource; + this.repositoryConfigResource = repositoryConfigResource; } /** @@ -85,7 +89,7 @@ public class HgConfigResource { description = "success", content = @Content( mediaType = HgVndMediaType.CONFIG, - schema = @Schema(implementation = HgConfigDto.class) + schema = @Schema(implementation = HgGlobalGlobalConfigDto.class) ) ) @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") @@ -99,12 +103,12 @@ public class HgConfigResource { )) public Response get() { - ConfigurationPermissions.read(HgConfig.PERMISSION).check(); + ConfigurationPermissions.read(HgGlobalConfig.PERMISSION).check(); - HgConfig config = repositoryHandler.getConfig(); + HgGlobalConfig config = repositoryHandler.getConfig(); if (config == null) { - config = new HgConfig(); + config = new HgGlobalConfig(); repositoryHandler.setConfig(config); } @@ -127,7 +131,7 @@ public class HgConfigResource { requestBody = @RequestBody( content = @Content( mediaType = HgVndMediaType.CONFIG, - schema = @Schema(implementation = UpdateHgConfigDto.class), + schema = @Schema(implementation = UpdateHgGlobalConfigDto.class), examples = @ExampleObject( name = "Overwrites current configuration with this one.", value = "{\n \"disabled\":false,\n \"hgBinary\":\"hg\",\n \"encoding\":\"UTF-8\",\n \"showRevisionInId\":false,\n \"enableHttpPostArgs\":false\n}", @@ -149,9 +153,9 @@ public class HgConfigResource { mediaType = VndMediaType.ERROR_TYPE, schema = @Schema(implementation = ErrorDto.class) )) - public Response update(HgConfigDto configDto) { + public Response update(@Valid HgGlobalGlobalConfigDto configDto) { - HgConfig config = dtoToConfigMapper.map(configDto); + HgGlobalConfig config = dtoToConfigMapper.map(configDto); ConfigurationPermissions.write(config).check(); @@ -162,7 +166,13 @@ public class HgConfigResource { } @Path("auto-configuration") - public HgConfigAutoConfigurationResource getAutoConfigurationResource() { + public HgGlobalConfigAutoConfigurationResource getAutoConfigurationResource() { return autoconfigResource.get(); } + + + @Path("{namespace}/{name}") + public HgRepositoryConfigResource getRepositoryConfigResource() { + return repositoryConfigResource.get(); + } } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigAutoConfigurationResource.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgGlobalConfigAutoConfigurationResource.java similarity index 89% rename from scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigAutoConfigurationResource.java rename to scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgGlobalConfigAutoConfigurationResource.java index 70eb367d3c..a433eb47f8 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigAutoConfigurationResource.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgGlobalConfigAutoConfigurationResource.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.api.v2.resources; import com.google.inject.Inject; @@ -32,7 +32,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.parameters.RequestBody; import io.swagger.v3.oas.annotations.responses.ApiResponse; import sonia.scm.config.ConfigurationPermissions; -import sonia.scm.repository.HgConfig; +import sonia.scm.repository.HgGlobalConfig; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.web.HgVndMediaType; import sonia.scm.web.VndMediaType; @@ -42,14 +42,14 @@ import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.core.Response; -public class HgConfigAutoConfigurationResource { +public class HgGlobalConfigAutoConfigurationResource { private final HgRepositoryHandler repositoryHandler; - private final HgConfigDtoToHgConfigMapper dtoToConfigMapper; + private final HgGlobalConfigDtoToHgConfigMapper dtoToConfigMapper; @Inject - public HgConfigAutoConfigurationResource(HgConfigDtoToHgConfigMapper dtoToConfigMapper, - HgRepositoryHandler repositoryHandler) { + public HgGlobalConfigAutoConfigurationResource(HgGlobalConfigDtoToHgConfigMapper dtoToConfigMapper, + HgRepositoryHandler repositoryHandler) { this.dtoToConfigMapper = dtoToConfigMapper; this.repositoryHandler = repositoryHandler; } @@ -92,7 +92,7 @@ public class HgConfigAutoConfigurationResource { requestBody = @RequestBody( content = @Content( mediaType = HgVndMediaType.CONFIG, - schema = @Schema(implementation = UpdateHgConfigDto.class), + schema = @Schema(implementation = UpdateHgGlobalConfigDto.class), examples = @ExampleObject( name = "Overwrites current configuration with this one and installs the mercurial binary.", value = "{\n \"disabled\":false,\n \"hgBinary\":\"hg\",\n \"pythonBinary\":\"python\",\n \"pythonPath\":\"\",\n \"encoding\":\"UTF-8\",\n \"useOptimizedBytecode\":false,\n \"showRevisionInId\":false,\n \"disableHookSSLValidation\":false,\n \"enableHttpPostArgs\":false\n}", @@ -114,14 +114,14 @@ public class HgConfigAutoConfigurationResource { mediaType = VndMediaType.ERROR_TYPE, schema = @Schema(implementation = ErrorDto.class) )) - public Response autoConfiguration(HgConfigDto configDto) { + public Response autoConfiguration(HgGlobalGlobalConfigDto configDto) { - HgConfig config; + HgGlobalConfig config; if (configDto != null) { config = dtoToConfigMapper.map(configDto); } else { - config = new HgConfig(); + config = new HgGlobalConfig(); } ConfigurationPermissions.write(config).check(); diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigDtoToHgConfigMapper.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgGlobalConfigDtoToHgConfigMapper.java similarity index 89% rename from scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigDtoToHgConfigMapper.java rename to scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgGlobalConfigDtoToHgConfigMapper.java index 38a8fe9000..a83b81c369 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigDtoToHgConfigMapper.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgGlobalConfigDtoToHgConfigMapper.java @@ -21,15 +21,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.api.v2.resources; import org.mapstruct.Mapper; -import sonia.scm.repository.HgConfig; +import sonia.scm.repository.HgGlobalConfig; // Mapstruct does not support parameterized (i.e. non-default) constructors. Thus, we need to use field injection. @SuppressWarnings("squid:S3306") @Mapper -public abstract class HgConfigDtoToHgConfigMapper { - public abstract HgConfig map(HgConfigDto dto); +public abstract class HgGlobalConfigDtoToHgConfigMapper { + public abstract HgGlobalConfig map(HgGlobalGlobalConfigDto dto); } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigInIndexResource.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgGlobalConfigInIndexResource.java similarity index 88% rename from scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigInIndexResource.java rename to scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgGlobalConfigInIndexResource.java index 46fb200c75..a6949e0391 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigInIndexResource.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgGlobalConfigInIndexResource.java @@ -21,14 +21,14 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.api.v2.resources; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import sonia.scm.config.ConfigurationPermissions; import sonia.scm.plugin.Extension; -import sonia.scm.repository.HgConfig; +import sonia.scm.repository.HgGlobalConfig; import sonia.scm.web.JsonEnricherBase; import sonia.scm.web.JsonEnricherContext; @@ -39,19 +39,19 @@ import static java.util.Collections.singletonMap; import static sonia.scm.web.VndMediaType.INDEX; @Extension -public class HgConfigInIndexResource extends JsonEnricherBase { +public class HgGlobalConfigInIndexResource extends JsonEnricherBase { private final Provider scmPathInfoStore; @Inject - public HgConfigInIndexResource(Provider scmPathInfoStore, ObjectMapper objectMapper) { + public HgGlobalConfigInIndexResource(Provider scmPathInfoStore, ObjectMapper objectMapper) { super(objectMapper); this.scmPathInfoStore = scmPathInfoStore; } @Override public void enrich(JsonEnricherContext context) { - if (resultHasMediaType(INDEX, context) && ConfigurationPermissions.read(HgConfig.PERMISSION).isPermitted()) { + if (resultHasMediaType(INDEX, context) && ConfigurationPermissions.read(HgGlobalConfig.PERMISSION).isPermitted()) { String hgConfigUrl = new LinkBuilder(scmPathInfoStore.get().get(), HgConfigResource.class) .method("get") .parameters() diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigToHgConfigDtoMapper.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgGlobalConfigToHgGlobalConfigDtoMapper.java similarity index 71% rename from scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigToHgConfigDtoMapper.java rename to scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgGlobalConfigToHgGlobalConfigDtoMapper.java index 282bcbd11b..40e6670a82 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigToHgConfigDtoMapper.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgGlobalConfigToHgGlobalConfigDtoMapper.java @@ -21,15 +21,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.api.v2.resources; +import com.google.common.annotations.VisibleForTesting; import de.otto.edison.hal.Links; import org.mapstruct.AfterMapping; import org.mapstruct.Mapper; import org.mapstruct.MappingTarget; import sonia.scm.config.ConfigurationPermissions; -import sonia.scm.repository.HgConfig; +import sonia.scm.repository.HgGlobalConfig; import javax.inject.Inject; @@ -39,27 +40,23 @@ 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 HgConfigToHgConfigDtoMapper extends BaseMapper { +public abstract class HgGlobalConfigToHgGlobalConfigDtoMapper extends BaseMapper { @Inject - private ScmPathInfoStore scmPathInfoStore; + private HgConfigLinks links; + + @VisibleForTesting + void setLinks(HgConfigLinks links) { + this.links = links; + } @AfterMapping - void appendLinks(HgConfig config, @MappingTarget HgConfigDto target) { - Links.Builder linksBuilder = linkingTo().self(self()); + void appendLinks(HgGlobalConfig config, @MappingTarget HgGlobalGlobalConfigDto target) { + HgConfigLinks.ConfigLinks configLinks = links.global(); + Links.Builder linksBuilder = linkingTo().self(configLinks.get()); if (ConfigurationPermissions.write(config).isPermitted()) { - linksBuilder.single(link("update", update())); + linksBuilder.single(link("update", configLinks.update())); } target.add(linksBuilder.build()); } - - private String self() { - LinkBuilder linkBuilder = new LinkBuilder(scmPathInfoStore.get(), HgConfigResource.class); - return linkBuilder.method("get").parameters().href(); - } - - private String update() { - LinkBuilder linkBuilder = new LinkBuilder(scmPathInfoStore.get(), HgConfigResource.class); - return linkBuilder.method("update").parameters().href(); - } } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigDto.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgGlobalGlobalConfigDto.java similarity index 94% rename from scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigDto.java rename to scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgGlobalGlobalConfigDto.java index ec9c28587f..0a6a4d2fab 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigDto.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgGlobalGlobalConfigDto.java @@ -34,12 +34,13 @@ import lombok.Setter; @Setter @NoArgsConstructor @SuppressWarnings("java:S2160") // we don't need equals for dto -public class HgConfigDto extends HalRepresentation implements UpdateHgConfigDto { - +public class HgGlobalGlobalConfigDto extends HalRepresentation implements UpdateHgGlobalConfigDto { private boolean disabled; + @Encoding private String encoding; + private String hgBinary; private boolean showRevisionInId; private boolean enableHttpPostArgs; diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgRepositoryConfigDto.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgRepositoryConfigDto.java new file mode 100644 index 0000000000..f3cea7a4bf --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgRepositoryConfigDto.java @@ -0,0 +1,45 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.api.v2.resources; + +import de.otto.edison.hal.HalRepresentation; +import de.otto.edison.hal.Links; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@SuppressWarnings("java:S2160") // we don't need equals for a dto +public class HgRepositoryConfigDto extends HalRepresentation { + + @Encoding + private String encoding; + + public HgRepositoryConfigDto(Links links) { + super(links); + } +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgRepositoryConfigEnricher.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgRepositoryConfigEnricher.java new file mode 100644 index 0000000000..51987ad263 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgRepositoryConfigEnricher.java @@ -0,0 +1,55 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.api.v2.resources; + +import sonia.scm.plugin.Extension; +import sonia.scm.repository.HgRepositoryHandler; +import sonia.scm.repository.Repository; + +import javax.inject.Inject; + +@Extension +@Enrich(Repository.class) +public class HgRepositoryConfigEnricher implements HalEnricher { + + private final HgConfigLinks links; + + @Inject + public HgRepositoryConfigEnricher(HgConfigLinks links) { + this.links = links; + } + + @Override + public void enrich(HalEnricherContext context, HalAppender appender) { + Repository repository = context.oneRequireByType(Repository.class); + if (isHgRepository(repository)) { + appender.appendLink("configuration", links.repository(repository).get()); + } + } + + private boolean isHgRepository(Repository repository) { + return HgRepositoryHandler.TYPE_NAME.equals(repository.getType()); + } +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgRepositoryConfigMapper.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgRepositoryConfigMapper.java new file mode 100644 index 0000000000..060b4ea80c --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgRepositoryConfigMapper.java @@ -0,0 +1,66 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.api.v2.resources; + +import com.google.common.annotations.VisibleForTesting; +import de.otto.edison.hal.Links; +import org.mapstruct.Context; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ObjectFactory; +import sonia.scm.repository.HgRepositoryConfig; +import sonia.scm.repository.Repository; +import sonia.scm.repository.RepositoryPermissions; + +import javax.inject.Inject; + +import static de.otto.edison.hal.Link.link; +import static de.otto.edison.hal.Links.linkingTo; + +@Mapper +public abstract class HgRepositoryConfigMapper { + + @Inject + private HgConfigLinks links; + + @VisibleForTesting + void setLinks(HgConfigLinks links) { + this.links = links; + } + + @Mapping(target = "attributes", ignore = true) // We do not map HAL attributes + abstract HgRepositoryConfigDto map(@Context Repository repository, HgRepositoryConfig config); + abstract HgRepositoryConfig map(HgRepositoryConfigDto dto); + + @ObjectFactory + HgRepositoryConfigDto createDto(@Context Repository repository) { + HgConfigLinks.ConfigLinks configLinks = this.links.repository(repository); + Links.Builder linksBuilder = linkingTo().self(configLinks.get()); + if (RepositoryPermissions.custom("hg", repository).isPermitted()) { + linksBuilder.single(link("update", configLinks.update())); + } + return new HgRepositoryConfigDto(linksBuilder.build()); + } +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgRepositoryConfigResource.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgRepositoryConfigResource.java new file mode 100644 index 0000000000..759b8b4b86 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgRepositoryConfigResource.java @@ -0,0 +1,149 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.api.v2.resources; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import sonia.scm.repository.HgRepositoryConfigStore; +import sonia.scm.repository.NamespaceAndName; +import sonia.scm.repository.Repository; +import sonia.scm.repository.RepositoryManager; +import sonia.scm.web.HgVndMediaType; +import sonia.scm.web.VndMediaType; + +import javax.inject.Inject; +import javax.validation.Valid; +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; +import javax.ws.rs.core.Response; + +import static sonia.scm.ContextEntry.ContextBuilder.entity; +import static sonia.scm.NotFoundException.notFound; + +public class HgRepositoryConfigResource { + + private final RepositoryManager repositoryManager; + private final HgRepositoryConfigStore store; + private final HgRepositoryConfigMapper mapper; + + @Inject + public HgRepositoryConfigResource(RepositoryManager repositoryManager, HgRepositoryConfigStore store, HgRepositoryConfigMapper mapper) { + this.repositoryManager = repositoryManager; + this.store = store; + this.mapper = mapper; + } + + @GET + @Path("") + @Produces(HgVndMediaType.REPO_CONFIG) + @Operation( + summary = "Hg configuration", + description = "Returns the global mercurial configuration.", + tags = "Mercurial", + operationId = "hg_get_repo_config" + ) + @ApiResponse( + responseCode = "200", + description = "success", + content = @Content( + mediaType = HgVndMediaType.REPO_CONFIG, + schema = @Schema(implementation = HgGlobalGlobalConfigDto.class) + ) + ) + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"repository:read:{repositoryId}\" privilege") + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) + public HgRepositoryConfigDto getHgRepositoryConfig(@PathParam("namespace") String namespace, @PathParam("name") String name) { + Repository repository = getRepository(namespace, name); + return mapper.map(repository, store.of(repository)); + } + + @PUT + @Path("") + @Consumes(HgVndMediaType.REPO_CONFIG) + @Operation( + summary = "Modify hg configuration", + description = "Modifies the repository specific mercurial configuration.", + tags = "Mercurial", + operationId = "hg_put_repo_config", + requestBody = @RequestBody( + content = @Content( + mediaType = HgVndMediaType.CONFIG, + schema = @Schema(implementation = UpdateHgGlobalConfigDto.class), + examples = @ExampleObject( + name = "Overwrites current configuration with this one.", + value = "{\n \"encoding\":\"UTF-8\" \n}", + summary = "Simple update configuration" + ) + ) + ) + ) + @ApiResponse( + responseCode = "204", + description = "update success" + ) + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"repository:hg:{repositoryId}\" privilege") + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) + public Response updateHgRepositoryConfig( + @PathParam("namespace") String namespace, + @PathParam("name") String name, + @Valid HgRepositoryConfigDto dto + ) { + Repository repository = getRepository(namespace, name); + store.store(repository, mapper.map(dto)); + return Response.noContent().build(); + } + + private Repository getRepository(String namespace, String name) { + NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, name); + Repository repository = repositoryManager.get(namespaceAndName); + if (repository == null) { + throw notFound(entity(namespaceAndName)); + } + return repository; + } + +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/UpdateHgConfigDto.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/UpdateHgGlobalConfigDto.java similarity index 97% rename from scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/UpdateHgConfigDto.java rename to scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/UpdateHgGlobalConfigDto.java index 126ca999c8..e269ac1e67 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/UpdateHgConfigDto.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/UpdateHgGlobalConfigDto.java @@ -24,7 +24,7 @@ package sonia.scm.api.v2.resources; -interface UpdateHgConfigDto { +interface UpdateHgGlobalConfigDto { boolean isDisabled(); String getHgBinary(); diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/autoconfig/AutoConfigurator.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/autoconfig/AutoConfigurator.java index 837daba9ee..10b76fc595 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/autoconfig/AutoConfigurator.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/autoconfig/AutoConfigurator.java @@ -24,10 +24,10 @@ package sonia.scm.autoconfig; -import sonia.scm.repository.HgConfig; +import sonia.scm.repository.HgGlobalConfig; public interface AutoConfigurator { - void configure(HgConfig config); + void configure(HgGlobalConfig config); } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/autoconfig/NoOpAutoConfigurator.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/autoconfig/NoOpAutoConfigurator.java index ef13b4da46..621386928c 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/autoconfig/NoOpAutoConfigurator.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/autoconfig/NoOpAutoConfigurator.java @@ -26,7 +26,7 @@ package sonia.scm.autoconfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.repository.HgConfig; +import sonia.scm.repository.HgGlobalConfig; public class NoOpAutoConfigurator implements AutoConfigurator { @@ -36,7 +36,7 @@ public class NoOpAutoConfigurator implements AutoConfigurator { } @Override - public void configure(HgConfig config) { + public void configure(HgGlobalConfig config) { // if we do not know the environment, we could not configure mercurial LOG.debug("no mercurial autoconfiguration available on this platform"); } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/autoconfig/PosixAutoConfigurator.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/autoconfig/PosixAutoConfigurator.java index e1f3c0aa62..a29493b816 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/autoconfig/PosixAutoConfigurator.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/autoconfig/PosixAutoConfigurator.java @@ -29,7 +29,7 @@ import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.repository.HgConfig; +import sonia.scm.repository.HgGlobalConfig; import sonia.scm.repository.HgVerifier; import java.io.File; @@ -68,7 +68,7 @@ public class PosixAutoConfigurator implements AutoConfigurator { } @Override - public void configure(HgConfig config) { + public void configure(HgGlobalConfig config) { Optional hg = findInPath(); if (hg.isPresent()) { config.setHgBinary(hg.get().toAbsolutePath().toString()); diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/autoconfig/WindowsAutoConfigurator.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/autoconfig/WindowsAutoConfigurator.java index fae599a602..e6f2e234f7 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/autoconfig/WindowsAutoConfigurator.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/autoconfig/WindowsAutoConfigurator.java @@ -28,7 +28,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Splitter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.repository.HgConfig; +import sonia.scm.repository.HgGlobalConfig; import sonia.scm.repository.HgVerifier; import java.io.File; @@ -72,7 +72,7 @@ public class WindowsAutoConfigurator implements AutoConfigurator { } @Override - public void configure(HgConfig config) { + public void configure(HgGlobalConfig config) { Set fsPaths = new LinkedHashSet<>(pathFromEnv()); resolveRegistryKeys(fsPaths); diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/DefaultHgEnvironmentBuilder.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/DefaultHgEnvironmentBuilder.java index ce4e9c4645..dcd7f9e942 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/DefaultHgEnvironmentBuilder.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/DefaultHgEnvironmentBuilder.java @@ -60,7 +60,7 @@ public class DefaultHgEnvironmentBuilder implements HgEnvironmentBuilder { static final String ENV_TRANSACTION_ID = "SCM_TRANSACTION_ID"; private final AccessTokenBuilderFactory accessTokenBuilderFactory; - private final HgRepositoryHandler repositoryHandler; + private final HgConfigResolver configResolver; private final HookEnvironment hookEnvironment; private final HookServer server; @@ -68,11 +68,11 @@ public class DefaultHgEnvironmentBuilder implements HgEnvironmentBuilder { @Inject public DefaultHgEnvironmentBuilder( - AccessTokenBuilderFactory accessTokenBuilderFactory, HgRepositoryHandler repositoryHandler, + AccessTokenBuilderFactory accessTokenBuilderFactory, HgConfigResolver configResolver, HookEnvironment hookEnvironment, HookServer server ) { this.accessTokenBuilderFactory = accessTokenBuilderFactory; - this.repositoryHandler = repositoryHandler; + this.configResolver = configResolver; this.hookEnvironment = hookEnvironment; this.server = server; } @@ -94,9 +94,8 @@ public class DefaultHgEnvironmentBuilder implements HgEnvironmentBuilder { } private void read(ImmutableMap.Builder env, Repository repository) { - HgConfig config = repositoryHandler.getConfig(); - - File directory = repositoryHandler.getDirectory(repository.getId()); + HgConfig config = configResolver.resolve(repository); + File directory = config.getDirectory(); env.put(ENV_REPOSITORY_NAME, repository.getNamespace() + "/" + repository.getName()); env.put(ENV_REPOSITORY_ID, repository.getId()); diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgConfig.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgConfig.java index 5909a22158..552c9e8b60 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgConfig.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgConfig.java @@ -24,128 +24,17 @@ package sonia.scm.repository; +import lombok.Value; -import sonia.scm.util.Util; +import java.io.File; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlTransient; +@Value +public class HgConfig { - -/** - * - * @author Sebastian Sdorra - */ -@XmlRootElement(name = "config") -public class HgConfig extends RepositoryConfig { - - public static final String PERMISSION = "hg"; - - @Override - @XmlTransient // Only for permission checks, don't serialize to XML - public String getId() { - // Don't change this without migrating SCM permission configuration! - return PERMISSION; - } - - /** - * Method description - * - * - * @return - */ - public String getEncoding() - { - return encoding; - } - - /** - * Method description - * - * - * @return - */ - public String getHgBinary() - { - return hgBinary; - } - - /** - * Method description - * - * - * @return - */ - public boolean isShowRevisionInId() - { - return showRevisionInId; - } - - public boolean isEnableHttpPostArgs() { - return enableHttpPostArgs; - } - - /** - * Method description - * - * - * @return - */ - @Override - public boolean isValid() - { - return super.isValid() && Util.isNotEmpty(hgBinary); - } - - //~--- set methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @param encoding - */ - public void setEncoding(String encoding) - { - this.encoding = encoding; - } - - /** - * Method description - * - * - * @param hgBinary - */ - public void setHgBinary(String hgBinary) - { - this.hgBinary = hgBinary; - } - - /** - * Method description - * - * - * @param showRevisionInId - */ - public void setShowRevisionInId(boolean showRevisionInId) - { - this.showRevisionInId = showRevisionInId; - } - - public void setEnableHttpPostArgs(boolean enableHttpPostArgs) { - this.enableHttpPostArgs = enableHttpPostArgs; - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private String encoding = "UTF-8"; - - /** Field description */ - private String hgBinary; - - /** Field description */ - private boolean showRevisionInId = false; - - private boolean enableHttpPostArgs = false; + String hgBinary; + String encoding; + boolean showRevisionInId; + boolean enableHttpPostArgs; + File directory; } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgConfigResolver.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgConfigResolver.java new file mode 100644 index 0000000000..70197e36ad --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgConfigResolver.java @@ -0,0 +1,86 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.repository; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.MoreObjects; + +import javax.inject.Inject; +import java.io.File; +import java.util.function.Function; + +public class HgConfigResolver { + + private final HgRepositoryHandler repositoryHandler; + private final Function repositoryConfigResolver; + private final Function directoryResolver; + + @Inject + public HgConfigResolver(HgRepositoryHandler repositoryHandler, HgRepositoryConfigStore repositoryConfigStore) { + this( + repositoryHandler, + repositoryConfigStore::of, + repository -> repositoryHandler.getDirectory(repository.getId()) + ); + } + + @VisibleForTesting + public HgConfigResolver(HgRepositoryHandler repositoryHandler) { + this( + repositoryHandler, + repository -> repositoryHandler.getDirectory(repository.getId()) + ); + } + + @VisibleForTesting + public HgConfigResolver(HgRepositoryHandler repositoryHandler, Function directoryResolver) { + this.repositoryHandler = repositoryHandler; + this.repositoryConfigResolver = (repository -> new HgRepositoryConfig()); + this.directoryResolver = directoryResolver; + } + + @VisibleForTesting + public HgConfigResolver(HgRepositoryHandler repositoryHandler, Function repositoryConfigResolver, Function directoryResolver) { + this.repositoryHandler = repositoryHandler; + this.repositoryConfigResolver = repositoryConfigResolver; + this.directoryResolver = directoryResolver; + } + + public boolean isConfigured() { + return repositoryHandler.isConfigured(); + } + + public HgConfig resolve(Repository repository) { + HgGlobalConfig globalConfig = repositoryHandler.getConfig(); + HgRepositoryConfig repositoryConfig = repositoryConfigResolver.apply(repository); + return new HgConfig( + globalConfig.getHgBinary(), + MoreObjects.firstNonNull(repositoryConfig.getEncoding(), globalConfig.getEncoding()), + globalConfig.isShowRevisionInId(), + globalConfig.isEnableHttpPostArgs(), + directoryResolver.apply(repository) + ); + } +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgGlobalConfig.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgGlobalConfig.java new file mode 100644 index 0000000000..a1e671e28a --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgGlobalConfig.java @@ -0,0 +1,151 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.repository; + + +import sonia.scm.util.Util; + +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlTransient; + + +/** + * + * @author Sebastian Sdorra + */ +@XmlRootElement(name = "config") +public class HgGlobalConfig extends RepositoryConfig { + + public static final String PERMISSION = "hg"; + + @Override + @XmlTransient // Only for permission checks, don't serialize to XML + public String getId() { + // Don't change this without migrating SCM permission configuration! + return PERMISSION; + } + + /** + * Method description + * + * + * @return + */ + public String getEncoding() + { + return encoding; + } + + /** + * Method description + * + * + * @return + */ + public String getHgBinary() + { + return hgBinary; + } + + /** + * Method description + * + * + * @return + */ + public boolean isShowRevisionInId() + { + return showRevisionInId; + } + + public boolean isEnableHttpPostArgs() { + return enableHttpPostArgs; + } + + /** + * Method description + * + * + * @return + */ + @Override + public boolean isValid() + { + return super.isValid() && Util.isNotEmpty(hgBinary); + } + + //~--- set methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param encoding + */ + public void setEncoding(String encoding) + { + this.encoding = encoding; + } + + /** + * Method description + * + * + * @param hgBinary + */ + public void setHgBinary(String hgBinary) + { + this.hgBinary = hgBinary; + } + + /** + * Method description + * + * + * @param showRevisionInId + */ + public void setShowRevisionInId(boolean showRevisionInId) + { + this.showRevisionInId = showRevisionInId; + } + + public void setEnableHttpPostArgs(boolean enableHttpPostArgs) { + this.enableHttpPostArgs = enableHttpPostArgs; + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private String encoding = "UTF-8"; + + /** Field description */ + private String hgBinary; + + /** Field description */ + private boolean showRevisionInId = false; + + private boolean enableHttpPostArgs = false; + +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryConfig.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryConfig.java new file mode 100644 index 0000000000..4b401462db --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryConfig.java @@ -0,0 +1,35 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.repository; + +import lombok.Data; + +import javax.xml.bind.annotation.XmlRootElement; + +@Data +@XmlRootElement +public class HgRepositoryConfig { + String encoding; +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryConfigStore.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryConfigStore.java new file mode 100644 index 0000000000..0d1c08c12b --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryConfigStore.java @@ -0,0 +1,59 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.repository; + +import sonia.scm.store.ConfigurationStore; +import sonia.scm.store.ConfigurationStoreFactory; + +import javax.inject.Inject; + +public class HgRepositoryConfigStore { + + private static final String STORE_NAME = "hgConfig"; + private final ConfigurationStoreFactory factory; + + @Inject + public HgRepositoryConfigStore(ConfigurationStoreFactory factory) { + this.factory = factory; + } + + public HgRepositoryConfig of(Repository repository) { + ConfigurationStore store = store(repository); + return store.getOptional().orElse(new HgRepositoryConfig()); + } + + public void store(Repository repository, HgRepositoryConfig config) { + RepositoryPermissions.custom("hg", repository).check(); + store(repository).set(config); + } + + private ConfigurationStore store(Repository repository) { + return factory.withType(HgRepositoryConfig.class) + .withName(STORE_NAME) + .forRepository(repository) + .build(); + } + +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryFactory.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryFactory.java index 5b17ad8bd4..77daef742b 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryFactory.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryFactory.java @@ -25,7 +25,6 @@ package sonia.scm.repository; import com.aragost.javahg.RepositoryConfiguration; -import com.google.common.annotations.VisibleForTesting; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.repository.hooks.HookEnvironment; @@ -38,32 +37,21 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.charset.UnsupportedCharsetException; import java.util.Map; -import java.util.function.Function; @Singleton public class HgRepositoryFactory { private static final Logger LOG = LoggerFactory.getLogger(HgRepositoryFactory.class); - private final HgRepositoryHandler handler; + private final HgConfigResolver configResolver; private final HookEnvironment hookEnvironment; private final HgEnvironmentBuilder environmentBuilder; - private final Function directoryResolver; @Inject - public HgRepositoryFactory(HgRepositoryHandler handler, HookEnvironment hookEnvironment, HgEnvironmentBuilder environmentBuilder) { - this( - handler, hookEnvironment, environmentBuilder, - repository -> handler.getDirectory(repository.getId()) - ); - } - - @VisibleForTesting - public HgRepositoryFactory(HgRepositoryHandler handler, HookEnvironment hookEnvironment, HgEnvironmentBuilder environmentBuilder, Function directoryResolver) { - this.handler = handler; + public HgRepositoryFactory(HgConfigResolver configResolver, HookEnvironment hookEnvironment, HgEnvironmentBuilder environmentBuilder) { + this.configResolver = configResolver; this.hookEnvironment = hookEnvironment; this.environmentBuilder = environmentBuilder; - this.directoryResolver = directoryResolver; } public com.aragost.javahg.Repository openForRead(Repository repository) { @@ -75,7 +63,8 @@ public class HgRepositoryFactory { } private com.aragost.javahg.Repository open(Repository repository, Map environment) { - File directory = directoryResolver.apply(repository); + HgConfig config = configResolver.resolve(repository); + File directory = config.getDirectory(); RepositoryConfiguration repoConfiguration = RepositoryConfiguration.DEFAULT; repoConfiguration.getEnvironment().putAll(environment); @@ -84,18 +73,18 @@ public class HgRepositoryFactory { boolean pending = hookEnvironment.isPending(); repoConfiguration.setEnablePendingChangesets(pending); - Charset encoding = encoding(); + Charset encoding = encoding(config); repoConfiguration.setEncoding(encoding); - repoConfiguration.setHgBin(handler.getConfig().getHgBinary()); + repoConfiguration.setHgBin(config.getHgBinary()); LOG.trace("open hg repository {}: encoding: {}, pending: {}", directory, encoding, pending); return com.aragost.javahg.Repository.open(repoConfiguration, directory); } - private Charset encoding() { - String charset = handler.getConfig().getEncoding(); + private Charset encoding(HgConfig config) { + String charset = config.getEncoding(); try { return Charset.forName(charset); } catch (UnsupportedCharsetException ex) { diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java index a99df0a415..4498301e65 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java @@ -55,7 +55,7 @@ import java.io.OutputStream; @Singleton @Extension public class HgRepositoryHandler - extends AbstractSimpleRepositoryHandler { + extends AbstractSimpleRepositoryHandler { public static final String TYPE_DISPLAYNAME = "Mercurial"; public static final String TYPE_NAME = "hg"; @@ -84,7 +84,7 @@ public class HgRepositoryHandler this.configurator = configurator; } - public void doAutoConfiguration(HgConfig autoConfig) { + public void doAutoConfiguration(HgGlobalConfig autoConfig) { configurator.configure(autoConfig); } @@ -99,7 +99,7 @@ public class HgRepositoryHandler super.loadConfig(); if (config == null) { - config = new HgConfig(); + config = new HgGlobalConfig(); storeConfig(); } @@ -109,7 +109,7 @@ public class HgRepositoryHandler } } - private boolean isConfigValid(HgConfig config) { + private boolean isConfigValid(HgGlobalConfig config) { return config.isValid() && new HgVerifier().isValid(config); } @@ -175,8 +175,8 @@ public class HgRepositoryHandler } @Override - protected Class getConfigClass() { - return HgConfig.class; + protected Class getConfigClass() { + return HgGlobalConfig.class; } private void writeHgExtensions(SCMContextProvider context) { diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgVerifier.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgVerifier.java index 0bca79987c..ef332b8770 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgVerifier.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgVerifier.java @@ -47,7 +47,7 @@ public class HgVerifier { this.versionResolver = versionResolver; } - public boolean isValid(HgConfig config) { + public boolean isValid(HgGlobalConfig config) { return isValid(config.getHgBinary()); } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookContextProviderFactory.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookContextProviderFactory.java index 04f88189ce..fa9773fad2 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookContextProviderFactory.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/hooks/HookContextProviderFactory.java @@ -25,8 +25,8 @@ package sonia.scm.repository.hooks; import sonia.scm.NotFoundException; +import sonia.scm.repository.HgConfigResolver; import sonia.scm.repository.HgRepositoryFactory; -import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryManager; import sonia.scm.repository.spi.HgHookContextProvider; @@ -36,13 +36,13 @@ import javax.inject.Inject; public class HookContextProviderFactory { private final RepositoryManager repositoryManager; - private final HgRepositoryHandler repositoryHandler; + private final HgConfigResolver configResolver; private final HgRepositoryFactory repositoryFactory; @Inject - public HookContextProviderFactory(RepositoryManager repositoryManager, HgRepositoryHandler repositoryHandler, HgRepositoryFactory repositoryFactory) { + public HookContextProviderFactory(RepositoryManager repositoryManager, HgConfigResolver configResolver, HgRepositoryFactory repositoryFactory) { this.repositoryManager = repositoryManager; - this.repositoryHandler = repositoryHandler; + this.configResolver = configResolver; this.repositoryFactory = repositoryFactory; } @@ -51,7 +51,7 @@ public class HookContextProviderFactory { if (repository == null) { throw new NotFoundException(Repository.class, repositoryId); } - return new HgHookContextProvider(repositoryHandler, repositoryFactory, repository, node); + return new HgHookContextProvider(configResolver, repositoryFactory, repository, node); } } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgCommandContext.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgCommandContext.java index 2679a3473d..45d675783e 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgCommandContext.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgCommandContext.java @@ -28,8 +28,8 @@ package sonia.scm.repository.spi; import com.aragost.javahg.Repository; import sonia.scm.repository.HgConfig; +import sonia.scm.repository.HgConfigResolver; import sonia.scm.repository.HgRepositoryFactory; -import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.repository.RepositoryProvider; import java.io.Closeable; @@ -43,14 +43,14 @@ import java.io.File; */ public class HgCommandContext implements Closeable, RepositoryProvider { - private final HgRepositoryHandler handler; + private final HgConfigResolver configResolver; private final HgRepositoryFactory factory; private final sonia.scm.repository.Repository scmRepository; private Repository repository; - public HgCommandContext(HgRepositoryHandler handler, HgRepositoryFactory factory, sonia.scm.repository.Repository scmRepository) { - this.handler = handler; + public HgCommandContext(HgConfigResolver configResolver, HgRepositoryFactory factory, sonia.scm.repository.Repository scmRepository) { + this.configResolver = configResolver; this.factory = factory; this.scmRepository = scmRepository; } @@ -66,14 +66,17 @@ public class HgCommandContext implements Closeable, RepositoryProvider { return factory.openForWrite(scmRepository); } + private HgConfig config; - public HgConfig getConfig() - { - return handler.getConfig(); + public HgConfig getConfig() { + if (config == null) { + config = configResolver.resolve(scmRepository); + } + return config; } public File getDirectory() { - return handler.getDirectory(scmRepository.getId()); + return getConfig().getDirectory(); } public sonia.scm.repository.Repository getScmRepository() { @@ -85,7 +88,6 @@ public class HgCommandContext implements Closeable, RepositoryProvider { return getScmRepository(); } - @Override public void close() { if (repository != null) { diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgHookChangesetProvider.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgHookChangesetProvider.java index 98bddd8c7c..ed94b60c9e 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgHookChangesetProvider.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgHookChangesetProvider.java @@ -27,8 +27,9 @@ package sonia.scm.repository.spi; import com.aragost.javahg.Repository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sonia.scm.repository.HgConfig; +import sonia.scm.repository.HgConfigResolver; import sonia.scm.repository.HgRepositoryFactory; -import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.repository.spi.javahg.HgLogChangesetCommand; import sonia.scm.web.HgUtil; @@ -40,15 +41,15 @@ public class HgHookChangesetProvider implements HookChangesetProvider { private static final Logger LOG = LoggerFactory.getLogger(HgHookChangesetProvider.class); - private final HgRepositoryHandler handler; + private final HgConfigResolver configResolver; private final HgRepositoryFactory factory; private final sonia.scm.repository.Repository scmRepository; private final String startRev; private HookChangesetResponse response; - public HgHookChangesetProvider(HgRepositoryHandler handler, HgRepositoryFactory factory, sonia.scm.repository.Repository scmRepository, String startRev) { - this.handler = handler; + public HgHookChangesetProvider(HgConfigResolver configResolver, HgRepositoryFactory factory, sonia.scm.repository.Repository scmRepository, String startRev) { + this.configResolver = configResolver; this.factory = factory; this.scmRepository = scmRepository; this.startRev = startRev; @@ -62,7 +63,8 @@ public class HgHookChangesetProvider implements HookChangesetProvider { try { repository = factory.openForRead(scmRepository); - HgLogChangesetCommand cmd = HgLogChangesetCommand.on(repository, handler.getConfig()); + HgConfig config = configResolver.resolve(scmRepository); + HgLogChangesetCommand cmd = HgLogChangesetCommand.on(repository, config); response = new HookChangesetResponse( cmd.rev(startRev.concat(":").concat(HgUtil.REVISION_TIP)).execute() diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgHookContextProvider.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgHookContextProvider.java index 0282729baa..eb38d9c792 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgHookContextProvider.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgHookContextProvider.java @@ -26,8 +26,8 @@ package sonia.scm.repository.spi; //~--- non-JDK imports -------------------------------------------------------- +import sonia.scm.repository.HgConfigResolver; import sonia.scm.repository.HgRepositoryFactory; -import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.repository.Repository; import sonia.scm.repository.api.HgHookBranchProvider; import sonia.scm.repository.api.HgHookMessageProvider; @@ -61,8 +61,8 @@ public class HgHookContextProvider extends HookContextProvider { private HgHookBranchProvider hookBranchProvider; private HgHookTagProvider hookTagProvider; - public HgHookContextProvider(HgRepositoryHandler handler, HgRepositoryFactory factory, Repository repository, String startRev) { - this.hookChangesetProvider = new HgHookChangesetProvider(handler, factory, repository, startRev); + public HgHookContextProvider(HgConfigResolver configResolver, HgRepositoryFactory factory, Repository repository, String startRev) { + this.hookChangesetProvider = new HgHookChangesetProvider(configResolver, factory, repository, startRev); } @Override diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java index 81ac655f6e..c469fb4079 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java @@ -27,6 +27,7 @@ package sonia.scm.repository.spi; import com.google.common.io.Closeables; import sonia.scm.event.ScmEventBus; import sonia.scm.repository.Feature; +import sonia.scm.repository.HgConfigResolver; import sonia.scm.repository.HgRepositoryFactory; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.repository.Repository; @@ -70,6 +71,7 @@ public class HgRepositoryServiceProvider extends RepositoryServiceProvider { private final ScmEventBus eventBus; HgRepositoryServiceProvider(HgRepositoryHandler handler, + HgConfigResolver configResolver, HgRepositoryFactory factory, HgRepositoryHookEventFactory eventFactory, ScmEventBus eventBus, @@ -77,7 +79,7 @@ public class HgRepositoryServiceProvider extends RepositoryServiceProvider { this.handler = handler; this.eventBus = eventBus; this.eventFactory = eventFactory; - this.context = new HgCommandContext(handler, factory, repository); + this.context = new HgCommandContext(configResolver, factory, repository); this.lazyChangesetResolver = new HgLazyChangesetResolver(factory, repository); } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceResolver.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceResolver.java index be10913fbb..76500d3a7e 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceResolver.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceResolver.java @@ -27,6 +27,7 @@ package sonia.scm.repository.spi; import com.google.inject.Inject; import sonia.scm.event.ScmEventBus; import sonia.scm.plugin.Extension; +import sonia.scm.repository.HgConfigResolver; import sonia.scm.repository.HgRepositoryFactory; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.repository.Repository; @@ -38,17 +39,20 @@ import sonia.scm.repository.Repository; public class HgRepositoryServiceResolver implements RepositoryServiceResolver { private final HgRepositoryHandler handler; + private final HgConfigResolver configResolver; private final HgRepositoryFactory factory; private final ScmEventBus eventBus; private final HgRepositoryHookEventFactory eventFactory; @Inject public HgRepositoryServiceResolver(HgRepositoryHandler handler, + HgConfigResolver configResolver, HgRepositoryFactory factory, ScmEventBus eventBus, HgRepositoryHookEventFactory eventFactory ) { this.handler = handler; + this.configResolver = configResolver; this.factory = factory; this.eventBus = eventBus; this.eventFactory = eventFactory; @@ -59,7 +63,7 @@ public class HgRepositoryServiceResolver implements RepositoryServiceResolver { HgRepositoryServiceProvider provider = null; if (HgRepositoryHandler.TYPE_NAME.equalsIgnoreCase(repository.getType())) { - provider = new HgRepositoryServiceProvider(handler, factory, eventFactory, eventBus, repository); + provider = new HgRepositoryServiceProvider(handler, configResolver, factory, eventFactory, eventBus, repository); } return provider; diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgVersionCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgVersionCommand.java index 52a513067a..37e8e2d22e 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgVersionCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgVersionCommand.java @@ -28,7 +28,7 @@ import com.google.common.io.ByteStreams; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.SCMContext; -import sonia.scm.repository.HgConfig; +import sonia.scm.repository.HgGlobalConfig; import sonia.scm.repository.HgExtensions; import java.io.IOException; @@ -41,15 +41,15 @@ public class HgVersionCommand { private static final Logger LOG = LoggerFactory.getLogger(HgVersionCommand.class); public static final String UNKNOWN = "python/x.y.z mercurial/x.y.z"; - private final HgConfig config; + private final HgGlobalConfig config; private final String extension; private final ProcessExecutor executor; - public HgVersionCommand(HgConfig config) { + public HgVersionCommand(HgGlobalConfig config) { this(config, extension(), command -> new ProcessBuilder(command).start()); } - HgVersionCommand(HgConfig config, String extension, ProcessExecutor executor) { + HgVersionCommand(HgGlobalConfig config, String extension, ProcessExecutor executor) { this.config = config; this.extension = extension; this.executor = executor; diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgIncomingChangesetCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgIncomingChangesetCommand.java index e8371186bb..3134f293d4 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgIncomingChangesetCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgIncomingChangesetCommand.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.repository.spi.javahg; //~--- non-JDK imports -------------------------------------------------------- @@ -61,9 +61,7 @@ public class HgIncomingChangesetCommand * * @return */ - public static HgIncomingChangesetCommand on(Repository repository, - HgConfig config) - { + public static HgIncomingChangesetCommand on(Repository repository, HgConfig config) { return new HgIncomingChangesetCommand(repository, config); } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgIncomingOutgoingChangesetCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgIncomingOutgoingChangesetCommand.java index 9892f423bc..cb216e7f1e 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgIncomingOutgoingChangesetCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgIncomingOutgoingChangesetCommand.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.repository.spi.javahg; //~--- non-JDK imports -------------------------------------------------------- @@ -52,9 +52,7 @@ public abstract class HgIncomingOutgoingChangesetCommand * @param repository * @param config */ - public HgIncomingOutgoingChangesetCommand(Repository repository, - HgConfig config) - { + public HgIncomingOutgoingChangesetCommand(Repository repository, HgConfig config) { super(repository, config); } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgOutgoingChangesetCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgOutgoingChangesetCommand.java index dc3140099e..a35e13139d 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgOutgoingChangesetCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgOutgoingChangesetCommand.java @@ -21,13 +21,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.repository.spi.javahg; //~--- non-JDK imports -------------------------------------------------------- import com.aragost.javahg.Repository; - import sonia.scm.repository.HgConfig; /** diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java index fab49af552..fcb256b528 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java @@ -31,10 +31,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.SCMContext; import sonia.scm.config.ScmConfiguration; -import sonia.scm.repository.HgConfig; import sonia.scm.repository.HgEnvironmentBuilder; import sonia.scm.repository.HgExtensions; -import sonia.scm.repository.HgRepositoryHandler; +import sonia.scm.repository.HgConfig; +import sonia.scm.repository.HgConfigResolver; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryRequestListenerUtil; import sonia.scm.repository.spi.ScmProviderHttpServlet; @@ -60,38 +60,41 @@ import java.util.List; @Singleton public class HgCGIServlet extends HttpServlet implements ScmProviderHttpServlet { - /** Field description */ private static final long serialVersionUID = -3492811300905099810L; /** the logger for HgCGIServlet */ private static final Logger logger = LoggerFactory.getLogger(HgCGIServlet.class); - //~--- constructors --------------------------------------------------------- + private final CGIExecutorFactory cgiExecutorFactory; + private final HgConfigResolver configResolver; + private final File extension; + private final ScmConfiguration configuration; + private final HgCGIExceptionHandler exceptionHandler; + private final RepositoryRequestListenerUtil requestListenerUtil; + private final HgEnvironmentBuilder environmentBuilder; @Inject public HgCGIServlet(CGIExecutorFactory cgiExecutorFactory, + HgConfigResolver configResolver, ScmConfiguration configuration, - HgRepositoryHandler handler, RepositoryRequestListenerUtil requestListenerUtil, HgEnvironmentBuilder environmentBuilder) { this.cgiExecutorFactory = cgiExecutorFactory; + this.configResolver = configResolver; this.configuration = configuration; - this.handler = handler; this.requestListenerUtil = requestListenerUtil; this.environmentBuilder = environmentBuilder; this.exceptionHandler = new HgCGIExceptionHandler(); this.extension = HgExtensions.CGISERVE.getFile(SCMContext.getContext()); } - //~--- methods -------------------------------------------------------------- - @Override public void service(HttpServletRequest request, HttpServletResponse response, Repository repository) { - if (!handler.isConfigured()) + if (!configResolver.isConfigured()) { exceptionHandler.sendFormattedError(request, response, HgCGIExceptionHandler.ERROR_NOT_CONFIGURED); @@ -141,10 +144,9 @@ public class HgCGIServlet extends HttpServlet implements ScmProviderHttpServlet EnvList env = executor.getEnvironment(); environmentBuilder.write(repository).forEach(env::set); - File directory = handler.getDirectory(repository.getId()); - executor.setWorkDirectory(directory); + HgConfig config = configResolver.resolve(repository); + executor.setWorkDirectory(config.getDirectory()); - HgConfig config = handler.getConfig(); executor.setArgs(createArgs(config)); executor.execute(config.getHgBinary()); } @@ -174,26 +176,4 @@ public class HgCGIServlet extends HttpServlet implements ScmProviderHttpServlet args.add("--config"); args.add(key + "=" + value); } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private final CGIExecutorFactory cgiExecutorFactory; - - /** Field description */ - private final File extension; - - /** Field description */ - private final ScmConfiguration configuration; - - /** Field description */ - private final HgCGIExceptionHandler exceptionHandler; - - /** Field description */ - private final HgRepositoryHandler handler; - - /** Field description */ - private final RepositoryRequestListenerUtil requestListenerUtil; - - private final HgEnvironmentBuilder environmentBuilder; } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java index a574971529..0a9783581e 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgServletModule.java @@ -28,8 +28,9 @@ package sonia.scm.web; import com.google.inject.servlet.ServletModule; import org.mapstruct.factory.Mappers; -import sonia.scm.api.v2.resources.HgConfigDtoToHgConfigMapper; -import sonia.scm.api.v2.resources.HgConfigToHgConfigDtoMapper; +import sonia.scm.api.v2.resources.HgGlobalConfigDtoToHgConfigMapper; +import sonia.scm.api.v2.resources.HgGlobalConfigToHgGlobalConfigDtoMapper; +import sonia.scm.api.v2.resources.HgRepositoryConfigMapper; import sonia.scm.plugin.Extension; import sonia.scm.repository.spi.HgWorkingCopyFactory; import sonia.scm.repository.spi.SimpleHgWorkingCopyFactory; @@ -43,8 +44,9 @@ public class HgServletModule extends ServletModule { @Override protected void configureServlets() { - bind(HgConfigDtoToHgConfigMapper.class).to(Mappers.getMapper(HgConfigDtoToHgConfigMapper.class).getClass()); - bind(HgConfigToHgConfigDtoMapper.class).to(Mappers.getMapper(HgConfigToHgConfigDtoMapper.class).getClass()); + bind(HgGlobalConfigDtoToHgConfigMapper.class).to(Mappers.getMapperClass(HgGlobalConfigDtoToHgConfigMapper.class)); + bind(HgGlobalConfigToHgGlobalConfigDtoMapper.class).to(Mappers.getMapperClass(HgGlobalConfigToHgGlobalConfigDtoMapper.class)); + bind(HgRepositoryConfigMapper.class).to(Mappers.getMapperClass(HgRepositoryConfigMapper.class)); bind(HgWorkingCopyFactory.class).to(SimpleHgWorkingCopyFactory.class); } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgVndMediaType.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgVndMediaType.java index d453d945b1..a282a2ccc3 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgVndMediaType.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgVndMediaType.java @@ -21,13 +21,14 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.web; public class HgVndMediaType { private static final String PREFIX = VndMediaType.PREFIX + "hgConfig"; + public static final String REPO_CONFIG = PREFIX + "-repo" + VndMediaType.SUFFIX; public static final String CONFIG = PREFIX + VndMediaType.SUFFIX; public static final String PACKAGES = PREFIX + "-packages" + VndMediaType.SUFFIX; public static final String INSTALLATIONS = PREFIX + "-installation" + VndMediaType.SUFFIX; diff --git a/scm-plugins/scm-hg-plugin/src/main/js/HgRepositoryConfigurationForm.tsx b/scm-plugins/scm-hg-plugin/src/main/js/HgRepositoryConfigurationForm.tsx new file mode 100644 index 0000000000..d8e94c3eec --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/js/HgRepositoryConfigurationForm.tsx @@ -0,0 +1,85 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +import { Repository } from "@scm-manager/ui-types"; +import { ErrorNotification, InputField, Level, Loading, SubmitButton, Notification } from "@scm-manager/ui-components"; +import React, { FC, FormEvent, useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { HgRepositoryConfiguration, useHgRepositoryConfiguration, useUpdateHgRepositoryConfiguration } from "./hooks"; + +type Props = { + repository: Repository; +}; + +const HgRepositoryConfigurationForm: FC = ({ repository }) => { + const [t] = useTranslation("plugins"); + const { isLoading, error, data } = useHgRepositoryConfiguration(repository); + const { isLoading: isUpdating, error: updateError, update, updated } = useUpdateHgRepositoryConfiguration(); + const [configuration, setConfiguration] = useState(null); + useEffect(() => { + setConfiguration(data); + }, [data]); + + if (error) { + return ; + } + + if (isLoading || !configuration) { + return ; + } + + const encodingChanged = (value: string) => { + const encoding = value ? value : undefined; + setConfiguration({ + ...configuration, + encoding + }); + }; + + const submit = (e: FormEvent) => { + e.preventDefault(); + update(configuration); + }; + + const readOnly = !configuration._links.update; + + return ( +
+ + {updated ? {t("scm-hg-plugin.config.success")} : null} + + {!readOnly ? ( + } /> + ) : null} + + ); +}; + +export default HgRepositoryConfigurationForm; diff --git a/scm-plugins/scm-hg-plugin/src/main/js/hooks.ts b/scm-plugins/scm-hg-plugin/src/main/js/hooks.ts new file mode 100644 index 0000000000..de6f7a1366 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/js/hooks.ts @@ -0,0 +1,86 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import { useEffect, useState } from "react"; +import { apiClient } from "@scm-manager/ui-components"; +import { HalRepresentation, Link, Repository } from "@scm-manager/ui-types"; + +export type HgRepositoryConfiguration = HalRepresentation & { + encoding?: string; +}; + +export const useHgRepositoryConfiguration = (repository: Repository) => { + const [isLoading, setLoading] = useState(false); + const [data, setData] = useState(null); + const [error, setError] = useState(null); + const link = (repository._links.configuration as Link)?.href; + useEffect(() => { + setError(null); + if (link) { + setLoading(true); + apiClient + .get(link) + .then(response => response.json()) + .then((config: HgRepositoryConfiguration) => { + setData(config); + }) + .catch(e => setError(e)) + .finally(() => setLoading(false)); + } + }, [link]); + + return { + isLoading, + error, + data + }; +}; + +export const useUpdateHgRepositoryConfiguration = () => { + const [isLoading, setLoading] = useState(false); + const [error, setError] = useState(null); + const [updated, setUpdated] = useState(false); + + const update = (configuration: HgRepositoryConfiguration) => { + if (!configuration._links.update) { + throw new Error("no update link on configuration"); + } + const link = (configuration._links.update as Link).href; + setLoading(true); + setUpdated(false); + setError(null); + apiClient + .put(link, configuration, "application/vnd.scmm-hgConfig-repo+json;v=2") + .then(() => setUpdated(true)) + .catch(e => setError(e)) + .finally(() => setLoading(false)); + }; + + return { + isLoading, + error, + update, + updated + }; +}; diff --git a/scm-plugins/scm-hg-plugin/src/main/js/index.ts b/scm-plugins/scm-hg-plugin/src/main/js/index.ts index 2365b1458a..2c37b05881 100644 --- a/scm-plugins/scm-hg-plugin/src/main/js/index.ts +++ b/scm-plugins/scm-hg-plugin/src/main/js/index.ts @@ -29,6 +29,7 @@ import { ConfigurationBinder as cfgBinder } from "@scm-manager/ui-components"; import HgGlobalConfiguration from "./HgGlobalConfiguration"; import HgBranchInformation from "./HgBranchInformation"; import HgTagInformation from "./HgTagInformation"; +import HgRepositoryConfigurationForm from "./HgRepositoryConfigurationForm"; const hgPredicate = (props: any) => { return props.repository && props.repository.type === "hg"; @@ -39,6 +40,10 @@ binder.bind("repos.branch-details.information", HgBranchInformation, hgPredicate binder.bind("repos.tag-details.information", HgTagInformation, hgPredicate); binder.bind("repos.repository-avatar", HgAvatar, hgPredicate); +// bind repository specific configuration + +binder.bind("repo-config.route", HgRepositoryConfigurationForm, hgPredicate); + // bind global configuration cfgBinder.bindGlobal("/hg", "scm-hg-plugin.config.link", "hgConfig", HgGlobalConfiguration); diff --git a/scm-plugins/scm-hg-plugin/src/main/resources/locales/de/plugins.json b/scm-plugins/scm-hg-plugin/src/main/resources/locales/de/plugins.json index 9c3f458d26..815fe1b6c1 100644 --- a/scm-plugins/scm-hg-plugin/src/main/resources/locales/de/plugins.json +++ b/scm-plugins/scm-hg-plugin/src/main/resources/locales/de/plugins.json @@ -21,7 +21,9 @@ "enableHttpPostArgsHelpText": "Aktiviert das experimentelle HttpPostArgs Protokoll von Mercurial. Das HttpPostArgs Protokoll verwendet den Post Request Body anstatt des HTTP Headers um Meta Informationen zu versenden. Dieses Vorgehen reduziert die Header Größe der Mercurial Requests. HttpPostArgs wird seit Mercurial 3.8 unterstützt.", "disabled": "Deaktiviert", "disabledHelpText": "Aktiviert oder deaktiviert das Mercurial Plugin.", - "required": "Dieser Konfigurationswert wird benötigt" + "required": "Dieser Konfigurationswert wird benötigt", + "submit": "Speichern", + "success": "Einstellungen wurden erfolgreich geändert" } }, "permissions" : { diff --git a/scm-plugins/scm-hg-plugin/src/main/resources/locales/en/plugins.json b/scm-plugins/scm-hg-plugin/src/main/resources/locales/en/plugins.json index c542511227..9ea5956306 100644 --- a/scm-plugins/scm-hg-plugin/src/main/resources/locales/en/plugins.json +++ b/scm-plugins/scm-hg-plugin/src/main/resources/locales/en/plugins.json @@ -21,7 +21,9 @@ "enableHttpPostArgsHelpText": "Enables the experimental HttpPostArgs Protocol of mercurial. The HttpPostArgs Protocol uses the body of post requests to send the meta information instead of http headers. This helps to reduce the header size of mercurial requests. HttpPostArgs is supported since mercurial 3.8.", "disabled": "Disabled", "disabledHelpText": "Enable or disable the Mercurial plugin.", - "required": "This configuration value is required" + "required": "This configuration value is required", + "submit": "Submit", + "success": "Configuration changed successfully" } }, "permissions" : { diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigResourceTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigResourceTest.java index ec686dc1e6..0a13590130 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigResourceTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigResourceTest.java @@ -34,11 +34,12 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mapstruct.factory.Mappers; import org.mockito.Answers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; -import sonia.scm.repository.HgConfig; +import sonia.scm.repository.HgGlobalConfig; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.web.HgVndMediaType; import sonia.scm.web.RestDispatcher; @@ -65,33 +66,41 @@ public class HgConfigResourceTest { @Rule public ShiroRule shiro = new ShiroRule(); - private RestDispatcher dispatcher = new RestDispatcher(); - - private final URI baseUri = URI.create("/"); + private final RestDispatcher dispatcher = new RestDispatcher(); @InjectMocks - private HgConfigDtoToHgConfigMapperImpl dtoToConfigMapper; - - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - private ScmPathInfoStore scmPathInfoStore; - - @InjectMocks - private HgConfigToHgConfigDtoMapperImpl configToDtoMapper; + private HgGlobalConfigDtoToHgConfigMapperImpl dtoToConfigMapper; @Mock private HgRepositoryHandler repositoryHandler; @Mock - private Provider autoconfigResource; + private Provider autoconfigResource; + + @Mock + private Provider repositoryConfigResource; @Before public void prepareEnvironment() { - HgConfig gitConfig = createConfiguration(); + HgGlobalConfig gitConfig = createConfiguration(); when(repositoryHandler.getConfig()).thenReturn(gitConfig); - HgConfigResource gitConfigResource = - new HgConfigResource(dtoToConfigMapper, configToDtoMapper, repositoryHandler, autoconfigResource); + + HgConfigResource gitConfigResource = new HgConfigResource( + dtoToConfigMapper, createConfigToDtoMapper(), repositoryHandler, + autoconfigResource, repositoryConfigResource + ); dispatcher.addSingletonResource(gitConfigResource); - when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri); + } + + private HgGlobalConfigToHgGlobalConfigDtoMapper createConfigToDtoMapper() { + ScmPathInfoStore store = new ScmPathInfoStore(); + store.set(() -> URI.create("/")); + HgConfigLinks links = new HgConfigLinks(store); + HgGlobalConfigToHgGlobalConfigDtoMapper mapper = Mappers.getMapper( + HgGlobalConfigToHgGlobalConfigDtoMapper.class + ); + mapper.setLinks(links); + return mapper; } @Test @@ -172,8 +181,8 @@ public class HgConfigResourceTest { return response; } - private HgConfig createConfiguration() { - HgConfig config = new HgConfig(); + private HgGlobalConfig createConfiguration() { + HgGlobalConfig config = new HgGlobalConfig(); config.setDisabled(false); return config; } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigToHgConfigDtoMapperTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigToHgConfigDtoMapperTest.java deleted file mode 100644 index 9cd83c088c..0000000000 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigToHgConfigDtoMapperTest.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2020-present Cloudogu GmbH and Contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package sonia.scm.api.v2.resources; - -import org.apache.shiro.subject.Subject; -import org.apache.shiro.subject.support.SubjectThreadState; -import org.apache.shiro.util.ThreadContext; -import org.apache.shiro.util.ThreadState; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Answers; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; -import sonia.scm.repository.HgConfig; - -import java.net.URI; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static sonia.scm.api.v2.resources.HgConfigTests.assertEqualsConfiguration; -import static sonia.scm.api.v2.resources.HgConfigTests.createConfiguration; - -@RunWith(MockitoJUnitRunner.class) -public class HgConfigToHgConfigDtoMapperTest { - - private URI baseUri = URI.create("http://example.com/base/"); - - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - private ScmPathInfoStore scmPathInfoStore; - - @InjectMocks - private HgConfigToHgConfigDtoMapperImpl mapper; - - private final Subject subject = mock(Subject.class); - private final ThreadState subjectThreadState = new SubjectThreadState(subject); - - private URI expectedBaseUri; - - @Before - public void init() { - when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri); - expectedBaseUri = baseUri.resolve(HgConfigResource.HG_CONFIG_PATH_V2); - subjectThreadState.bind(); - ThreadContext.bind(subject); - } - - @After - public void unbindSubject() { - ThreadContext.unbindSubject(); - } - - @Test - public void shouldMapFields() { - HgConfig config = createConfiguration(); - - when(subject.isPermitted("configuration:write:hg")).thenReturn(true); - HgConfigDto dto = mapper.map(config); - - assertEqualsConfiguration(dto); - - assertEquals(expectedBaseUri.toString(), dto.getLinks().getLinkBy("self").get().getHref()); - assertEquals(expectedBaseUri.toString(), dto.getLinks().getLinkBy("update").get().getHref()); - } - - @Test - public void shouldMapFieldsWithoutUpdate() { - HgConfig config = createConfiguration(); - - when(subject.isPermitted("configuration:write:hg")).thenReturn(false); - HgConfigDto dto = mapper.map(config); - - assertEquals(expectedBaseUri.toString(), dto.getLinks().getLinkBy("self").get().getHref()); - assertFalse(dto.getLinks().hasLink("update")); - } -} diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigAutoConfigurationResourceTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgGlobalConfigAutoConfigurationResourceTest.java similarity index 82% rename from scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigAutoConfigurationResourceTest.java rename to scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgGlobalConfigAutoConfigurationResourceTest.java index 5820c58cad..8b2cec1feb 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigAutoConfigurationResourceTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgGlobalConfigAutoConfigurationResourceTest.java @@ -36,7 +36,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; -import sonia.scm.repository.HgConfig; +import sonia.scm.repository.HgGlobalConfig; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.web.HgVndMediaType; import sonia.scm.web.RestDispatcher; @@ -56,31 +56,34 @@ import static org.mockito.Mockito.when; password = "secret" ) @RunWith(MockitoJUnitRunner.class) -public class HgConfigAutoConfigurationResourceTest { +public class HgGlobalConfigAutoConfigurationResourceTest { @Rule public ShiroRule shiro = new ShiroRule(); - private RestDispatcher dispatcher = new RestDispatcher(); + private final RestDispatcher dispatcher = new RestDispatcher(); @InjectMocks - private HgConfigDtoToHgConfigMapperImpl dtoToConfigMapper; + private HgGlobalConfigDtoToHgConfigMapperImpl dtoToConfigMapper; @Mock private HgRepositoryHandler repositoryHandler; @Mock - private Provider resourceProvider; + private Provider resourceProvider; + + @Mock + private Provider repositoryConfigResource; @Before public void prepareEnvironment() { - HgConfigAutoConfigurationResource resource = - new HgConfigAutoConfigurationResource(dtoToConfigMapper, repositoryHandler); + HgGlobalConfigAutoConfigurationResource resource = new HgGlobalConfigAutoConfigurationResource(dtoToConfigMapper, repositoryHandler); when(resourceProvider.get()).thenReturn(resource); - dispatcher.addSingletonResource( - new HgConfigResource(null, null, null, - resourceProvider)); + dispatcher.addSingletonResource(new HgConfigResource( + null, null, null, + resourceProvider, repositoryConfigResource + )); } @Test @@ -90,7 +93,7 @@ public class HgConfigAutoConfigurationResourceTest { assertEquals(HttpServletResponse.SC_NO_CONTENT, response.getStatus()); - HgConfig actualConfig = captureConfig(); + HgGlobalConfig actualConfig = captureConfig(); assertFalse(actualConfig.isDisabled()); } @@ -110,7 +113,7 @@ public class HgConfigAutoConfigurationResourceTest { assertEquals(HttpServletResponse.SC_NO_CONTENT, response.getStatus()); - HgConfig actualConfig = captureConfig(); + HgGlobalConfig actualConfig = captureConfig(); assertTrue(actualConfig.isDisabled()); } @@ -137,8 +140,8 @@ public class HgConfigAutoConfigurationResourceTest { return response; } - private HgConfig captureConfig() { - ArgumentCaptor configCaptor = ArgumentCaptor.forClass(HgConfig.class); + private HgGlobalConfig captureConfig() { + ArgumentCaptor configCaptor = ArgumentCaptor.forClass(HgGlobalConfig.class); verify(repositoryHandler).doAutoConfiguration(configCaptor.capture()); return configCaptor.getValue(); } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigDtoToHgConfigMapperTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgGlobalConfigDtoToHgGlobalConfigMapperTest.java similarity index 83% rename from scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigDtoToHgConfigMapperTest.java rename to scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgGlobalConfigDtoToHgGlobalConfigMapperTest.java index 887ae234a6..d11a53db57 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigDtoToHgConfigMapperTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgGlobalConfigDtoToHgGlobalConfigMapperTest.java @@ -28,21 +28,21 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.junit.MockitoJUnitRunner; -import sonia.scm.repository.HgConfig; +import sonia.scm.repository.HgGlobalConfig; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @RunWith(MockitoJUnitRunner.class) -public class HgConfigDtoToHgConfigMapperTest { +public class HgGlobalConfigDtoToHgGlobalConfigMapperTest { @InjectMocks - private HgConfigDtoToHgConfigMapperImpl mapper; + private HgGlobalConfigDtoToHgConfigMapperImpl mapper; @Test public void shouldMapFields() { - HgConfigDto dto = createDefaultDto(); - HgConfig config = mapper.map(dto); + HgGlobalGlobalConfigDto dto = createDefaultDto(); + HgGlobalConfig config = mapper.map(dto); assertTrue(config.isDisabled()); @@ -52,8 +52,8 @@ public class HgConfigDtoToHgConfigMapperTest { assertTrue(config.isEnableHttpPostArgs()); } - private HgConfigDto createDefaultDto() { - HgConfigDto configDto = new HgConfigDto(); + private HgGlobalGlobalConfigDto createDefaultDto() { + HgGlobalGlobalConfigDto configDto = new HgGlobalGlobalConfigDto(); configDto.setDisabled(true); configDto.setEncoding("ABC"); configDto.setHgBinary("/etc/hg"); diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInIndexResourceTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgGlobalConfigInIndexResourceTest.java similarity index 87% rename from scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInIndexResourceTest.java rename to scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgGlobalConfigInIndexResourceTest.java index 70761021c2..02b8cbc6b2 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInIndexResourceTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgGlobalConfigInIndexResourceTest.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.api.v2.resources; import com.fasterxml.jackson.databind.ObjectMapper; @@ -42,20 +42,20 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @SubjectAware(configuration = "classpath:sonia/scm/configuration/shiro.ini") -public class HgConfigInIndexResourceTest { +public class HgGlobalConfigInIndexResourceTest { @Rule public final ShiroRule shiroRule = new ShiroRule(); private final ObjectMapper objectMapper = new ObjectMapper(); private final ObjectNode root = objectMapper.createObjectNode(); - private final HgConfigInIndexResource hgConfigInIndexResource; + private final HgGlobalConfigInIndexResource hgGlobalConfigInIndexResource; - public HgConfigInIndexResourceTest() { + public HgGlobalConfigInIndexResourceTest() { root.put("_links", objectMapper.createObjectNode()); ScmPathInfoStore pathInfoStore = new ScmPathInfoStore(); pathInfoStore.set(() -> URI.create("/")); - hgConfigInIndexResource = new HgConfigInIndexResource(Providers.of(pathInfoStore), objectMapper); + hgGlobalConfigInIndexResource = new HgGlobalConfigInIndexResource(Providers.of(pathInfoStore), objectMapper); } @Test @@ -63,7 +63,7 @@ public class HgConfigInIndexResourceTest { public void admin() { JsonEnricherContext context = new JsonEnricherContext(URI.create("/index"), MediaType.valueOf(VndMediaType.INDEX), root); - hgConfigInIndexResource.enrich(context); + hgGlobalConfigInIndexResource.enrich(context); assertEquals("/v2/config/hg", root.get("_links").get("hgConfig").get("href").asText()); } @@ -73,7 +73,7 @@ public class HgConfigInIndexResourceTest { public void user() { JsonEnricherContext context = new JsonEnricherContext(URI.create("/index"), MediaType.valueOf(VndMediaType.INDEX), root); - hgConfigInIndexResource.enrich(context); + hgGlobalConfigInIndexResource.enrich(context); assertTrue(root.get("_links").iterator().hasNext()); } @@ -82,7 +82,7 @@ public class HgConfigInIndexResourceTest { public void anonymous() { JsonEnricherContext context = new JsonEnricherContext(URI.create("/index"), MediaType.valueOf(VndMediaType.INDEX), root); - hgConfigInIndexResource.enrich(context); + hgGlobalConfigInIndexResource.enrich(context); assertFalse(root.get("_links").iterator().hasNext()); } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigTests.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgGlobalConfigTestUtil.java similarity index 85% rename from scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigTests.java rename to scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgGlobalConfigTestUtil.java index d793180d1c..127f726635 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigTests.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgGlobalConfigTestUtil.java @@ -24,18 +24,18 @@ package sonia.scm.api.v2.resources; -import sonia.scm.repository.HgConfig; +import sonia.scm.repository.HgGlobalConfig; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -class HgConfigTests { +class HgGlobalConfigTestUtil { - private HgConfigTests() { + private HgGlobalConfigTestUtil() { } - static HgConfig createConfiguration() { - HgConfig config = new HgConfig(); + static HgGlobalConfig createConfiguration() { + HgGlobalConfig config = new HgGlobalConfig(); config.setDisabled(true); config.setEncoding("ABC"); @@ -45,7 +45,7 @@ class HgConfigTests { return config; } - static void assertEqualsConfiguration(HgConfigDto dto) { + static void assertEqualsConfiguration(HgGlobalGlobalConfigDto dto) { assertTrue(dto.isDisabled()); assertEquals("ABC", dto.getEncoding()); diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgGlobalConfigToHgGlobalConfigDtoMapperTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgGlobalConfigToHgGlobalConfigDtoMapperTest.java new file mode 100644 index 0000000000..6403c62cf8 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgGlobalConfigToHgGlobalConfigDtoMapperTest.java @@ -0,0 +1,101 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.api.v2.resources; + +import org.apache.shiro.subject.Subject; +import org.apache.shiro.util.ThreadContext; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mapstruct.factory.Mappers; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import sonia.scm.repository.HgGlobalConfig; + +import java.net.URI; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; +import static sonia.scm.api.v2.resources.HgGlobalConfigTestUtil.assertEqualsConfiguration; +import static sonia.scm.api.v2.resources.HgGlobalConfigTestUtil.createConfiguration; + +@ExtendWith(MockitoExtension.class) +class HgGlobalConfigToHgGlobalConfigDtoMapperTest { + + private final URI baseUri = URI.create("http://example.com/base/"); + private final URI expectedBaseUri = baseUri.resolve(HgConfigResource.HG_CONFIG_PATH_V2); + + @Mock + private Subject subject; + + private HgGlobalConfigToHgGlobalConfigDtoMapper mapper; + + @BeforeEach + void init() { + ThreadContext.bind(subject); + + ScmPathInfoStore store = new ScmPathInfoStore(); + store.set(() -> baseUri); + + mapper = Mappers.getMapper(HgGlobalConfigToHgGlobalConfigDtoMapper.class); + mapper.setLinks(new HgConfigLinks(store)); + } + + @AfterEach + void unbindSubject() { + ThreadContext.unbindSubject(); + } + + @Test + void shouldMapFields() { + HgGlobalConfig config = createConfiguration(); + + when(subject.isPermitted("configuration:write:hg")).thenReturn(true); + HgGlobalGlobalConfigDto dto = mapper.map(config); + + assertEqualsConfiguration(dto); + + assertThat(dto.getLinks().getLinkBy("self")).hasValueSatisfying( + link -> assertThat(link.getHref()).isEqualTo(expectedBaseUri.toString()) + ); + assertThat(dto.getLinks().getLinkBy("update")).hasValueSatisfying( + link -> assertThat(link.getHref()).isEqualTo(expectedBaseUri.toString()) + ); + } + + @Test + void shouldMapFieldsWithoutUpdate() { + HgGlobalConfig config = createConfiguration(); + + when(subject.isPermitted("configuration:write:hg")).thenReturn(false); + HgGlobalGlobalConfigDto dto = mapper.map(config); + + assertThat(dto.getLinks().getLinkBy("self")).hasValueSatisfying( + link -> assertThat(link.getHref()).isEqualTo(expectedBaseUri.toString()) + ); + assertThat(dto.getLinks().hasLink("update")).isFalse(); + } +} diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgRepositoryConfigEnricherTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgRepositoryConfigEnricherTest.java new file mode 100644 index 0000000000..f7bbbf0c52 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgRepositoryConfigEnricherTest.java @@ -0,0 +1,77 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.api.v2.resources; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import sonia.scm.repository.Repository; +import sonia.scm.repository.RepositoryTestData; + +import java.net.URI; + +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class HgRepositoryConfigEnricherTest { + + private HgRepositoryConfigEnricher enricher; + + @Mock + private HalEnricherContext context; + + @Mock + private HalAppender appender; + + @BeforeEach + void setUp() { + ScmPathInfoStore store = new ScmPathInfoStore(); + store.set(() -> URI.create("/")); + HgConfigLinks links = new HgConfigLinks(store); + enricher = new HgRepositoryConfigEnricher(links); + } + + @Test + void shouldEnrichHgRepository() { + addRepositoryToContext("hg"); + enricher.enrich(context, appender); + verify(appender).appendLink("configuration", "/v2/config/hg/hitchhiker/HeartOfGold"); + } + + @Test + void shouldNotEnrichNonHgRepository() { + addRepositoryToContext("git"); + enricher.enrich(context, appender); + verify(appender, never()).appendLink(anyString(), anyString()); + } + + private void addRepositoryToContext(String type) { + Repository repository = RepositoryTestData.createHeartOfGold(type); + when(context.oneRequireByType(Repository.class)).thenReturn(repository); + } + +} diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgRepositoryConfigMapperTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgRepositoryConfigMapperTest.java new file mode 100644 index 0000000000..21b2b7b51b --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgRepositoryConfigMapperTest.java @@ -0,0 +1,95 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.api.v2.resources; + + +import org.apache.shiro.subject.Subject; +import org.apache.shiro.util.ThreadContext; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Answers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import sonia.scm.repository.HgRepositoryConfig; +import sonia.scm.repository.Repository; +import sonia.scm.repository.RepositoryTestData; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class HgRepositoryConfigMapperTest { + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private HgConfigLinks links; + + @InjectMocks + private HgRepositoryConfigMapperImpl mapper; + + @Mock + private Subject subject; + + @BeforeEach + void setUpSubject() { + ThreadContext.bind(subject); + } + + @AfterEach + void tearDownSubject() { + ThreadContext.unbindSubject(); + } + + @Test + void shouldMapToDto() { + Repository repository = RepositoryTestData.createHeartOfGold("hg"); + when(links.repository(repository).get()).thenReturn("/hg/config"); + + HgRepositoryConfig config = new HgRepositoryConfig(); + config.setEncoding("UTF-8"); + + HgRepositoryConfigDto dto = mapper.map(repository, config); + assertThat(dto.getEncoding()).isEqualTo("UTF-8"); + assertThat(dto.getLinks().getLinkBy("self")).hasValueSatisfying( + link -> assertThat(link.getHref()).isEqualTo("/hg/config") + ); + } + + @Test + void shouldAppendUpdateLink() { + Repository repository = RepositoryTestData.createHeartOfGold("hg"); + when(links.repository(repository).get()).thenReturn("/hg/config"); + when(links.repository(repository).update()).thenReturn("/hg/config/update"); + when(subject.isPermitted("repository:hg:" + repository.getId())).thenReturn(true); + + HgRepositoryConfigDto dto = mapper.map(repository, new HgRepositoryConfig()); + assertThat(dto.getLinks().getLinkBy("update")).hasValueSatisfying( + link -> assertThat(link.getHref()).isEqualTo("/hg/config/update") + ); + } + +} diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgRepositoryConfigResourceTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgRepositoryConfigResourceTest.java new file mode 100644 index 0000000000..d453fda7e0 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgRepositoryConfigResourceTest.java @@ -0,0 +1,165 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.api.v2.resources; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.inject.util.Providers; +import de.otto.edison.hal.Links; +import org.apache.shiro.subject.Subject; +import org.apache.shiro.util.ThreadContext; +import org.jboss.resteasy.mock.MockHttpRequest; +import org.jboss.resteasy.mock.MockHttpResponse; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mapstruct.factory.Mappers; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import sonia.scm.repository.HgRepositoryConfig; +import sonia.scm.repository.HgRepositoryConfigStore; +import sonia.scm.repository.HgRepositoryHandler; +import sonia.scm.repository.NamespaceAndName; +import sonia.scm.repository.Repository; +import sonia.scm.repository.RepositoryManager; +import sonia.scm.repository.RepositoryTestData; +import sonia.scm.web.HgVndMediaType; +import sonia.scm.web.RestDispatcher; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class HgRepositoryConfigResourceTest { + + private final ObjectMapper objectMapper = new ObjectMapper(); + + private RestDispatcher dispatcher; + + @Mock + private RepositoryManager repositoryManager; + + @Mock + private HgRepositoryConfigStore store; + + @Mock + private Subject subject; + + @BeforeEach + void setUp() { + ThreadContext.bind(subject); + + HgRepositoryConfigMapper mapper = createConfigMapper(); + dispatcher = new RestDispatcher(); + dispatcher.addSingletonResource(createRootResource( + new HgRepositoryConfigResource(repositoryManager, store, mapper) + )); + } + + @AfterEach + void tearDownSubject() { + ThreadContext.unbindSubject(); + } + + private HgConfigResource createRootResource(HgRepositoryConfigResource resource) { + return new HgConfigResource( + mock(HgGlobalConfigDtoToHgConfigMapper.class), + mock(HgGlobalConfigToHgGlobalConfigDtoMapper.class), + mock(HgRepositoryHandler.class), + Providers.of(mock(HgGlobalConfigAutoConfigurationResource.class)), + Providers.of(resource) + ); + } + + private HgRepositoryConfigMapper createConfigMapper() { + ScmPathInfoStore pathInfoStore = new ScmPathInfoStore(); + pathInfoStore.set(() -> URI.create("/")); + HgConfigLinks links = new HgConfigLinks(pathInfoStore); + HgRepositoryConfigMapper mapper = Mappers.getMapper(HgRepositoryConfigMapper.class); + mapper.setLinks(links); + return mapper; + } + + @Test + void shouldGetConfig() throws IOException, URISyntaxException { + HgRepositoryConfig config = new HgRepositoryConfig(); + config.setEncoding("ISO-8859-15"); + + Repository repository = RepositoryTestData.createHeartOfGold("hg"); + when(repositoryManager.get(new NamespaceAndName("hitchhiker", "hog"))).thenReturn(repository); + when(store.of(repository)).thenReturn(config); + + MockHttpRequest request = MockHttpRequest.get("/v2/config/hg/hitchhiker/hog"); + MockHttpResponse response = new MockHttpResponse(); + dispatcher.invoke(request, response); + + assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK); + + JsonNode node = objectMapper.readTree(response.getContentAsString()); + assertThat(node.get("encoding").asText()).isEqualTo("ISO-8859-15"); + assertThat(node.get("_links").get("self").get("href").asText()).isEqualTo("/v2/config/hg/hitchhiker/HeartOfGold"); + } + + @Test + void shouldUpdateConfig() throws IOException, URISyntaxException { + Repository repository = RepositoryTestData.createHeartOfGold("hg"); + when(repositoryManager.get(new NamespaceAndName("hitchhiker", "hog"))).thenReturn(repository); + + HgRepositoryConfigDto dto = new HgRepositoryConfigDto(Links.emptyLinks()); + dto.setEncoding("UTF-8"); + MockHttpRequest request = MockHttpRequest.put("/v2/config/hg/hitchhiker/hog").contentType( + HgVndMediaType.REPO_CONFIG + ).content(objectMapper.writeValueAsBytes(dto)); + MockHttpResponse response = new MockHttpResponse(); + dispatcher.invoke(request, response); + + ArgumentCaptor captor = ArgumentCaptor.forClass(HgRepositoryConfig.class); + verify(store).store(eq(repository), captor.capture()); + + assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_NO_CONTENT); + assertThat(captor.getValue().getEncoding()).isEqualTo("UTF-8"); + } + + @Test + void shouldFailWithInvalidEncoding() throws IOException, URISyntaxException { + HgRepositoryConfigDto dto = new HgRepositoryConfigDto(Links.emptyLinks()); + dto.setEncoding("XA"); + + MockHttpRequest request = MockHttpRequest.put("/v2/config/hg/hitchhiker/hog") + .contentType(HgVndMediaType.REPO_CONFIG).content(objectMapper.writeValueAsBytes(dto)); + MockHttpResponse response = new MockHttpResponse(); + dispatcher.invoke(request, response); + + assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST); + } + +} diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/autoconfig/PosixAutoConfiguratorTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/autoconfig/PosixAutoConfiguratorTest.java index 823f52376c..c9ac5be62c 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/autoconfig/PosixAutoConfiguratorTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/autoconfig/PosixAutoConfiguratorTest.java @@ -32,7 +32,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.io.TempDir; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import sonia.scm.repository.HgConfig; +import sonia.scm.repository.HgGlobalConfig; import sonia.scm.repository.HgVerifier; import java.io.File; @@ -57,7 +57,7 @@ class PosixAutoConfiguratorTest { PosixAutoConfigurator configurator = create(directory); - HgConfig config = new HgConfig(); + HgGlobalConfig config = new HgGlobalConfig(); configurator.configure(config); assertThat(config.getHgBinary()).isEqualTo(hg.toString()); @@ -84,7 +84,7 @@ class PosixAutoConfiguratorTest { verifier, createEnv(def), ImmutableList.of(additional.toAbsolutePath().toString()) ); - HgConfig config = new HgConfig(); + HgGlobalConfig config = new HgGlobalConfig(); configurator.configure(config); assertThat(config.getHgBinary()).isEqualTo(hg.toString()); @@ -109,7 +109,7 @@ class PosixAutoConfiguratorTest { ) ); - HgConfig config = new HgConfig(); + HgGlobalConfig config = new HgGlobalConfig(); configurator.configure(config); assertThat(config.getHgBinary()).isEqualTo(hg.toString()); @@ -119,7 +119,7 @@ class PosixAutoConfiguratorTest { void shouldNotConfigureMercurial(@TempDir Path directory) { PosixAutoConfigurator configurator = create(directory); - HgConfig config = new HgConfig(); + HgGlobalConfig config = new HgGlobalConfig(); configurator.configure(config); assertThat(config.getHgBinary()).isNull(); diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/autoconfig/WindowsAutoConfiguratorTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/autoconfig/WindowsAutoConfiguratorTest.java index 22e13e2bfb..0be387c17a 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/autoconfig/WindowsAutoConfiguratorTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/autoconfig/WindowsAutoConfiguratorTest.java @@ -31,7 +31,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import sonia.scm.repository.HgConfig; +import sonia.scm.repository.HgGlobalConfig; import sonia.scm.repository.HgVerifier; import java.io.File; @@ -129,7 +129,7 @@ class WindowsAutoConfiguratorTest { } private String configure(String path) { - HgConfig config = new HgConfig(); + HgGlobalConfig config = new HgGlobalConfig(); configurator(path).configure(config); return config.getHgBinary(); } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/DefaultHgEnvironmentBuilderTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/DefaultHgEnvironmentBuilderTest.java index db8cec1502..812b127ae2 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/DefaultHgEnvironmentBuilderTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/DefaultHgEnvironmentBuilderTest.java @@ -42,7 +42,6 @@ import sonia.scm.security.CipherUtil; import sonia.scm.security.Xsrf; import javax.annotation.Nonnull; -import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.util.Map; @@ -60,7 +59,7 @@ class DefaultHgEnvironmentBuilderTest { private AccessTokenBuilderFactory accessTokenBuilderFactory; @Mock - private HgRepositoryHandler repositoryHandler; + private HgConfigResolver repositoryConfigResolver; @Mock private HookEnvironment hookEnvironment; @@ -71,6 +70,9 @@ class DefaultHgEnvironmentBuilderTest { @InjectMocks private DefaultHgEnvironmentBuilder builder; + @Mock + private HgConfig config; + private Path directory; @BeforeEach @@ -141,14 +143,12 @@ class DefaultHgEnvironmentBuilderTest { @Nonnull private Repository prepareForRead(String id) { - when(repositoryHandler.getDirectory(id)).thenReturn(directory.resolve("repo").toFile()); - - HgConfig config = new HgConfig(); - when(repositoryHandler.getConfig()).thenReturn(config); - Repository heartOfGold = RepositoryTestData.createHeartOfGold(); heartOfGold.setId(id); + when(repositoryConfigResolver.resolve(heartOfGold)).thenReturn(config); + when(config.getDirectory()).thenReturn(directory.resolve("repo").toFile()); + return heartOfGold; } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgConfigResolverTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgConfigResolverTest.java new file mode 100644 index 0000000000..7c38c82c65 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgConfigResolverTest.java @@ -0,0 +1,85 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.repository; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.nio.file.Path; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class HgConfigResolverTest { + + @Mock + private HgRepositoryHandler handler; + + @Mock + private HgRepositoryConfigStore repositoryConfigStore; + + private HgConfigResolver resolver; + + private Repository heartOfGold = RepositoryTestData.createHeartOfGold("hg"); + private HgGlobalConfig globalConfig; + private HgRepositoryConfig repositoryConfig; + + @BeforeEach + void setUpResolver(@TempDir Path directory) { + globalConfig = new HgGlobalConfig(); + repositoryConfig = new HgRepositoryConfig(); + + when(handler.getDirectory(heartOfGold.getId())).thenReturn(directory.toFile()); + when(handler.getConfig()).thenReturn(globalConfig); + + when(repositoryConfigStore.of(heartOfGold)).thenReturn(repositoryConfig); + + resolver = new HgConfigResolver(handler, repositoryConfigStore); + } + + @Test + void shouldReturnEncodingFromRepositoryConfig() { + globalConfig.setEncoding("ISO-8859-1"); + repositoryConfig.setEncoding("ISO-8859-15"); + + HgConfig config = resolver.resolve(heartOfGold); + assertThat(config.getEncoding()).isEqualTo("ISO-8859-15"); + } + + @Test + void shouldReturnEncodingFromGlobalConfig() { + globalConfig.setEncoding("ISO-8859-1"); + + HgConfig config = resolver.resolve(heartOfGold); + assertThat(config.getEncoding()).isEqualTo("ISO-8859-1"); + } +} diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryConfigStoreTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryConfigStoreTest.java new file mode 100644 index 0000000000..58d675c38d --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryConfigStoreTest.java @@ -0,0 +1,84 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.repository; + +import org.apache.shiro.authz.AuthorizationException; +import org.apache.shiro.subject.Subject; +import org.apache.shiro.util.ThreadContext; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import sonia.scm.store.InMemoryConfigurationStoreFactory; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class HgRepositoryConfigStoreTest { + + @Mock + private Subject subject; + + private HgRepositoryConfigStore store; + + @BeforeEach + void setUp() { + ThreadContext.bind(subject); + + store = new HgRepositoryConfigStore(new InMemoryConfigurationStoreFactory()); + } + + @AfterEach + void tearDownSubject() { + ThreadContext.unbindSubject(); + } + + @Test + void shouldThrowAuthorizationException() { + Repository repository = RepositoryTestData.createHeartOfGold("hg"); + doThrow(AuthorizationException.class) + .when(subject) + .checkPermission("repository:hg:" + repository.getId()); + + HgRepositoryConfig config = new HgRepositoryConfig(); + assertThrows(AuthorizationException.class, () -> store.store(repository, config)); + } + + @Test + void shouldReadAndStore() { + Repository repository = RepositoryTestData.createHeartOfGold("hg"); + HgRepositoryConfig config = store.of(repository); + config.setEncoding("ISO-8859-15"); + store.store(repository, config); + + assertThat(store.of(repository).getEncoding()).isEqualTo("ISO-8859-15"); + } + +} diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryFactoryTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryFactoryTest.java index ffeb42f613..72e06cbd32 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryFactoryTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryFactoryTest.java @@ -63,7 +63,8 @@ class HgRepositoryFactoryTest { handler = HgTestUtil.createHandler(directory.toFile()); assumeTrue(handler.isConfigured()); - factory = new HgRepositoryFactory(handler, hookEnvironment, environmentBuilder); + HgConfigResolver resolver = new HgConfigResolver(handler); + factory = new HgRepositoryFactory(resolver, hookEnvironment, environmentBuilder); heartOfGold = createRepository(); } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryHandlerTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryHandlerTest.java index 516584bd33..85ff937b22 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryHandlerTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryHandlerTest.java @@ -85,9 +85,9 @@ public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase { public void getDirectory() { HgRepositoryHandler repositoryHandler = createHandler(factory, locationResolver); - HgConfig hgConfig = new HgConfig(); - hgConfig.setHgBinary("hg"); - repositoryHandler.setConfig(hgConfig); + HgGlobalConfig hgGlobalConfig = new HgGlobalConfig(); + hgGlobalConfig.setHgBinary("hg"); + repositoryHandler.setConfig(hgGlobalConfig); initRepository(); File path = repositoryHandler.getDirectory(repository.getId()); diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgTestUtil.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgTestUtil.java index 49a0fc1a36..975c713cec 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgTestUtil.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgTestUtil.java @@ -41,38 +41,32 @@ import static org.mockito.Mockito.mock; //~--- JDK imports ------------------------------------------------------------ /** - * * @author Sebastian Sdorra */ -public final class HgTestUtil -{ +public final class HgTestUtil { /** * Constructs ... - * */ - private HgTestUtil() {} + private HgTestUtil() { + } //~--- methods -------------------------------------------------------------- /** * Method description * - * * @param handler */ - public static void checkForSkip(HgRepositoryHandler handler) - { + public static void checkForSkip(HgRepositoryHandler handler) { // skip tests if hg not in path - if (!handler.isConfigured()) - { + if (!handler.isConfigured()) { System.out.println("WARNING could not find hg, skipping test"); Assume.assumeTrue(false); } - if (Boolean.getBoolean("sonia.scm.test.skip.hg")) - { + if (Boolean.getBoolean("sonia.scm.test.skip.hg")) { System.out.println("WARNING mercurial test are disabled"); Assume.assumeTrue(false); } @@ -97,8 +91,9 @@ public final class HgTestUtil } public static HgRepositoryFactory createFactory(HgRepositoryHandler handler, File directory) { + HgConfigResolver resolver = new HgConfigResolver(handler, repository -> directory); return new HgRepositoryFactory( - handler, new HookEnvironment(), new EmptyHgEnvironmentBuilder(), repository -> directory + resolver, new HookEnvironment(), new EmptyHgEnvironmentBuilder() ); } } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgVerifierTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgVerifierTest.java index ff8f095dcd..707997e124 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgVerifierTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgVerifierTest.java @@ -107,7 +107,7 @@ class HgVerifierTest { } private boolean verify(Path hg) { - HgConfig config = new HgConfig(); + HgGlobalConfig config = new HgGlobalConfig(); config.setHgBinary(hg.toString()); return verifier.isValid(config); } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/AbstractHgCommandTestBase.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/AbstractHgCommandTestBase.java index d115123c52..c80e807dbf 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/AbstractHgCommandTestBase.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/AbstractHgCommandTestBase.java @@ -29,16 +29,15 @@ package sonia.scm.repository.spi; import org.junit.After; import org.junit.Before; +import sonia.scm.repository.HgConfigResolver; import sonia.scm.repository.HgRepositoryFactory; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.repository.HgTestUtil; import sonia.scm.repository.RepositoryTestData; -import sonia.scm.repository.hooks.HookEnvironment; import sonia.scm.util.MockUtil; //~--- JDK imports ------------------------------------------------------------ -import java.io.File; import java.io.IOException; /** @@ -66,8 +65,10 @@ public class AbstractHgCommandTestBase extends ZippedRepositoryTestBase this.handler = HgTestUtil.createHandler(tempFolder.newFolder()); HgTestUtil.checkForSkip(handler); + HgConfigResolver resolver = new HgConfigResolver(handler); + HgRepositoryFactory factory = HgTestUtil.createFactory(handler, repositoryDirectory); - cmdContext = new HgCommandContext(handler, factory, RepositoryTestData.createHeartOfGold()); + cmdContext = new HgCommandContext(resolver, factory, RepositoryTestData.createHeartOfGold()); } //~--- set methods ---------------------------------------------------------- diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgIncomingCommandTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgIncomingCommandTest.java index 9d04255027..79501e6baf 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgIncomingCommandTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgIncomingCommandTest.java @@ -29,6 +29,7 @@ package sonia.scm.repository.spi; import com.aragost.javahg.Changeset; import org.junit.Test; import sonia.scm.repository.ChangesetPagingResult; +import sonia.scm.repository.HgConfigResolver; import sonia.scm.repository.HgTestUtil; import sonia.scm.repository.InternalRepositoryException; @@ -111,8 +112,9 @@ public class HgIncomingCommandTest extends IncomingOutgoingTestBase } private HgIncomingCommand createIncomingCommand() { + HgConfigResolver resolver = new HgConfigResolver(handler); return new HgIncomingCommand( - new HgCommandContext(handler, HgTestUtil.createFactory(handler, incomingDirectory), incomingRepository), + new HgCommandContext(resolver, HgTestUtil.createFactory(handler, incomingDirectory), incomingRepository), handler ); } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModificationsCommandTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModificationsCommandTest.java index 2e645c04c7..375599dba6 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModificationsCommandTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgModificationsCommandTest.java @@ -30,6 +30,7 @@ import com.aragost.javahg.commands.RemoveCommand; import com.aragost.javahg.commands.RenameCommand; import org.junit.Before; import org.junit.Test; +import sonia.scm.repository.HgConfigResolver; import sonia.scm.repository.HgTestUtil; import sonia.scm.repository.Modifications; @@ -44,7 +45,8 @@ public class HgModificationsCommandTest extends IncomingOutgoingTestBase { @Before public void init() { - HgCommandContext outgoingContext = new HgCommandContext(handler, HgTestUtil.createFactory(handler, outgoingDirectory), outgoingRepository); + HgConfigResolver configResolver = new HgConfigResolver(handler); + HgCommandContext outgoingContext = new HgCommandContext(configResolver, HgTestUtil.createFactory(handler, outgoingDirectory), outgoingRepository); outgoingModificationsCommand = new HgModificationsCommand(outgoingContext); } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgOutgoingCommandTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgOutgoingCommandTest.java index 426eff8537..4746867b29 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgOutgoingCommandTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgOutgoingCommandTest.java @@ -29,6 +29,7 @@ package sonia.scm.repository.spi; import com.aragost.javahg.Changeset; import org.junit.Test; import sonia.scm.repository.ChangesetPagingResult; +import sonia.scm.repository.HgConfigResolver; import sonia.scm.repository.HgTestUtil; import sonia.scm.repository.InternalRepositoryException; @@ -107,8 +108,9 @@ public class HgOutgoingCommandTest extends IncomingOutgoingTestBase } private HgOutgoingCommand createOutgoingCommand() { + HgConfigResolver resolver = new HgConfigResolver(handler); return new HgOutgoingCommand( - new HgCommandContext(handler, HgTestUtil.createFactory(handler, outgoingDirectory), outgoingRepository), + new HgCommandContext(resolver, HgTestUtil.createFactory(handler, outgoingDirectory), outgoingRepository), handler ); } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgVersionCommandTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgVersionCommandTest.java index 6ecf631a96..0e710b8966 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgVersionCommandTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgVersionCommandTest.java @@ -28,7 +28,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.io.TempDir; import org.mockito.junit.jupiter.MockitoExtension; -import sonia.scm.repository.HgConfig; +import sonia.scm.repository.HgGlobalConfig; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.repository.HgTestUtil; @@ -54,7 +54,7 @@ class HgVersionCommandTest { @Test void shouldReturnUnknownForIOException() { - HgVersionCommand command = new HgVersionCommand(new HgConfig(), "/i/dont/know", cmd -> { + HgVersionCommand command = new HgVersionCommand(new HgGlobalConfig(), "/i/dont/know", cmd -> { throw new IOException("failed"); }); diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/IncomingOutgoingTestBase.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/IncomingOutgoingTestBase.java index a5b103ed93..7a3936c666 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/IncomingOutgoingTestBase.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/IncomingOutgoingTestBase.java @@ -38,7 +38,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.rules.TemporaryFolder; import sonia.scm.AbstractTestBase; -import sonia.scm.repository.HgConfig; +import sonia.scm.repository.HgGlobalConfig; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.repository.HgTestUtil; import sonia.scm.user.User; @@ -182,7 +182,7 @@ public abstract class IncomingOutgoingTestBase extends AbstractTestBase */ private RepositoryConfiguration createConfig(HgRepositoryHandler handler) { - HgConfig cfg = handler.getConfig(); + HgGlobalConfig cfg = handler.getConfig(); RepositoryConfiguration configuration = RepositoryConfiguration.DEFAULT; configuration.setHgBin(cfg.getHgBinary()); diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgPermissionFilterTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgPermissionFilterTest.java index a639ced66c..24f3d7c178 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgPermissionFilterTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgPermissionFilterTest.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.web; import org.junit.Before; @@ -32,7 +32,7 @@ import org.mockito.Mock; import static org.mockito.Mockito.*; import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.config.ScmConfiguration; -import sonia.scm.repository.HgConfig; +import sonia.scm.repository.HgGlobalConfig; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.repository.RepositoryProvider; @@ -72,7 +72,7 @@ public class HgPermissionFilterTest { @Before public void setUp() { - when(hgRepositoryHandler.getConfig()).thenReturn(new HgConfig()); + when(hgRepositoryHandler.getConfig()).thenReturn(new HgGlobalConfig()); } /** @@ -82,9 +82,9 @@ public class HgPermissionFilterTest { public void testWrapRequestIfRequired() { assertSame(request, filter.wrapRequestIfRequired(request)); - HgConfig hgConfig = new HgConfig(); - hgConfig.setEnableHttpPostArgs(true); - when(hgRepositoryHandler.getConfig()).thenReturn(hgConfig); + HgGlobalConfig hgGlobalConfig = new HgGlobalConfig(); + hgGlobalConfig.setEnableHttpPostArgs(true); + when(hgRepositoryHandler.getConfig()).thenReturn(hgGlobalConfig); assertThat(filter.wrapRequestIfRequired(request), is(instanceOf(HgServletRequest.class))); } @@ -112,7 +112,7 @@ public class HgPermissionFilterTest { */ @Test public void testIsWriteRequestWithEnabledHttpPostArgs() { - HgConfig config = new HgConfig(); + HgGlobalConfig config = new HgGlobalConfig(); config.setEnableHttpPostArgs(true); when(hgRepositoryHandler.getConfig()).thenReturn(config);