From 548bf97c57dbc345b88f078518a880123c1f9e3c Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Wed, 1 Apr 2020 16:01:26 +0200 Subject: [PATCH 1/4] make scm-webapp depend optional of scm-landingpage-plugin // add some events for landingpage --- scm-webapp/pom.xml | 9 ++ .../scm/plugin/DefaultPluginManager.java | 7 +- .../sonia/scm/plugin/PluginCenterLoader.java | 15 ++- .../java/sonia/scm/plugin/PluginEvent.java | 39 +++++++ .../sonia/scm/plugin/PluginEventType.java | 29 +++++ ...uginInstallationFailedEventSubscriber.java | 86 ++++++++++++++ .../PluginInstalledEventSubscriber.java | 98 ++++++++++++++++ .../RepositoryCreatedEventSubscriber.java | 88 ++++++++++++++ .../scm/plugin/DefaultPluginManagerTest.java | 38 ++++++ .../scm/plugin/PluginCenterLoaderTest.java | 20 ++++ ...InstallationFailedEventSubscriberTest.java | 98 ++++++++++++++++ .../PluginInstalledEventSubscriberTest.java | 106 +++++++++++++++++ .../RepositoryCreatedEventSubscriberTest.java | 109 ++++++++++++++++++ yarn.lock | 11 +- 14 files changed, 741 insertions(+), 12 deletions(-) create mode 100644 scm-webapp/src/main/java/sonia/scm/plugin/PluginEvent.java create mode 100644 scm-webapp/src/main/java/sonia/scm/plugin/PluginEventType.java create mode 100644 scm-webapp/src/main/java/sonia/scm/plugin/PluginInstallationFailedEventSubscriber.java create mode 100644 scm-webapp/src/main/java/sonia/scm/plugin/PluginInstalledEventSubscriber.java create mode 100644 scm-webapp/src/main/java/sonia/scm/repository/RepositoryCreatedEventSubscriber.java create mode 100644 scm-webapp/src/test/java/sonia/scm/plugin/PluginInstallationFailedEventSubscriberTest.java create mode 100644 scm-webapp/src/test/java/sonia/scm/plugin/PluginInstalledEventSubscriberTest.java create mode 100644 scm-webapp/src/test/java/sonia/scm/repository/RepositoryCreatedEventSubscriberTest.java diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index f7ae938b5b..1d85d6a2df 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -463,6 +463,15 @@ provided + + + + sonia.scm.plugins + scm-landingpage-plugin + 1.0.0-SNAPSHOT + true + + diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java index ba41ada287..40b6230261 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java @@ -31,7 +31,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.NotFoundException; import sonia.scm.event.ScmEventBus; -import sonia.scm.lifecycle.RestartEvent; import sonia.scm.lifecycle.Restarter; import sonia.scm.version.Version; @@ -64,17 +63,19 @@ public class DefaultPluginManager implements PluginManager { private final PluginCenter center; private final PluginInstaller installer; private final Restarter restarter; + private final ScmEventBus eventBus; private final Collection pendingInstallQueue = new ArrayList<>(); private final Collection pendingUninstallQueue = new ArrayList<>(); private final PluginDependencyTracker dependencyTracker = new PluginDependencyTracker(); @Inject - public DefaultPluginManager(PluginLoader loader, PluginCenter center, PluginInstaller installer, Restarter restarter) { + public DefaultPluginManager(PluginLoader loader, PluginCenter center, PluginInstaller installer, Restarter restarter, ScmEventBus eventBus) { this.loader = loader; this.center = center; this.installer = installer; this.restarter = restarter; + this.eventBus = eventBus; this.computeInstallationDependencies(); } @@ -172,8 +173,10 @@ public class DefaultPluginManager implements PluginManager { PendingPluginInstallation pending = installer.install(plugin); dependencyTracker.addInstalled(plugin.getDescriptor()); pendingInstallations.add(pending); + eventBus.post(new PluginEvent(PluginEventType.INSTALLED, plugin)); } catch (PluginInstallException ex) { cancelPending(pendingInstallations); + eventBus.post(new PluginEvent(PluginEventType.INSTALLATION_FAILED, plugin)); throw ex; } } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterLoader.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterLoader.java index efc7d9f136..16f3123157 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterLoader.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterLoader.java @@ -27,10 +27,11 @@ package sonia.scm.plugin; import com.google.common.annotations.VisibleForTesting; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sonia.scm.event.Event; +import sonia.scm.event.ScmEventBus; import sonia.scm.net.ahc.AdvancedHttpClient; import javax.inject.Inject; -import java.io.IOException; import java.util.Collections; import java.util.Set; @@ -40,16 +41,18 @@ class PluginCenterLoader { private final AdvancedHttpClient client; private final PluginCenterDtoMapper mapper; + private final ScmEventBus eventBus; @Inject - public PluginCenterLoader(AdvancedHttpClient client) { - this(client, PluginCenterDtoMapper.INSTANCE); + public PluginCenterLoader(AdvancedHttpClient client, ScmEventBus eventBus) { + this(client, PluginCenterDtoMapper.INSTANCE, eventBus); } @VisibleForTesting - PluginCenterLoader(AdvancedHttpClient client, PluginCenterDtoMapper mapper) { + PluginCenterLoader(AdvancedHttpClient client, PluginCenterDtoMapper mapper, ScmEventBus eventBus) { this.client = client; this.mapper = mapper; + this.eventBus = eventBus; } Set load(String url) { @@ -59,8 +62,12 @@ class PluginCenterLoader { return mapper.map(pluginCenterDto); } catch (Exception ex) { LOG.error("failed to load plugins from plugin center, returning empty list", ex); + eventBus.post(new PluginCenterNotAvailableEvent()); return Collections.emptySet(); } } + @Event + class PluginCenterNotAvailableEvent {} + } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginEvent.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginEvent.java new file mode 100644 index 0000000000..45716ae04b --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginEvent.java @@ -0,0 +1,39 @@ +/* + * 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.plugin; + +import lombok.Getter; +import sonia.scm.event.Event; + +@Getter +@Event +public class PluginEvent { + private final PluginEventType eventType; + private final AvailablePlugin plugin; + public PluginEvent(PluginEventType eventType, AvailablePlugin plugin) { + this.eventType = eventType; + this.plugin = plugin; + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginEventType.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginEventType.java new file mode 100644 index 0000000000..2cde7c35f8 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginEventType.java @@ -0,0 +1,29 @@ +/* + * 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.plugin; + +enum PluginEventType { + INSTALLED, INSTALLATION_FAILED +} diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstallationFailedEventSubscriber.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstallationFailedEventSubscriber.java new file mode 100644 index 0000000000..5fd4188606 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstallationFailedEventSubscriber.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.plugin; + +import com.cloudogu.scm.myevents.MyEvent; +import com.github.legman.Subscribe; +import lombok.Getter; +import lombok.NoArgsConstructor; +import sonia.scm.EagerSingleton; +import sonia.scm.event.ScmEventBus; +import sonia.scm.xml.XmlInstantAdapter; + +import javax.inject.Inject; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import java.time.Instant; + +@Extension(requires = "scm-landingpage-plugin") +@EagerSingleton +public class PluginInstallationFailedEventSubscriber { + + private final ScmEventBus eventBus; + + @Inject + public PluginInstallationFailedEventSubscriber(ScmEventBus eventBus) { + this.eventBus = eventBus; + } + + @Subscribe + public void handleEvent(PluginEvent pluginEvent) { + if (pluginEvent.getEventType() == PluginEventType.INSTALLATION_FAILED) { + AvailablePlugin newPlugin = pluginEvent.getPlugin(); + + String permission = PluginPermissions.manage().asShiroString(); + String pluginName = newPlugin.getDescriptor().getInformation().getDisplayName(); + + String pluginVersion = newPlugin.getDescriptor().getInformation().getVersion(); + + eventBus.post(new PluginInstallationFailedEvent(permission, pluginName, pluginVersion, Instant.now())); + } + } + + @XmlRootElement + @XmlAccessorType(XmlAccessType.FIELD) + @Getter + @NoArgsConstructor + static class PluginInstallationFailedEvent extends MyEvent { + private String pluginName; + private String pluginVersion; + @XmlJavaTypeAdapter(XmlInstantAdapter.class) + private Instant date; + + PluginInstallationFailedEvent(String permission, String pluginName, String pluginVersion, Instant date) { + super(PluginInstalledEventSubscriber.PluginInstalledEvent.class.getSimpleName(), permission); + + this.pluginName = pluginName; + this.pluginVersion = pluginVersion; + this.date = date; + } + } + +} diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstalledEventSubscriber.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstalledEventSubscriber.java new file mode 100644 index 0000000000..4913eb80cf --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstalledEventSubscriber.java @@ -0,0 +1,98 @@ +/* + * 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.plugin; + +import com.cloudogu.scm.myevents.MyEvent; +import com.github.legman.Subscribe; +import lombok.Getter; +import lombok.NoArgsConstructor; +import sonia.scm.EagerSingleton; +import sonia.scm.event.ScmEventBus; +import sonia.scm.xml.XmlInstantAdapter; + +import javax.inject.Inject; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import java.time.Instant; +import java.util.Optional; + +@Extension(requires = "scm-landingpage-plugin") +@EagerSingleton +public class PluginInstalledEventSubscriber { + + private final ScmEventBus eventBus; + private final PluginManager pluginManager; + + @Inject + public PluginInstalledEventSubscriber(ScmEventBus eventBus, PluginManager pluginManager) { + this.eventBus = eventBus; + this.pluginManager = pluginManager; + } + + @Subscribe + public void handleEvent(PluginEvent pluginEvent) { + if (pluginEvent.getEventType() == PluginEventType.INSTALLED) { + AvailablePlugin newPlugin = pluginEvent.getPlugin(); + Optional installedPlugin = pluginManager.getInstalled(newPlugin.getDescriptor().getInformation().getDisplayName()); + + String permission = PluginPermissions.manage().asShiroString(); + + String pluginName = newPlugin.getDescriptor().getInformation().getName(); + + String previousPluginVersion = null; + if (installedPlugin.isPresent()) { + previousPluginVersion = installedPlugin.get().getDescriptor().getInformation().getVersion(); + } + + String newPluginVersion = newPlugin.getDescriptor().getInformation().getVersion(); + + eventBus.post(new PluginInstalledEvent(permission, pluginName, previousPluginVersion, newPluginVersion, Instant.now())); + } + } + + @XmlRootElement + @XmlAccessorType(XmlAccessType.FIELD) + @Getter + @NoArgsConstructor + static class PluginInstalledEvent extends MyEvent { + private String pluginName; + private String previousPluginVersion; + private String newPluginVersion; + @XmlJavaTypeAdapter(XmlInstantAdapter.class) + private Instant date; + + PluginInstalledEvent(String permission, String pluginName, String previousPluginVersion, String newPluginVersion, Instant date) { + super(PluginInstalledEventSubscriber.PluginInstalledEvent.class.getSimpleName(), permission); + + this.pluginName = pluginName; + this.previousPluginVersion = previousPluginVersion; + this.newPluginVersion = newPluginVersion; + this.date = date; + } + } + +} diff --git a/scm-webapp/src/main/java/sonia/scm/repository/RepositoryCreatedEventSubscriber.java b/scm-webapp/src/main/java/sonia/scm/repository/RepositoryCreatedEventSubscriber.java new file mode 100644 index 0000000000..746d73b2d2 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/repository/RepositoryCreatedEventSubscriber.java @@ -0,0 +1,88 @@ +/* + * 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.cloudogu.scm.myevents.MyEvent; +import com.github.legman.Subscribe; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.apache.shiro.SecurityUtils; +import sonia.scm.EagerSingleton; +import sonia.scm.HandlerEventType; +import sonia.scm.event.ScmEventBus; +import sonia.scm.plugin.Extension; +import sonia.scm.user.User; +import sonia.scm.xml.XmlInstantAdapter; + +import javax.inject.Inject; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import java.time.Instant; + +@Extension(requires = "scm-landingpage-plugin") +@EagerSingleton +public class RepositoryCreatedEventSubscriber { + + private final ScmEventBus eventBus; + + @Inject + public RepositoryCreatedEventSubscriber(ScmEventBus eventBus) { + this.eventBus = eventBus; + } + + @Subscribe + public void handleEvent(RepositoryEvent event) { + if (event.getEventType() == HandlerEventType.CREATE) { + Repository eventRepo = event.getItem(); + String permission = RepositoryPermissions.read(event.getItem()).asShiroString(); + String repository = eventRepo.getNamespace() + "/" + eventRepo.getName(); + String creator = SecurityUtils.getSubject().getPrincipals().oneByType(User.class).getDisplayName(); + Instant date = Instant.now(); + + eventBus.post(new RepositoryCreatedEvent(permission, repository, creator, date)); + } + } + + @XmlRootElement + @XmlAccessorType(XmlAccessType.FIELD) + @Getter + @NoArgsConstructor + static class RepositoryCreatedEvent extends MyEvent { + private String repository; + private String creator; + @XmlJavaTypeAdapter(XmlInstantAdapter.class) + private Instant date; + + RepositoryCreatedEvent(String permission, String repository, String creator, Instant date) { + super(RepositoryCreatedEvent.class.getSimpleName(), permission); + this.repository = repository; + this.creator = creator; + this.date = date; + } + } + +} diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java index 8f3f7bbe57..aa4d5cb5b1 100644 --- a/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java +++ b/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java @@ -36,16 +36,19 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junitpioneer.jupiter.TempDirectory; 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.NotFoundException; import sonia.scm.ScmConstraintViolationException; +import sonia.scm.event.ScmEventBus; import sonia.scm.lifecycle.Restarter; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Collections; import java.util.List; import java.util.Optional; @@ -53,6 +56,7 @@ import static java.util.Arrays.asList; import static java.util.Collections.singleton; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.in; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.any; import static org.mockito.Mockito.doNothing; @@ -83,6 +87,12 @@ class DefaultPluginManagerTest { @Mock private Restarter restarter; + @Mock + private ScmEventBus eventBus; + + @Captor + private ArgumentCaptor eventCaptor; + @InjectMocks private DefaultPluginManager manager; @@ -537,8 +547,36 @@ class DefaultPluginManagerTest { verify(installer, never()).install(oldScriptPlugin); } + + @Test + void shouldFirePluginEventOnInstallation() { + AvailablePlugin review = createAvailable("scm-review-plugin"); + when(center.getAvailable()).thenReturn(ImmutableSet.of(review)); + + manager.install("scm-review-plugin", false); + + verify(eventBus).post(eventCaptor.capture()); + + assertThat(eventCaptor.getValue().getEventType()).isEqualTo(PluginEventType.INSTALLED); + assertThat(eventCaptor.getValue().getPlugin()).isEqualTo(review); + } + + @Test + void shouldFirePluginEventOnFailedInstallation() { + AvailablePlugin review = createAvailable("scm-review-plugin"); + when(center.getAvailable()).thenReturn(ImmutableSet.of(review)); + doThrow(new PluginDownloadException(review, new IOException())).when(installer).install(review); + + assertThrows(PluginDownloadException.class, () -> manager.install("scm-review-plugin", false)); + + verify(eventBus).post(eventCaptor.capture()); + assertThat(eventCaptor.getValue().getEventType()).isEqualTo(PluginEventType.INSTALLATION_FAILED); + assertThat(eventCaptor.getValue().getPlugin()).isEqualTo(review); + } + } + @Nested class WithoutReadPermissions { diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterLoaderTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterLoaderTest.java index 01fe540206..b00241eebf 100644 --- a/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterLoaderTest.java +++ b/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterLoaderTest.java @@ -24,12 +24,16 @@ package sonia.scm.plugin; +import com.sun.mail.iap.Argument; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; 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.net.ahc.AdvancedHttpClient; import java.io.IOException; @@ -37,6 +41,10 @@ import java.util.Collections; import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) @@ -50,6 +58,9 @@ class PluginCenterLoaderTest { @Mock private PluginCenterDtoMapper mapper; + @Mock + private ScmEventBus eventBus; + @InjectMocks private PluginCenterLoader loader; @@ -71,4 +82,13 @@ class PluginCenterLoaderTest { Set fetch = loader.load(PLUGIN_URL); assertThat(fetch).isEmpty(); } + + @Test + void shouldThrowExceptionAndFirePluginCenterNotAvailableEvent() throws IOException { + when(client.get(PLUGIN_URL).request()).thenThrow(new IOException("failed to fetch")); + + loader.load(PLUGIN_URL); + + verify(eventBus).post(any(PluginCenterLoader.PluginCenterNotAvailableEvent.class)); + } } diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/PluginInstallationFailedEventSubscriberTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/PluginInstallationFailedEventSubscriberTest.java new file mode 100644 index 0000000000..88dff091cf --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/plugin/PluginInstallationFailedEventSubscriberTest.java @@ -0,0 +1,98 @@ +/* + * 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.plugin; + +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.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 static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class PluginInstallationFailedEventSubscriberTest { + + private AvailablePlugin newPlugin = PluginTestHelper.createAvailable("scm-hitchhiker-plugin"); + + @Mock + private Subject subject; + + @Mock + private ScmEventBus eventBus; + + @Mock + private PluginEvent event; + + @Captor + private ArgumentCaptor eventCaptor; + + @InjectMocks + private PluginInstallationFailedEventSubscriber subscriber; + + @BeforeEach + void bindSubject() { + ThreadContext.bind(subject); + } + + @AfterEach + void tearDownSubject() { + ThreadContext.unbindSubject(); + } + + @Test + void shouldFireMyEvent() { + when(event.getEventType()).thenReturn(PluginEventType.INSTALLATION_FAILED); + when(event.getPlugin()).thenReturn(newPlugin); + + subscriber.handleEvent(event); + + verify(eventBus).post(eventCaptor.capture()); + + PluginInstallationFailedEventSubscriber.PluginInstallationFailedEvent pluginInstalledEvent = eventCaptor.getValue(); + assertThat(pluginInstalledEvent.getPermission()).isEqualTo("plugin:manage"); + assertThat(pluginInstalledEvent.getPluginVersion()).isEqualTo("1.0"); + assertThat(pluginInstalledEvent.getPluginName()).isEqualTo(newPlugin.getDescriptor().getInformation().getDisplayName()); + } + + @Test + void shouldNotFireMyEventWhenNotCreatedEvent() { + when(event.getEventType()).thenReturn(PluginEventType.INSTALLED); + subscriber.handleEvent(event); + + verify(eventBus, never()).post(eventCaptor.capture()); + } + +} diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/PluginInstalledEventSubscriberTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/PluginInstalledEventSubscriberTest.java new file mode 100644 index 0000000000..61348c96c3 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/plugin/PluginInstalledEventSubscriberTest.java @@ -0,0 +1,106 @@ +/* + * 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.plugin; + +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.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 java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class PluginInstalledEventSubscriberTest { + + private InstalledPlugin oldPlugin = PluginTestHelper.createInstalled("scm-hitchhiker-plugin"); + private AvailablePlugin newPlugin = PluginTestHelper.createAvailable("scm-hitchhiker-plugin", "1.1"); + + @Mock + private Subject subject; + + @Mock + private ScmEventBus eventBus; + + @Mock + private PluginEvent event; + + @Mock + private PluginManager pluginManager; + + @Captor + private ArgumentCaptor eventCaptor; + + @InjectMocks + private PluginInstalledEventSubscriber subscriber; + + @BeforeEach + void bindSubject() { + ThreadContext.bind(subject); + } + + @AfterEach + void tearDownSubject() { + ThreadContext.unbindSubject(); + } + + @Test + void shouldFireMyEvent() { + when(event.getEventType()).thenReturn(PluginEventType.INSTALLED); + when(event.getPlugin()).thenReturn(newPlugin); + when(pluginManager.getInstalled("scm-hitchhiker-plugin")).thenReturn(Optional.of(oldPlugin)); + + subscriber.handleEvent(event); + + verify(eventBus).post(eventCaptor.capture()); + + PluginInstalledEventSubscriber.PluginInstalledEvent pluginInstalledEvent = eventCaptor.getValue(); + assertThat(pluginInstalledEvent.getPermission()).isEqualTo("plugin:manage"); + assertThat(pluginInstalledEvent.getPreviousPluginVersion()).isEqualTo("1.0"); + assertThat(pluginInstalledEvent.getNewPluginVersion()).isEqualTo("1.1"); + assertThat(pluginInstalledEvent.getPluginName()).isEqualTo(newPlugin.getDescriptor().getInformation().getDisplayName()); + } + + @Test + void shouldNotFireMyEventWhenNotCreatedEvent() { + when(event.getEventType()).thenReturn(PluginEventType.INSTALLATION_FAILED); + subscriber.handleEvent(event); + + verify(eventBus, never()).post(eventCaptor.capture()); + } + +} diff --git a/scm-webapp/src/test/java/sonia/scm/repository/RepositoryCreatedEventSubscriberTest.java b/scm-webapp/src/test/java/sonia/scm/repository/RepositoryCreatedEventSubscriberTest.java new file mode 100644 index 0000000000..59930d1709 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/repository/RepositoryCreatedEventSubscriberTest.java @@ -0,0 +1,109 @@ +/* + * 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.subject.PrincipalCollection; +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.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import sonia.scm.HandlerEventType; +import sonia.scm.event.ScmEventBus; +import sonia.scm.user.User; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class RepositoryCreatedEventSubscriberTest { + + private Repository REPOSITORY = new Repository("1", "git", "nicest", "repo"); + + @Mock + private Subject subject; + + @Mock + private ScmEventBus eventBus; + + @Mock + private RepositoryEvent event; + + @Mock + private PrincipalCollection principalCollection; + + @Captor + private ArgumentCaptor eventCaptor; + + @InjectMocks + private RepositoryCreatedEventSubscriber subscriber; + + @BeforeEach + void bindSubject() { + ThreadContext.bind(subject); + } + + @AfterEach + void tearDownSubject() { + ThreadContext.unbindSubject(); + } + + @Test + void shouldFireMyEvent() { + User trillian = new User("trillian", "Trillian", "tricia@hitchhiker.org"); + + when(event.getEventType()).thenReturn(HandlerEventType.CREATE); + when(event.getItem()).thenReturn(REPOSITORY); + when(subject.getPrincipals()).thenReturn(principalCollection); + when(principalCollection.oneByType(User.class)).thenReturn(trillian); + + subscriber.handleEvent(event); + + verify(eventBus).post(eventCaptor.capture()); + + RepositoryCreatedEventSubscriber.RepositoryCreatedEvent repositoryCreatedEvent = eventCaptor.getValue(); + assertThat(repositoryCreatedEvent.getPermission()).isEqualTo("repository:read:1"); + assertThat(repositoryCreatedEvent.getRepository()).isEqualTo(REPOSITORY.getNamespace() + "/" + REPOSITORY.getName()); + assertThat(repositoryCreatedEvent.getType()).isEqualTo(RepositoryCreatedEventSubscriber.RepositoryCreatedEvent.class.getSimpleName()); + assertThat(repositoryCreatedEvent.getCreator()).isEqualTo("Trillian"); + } + + @Test + void shouldNotFireMyEventWhenNotCreatedEvent() { + when(event.getEventType()).thenReturn(HandlerEventType.BEFORE_CREATE); + subscriber.handleEvent(event); + + verify(eventBus, never()).post(eventCaptor.capture()); + } + +} diff --git a/yarn.lock b/yarn.lock index 1f60f6bbed..61c247fdc5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2047,17 +2047,16 @@ dependencies: "@types/node" ">= 8" -"@pmmmwh/react-refresh-webpack-plugin@^0.1.3": - version "0.1.3" - resolved "https://registry.yarnpkg.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.1.3.tgz#bb6815315028087e6af4f96d063376880caf9c82" - integrity sha512-FJ8WzpGrao8Gz8KNAeU9dcTYr1RjbAGnXJMKKXTp4oAw494SqQK4HyGT8HMmIQt0ayukuP+A71w1oV8/5xSAWQ== +"@pmmmwh/react-refresh-webpack-plugin@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.2.0.tgz#e2a684d430f74ad6465680d9a5869f52f307ec1e" + integrity sha512-rjdNzcWroULJeD/Y0+eETy9LhM7c5tbPF+wqT5G680rwDkh3iothIPEqGAuEE2WJlXEaAq293aO6ySzsIU518Q== dependencies: ansi-html "^0.0.7" error-stack-parser "^2.0.4" html-entities "^1.2.1" lodash.debounce "^4.0.8" react-dev-utils "^9.1.0" - sockjs-client "^1.4.0" "@reach/router@^1.2.1": version "1.2.1" @@ -13389,7 +13388,7 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" -sockjs-client@1.4.0, sockjs-client@^1.4.0: +sockjs-client@1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.4.0.tgz#c9f2568e19c8fd8173b4997ea3420e0bb306c7d5" integrity sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g== From 405bc392a8f9bc48bd1600a1dbf902212f46e92c Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Thu, 2 Apr 2020 10:43:07 +0200 Subject: [PATCH 2/4] move events to scm-core / move subscriber to landingpage-plugin --- .../sonia/scm/plugin/PluginCenterEvent.java | 7 +- .../java/sonia/scm/plugin/PluginEvent.java | 4 + scm-webapp/pom.xml | 13 +-- .../scm/plugin/DefaultPluginManager.java | 9 +- .../sonia/scm/plugin/PluginCenterLoader.java | 7 +- ...uginInstallationFailedEventSubscriber.java | 86 -------------- .../PluginInstalledEventSubscriber.java | 98 ---------------- .../RepositoryCreatedEventSubscriber.java | 88 -------------- .../api/v2/resources/PluginDtoMapperTest.java | 4 - .../scm/plugin/DefaultPluginManagerTest.java | 4 +- .../scm/plugin/PluginCenterLoaderTest.java | 9 +- ...InstallationFailedEventSubscriberTest.java | 98 ---------------- .../PluginInstalledEventSubscriberTest.java | 106 ----------------- .../RepositoryCreatedEventSubscriberTest.java | 109 ------------------ 14 files changed, 19 insertions(+), 623 deletions(-) rename scm-webapp/src/main/java/sonia/scm/plugin/PluginEventType.java => scm-core/src/main/java/sonia/scm/plugin/PluginCenterEvent.java (94%) rename {scm-webapp => scm-core}/src/main/java/sonia/scm/plugin/PluginEvent.java (95%) delete mode 100644 scm-webapp/src/main/java/sonia/scm/plugin/PluginInstallationFailedEventSubscriber.java delete mode 100644 scm-webapp/src/main/java/sonia/scm/plugin/PluginInstalledEventSubscriber.java delete mode 100644 scm-webapp/src/main/java/sonia/scm/repository/RepositoryCreatedEventSubscriber.java delete mode 100644 scm-webapp/src/test/java/sonia/scm/plugin/PluginInstallationFailedEventSubscriberTest.java delete mode 100644 scm-webapp/src/test/java/sonia/scm/plugin/PluginInstalledEventSubscriberTest.java delete mode 100644 scm-webapp/src/test/java/sonia/scm/repository/RepositoryCreatedEventSubscriberTest.java diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginEventType.java b/scm-core/src/main/java/sonia/scm/plugin/PluginCenterEvent.java similarity index 94% rename from scm-webapp/src/main/java/sonia/scm/plugin/PluginEventType.java rename to scm-core/src/main/java/sonia/scm/plugin/PluginCenterEvent.java index 2cde7c35f8..fe0896c344 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginEventType.java +++ b/scm-core/src/main/java/sonia/scm/plugin/PluginCenterEvent.java @@ -24,6 +24,7 @@ package sonia.scm.plugin; -enum PluginEventType { - INSTALLED, INSTALLATION_FAILED -} +import sonia.scm.event.Event; + +@Event +public class PluginCenterEvent {} diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginEvent.java b/scm-core/src/main/java/sonia/scm/plugin/PluginEvent.java similarity index 95% rename from scm-webapp/src/main/java/sonia/scm/plugin/PluginEvent.java rename to scm-core/src/main/java/sonia/scm/plugin/PluginEvent.java index 45716ae04b..7802b3954d 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginEvent.java +++ b/scm-core/src/main/java/sonia/scm/plugin/PluginEvent.java @@ -36,4 +36,8 @@ public class PluginEvent { this.eventType = eventType; this.plugin = plugin; } + + public enum PluginEventType { + INSTALLED, INSTALLATION_FAILED + } } diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index 1d85d6a2df..a95599aa08 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -24,7 +24,8 @@ SOFTWARE. --> - + 4.0.0 @@ -462,16 +463,6 @@ 2.1.1 provided - - - - - sonia.scm.plugins - scm-landingpage-plugin - 1.0.0-SNAPSHOT - true - - diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java index 40b6230261..bb6dc1491f 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.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.plugin; import com.google.common.annotations.VisibleForTesting; @@ -51,7 +51,6 @@ import static sonia.scm.ScmConstraintViolationException.Builder.doThrow; //~--- JDK imports ------------------------------------------------------------ /** - * * @author Sebastian Sdorra */ @Singleton @@ -173,10 +172,10 @@ public class DefaultPluginManager implements PluginManager { PendingPluginInstallation pending = installer.install(plugin); dependencyTracker.addInstalled(plugin.getDescriptor()); pendingInstallations.add(pending); - eventBus.post(new PluginEvent(PluginEventType.INSTALLED, plugin)); + eventBus.post(new PluginEvent(PluginEvent.PluginEventType.INSTALLED, plugin)); } catch (PluginInstallException ex) { cancelPending(pendingInstallations); - eventBus.post(new PluginEvent(PluginEventType.INSTALLATION_FAILED, plugin)); + eventBus.post(new PluginEvent(PluginEvent.PluginEventType.INSTALLATION_FAILED, plugin)); throw ex; } } @@ -258,7 +257,7 @@ public class DefaultPluginManager implements PluginManager { Set dependencies = plugin.getDescriptor().getDependencies(); if (dependencies != null) { - for (String dependency: dependencies){ + for (String dependency : dependencies) { collectPluginsToInstall(plugins, dependency, false); } } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterLoader.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterLoader.java index 16f3123157..d48a6468a3 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterLoader.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterLoader.java @@ -27,7 +27,6 @@ package sonia.scm.plugin; import com.google.common.annotations.VisibleForTesting; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.event.Event; import sonia.scm.event.ScmEventBus; import sonia.scm.net.ahc.AdvancedHttpClient; @@ -62,12 +61,8 @@ class PluginCenterLoader { return mapper.map(pluginCenterDto); } catch (Exception ex) { LOG.error("failed to load plugins from plugin center, returning empty list", ex); - eventBus.post(new PluginCenterNotAvailableEvent()); + eventBus.post(new PluginCenterEvent()); return Collections.emptySet(); } } - - @Event - class PluginCenterNotAvailableEvent {} - } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstallationFailedEventSubscriber.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstallationFailedEventSubscriber.java deleted file mode 100644 index 5fd4188606..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstallationFailedEventSubscriber.java +++ /dev/null @@ -1,86 +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.plugin; - -import com.cloudogu.scm.myevents.MyEvent; -import com.github.legman.Subscribe; -import lombok.Getter; -import lombok.NoArgsConstructor; -import sonia.scm.EagerSingleton; -import sonia.scm.event.ScmEventBus; -import sonia.scm.xml.XmlInstantAdapter; - -import javax.inject.Inject; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; -import java.time.Instant; - -@Extension(requires = "scm-landingpage-plugin") -@EagerSingleton -public class PluginInstallationFailedEventSubscriber { - - private final ScmEventBus eventBus; - - @Inject - public PluginInstallationFailedEventSubscriber(ScmEventBus eventBus) { - this.eventBus = eventBus; - } - - @Subscribe - public void handleEvent(PluginEvent pluginEvent) { - if (pluginEvent.getEventType() == PluginEventType.INSTALLATION_FAILED) { - AvailablePlugin newPlugin = pluginEvent.getPlugin(); - - String permission = PluginPermissions.manage().asShiroString(); - String pluginName = newPlugin.getDescriptor().getInformation().getDisplayName(); - - String pluginVersion = newPlugin.getDescriptor().getInformation().getVersion(); - - eventBus.post(new PluginInstallationFailedEvent(permission, pluginName, pluginVersion, Instant.now())); - } - } - - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) - @Getter - @NoArgsConstructor - static class PluginInstallationFailedEvent extends MyEvent { - private String pluginName; - private String pluginVersion; - @XmlJavaTypeAdapter(XmlInstantAdapter.class) - private Instant date; - - PluginInstallationFailedEvent(String permission, String pluginName, String pluginVersion, Instant date) { - super(PluginInstalledEventSubscriber.PluginInstalledEvent.class.getSimpleName(), permission); - - this.pluginName = pluginName; - this.pluginVersion = pluginVersion; - this.date = date; - } - } - -} diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstalledEventSubscriber.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstalledEventSubscriber.java deleted file mode 100644 index 4913eb80cf..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstalledEventSubscriber.java +++ /dev/null @@ -1,98 +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.plugin; - -import com.cloudogu.scm.myevents.MyEvent; -import com.github.legman.Subscribe; -import lombok.Getter; -import lombok.NoArgsConstructor; -import sonia.scm.EagerSingleton; -import sonia.scm.event.ScmEventBus; -import sonia.scm.xml.XmlInstantAdapter; - -import javax.inject.Inject; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; -import java.time.Instant; -import java.util.Optional; - -@Extension(requires = "scm-landingpage-plugin") -@EagerSingleton -public class PluginInstalledEventSubscriber { - - private final ScmEventBus eventBus; - private final PluginManager pluginManager; - - @Inject - public PluginInstalledEventSubscriber(ScmEventBus eventBus, PluginManager pluginManager) { - this.eventBus = eventBus; - this.pluginManager = pluginManager; - } - - @Subscribe - public void handleEvent(PluginEvent pluginEvent) { - if (pluginEvent.getEventType() == PluginEventType.INSTALLED) { - AvailablePlugin newPlugin = pluginEvent.getPlugin(); - Optional installedPlugin = pluginManager.getInstalled(newPlugin.getDescriptor().getInformation().getDisplayName()); - - String permission = PluginPermissions.manage().asShiroString(); - - String pluginName = newPlugin.getDescriptor().getInformation().getName(); - - String previousPluginVersion = null; - if (installedPlugin.isPresent()) { - previousPluginVersion = installedPlugin.get().getDescriptor().getInformation().getVersion(); - } - - String newPluginVersion = newPlugin.getDescriptor().getInformation().getVersion(); - - eventBus.post(new PluginInstalledEvent(permission, pluginName, previousPluginVersion, newPluginVersion, Instant.now())); - } - } - - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) - @Getter - @NoArgsConstructor - static class PluginInstalledEvent extends MyEvent { - private String pluginName; - private String previousPluginVersion; - private String newPluginVersion; - @XmlJavaTypeAdapter(XmlInstantAdapter.class) - private Instant date; - - PluginInstalledEvent(String permission, String pluginName, String previousPluginVersion, String newPluginVersion, Instant date) { - super(PluginInstalledEventSubscriber.PluginInstalledEvent.class.getSimpleName(), permission); - - this.pluginName = pluginName; - this.previousPluginVersion = previousPluginVersion; - this.newPluginVersion = newPluginVersion; - this.date = date; - } - } - -} diff --git a/scm-webapp/src/main/java/sonia/scm/repository/RepositoryCreatedEventSubscriber.java b/scm-webapp/src/main/java/sonia/scm/repository/RepositoryCreatedEventSubscriber.java deleted file mode 100644 index 746d73b2d2..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/repository/RepositoryCreatedEventSubscriber.java +++ /dev/null @@ -1,88 +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.repository; - -import com.cloudogu.scm.myevents.MyEvent; -import com.github.legman.Subscribe; -import lombok.Getter; -import lombok.NoArgsConstructor; -import org.apache.shiro.SecurityUtils; -import sonia.scm.EagerSingleton; -import sonia.scm.HandlerEventType; -import sonia.scm.event.ScmEventBus; -import sonia.scm.plugin.Extension; -import sonia.scm.user.User; -import sonia.scm.xml.XmlInstantAdapter; - -import javax.inject.Inject; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; -import java.time.Instant; - -@Extension(requires = "scm-landingpage-plugin") -@EagerSingleton -public class RepositoryCreatedEventSubscriber { - - private final ScmEventBus eventBus; - - @Inject - public RepositoryCreatedEventSubscriber(ScmEventBus eventBus) { - this.eventBus = eventBus; - } - - @Subscribe - public void handleEvent(RepositoryEvent event) { - if (event.getEventType() == HandlerEventType.CREATE) { - Repository eventRepo = event.getItem(); - String permission = RepositoryPermissions.read(event.getItem()).asShiroString(); - String repository = eventRepo.getNamespace() + "/" + eventRepo.getName(); - String creator = SecurityUtils.getSubject().getPrincipals().oneByType(User.class).getDisplayName(); - Instant date = Instant.now(); - - eventBus.post(new RepositoryCreatedEvent(permission, repository, creator, date)); - } - } - - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) - @Getter - @NoArgsConstructor - static class RepositoryCreatedEvent extends MyEvent { - private String repository; - private String creator; - @XmlJavaTypeAdapter(XmlInstantAdapter.class) - private Instant date; - - RepositoryCreatedEvent(String permission, String repository, String creator, Instant date) { - super(RepositoryCreatedEvent.class.getSimpleName(), permission); - this.repository = repository; - this.creator = creator; - this.date = date; - } - } - -} diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginDtoMapperTest.java index 51a7001140..8301e6fb76 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginDtoMapperTest.java @@ -31,22 +31,18 @@ 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.lifecycle.Restarter; import sonia.scm.plugin.AvailablePlugin; -import sonia.scm.plugin.AvailablePluginDescriptor; import sonia.scm.plugin.InstalledPlugin; import sonia.scm.plugin.PluginInformation; import java.net.URI; -import java.util.Collections; import static java.util.Collections.emptyList; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static sonia.scm.plugin.PluginTestHelper.createAvailable; import static sonia.scm.plugin.PluginTestHelper.createInstalled; diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java index aa4d5cb5b1..03b12f92cf 100644 --- a/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java +++ b/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java @@ -557,7 +557,7 @@ class DefaultPluginManagerTest { verify(eventBus).post(eventCaptor.capture()); - assertThat(eventCaptor.getValue().getEventType()).isEqualTo(PluginEventType.INSTALLED); + assertThat(eventCaptor.getValue().getEventType()).isEqualTo(PluginEvent.PluginEventType.INSTALLED); assertThat(eventCaptor.getValue().getPlugin()).isEqualTo(review); } @@ -570,7 +570,7 @@ class DefaultPluginManagerTest { assertThrows(PluginDownloadException.class, () -> manager.install("scm-review-plugin", false)); verify(eventBus).post(eventCaptor.capture()); - assertThat(eventCaptor.getValue().getEventType()).isEqualTo(PluginEventType.INSTALLATION_FAILED); + assertThat(eventCaptor.getValue().getEventType()).isEqualTo(PluginEvent.PluginEventType.INSTALLATION_FAILED); assertThat(eventCaptor.getValue().getPlugin()).isEqualTo(review); } diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterLoaderTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterLoaderTest.java index b00241eebf..d05cbc0107 100644 --- a/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterLoaderTest.java +++ b/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterLoaderTest.java @@ -21,15 +21,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.plugin; -import com.sun.mail.iap.Argument; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; 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; @@ -41,9 +38,7 @@ import java.util.Collections; import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -89,6 +84,6 @@ class PluginCenterLoaderTest { loader.load(PLUGIN_URL); - verify(eventBus).post(any(PluginCenterLoader.PluginCenterNotAvailableEvent.class)); + verify(eventBus).post(any(PluginCenterEvent.class)); } } diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/PluginInstallationFailedEventSubscriberTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/PluginInstallationFailedEventSubscriberTest.java deleted file mode 100644 index 88dff091cf..0000000000 --- a/scm-webapp/src/test/java/sonia/scm/plugin/PluginInstallationFailedEventSubscriberTest.java +++ /dev/null @@ -1,98 +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.plugin; - -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.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 static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@ExtendWith(MockitoExtension.class) -class PluginInstallationFailedEventSubscriberTest { - - private AvailablePlugin newPlugin = PluginTestHelper.createAvailable("scm-hitchhiker-plugin"); - - @Mock - private Subject subject; - - @Mock - private ScmEventBus eventBus; - - @Mock - private PluginEvent event; - - @Captor - private ArgumentCaptor eventCaptor; - - @InjectMocks - private PluginInstallationFailedEventSubscriber subscriber; - - @BeforeEach - void bindSubject() { - ThreadContext.bind(subject); - } - - @AfterEach - void tearDownSubject() { - ThreadContext.unbindSubject(); - } - - @Test - void shouldFireMyEvent() { - when(event.getEventType()).thenReturn(PluginEventType.INSTALLATION_FAILED); - when(event.getPlugin()).thenReturn(newPlugin); - - subscriber.handleEvent(event); - - verify(eventBus).post(eventCaptor.capture()); - - PluginInstallationFailedEventSubscriber.PluginInstallationFailedEvent pluginInstalledEvent = eventCaptor.getValue(); - assertThat(pluginInstalledEvent.getPermission()).isEqualTo("plugin:manage"); - assertThat(pluginInstalledEvent.getPluginVersion()).isEqualTo("1.0"); - assertThat(pluginInstalledEvent.getPluginName()).isEqualTo(newPlugin.getDescriptor().getInformation().getDisplayName()); - } - - @Test - void shouldNotFireMyEventWhenNotCreatedEvent() { - when(event.getEventType()).thenReturn(PluginEventType.INSTALLED); - subscriber.handleEvent(event); - - verify(eventBus, never()).post(eventCaptor.capture()); - } - -} diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/PluginInstalledEventSubscriberTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/PluginInstalledEventSubscriberTest.java deleted file mode 100644 index 61348c96c3..0000000000 --- a/scm-webapp/src/test/java/sonia/scm/plugin/PluginInstalledEventSubscriberTest.java +++ /dev/null @@ -1,106 +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.plugin; - -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.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 java.util.Optional; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@ExtendWith(MockitoExtension.class) -class PluginInstalledEventSubscriberTest { - - private InstalledPlugin oldPlugin = PluginTestHelper.createInstalled("scm-hitchhiker-plugin"); - private AvailablePlugin newPlugin = PluginTestHelper.createAvailable("scm-hitchhiker-plugin", "1.1"); - - @Mock - private Subject subject; - - @Mock - private ScmEventBus eventBus; - - @Mock - private PluginEvent event; - - @Mock - private PluginManager pluginManager; - - @Captor - private ArgumentCaptor eventCaptor; - - @InjectMocks - private PluginInstalledEventSubscriber subscriber; - - @BeforeEach - void bindSubject() { - ThreadContext.bind(subject); - } - - @AfterEach - void tearDownSubject() { - ThreadContext.unbindSubject(); - } - - @Test - void shouldFireMyEvent() { - when(event.getEventType()).thenReturn(PluginEventType.INSTALLED); - when(event.getPlugin()).thenReturn(newPlugin); - when(pluginManager.getInstalled("scm-hitchhiker-plugin")).thenReturn(Optional.of(oldPlugin)); - - subscriber.handleEvent(event); - - verify(eventBus).post(eventCaptor.capture()); - - PluginInstalledEventSubscriber.PluginInstalledEvent pluginInstalledEvent = eventCaptor.getValue(); - assertThat(pluginInstalledEvent.getPermission()).isEqualTo("plugin:manage"); - assertThat(pluginInstalledEvent.getPreviousPluginVersion()).isEqualTo("1.0"); - assertThat(pluginInstalledEvent.getNewPluginVersion()).isEqualTo("1.1"); - assertThat(pluginInstalledEvent.getPluginName()).isEqualTo(newPlugin.getDescriptor().getInformation().getDisplayName()); - } - - @Test - void shouldNotFireMyEventWhenNotCreatedEvent() { - when(event.getEventType()).thenReturn(PluginEventType.INSTALLATION_FAILED); - subscriber.handleEvent(event); - - verify(eventBus, never()).post(eventCaptor.capture()); - } - -} diff --git a/scm-webapp/src/test/java/sonia/scm/repository/RepositoryCreatedEventSubscriberTest.java b/scm-webapp/src/test/java/sonia/scm/repository/RepositoryCreatedEventSubscriberTest.java deleted file mode 100644 index 59930d1709..0000000000 --- a/scm-webapp/src/test/java/sonia/scm/repository/RepositoryCreatedEventSubscriberTest.java +++ /dev/null @@ -1,109 +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.repository; - -import org.apache.shiro.subject.PrincipalCollection; -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.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import sonia.scm.HandlerEventType; -import sonia.scm.event.ScmEventBus; -import sonia.scm.user.User; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@ExtendWith(MockitoExtension.class) -class RepositoryCreatedEventSubscriberTest { - - private Repository REPOSITORY = new Repository("1", "git", "nicest", "repo"); - - @Mock - private Subject subject; - - @Mock - private ScmEventBus eventBus; - - @Mock - private RepositoryEvent event; - - @Mock - private PrincipalCollection principalCollection; - - @Captor - private ArgumentCaptor eventCaptor; - - @InjectMocks - private RepositoryCreatedEventSubscriber subscriber; - - @BeforeEach - void bindSubject() { - ThreadContext.bind(subject); - } - - @AfterEach - void tearDownSubject() { - ThreadContext.unbindSubject(); - } - - @Test - void shouldFireMyEvent() { - User trillian = new User("trillian", "Trillian", "tricia@hitchhiker.org"); - - when(event.getEventType()).thenReturn(HandlerEventType.CREATE); - when(event.getItem()).thenReturn(REPOSITORY); - when(subject.getPrincipals()).thenReturn(principalCollection); - when(principalCollection.oneByType(User.class)).thenReturn(trillian); - - subscriber.handleEvent(event); - - verify(eventBus).post(eventCaptor.capture()); - - RepositoryCreatedEventSubscriber.RepositoryCreatedEvent repositoryCreatedEvent = eventCaptor.getValue(); - assertThat(repositoryCreatedEvent.getPermission()).isEqualTo("repository:read:1"); - assertThat(repositoryCreatedEvent.getRepository()).isEqualTo(REPOSITORY.getNamespace() + "/" + REPOSITORY.getName()); - assertThat(repositoryCreatedEvent.getType()).isEqualTo(RepositoryCreatedEventSubscriber.RepositoryCreatedEvent.class.getSimpleName()); - assertThat(repositoryCreatedEvent.getCreator()).isEqualTo("Trillian"); - } - - @Test - void shouldNotFireMyEventWhenNotCreatedEvent() { - when(event.getEventType()).thenReturn(HandlerEventType.BEFORE_CREATE); - subscriber.handleEvent(event); - - verify(eventBus, never()).post(eventCaptor.capture()); - } - -} From b369fac1e801f5cfe3fccb6cf4ca721636d6e713 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Thu, 2 Apr 2020 13:37:05 +0200 Subject: [PATCH 3/4] rename PluginCenterEvent --- .../{PluginCenterEvent.java => PluginCenterErrorEvent.java} | 2 +- .../src/main/java/sonia/scm/plugin/PluginCenterLoader.java | 2 +- .../test/java/sonia/scm/plugin/PluginCenterLoaderTest.java | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename scm-core/src/main/java/sonia/scm/plugin/{PluginCenterEvent.java => PluginCenterErrorEvent.java} (96%) diff --git a/scm-core/src/main/java/sonia/scm/plugin/PluginCenterEvent.java b/scm-core/src/main/java/sonia/scm/plugin/PluginCenterErrorEvent.java similarity index 96% rename from scm-core/src/main/java/sonia/scm/plugin/PluginCenterEvent.java rename to scm-core/src/main/java/sonia/scm/plugin/PluginCenterErrorEvent.java index fe0896c344..b83a06205d 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/PluginCenterEvent.java +++ b/scm-core/src/main/java/sonia/scm/plugin/PluginCenterErrorEvent.java @@ -27,4 +27,4 @@ package sonia.scm.plugin; import sonia.scm.event.Event; @Event -public class PluginCenterEvent {} +public class PluginCenterErrorEvent {} diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterLoader.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterLoader.java index d48a6468a3..efc967df49 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterLoader.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterLoader.java @@ -61,7 +61,7 @@ class PluginCenterLoader { return mapper.map(pluginCenterDto); } catch (Exception ex) { LOG.error("failed to load plugins from plugin center, returning empty list", ex); - eventBus.post(new PluginCenterEvent()); + eventBus.post(new PluginCenterErrorEvent()); return Collections.emptySet(); } } diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterLoaderTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterLoaderTest.java index d05cbc0107..cc59e42f62 100644 --- a/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterLoaderTest.java +++ b/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterLoaderTest.java @@ -79,11 +79,11 @@ class PluginCenterLoaderTest { } @Test - void shouldThrowExceptionAndFirePluginCenterNotAvailableEvent() throws IOException { + void shouldFirePluginCenterErrorEvent() throws IOException { when(client.get(PLUGIN_URL).request()).thenThrow(new IOException("failed to fetch")); loader.load(PLUGIN_URL); - verify(eventBus).post(any(PluginCenterEvent.class)); + verify(eventBus).post(any(PluginCenterErrorEvent.class)); } } From 490ef643b2eeff5f1a796be1a269fcc74a80f6a8 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Thu, 2 Apr 2020 13:48:00 +0200 Subject: [PATCH 4/4] update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97dff642cb..f29711484a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased +### Added +- Fire various plugin events ([#1088](https://github.com/scm-manager/scm-manager/pull/1088)) + ### Changed - Simplified collapse state management of the secondary navigation ([#1086](https://github.com/scm-manager/scm-manager/pull/1086)