diff --git a/scm-core/src/main/java/sonia/scm/repository/ImportRepositoryHookEvent.java b/scm-core/src/main/java/sonia/scm/repository/ImportRepositoryHookEvent.java new file mode 100644 index 0000000000..c802958193 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/repository/ImportRepositoryHookEvent.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.repository; + +import sonia.scm.event.Event; + +/** + * Import receive repository hook events are fired after a repository was imported. + * This event should only be fired if the repository was imported with meta data. + * This class is wrapper of {@link RepositoryHookEvent} for the event system of + * SCM-Manager. + * + * @author Sebastian Sdorra + * @since 2.14.0 + */ +@Event +public final class ImportRepositoryHookEvent extends WrappedRepositoryHookEvent { + + public ImportRepositoryHookEvent(RepositoryHookEvent wrappedEvent) { + super(wrappedEvent); + } + +} diff --git a/scm-core/src/main/java/sonia/scm/repository/api/UnbundleCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/UnbundleCommandBuilder.java index dd1d4b60da..d65e0a738e 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/UnbundleCommandBuilder.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/UnbundleCommandBuilder.java @@ -31,12 +31,14 @@ import com.google.common.io.Files; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.repository.Repository; +import sonia.scm.repository.RepositoryHookEvent; import sonia.scm.repository.spi.UnbundleCommand; import sonia.scm.repository.spi.UnbundleCommandRequest; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.util.function.Consumer; import java.util.zip.GZIPInputStream; import static com.google.common.base.Preconditions.checkArgument; @@ -155,6 +157,20 @@ public final class UnbundleCommandBuilder return this; } + /** + * Sets a event sink to receive a {@link RepositoryHookEvent} on successful unbundle. + * The default implementation wraps the event and sends it to the event bus. + * + * @param postEventSink consumer to process the event + * @return {@code this} + * + * @since 2.14.0 + */ + public UnbundleCommandBuilder setPostEventSink(Consumer postEventSink) { + this.postEventSink = postEventSink; + return this; + } + //~--- methods -------------------------------------------------------------- /** @@ -200,7 +216,11 @@ public final class UnbundleCommandBuilder bs = source; } - return new UnbundleCommandRequest(bs); + UnbundleCommandRequest request = new UnbundleCommandRequest(bs); + if (postEventSink != null) { + request.setPostEventSink(postEventSink); + } + return request; } //~--- inner classes -------------------------------------------------------- @@ -255,4 +275,6 @@ public final class UnbundleCommandBuilder /** Field description */ private boolean compressed = false; + + private Consumer postEventSink; } diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/UnbundleCommandRequest.java b/scm-core/src/main/java/sonia/scm/repository/spi/UnbundleCommandRequest.java index 202fee4a5b..29d2e0fd3e 100644 --- a/scm-core/src/main/java/sonia/scm/repository/spi/UnbundleCommandRequest.java +++ b/scm-core/src/main/java/sonia/scm/repository/spi/UnbundleCommandRequest.java @@ -21,13 +21,18 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.repository.spi; //~--- non-JDK imports -------------------------------------------------------- import com.google.common.base.Objects; import com.google.common.io.ByteSource; +import sonia.scm.event.ScmEventBus; +import sonia.scm.repository.PostReceiveRepositoryHookEvent; +import sonia.scm.repository.RepositoryHookEvent; + +import java.util.function.Consumer; /** * Request object for the unbundle command. @@ -35,17 +40,14 @@ import com.google.common.io.ByteSource; * @author Sebastian Sdorra * @since 1.43 */ -public final class UnbundleCommandRequest -{ +public final class UnbundleCommandRequest { /** * Constructs a new unbundle command request. * - * * @param archive byte source archive */ - public UnbundleCommandRequest(ByteSource archive) - { + public UnbundleCommandRequest(ByteSource archive) { this.archive = archive; } @@ -55,15 +57,12 @@ public final class UnbundleCommandRequest * {@inheritDoc} */ @Override - public boolean equals(Object obj) - { - if (obj == null) - { + public boolean equals(Object obj) { + if (obj == null) { return false; } - if (getClass() != obj.getClass()) - { + if (getClass() != obj.getClass()) { return false; } @@ -76,8 +75,7 @@ public final class UnbundleCommandRequest * {@inheritDoc} */ @Override - public int hashCode() - { + public int hashCode() { return Objects.hashCode(archive); } @@ -86,16 +84,28 @@ public final class UnbundleCommandRequest /** * Returns the archive as {@link ByteSource}. * - * * @return {@link ByteSource} archive */ - public ByteSource getArchive() - { + public ByteSource getArchive() { return archive; } + + public Consumer getPostEventSink() { + return postEventSink; + } + + public void setPostEventSink(Consumer postEventSink) { + this.postEventSink = postEventSink; + } + //~--- fields --------------------------------------------------------------- - /** byte source archive */ + private Consumer postEventSink = event -> + ScmEventBus.getInstance().post(new PostReceiveRepositoryHookEvent(event)); + + /** + * byte source archive + */ private final ByteSource archive; } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitPullCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitPullCommand.java index 73130e7880..3bf5b10298 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitPullCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitPullCommand.java @@ -62,13 +62,13 @@ public class GitPullCommand extends AbstractGitPushOrPullCommand private static final String REF_SPEC = "refs/heads/*:refs/heads/*"; private static final Logger LOG = LoggerFactory.getLogger(GitPullCommand.class); private final ScmEventBus eventBus; - private final GitPostReceiveRepositoryHookEventFactory eventFactory; + private final GitRepositoryHookEventFactory eventFactory; @Inject public GitPullCommand(GitRepositoryHandler handler, GitContext context, ScmEventBus eventBus, - GitPostReceiveRepositoryHookEventFactory eventFactory) { + GitRepositoryHookEventFactory eventFactory) { super(handler, context); this.eventBus = eventBus; this.eventFactory = eventFactory; diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitPostReceiveRepositoryHookEventFactory.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitRepositoryHookEventFactory.java similarity index 82% rename from scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitPostReceiveRepositoryHookEventFactory.java rename to scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitRepositoryHookEventFactory.java index 3a505140ef..69b3bd2f77 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitPostReceiveRepositoryHookEventFactory.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitRepositoryHookEventFactory.java @@ -26,7 +26,6 @@ package sonia.scm.repository.spi; import sonia.scm.repository.GitChangesetConverter; import sonia.scm.repository.GitChangesetConverterFactory; -import sonia.scm.repository.PostReceiveRepositoryHookEvent; import sonia.scm.repository.RepositoryHookEvent; import sonia.scm.repository.Tag; import sonia.scm.repository.api.HookContext; @@ -38,18 +37,18 @@ import java.util.List; import static sonia.scm.repository.RepositoryHookType.POST_RECEIVE; -class GitPostReceiveRepositoryHookEventFactory { +class GitRepositoryHookEventFactory { private final HookContextFactory hookContextFactory; private final GitChangesetConverterFactory changesetConverterFactory; @Inject - public GitPostReceiveRepositoryHookEventFactory(HookContextFactory hookContextFactory, GitChangesetConverterFactory changesetConverterFactory) { + public GitRepositoryHookEventFactory(HookContextFactory hookContextFactory, GitChangesetConverterFactory changesetConverterFactory) { this.hookContextFactory = hookContextFactory; this.changesetConverterFactory = changesetConverterFactory; } - PostReceiveRepositoryHookEvent createEvent(GitContext gitContext, + RepositoryHookEvent createEvent(GitContext gitContext, List branches, List tags, GitLazyChangesetResolver changesetResolver @@ -57,7 +56,6 @@ class GitPostReceiveRepositoryHookEventFactory { GitChangesetConverter converter = changesetConverterFactory.create(gitContext.open()); GitImportHookContextProvider contextProvider = new GitImportHookContextProvider(converter, branches, tags, changesetResolver); HookContext context = hookContextFactory.createContext(contextProvider, gitContext.getRepository()); - RepositoryHookEvent repositoryHookEvent = new RepositoryHookEvent(context, gitContext.getRepository(), POST_RECEIVE); - return new PostReceiveRepositoryHookEvent(repositoryHookEvent); + return new RepositoryHookEvent(context, gitContext.getRepository(), POST_RECEIVE); } } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitUnbundleCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitUnbundleCommand.java index 14ac835a3e..100840ccc7 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitUnbundleCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitUnbundleCommand.java @@ -30,7 +30,7 @@ import org.eclipse.jgit.lib.Ref; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.ContextEntry; -import sonia.scm.event.ScmEventBus; +import sonia.scm.repository.RepositoryHookEvent; import sonia.scm.repository.Tag; import sonia.scm.repository.api.ImportFailedException; import sonia.scm.repository.api.UnbundleResponse; @@ -49,15 +49,11 @@ public class GitUnbundleCommand extends AbstractGitCommand implements UnbundleCo private static final Logger LOG = LoggerFactory.getLogger(GitUnbundleCommand.class); - private final ScmEventBus eventBus; - private final GitPostReceiveRepositoryHookEventFactory eventFactory; + private final GitRepositoryHookEventFactory eventFactory; @Inject - GitUnbundleCommand(GitContext context, - ScmEventBus eventBus, - GitPostReceiveRepositoryHookEventFactory eventFactory) { + GitUnbundleCommand(GitContext context, GitRepositoryHookEventFactory eventFactory) { super(context); - this.eventBus = eventBus; this.eventFactory = eventFactory; } @@ -72,18 +68,21 @@ public class GitUnbundleCommand extends AbstractGitCommand implements UnbundleCo } unbundleRepositoryFromRequest(request, repositoryDir); - firePostReceiveRepositoryHookEvent(); + fireRepositoryHookEvent(request); return new UnbundleResponse(0); } - private void firePostReceiveRepositoryHookEvent() { + private void fireRepositoryHookEvent(UnbundleCommandRequest request) { try { Git git = Git.wrap(context.open()); List branches = extractBranches(git); List tags = extractTags(git); GitLazyChangesetResolver changesetResolver = new GitLazyChangesetResolver(context.getRepository(), git); - eventBus.post(eventFactory.createEvent(context, branches, tags, changesetResolver)); + RepositoryHookEvent event = eventFactory.createEvent(context, branches, tags, changesetResolver); + if (event != null) { + request.getPostEventSink().accept(event); + } } catch (IOException | GitAPIException e) { throw new ImportFailedException( ContextEntry.ContextBuilder.entity(context.getRepository()).build(), diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/AbstractRemoteCommandTestBase.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/AbstractRemoteCommandTestBase.java index 61de609842..5da5c8aaf4 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/AbstractRemoteCommandTestBase.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/AbstractRemoteCommandTestBase.java @@ -80,7 +80,7 @@ public class AbstractRemoteCommandTestBase { outgoing = Git.init().setDirectory(outgoingDirectory).setBare(false).call(); eventBus = mock(ScmEventBus.class); - eventFactory = mock(GitPostReceiveRepositoryHookEventFactory.class); + eventFactory = mock(GitRepositoryHookEventFactory.class); handler = mock(GitRepositoryHandler.class); when(handler.getDirectory(incomingRepository.getId())).thenReturn( @@ -212,5 +212,5 @@ public class AbstractRemoteCommandTestBase { private ScmTransportProtocol proto; protected ScmEventBus eventBus; - protected GitPostReceiveRepositoryHookEventFactory eventFactory; + protected GitRepositoryHookEventFactory eventFactory; } diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitPostReceiveRepositoryHookEventFactoryTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitPostReceiveRepositoryHookEventFactoryTest.java index 9aef4bffb7..6e2748e398 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitPostReceiveRepositoryHookEventFactoryTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitPostReceiveRepositoryHookEventFactoryTest.java @@ -30,7 +30,7 @@ import org.junit.Before; import org.junit.Test; import sonia.scm.repository.Changeset; import sonia.scm.repository.GitChangesetConverterFactory; -import sonia.scm.repository.PostReceiveRepositoryHookEvent; +import sonia.scm.repository.RepositoryHookEvent; import sonia.scm.repository.Tag; import sonia.scm.repository.api.HookContext; import sonia.scm.repository.api.HookContextFactory; @@ -48,7 +48,7 @@ public class GitPostReceiveRepositoryHookEventFactoryTest extends AbstractGitCom private HookContext hookContext; - private GitPostReceiveRepositoryHookEventFactory eventFactory; + private GitRepositoryHookEventFactory eventFactory; @Before public void init() { @@ -56,7 +56,7 @@ public class GitPostReceiveRepositoryHookEventFactoryTest extends AbstractGitCom hookContext = mock(HookContext.class, RETURNS_DEEP_STUBS); when(hookContextFactory.createContext(any(), eq(repository))).thenReturn(hookContext); GitChangesetConverterFactory converterFactory = mock(GitChangesetConverterFactory.class); - eventFactory = new GitPostReceiveRepositoryHookEventFactory(hookContextFactory, converterFactory); + eventFactory = new GitRepositoryHookEventFactory(hookContextFactory, converterFactory); } @Test @@ -68,7 +68,7 @@ public class GitPostReceiveRepositoryHookEventFactoryTest extends AbstractGitCom when(hookContext.getBranchProvider().getCreatedOrModified()).thenReturn(branches); when(hookContext.getTagProvider().getCreatedTags()).thenReturn(tags); - PostReceiveRepositoryHookEvent event = eventFactory.createEvent( + RepositoryHookEvent event = eventFactory.createEvent( createContext(), branches, tags, diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitUnbundleCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitUnbundleCommandTest.java index 0494b78bfb..ed296612c8 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitUnbundleCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitUnbundleCommandTest.java @@ -28,8 +28,6 @@ import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.junit.Before; import org.junit.Test; -import sonia.scm.event.ScmEventBus; -import sonia.scm.repository.PostReceiveRepositoryHookEvent; import sonia.scm.repository.RepositoryHookEvent; import sonia.scm.repository.RepositoryHookType; import sonia.scm.util.Archives; @@ -37,40 +35,41 @@ import sonia.scm.util.Archives; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; +import java.util.concurrent.atomic.AtomicReference; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class GitUnbundleCommandTest extends AbstractGitCommandTestBase { - private ScmEventBus eventBus; private GitUnbundleCommand unbundleCommand; - private GitPostReceiveRepositoryHookEventFactory eventFactory; + private GitRepositoryHookEventFactory eventFactory; @Before public void initUnbundleCommand() { - eventBus = mock(ScmEventBus.class); - eventFactory = mock(GitPostReceiveRepositoryHookEventFactory.class); - unbundleCommand = new GitUnbundleCommand(createContext(), eventBus, eventFactory); + eventFactory = mock(GitRepositoryHookEventFactory.class); + unbundleCommand = new GitUnbundleCommand(createContext(), eventFactory); } @Test public void shouldUnbundleRepositoryFiles() throws IOException { - when(eventFactory.createEvent(eq(createContext()), any(), any(), any())) - .thenReturn(new PostReceiveRepositoryHookEvent(new RepositoryHookEvent(null, repository, RepositoryHookType.POST_RECEIVE))); + RepositoryHookEvent event = new RepositoryHookEvent(null, repository, RepositoryHookType.POST_RECEIVE); + when(eventFactory.createEvent(eq(createContext()), any(), any(), any())).thenReturn(event); + + AtomicReference receivedEvent = new AtomicReference<>(); String filePath = "test-input"; String fileContent = "HeartOfGold"; UnbundleCommandRequest unbundleCommandRequest = createUnbundleCommandRequestForFile(filePath, fileContent); + unbundleCommandRequest.setPostEventSink(receivedEvent::set); unbundleCommand.unbundle(unbundleCommandRequest); assertFileWithContentWasCreated(createContext().getDirectory(), filePath, fileContent); - verify(eventBus).post(any(PostReceiveRepositoryHookEvent.class)); + assertThat(receivedEvent.get()).isSameAs(event); } @Test diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgPullCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgPullCommand.java index ba48592263..a8519ea1c8 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgPullCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgPullCommand.java @@ -50,13 +50,13 @@ public class HgPullCommand extends AbstractHgPushOrPullCommand implements PullCo private static final String AUTH_SECTION = "auth"; private final ScmEventBus eventBus; private final HgLazyChangesetResolver changesetResolver; - private final HgPostReceiveRepositoryHookEventFactory eventFactory; + private final HgRepositoryHookEventFactory eventFactory; public HgPullCommand(HgRepositoryHandler handler, HgCommandContext context, ScmEventBus eventBus, HgLazyChangesetResolver changesetResolver, - HgPostReceiveRepositoryHookEventFactory eventFactory + HgRepositoryHookEventFactory eventFactory ) { super(handler, context); this.eventBus = eventBus; diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgPostReceiveRepositoryHookEventFactory.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryHookEventFactory.java similarity index 80% rename from scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgPostReceiveRepositoryHookEventFactory.java rename to scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryHookEventFactory.java index 542253345a..161a9507c2 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgPostReceiveRepositoryHookEventFactory.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryHookEventFactory.java @@ -24,7 +24,6 @@ package sonia.scm.repository.spi; -import sonia.scm.repository.PostReceiveRepositoryHookEvent; import sonia.scm.repository.RepositoryHookEvent; import sonia.scm.repository.Tag; import sonia.scm.repository.api.HookContext; @@ -37,21 +36,20 @@ import static sonia.scm.repository.RepositoryHookType.POST_RECEIVE; import static sonia.scm.repository.spi.HgBranchesTagsExtractor.extractBranches; import static sonia.scm.repository.spi.HgBranchesTagsExtractor.extractTags; -class HgPostReceiveRepositoryHookEventFactory { +class HgRepositoryHookEventFactory { private final HookContextFactory hookContextFactory; @Inject - public HgPostReceiveRepositoryHookEventFactory(HookContextFactory hookContextFactory) { + public HgRepositoryHookEventFactory(HookContextFactory hookContextFactory) { this.hookContextFactory = hookContextFactory; } - PostReceiveRepositoryHookEvent createEvent(HgCommandContext hgContext, HgLazyChangesetResolver changesetResolver) { + RepositoryHookEvent createEvent(HgCommandContext hgContext, HgLazyChangesetResolver changesetResolver) { List branches = extractBranches(hgContext); List tags = extractTags(hgContext); HgImportHookContextProvider contextProvider = new HgImportHookContextProvider(branches, tags, changesetResolver); HookContext context = hookContextFactory.createContext(contextProvider, hgContext.getScmRepository()); - RepositoryHookEvent repositoryHookEvent = new RepositoryHookEvent(context, hgContext.getScmRepository(), POST_RECEIVE); - return new PostReceiveRepositoryHookEvent(repositoryHookEvent); + return new RepositoryHookEvent(context, hgContext.getScmRepository(), POST_RECEIVE); } } 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 2fe276a3d6..81ac655f6e 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 @@ -66,12 +66,12 @@ public class HgRepositoryServiceProvider extends RepositoryServiceProvider { private final HgRepositoryHandler handler; private final HgCommandContext context; private final HgLazyChangesetResolver lazyChangesetResolver; - private final HgPostReceiveRepositoryHookEventFactory eventFactory; + private final HgRepositoryHookEventFactory eventFactory; private final ScmEventBus eventBus; HgRepositoryServiceProvider(HgRepositoryHandler handler, HgRepositoryFactory factory, - HgPostReceiveRepositoryHookEventFactory eventFactory, + HgRepositoryHookEventFactory eventFactory, ScmEventBus eventBus, Repository repository) { this.handler = handler; @@ -184,6 +184,6 @@ public class HgRepositoryServiceProvider extends RepositoryServiceProvider { @Override public UnbundleCommand getUnbundleCommand() { - return new HgUnbundleCommand(context, eventBus, lazyChangesetResolver, eventFactory); + return new HgUnbundleCommand(context, lazyChangesetResolver, eventFactory); } } 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 3fd7f83378..be10913fbb 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 @@ -40,13 +40,13 @@ public class HgRepositoryServiceResolver implements RepositoryServiceResolver { private final HgRepositoryHandler handler; private final HgRepositoryFactory factory; private final ScmEventBus eventBus; - private final HgPostReceiveRepositoryHookEventFactory eventFactory; + private final HgRepositoryHookEventFactory eventFactory; @Inject public HgRepositoryServiceResolver(HgRepositoryHandler handler, HgRepositoryFactory factory, ScmEventBus eventBus, - HgPostReceiveRepositoryHookEventFactory eventFactory + HgRepositoryHookEventFactory eventFactory ) { this.handler = handler; this.factory = factory; diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgUnbundleCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgUnbundleCommand.java index 2a6aacad97..49f6d5973b 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgUnbundleCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgUnbundleCommand.java @@ -27,7 +27,7 @@ package sonia.scm.repository.spi; import com.google.common.io.ByteSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.event.ScmEventBus; +import sonia.scm.repository.RepositoryHookEvent; import sonia.scm.repository.api.UnbundleResponse; import java.io.IOException; @@ -41,17 +41,14 @@ public class HgUnbundleCommand implements UnbundleCommand { private static final Logger LOG = LoggerFactory.getLogger(HgUnbundleCommand.class); private final HgCommandContext context; - private final ScmEventBus eventBus; private final HgLazyChangesetResolver changesetResolver; - private final HgPostReceiveRepositoryHookEventFactory eventFactory; + private final HgRepositoryHookEventFactory eventFactory; HgUnbundleCommand(HgCommandContext context, - ScmEventBus eventBus, HgLazyChangesetResolver changesetResolver, - HgPostReceiveRepositoryHookEventFactory eventFactory + HgRepositoryHookEventFactory eventFactory ) { this.context = context; - this.eventBus = eventBus; this.changesetResolver = changesetResolver; this.eventFactory = eventFactory; } @@ -67,12 +64,15 @@ public class HgUnbundleCommand implements UnbundleCommand { } unbundleRepositoryFromRequest(request, repositoryDir); - firePostReceiveRepositoryHookEvent(); + fireHookEvent(request); return new UnbundleResponse(0); } - private void firePostReceiveRepositoryHookEvent() { - eventBus.post(eventFactory.createEvent(context, changesetResolver)); + private void fireHookEvent(UnbundleCommandRequest request) { + RepositoryHookEvent event = eventFactory.createEvent(context, changesetResolver); + if (event != null) { + request.getPostEventSink().accept(event); + } } private void unbundleRepositoryFromRequest(UnbundleCommandRequest request, Path repositoryDir) throws IOException { diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgPostReceiveRepositoryHookEventFactoryTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgPostReceiveRepositoryHookEventFactoryTest.java index 7851823c34..3037e76f85 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgPostReceiveRepositoryHookEventFactoryTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgPostReceiveRepositoryHookEventFactoryTest.java @@ -29,6 +29,7 @@ import org.junit.Before; import org.junit.Test; import sonia.scm.repository.Changeset; import sonia.scm.repository.PostReceiveRepositoryHookEvent; +import sonia.scm.repository.RepositoryHookEvent; import sonia.scm.repository.Tag; import sonia.scm.repository.api.HookContext; import sonia.scm.repository.api.HookContextFactory; @@ -43,14 +44,14 @@ public class HgPostReceiveRepositoryHookEventFactoryTest extends AbstractHgComma private HookContext hookContext; - private HgPostReceiveRepositoryHookEventFactory eventFactory; + private HgRepositoryHookEventFactory eventFactory; @Before public void init() { HookContextFactory hookContextFactory = mock(HookContextFactory.class); hookContext = mock(HookContext.class, RETURNS_DEEP_STUBS); when(hookContextFactory.createContext(any(), any())).thenReturn(hookContext); - eventFactory = new HgPostReceiveRepositoryHookEventFactory(hookContextFactory); + eventFactory = new HgRepositoryHookEventFactory(hookContextFactory); } @Test @@ -64,7 +65,7 @@ public class HgPostReceiveRepositoryHookEventFactoryTest extends AbstractHgComma HgLazyChangesetResolver changesetResolver = mock(HgLazyChangesetResolver.class); - PostReceiveRepositoryHookEvent event = eventFactory.createEvent(cmdContext, changesetResolver); + RepositoryHookEvent event = eventFactory.createEvent(cmdContext, changesetResolver); assertThat(event.getContext().getBranchProvider().getCreatedOrModified()).isSameAs(branches); assertThat(event.getContext().getTagProvider().getCreatedTags()).isSameAs(tags); diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgUnbundleCommandTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgUnbundleCommandTest.java index a9f53bd4b9..0813aa2900 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgUnbundleCommandTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgUnbundleCommandTest.java @@ -30,9 +30,7 @@ import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.junit.Before; import org.junit.Test; -import sonia.scm.event.ScmEventBus; import sonia.scm.repository.HgTestUtil; -import sonia.scm.repository.PostReceiveRepositoryHookEvent; import sonia.scm.repository.RepositoryHookEvent; import sonia.scm.repository.RepositoryHookType; import sonia.scm.util.Archives; @@ -41,41 +39,41 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.concurrent.atomic.AtomicReference; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class HgUnbundleCommandTest extends AbstractHgCommandTestBase { - private ScmEventBus eventBus; private HgUnbundleCommand unbundleCommand; - private HgPostReceiveRepositoryHookEventFactory eventFactory; + private HgRepositoryHookEventFactory eventFactory; @Before public void initUnbundleCommand() { - eventBus = mock(ScmEventBus.class); - eventFactory = mock(HgPostReceiveRepositoryHookEventFactory.class); - unbundleCommand = new HgUnbundleCommand(cmdContext, eventBus, new HgLazyChangesetResolver(HgTestUtil.createFactory(handler, repositoryDirectory), null), eventFactory); + eventFactory = mock(HgRepositoryHookEventFactory.class); + unbundleCommand = new HgUnbundleCommand(cmdContext, new HgLazyChangesetResolver(HgTestUtil.createFactory(handler, repositoryDirectory), null), eventFactory); } @Test public void shouldUnbundleRepositoryFiles() throws IOException { - when(eventFactory.createEvent(eq(cmdContext), any())) - .thenReturn(new PostReceiveRepositoryHookEvent(new RepositoryHookEvent(null, repository, RepositoryHookType.POST_RECEIVE))); + RepositoryHookEvent event = new RepositoryHookEvent(null, repository, RepositoryHookType.POST_RECEIVE); + when(eventFactory.createEvent(eq(cmdContext), any())).thenReturn(event); + AtomicReference receivedEvent = new AtomicReference<>(); String filePath = "test-input"; String fileContent = "HeartOfGold"; UnbundleCommandRequest unbundleCommandRequest = createUnbundleCommandRequestForFile(filePath, fileContent); + unbundleCommandRequest.setPostEventSink(receivedEvent::set); unbundleCommand.unbundle(unbundleCommandRequest); assertFileWithContentWasCreated(cmdContext.getDirectory(), filePath, fileContent); - verify(eventBus).post(any(PostReceiveRepositoryHookEvent.class)); + assertThat(receivedEvent.get()).isSameAs(event); } @Test diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnRepositoryServiceProvider.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnRepositoryServiceProvider.java index e6d89f419d..3ef452eb01 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnRepositoryServiceProvider.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnRepositoryServiceProvider.java @@ -26,7 +26,6 @@ package sonia.scm.repository.spi; import com.google.common.collect.ImmutableSet; import com.google.common.io.Closeables; -import sonia.scm.event.ScmEventBus; import sonia.scm.repository.Repository; import sonia.scm.repository.SvnRepositoryHandler; import sonia.scm.repository.SvnWorkingCopyFactory; @@ -52,17 +51,14 @@ public class SvnRepositoryServiceProvider extends RepositoryServiceProvider { private final SvnContext context; private final SvnWorkingCopyFactory workingCopyFactory; private final HookContextFactory hookContextFactory; - private final ScmEventBus eventBus; SvnRepositoryServiceProvider(SvnRepositoryHandler handler, Repository repository, SvnWorkingCopyFactory workingCopyFactory, - HookContextFactory hookContextFactory, - ScmEventBus eventBus) { + HookContextFactory hookContextFactory) { this.context = new SvnContext(repository, handler.getDirectory(repository.getId())); this.workingCopyFactory = workingCopyFactory; this.hookContextFactory = hookContextFactory; - this.eventBus = eventBus; } @Override @@ -122,6 +118,6 @@ public class SvnRepositoryServiceProvider extends RepositoryServiceProvider { @Override public UnbundleCommand getUnbundleCommand() { - return new SvnUnbundleCommand(context, hookContextFactory, eventBus, new SvnLogCommand(context)); + return new SvnUnbundleCommand(context, hookContextFactory, new SvnLogCommand(context)); } } diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnRepositoryServiceResolver.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnRepositoryServiceResolver.java index 9c66bd237b..827e3827ec 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnRepositoryServiceResolver.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnRepositoryServiceResolver.java @@ -25,7 +25,6 @@ package sonia.scm.repository.spi; import com.google.inject.Inject; -import sonia.scm.event.ScmEventBus; import sonia.scm.plugin.Extension; import sonia.scm.repository.Repository; import sonia.scm.repository.SvnRepositoryHandler; @@ -38,18 +37,14 @@ public class SvnRepositoryServiceResolver implements RepositoryServiceResolver { private final SvnRepositoryHandler handler; private final SvnWorkingCopyFactory workingCopyFactory; private final HookContextFactory hookContextFactory; - private final ScmEventBus eventBus; @Inject public SvnRepositoryServiceResolver(SvnRepositoryHandler handler, SvnWorkingCopyFactory workingCopyFactory, - HookContextFactory hookContextFactory, - ScmEventBus eventBus - ) { + HookContextFactory hookContextFactory) { this.handler = handler; this.workingCopyFactory = workingCopyFactory; this.hookContextFactory = hookContextFactory; - this.eventBus = eventBus; } @Override @@ -57,7 +52,7 @@ public class SvnRepositoryServiceResolver implements RepositoryServiceResolver { SvnRepositoryServiceProvider provider = null; if (SvnRepositoryHandler.TYPE_NAME.equalsIgnoreCase(repository.getType())) { - provider = new SvnRepositoryServiceProvider(handler, repository, workingCopyFactory, hookContextFactory, eventBus); + provider = new SvnRepositoryServiceProvider(handler, repository, workingCopyFactory, hookContextFactory); } return provider; diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnUnbundleCommand.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnUnbundleCommand.java index 5bc2cd92bf..31752546bd 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnUnbundleCommand.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnUnbundleCommand.java @@ -33,9 +33,7 @@ import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.wc.SVNClientManager; import org.tmatesoft.svn.core.wc.admin.SVNAdminClient; import sonia.scm.ContextEntry; -import sonia.scm.event.ScmEventBus; import sonia.scm.repository.Changeset; -import sonia.scm.repository.PostReceiveRepositoryHookEvent; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryHookEvent; import sonia.scm.repository.SvnUtil; @@ -64,17 +62,14 @@ public class SvnUnbundleCommand extends AbstractSvnCommand implements UnbundleCo private static final Logger LOG = LoggerFactory.getLogger(SvnUnbundleCommand.class); private final HookContextFactory hookContextFactory; - private final ScmEventBus eventBus; private final SvnLogCommand svnLogCommand; public SvnUnbundleCommand(SvnContext context, HookContextFactory hookContextFactory, - ScmEventBus eventBus, SvnLogCommand svnLogCommand ) { super(context); this.hookContextFactory = hookContextFactory; - this.eventBus = eventBus; this.svnLogCommand = svnLogCommand; } @@ -105,19 +100,18 @@ public class SvnUnbundleCommand extends AbstractSvnCommand implements UnbundleCo SvnUtil.dispose(clientManager); } - firePostReceiveRepositoryHookEvent(); + fireHookEvent(request); return response; } - private void firePostReceiveRepositoryHookEvent() { - eventBus.post(createEvent()); + private void fireHookEvent(UnbundleCommandRequest request) { + request.getPostEventSink().accept(createEvent()); } - private PostReceiveRepositoryHookEvent createEvent() { + private RepositoryHookEvent createEvent() { Repository repository = this.context.getRepository(); HookContext context = hookContextFactory.createContext(new SvnImportHookContextProvider(repository, svnLogCommand), repository); - RepositoryHookEvent repositoryHookEvent = new RepositoryHookEvent(context, repository, POST_RECEIVE); - return new PostReceiveRepositoryHookEvent(repositoryHookEvent); + return new RepositoryHookEvent(context, repository, POST_RECEIVE); } private void restore(SVNAdminClient adminClient, ByteSource dump, File repository) throws SVNException, IOException { diff --git a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnUnbundleCommandTest.java b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnUnbundleCommandTest.java index 0495c1514e..48ea937763 100644 --- a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnUnbundleCommandTest.java +++ b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnUnbundleCommandTest.java @@ -33,11 +33,10 @@ import org.mockito.Captor; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.core.io.SVNRepositoryFactory; -import sonia.scm.event.ScmEventBus; import sonia.scm.repository.Changeset; import sonia.scm.repository.Person; -import sonia.scm.repository.PostReceiveRepositoryHookEvent; import sonia.scm.repository.Repository; +import sonia.scm.repository.RepositoryHookEvent; import sonia.scm.repository.RepositoryTestData; import sonia.scm.repository.SvnUtil; import sonia.scm.repository.api.HookChangesetBuilder; @@ -48,30 +47,24 @@ import sonia.scm.repository.api.UnbundleResponse; import java.io.File; import java.io.IOException; import java.util.List; +import java.util.concurrent.atomic.AtomicReference; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class SvnUnbundleCommandTest extends AbstractSvnCommandTestBase { private final Repository repository = RepositoryTestData.createHeartOfGold("svn"); private HookContextFactory hookContextFactory; - private ScmEventBus eventBus; private SvnLogCommand logCommand; private HookChangesetBuilder hookChangesetBuilder; - @Captor - private final ArgumentCaptor eventCaptor = - ArgumentCaptor.forClass(PostReceiveRepositoryHookEvent.class); - @Before public void initMocks() { hookContextFactory = mock(HookContextFactory.class); - eventBus = mock(ScmEventBus.class); logCommand = mock(SvnLogCommand.class); HookContext hookContext = mock(HookContext.class); hookChangesetBuilder = mock(HookChangesetBuilder.class); @@ -80,28 +73,28 @@ public class SvnUnbundleCommandTest extends AbstractSvnCommandTestBase { } @Test - public void shouldFirePostCommitEventAfterUnbundle() throws IOException, SVNException { + public void shouldFireRepositoryHookEventAfterUnbundle() throws IOException, SVNException { Changeset first = new Changeset("1", 0L, new Person("trillian"), "first commit"); when(hookChangesetBuilder.getChangesetList()).thenReturn(ImmutableList.of(first)); File bundle = bundle(); SvnContext ctx = createEmptyContext(); - //J- - UnbundleResponse res = new SvnUnbundleCommand(ctx, hookContextFactory, eventBus, logCommand) - .unbundle(new UnbundleCommandRequest(Files.asByteSource(bundle)) - ); - //J+ + + AtomicReference eventSink = new AtomicReference<>(); + UnbundleCommandRequest request = new UnbundleCommandRequest(Files.asByteSource(bundle)); + request.setPostEventSink(eventSink::set); + + UnbundleResponse res = new SvnUnbundleCommand(ctx, hookContextFactory, logCommand) + .unbundle(request); assertThat(res).isNotNull(); assertThat(res.getChangesetCount()).isEqualTo(5); SVNRepository repo = ctx.open(); assertThat(repo.getLatestRevision()).isEqualTo(5); - verify(eventBus).post(eventCaptor.capture()); - PostReceiveRepositoryHookEvent event = eventCaptor.getValue(); + RepositoryHookEvent event = eventSink.get(); List changesets = event.getContext().getChangesetProvider().getChangesetList(); - assertThat(changesets).hasSize(1); - assertThat(changesets).contains(first); + assertThat(changesets).hasSize(1).contains(first); SvnUtil.closeSession(repo); } diff --git a/scm-webapp/src/main/java/sonia/scm/importexport/FullScmRepositoryImporter.java b/scm-webapp/src/main/java/sonia/scm/importexport/FullScmRepositoryImporter.java index 550fd7cedb..0c90aba232 100644 --- a/scm-webapp/src/main/java/sonia/scm/importexport/FullScmRepositoryImporter.java +++ b/scm-webapp/src/main/java/sonia/scm/importexport/FullScmRepositoryImporter.java @@ -31,6 +31,7 @@ import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.ContextEntry; +import sonia.scm.event.ScmEventBus; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryManager; import sonia.scm.repository.api.ImportFailedException; @@ -51,6 +52,7 @@ public class FullScmRepositoryImporter { private final ImportStep[] importSteps; private final RepositoryManager repositoryManager; private final RepositoryImportExportEncryption repositoryImportExportEncryption; + private final ScmEventBus eventBus; @Inject public FullScmRepositoryImporter(EnvironmentCheckStep environmentCheckStep, @@ -58,10 +60,13 @@ public class FullScmRepositoryImporter { StoreImportStep storeImportStep, RepositoryImportStep repositoryImportStep, RepositoryManager repositoryManager, - RepositoryImportExportEncryption repositoryImportExportEncryption) { + RepositoryImportExportEncryption repositoryImportExportEncryption, + ScmEventBus eventBus + ) { this.repositoryManager = repositoryManager; this.repositoryImportExportEncryption = repositoryImportExportEncryption; importSteps = new ImportStep[]{environmentCheckStep, metadataImportStep, storeImportStep, repositoryImportStep}; + this.eventBus = eventBus; } public Repository importFromStream(Repository repository, InputStream inputStream, String password) { @@ -109,12 +114,15 @@ public class FullScmRepositoryImporter { stream(importSteps).forEach(step -> step.finish(state)); return state.getRepository(); } finally { - stream(importSteps) - .forEach(step -> step.cleanup(state)); - if (!state.success()) { + stream(importSteps).forEach(step -> step.cleanup(state)); + if (state.success()) { + // send all pending events on successful import + state.getPendingEvents().forEach(eventBus::post); + } else { // Delete the repository if any error occurs during the import repositoryManager.delete(state.getRepository()); } + } } diff --git a/scm-webapp/src/main/java/sonia/scm/importexport/ImportState.java b/scm-webapp/src/main/java/sonia/scm/importexport/ImportState.java index 9e61702844..d16d220760 100644 --- a/scm-webapp/src/main/java/sonia/scm/importexport/ImportState.java +++ b/scm-webapp/src/main/java/sonia/scm/importexport/ImportState.java @@ -28,8 +28,10 @@ import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryPermission; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.List; import java.util.Optional; class ImportState { @@ -44,6 +46,8 @@ class ImportState { private Path temporaryRepositoryBundle; + private final List pendingEvents = new ArrayList<>(); + ImportState(Repository repository) { this.repository = repository; } @@ -95,4 +99,12 @@ class ImportState { public void repositoryImported() { this.repositoryImported = true; } + + public void addPendingEvent(Object event) { + this.pendingEvents.add(event); + } + + public Collection getPendingEvents() { + return Collections.unmodifiableCollection(pendingEvents); + } } diff --git a/scm-webapp/src/main/java/sonia/scm/importexport/RepositoryImportStep.java b/scm-webapp/src/main/java/sonia/scm/importexport/RepositoryImportStep.java index cb0976f773..983e397b4e 100644 --- a/scm-webapp/src/main/java/sonia/scm/importexport/RepositoryImportStep.java +++ b/scm-webapp/src/main/java/sonia/scm/importexport/RepositoryImportStep.java @@ -28,10 +28,13 @@ import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.ContextEntry; +import sonia.scm.repository.ImportRepositoryHookEvent; import sonia.scm.repository.Repository; +import sonia.scm.repository.RepositoryHookEvent; import sonia.scm.repository.api.ImportFailedException; import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryServiceFactory; +import sonia.scm.repository.api.UnbundleCommandBuilder; import sonia.scm.repository.work.WorkdirProvider; import sonia.scm.util.IOUtil; @@ -40,6 +43,7 @@ import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReference; import static sonia.scm.ContextEntry.ContextBuilder.entity; @@ -99,8 +103,18 @@ class RepositoryImportStep implements ImportStep { private void unbundleRepository(ImportState state, InputStream is) { try (RepositoryService service = serviceFactory.create(state.getRepository())) { - service.getUnbundleCommand().unbundle(new NoneClosingInputStream(is)); + AtomicReference eventSink = new AtomicReference<>(); + + UnbundleCommandBuilder unbundleCommand = service.getUnbundleCommand(); + unbundleCommand.setPostEventSink(eventSink::set); + unbundleCommand.unbundle(new NoneClosingInputStream(is)); + state.repositoryImported(); + + RepositoryHookEvent repositoryHookEvent = eventSink.get(); + if (repositoryHookEvent != null) { + state.addPendingEvent(new ImportRepositoryHookEvent(repositoryHookEvent)); + } } catch (IOException e) { throw new ImportFailedException( entity(state.getRepository()).build(), diff --git a/scm-webapp/src/test/java/sonia/scm/importexport/FullScmRepositoryImporterTest.java b/scm-webapp/src/test/java/sonia/scm/importexport/FullScmRepositoryImporterTest.java index ee915d7856..1c7d8f2d69 100644 --- a/scm-webapp/src/test/java/sonia/scm/importexport/FullScmRepositoryImporterTest.java +++ b/scm-webapp/src/test/java/sonia/scm/importexport/FullScmRepositoryImporterTest.java @@ -31,10 +31,15 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.io.TempDir; import org.mockito.Answers; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import sonia.scm.event.ScmEventBus; +import sonia.scm.repository.ImportRepositoryHookEvent; import sonia.scm.repository.Repository; +import sonia.scm.repository.RepositoryHookEvent; import sonia.scm.repository.RepositoryManager; import sonia.scm.repository.RepositoryPermission; import sonia.scm.repository.RepositoryTestData; @@ -52,6 +57,7 @@ import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collection; +import java.util.function.Consumer; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -98,6 +104,15 @@ class FullScmRepositoryImporterTest { @InjectMocks private RepositoryImportStep repositoryImportStep; + @Mock + private ScmEventBus eventBus; + + @Mock + private RepositoryHookEvent event; + + @Captor + private ArgumentCaptor> eventSinkCaptor; + private FullScmRepositoryImporter fullImporter; @BeforeEach @@ -108,7 +123,9 @@ class FullScmRepositoryImporterTest { storeImportStep, repositoryImportStep, repositoryManager, - repositoryImportExportEncryption); + repositoryImportExportEncryption, + eventBus + ); } @BeforeEach @@ -147,7 +164,7 @@ class FullScmRepositoryImporterTest { lenient().when(workdirProvider.createNewWorkdir(REPOSITORY.getId())).thenReturn(temp.toFile()); when(compatibilityChecker.check(any())).thenReturn(true); - when(repositoryManager.create(eq(REPOSITORY))).thenReturn(REPOSITORY); + when(repositoryManager.create(REPOSITORY)).thenReturn(REPOSITORY); } @Test @@ -197,13 +214,22 @@ class FullScmRepositoryImporterTest { } @Test - void shouldDecryptStreamWhenPasswordSet() throws IOException { + void shouldFireImportRepositoryHookEvent() throws IOException { InputStream stream = Resources.getResource("sonia/scm/repository/import/scm-import.tar.gz").openStream(); - when(repositoryImportExportEncryption.decrypt(any(), eq("hg2tg"))).thenAnswer(invocation -> invocation.getArgument(0)); - fullImporter.importFromStream(REPOSITORY, stream, "hg2tg"); + // capture the event sink which is registered by RepositoryImportStep + // we use when here instead of verify, because for verify we have used it before + when(unbundleCommandBuilder.setPostEventSink(eventSinkCaptor.capture())).thenReturn(unbundleCommandBuilder); + // when the RepositoryImportStep calls unbundle we pass our mocked event to the captured sink + when(unbundleCommandBuilder.unbundle(any(InputStream.class))).then(ic -> { + eventSinkCaptor.getValue().accept(event); + // RepositoryImportStep does not evaluate the return value of unbundle + return null; + }); - verify(updateEngine).update(REPOSITORY.getId()); + fullImporter.importFromStream(REPOSITORY, stream, ""); + + verify(eventBus).post(any(ImportRepositoryHookEvent.class)); } } }