diff options
author | Sébastien Lesaint <sebastien.lesaint@sonarsource.com> | 2017-09-25 16:26:52 +0200 |
---|---|---|
committer | Sébastien Lesaint <sebastien.lesaint@sonarsource.com> | 2017-10-17 15:13:58 +0200 |
commit | 3c8d83b73b9f9af0f1395e2a90186982bff32711 (patch) | |
tree | 528858f3eb52a49add30c13194bfc622c13bbe33 | |
parent | 020273df819300ced5bf0f74436f8c57f4b07950 (diff) | |
download | sonarqube-3c8d83b73b9f9af0f1395e2a90186982bff32711.tar.gz sonarqube-3c8d83b73b9f9af0f1395e2a90186982bff32711.zip |
SONAR-9871 move webhook code to use it in web
as well as we use it in CE report processing
25 files changed, 418 insertions, 220 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java index 181c99bfcbd..590813d7df7 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java @@ -117,11 +117,13 @@ import org.sonar.server.computation.task.projectanalysis.source.SourceHashReposi import org.sonar.server.computation.task.projectanalysis.source.SourceLinesRepositoryImpl; import org.sonar.server.computation.task.projectanalysis.step.ReportComputationSteps; import org.sonar.server.computation.task.projectanalysis.step.SmallChangesetQualityGateSpecialCase; -import org.sonar.server.computation.task.projectanalysis.webhook.WebhookModule; +import org.sonar.server.computation.task.projectanalysis.webhook.WebhookPayloadFactoryImpl; +import org.sonar.server.computation.task.projectanalysis.webhook.WebhookPostTask; import org.sonar.server.computation.task.step.ComputationStepExecutor; import org.sonar.server.computation.task.step.ComputationSteps; import org.sonar.server.computation.taskprocessor.MutableTaskResultHolderImpl; import org.sonar.server.view.index.ViewIndex; +import org.sonar.server.webhook.WebhookModule; public final class ProjectAnalysisTaskContainerPopulator implements ContainerPopulator<TaskContainer> { private static final ReportAnalysisComponentProvider[] NO_REPORT_ANALYSIS_COMPONENT_PROVIDERS = new ReportAnalysisComponentProvider[0]; @@ -271,7 +273,9 @@ public final class ProjectAnalysisTaskContainerPopulator implements ContainerPop SmallChangesetQualityGateSpecialCase.class, // webhooks - WebhookModule.class); + WebhookModule.class, + WebhookPayloadFactoryImpl.class, + WebhookPostTask.class); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPayloadFactory.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPayloadFactory.java index 8c9991db6c2..9f5891ae924 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPayloadFactory.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPayloadFactory.java @@ -20,6 +20,7 @@ package org.sonar.server.computation.task.projectanalysis.webhook; import org.sonar.api.ce.posttask.PostProjectAnalysisTask; +import org.sonar.server.webhook.WebhookPayload; @FunctionalInterface public interface WebhookPayloadFactory { diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPayloadFactoryImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPayloadFactoryImpl.java index 4e95d50026e..4990f9b3fee 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPayloadFactoryImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPayloadFactoryImpl.java @@ -35,6 +35,7 @@ import org.sonar.api.ce.posttask.ScannerContext; import org.sonar.api.platform.Server; import org.sonar.api.utils.System2; import org.sonar.api.utils.text.JsonWriter; +import org.sonar.server.webhook.WebhookPayload; import static java.lang.String.format; import static org.sonar.core.config.WebhookProperties.ANALYSIS_PROPERTY_PREFIX; diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPostTask.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPostTask.java index 7b124b75699..41de76dc710 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPostTask.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPostTask.java @@ -19,82 +19,31 @@ */ package org.sonar.server.computation.task.projectanalysis.webhook; -import com.google.common.collect.Iterables; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; import org.sonar.api.ce.posttask.PostProjectAnalysisTask; import org.sonar.api.config.Configuration; -import org.sonar.api.utils.log.Logger; -import org.sonar.api.utils.log.Loggers; -import org.sonar.core.config.WebhookProperties; -import org.sonar.core.util.stream.MoreCollectors; import org.sonar.server.computation.task.projectanalysis.component.ConfigurationRepository; - -import static java.lang.String.format; -import static org.sonar.core.config.WebhookProperties.MAX_WEBHOOKS_PER_TYPE; +import org.sonar.server.webhook.WebHooks; public class WebhookPostTask implements PostProjectAnalysisTask { - private static final Logger LOGGER = Loggers.get(WebhookPostTask.class); - private final ConfigurationRepository configRepository; private final WebhookPayloadFactory payloadFactory; - private final WebhookCaller caller; - private final WebhookDeliveryStorage deliveryStorage; + private final WebHooks webHooks; - public WebhookPostTask(ConfigurationRepository configRepository, WebhookPayloadFactory payloadFactory, - WebhookCaller caller, WebhookDeliveryStorage deliveryStorage) { + public WebhookPostTask(ConfigurationRepository configRepository, WebhookPayloadFactory payloadFactory, WebHooks webHooks) { this.configRepository = configRepository; this.payloadFactory = payloadFactory; - this.caller = caller; - this.deliveryStorage = deliveryStorage; + this.webHooks = webHooks; } @Override public void finished(ProjectAnalysis analysis) { Configuration config = configRepository.getConfiguration(); - Iterable<String> webhookProps = Iterables.concat( - getWebhookProperties(config, WebhookProperties.GLOBAL_KEY), - getWebhookProperties(config, WebhookProperties.PROJECT_KEY)); - if (!Iterables.isEmpty(webhookProps)) { - process(config, analysis, webhookProps); - deliveryStorage.purge(analysis.getProject().getUuid()); - } - } - - private static List<String> getWebhookProperties(Configuration config, String propertyKey) { - String[] webhookIds = config.getStringArray(propertyKey); - return Arrays.stream(webhookIds) - .map(webhookId -> format("%s.%s", propertyKey, webhookId)) - .limit(MAX_WEBHOOKS_PER_TYPE) - .collect(MoreCollectors.toList(webhookIds.length)); + webHooks.sendProjectAnalysisUpdate( + config, + new WebHooks.Analysis(analysis.getProject().getUuid(), analysis.getCeTask().getId()), + () -> payloadFactory.create(analysis)); } - private void process(Configuration config, ProjectAnalysis analysis, Iterable<String> webhookProperties) { - WebhookPayload payload = payloadFactory.create(analysis); - for (String webhookProp : webhookProperties) { - String name = config.get(format("%s.%s", webhookProp, WebhookProperties.NAME_FIELD)).orElse(null); - String url = config.get(format("%s.%s", webhookProp, WebhookProperties.URL_FIELD)).orElse(null); - // as webhooks are defined as property sets, we can't ensure validity of fields on creation. - if (name != null && url != null) { - Webhook webhook = new Webhook(analysis.getProject().getUuid(), analysis.getCeTask().getId(), name, url); - WebhookDelivery delivery = caller.call(webhook, payload); - log(delivery); - deliveryStorage.persist(delivery); - } - } - } - - private static void log(WebhookDelivery delivery) { - Optional<String> error = delivery.getErrorMessage(); - if (error.isPresent()) { - LOGGER.debug("Failed to send webhook '{}' | url={} | message={}", - delivery.getWebhook().getName(), delivery.getWebhook().getUrl(), error.get()); - } else { - LOGGER.debug("Sent webhook '{}' | url={} | time={}ms | status={}", - delivery.getWebhook().getName(), delivery.getWebhook().getUrl(), delivery.getDurationInMs().orElse(-1), delivery.getHttpStatus().orElse(-1)); - } - } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java index aeed2de2dac..ed55f0e1cb8 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java @@ -221,6 +221,7 @@ import org.sonar.server.util.TypeValidationModule; import org.sonar.server.view.index.ViewIndex; import org.sonar.server.view.index.ViewIndexDefinition; import org.sonar.server.view.index.ViewIndexer; +import org.sonar.server.webhook.WebhookModule; import org.sonar.server.webhook.ws.WebhooksWsModule; import org.sonar.server.ws.DeprecatedPropertiesWsFilter; import org.sonar.server.ws.WebServiceEngine; @@ -536,6 +537,7 @@ public class PlatformLevel4 extends PlatformLevel { RootWsModule.class, // webhooks + WebhookModule.class, WebhooksWsModule.class, // Http Request ID diff --git a/server/sonar-server/src/main/java/org/sonar/server/webhook/WebHooks.java b/server/sonar-server/src/main/java/org/sonar/server/webhook/WebHooks.java new file mode 100644 index 00000000000..980900eec87 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/webhook/WebHooks.java @@ -0,0 +1,65 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.webhook; + +import java.util.Objects; +import java.util.function.Supplier; +import org.sonar.api.config.Configuration; + +import static java.util.Objects.requireNonNull; + +public interface WebHooks { + void sendProjectAnalysisUpdate(Configuration configuration, Analysis analysis, Supplier<WebhookPayload> payloadSupplier); + + final class Analysis { + private final String projectUuid; + private final String ceTaskUuid; + + public Analysis(String projectUuid, String ceTaskUuid) { + this.projectUuid = requireNonNull(projectUuid, "projectUuid can't be null"); + this.ceTaskUuid = requireNonNull(ceTaskUuid, "ceTaskUuid can't be null"); + } + + public String getProjectUuid() { + return projectUuid; + } + + public String getCeTaskUuid() { + return ceTaskUuid; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Analysis analysis = (Analysis) o; + return projectUuid.equals(analysis.projectUuid) && ceTaskUuid.equals(analysis.ceTaskUuid); + } + + @Override + public int hashCode() { + return Objects.hash(projectUuid, ceTaskUuid); + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/webhook/WebHooksImpl.java b/server/sonar-server/src/main/java/org/sonar/server/webhook/WebHooksImpl.java new file mode 100644 index 00000000000..6a3c11a804f --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/webhook/WebHooksImpl.java @@ -0,0 +1,101 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.webhook; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Supplier; +import java.util.stream.Stream; +import org.sonar.api.config.Configuration; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.core.config.WebhookProperties; +import org.sonar.core.util.stream.MoreCollectors; + +import static java.lang.String.format; +import static org.sonar.core.config.WebhookProperties.MAX_WEBHOOKS_PER_TYPE; + +public class WebHooksImpl implements WebHooks { + + private static final Logger LOGGER = Loggers.get(WebHooksImpl.class); + private static final String WEBHOOK_PROPERTY_FORMAT = "%s.%s"; + + private final WebhookCaller caller; + private final WebhookDeliveryStorage deliveryStorage; + + public WebHooksImpl(WebhookCaller caller, WebhookDeliveryStorage deliveryStorage) { + this.caller = caller; + this.deliveryStorage = deliveryStorage; + } + + @Override + public void sendProjectAnalysisUpdate(Configuration configuration, Analysis analysis, Supplier<WebhookPayload> payloadSupplier) { + List<Webhook> webhooks = loadWebhooks(analysis, configuration); + if (webhooks.isEmpty()) { + return; + } + + WebhookPayload payload = payloadSupplier.get(); + webhooks.forEach(webhook -> { + WebhookDelivery delivery = caller.call(webhook, payload); + log(delivery); + deliveryStorage.persist(delivery); + }); + deliveryStorage.purge(analysis.getProjectUuid()); + } + + private List<Webhook> loadWebhooks(Analysis analysis, Configuration config) { + return Stream.concat( + getWebhookProperties(config, WebhookProperties.GLOBAL_KEY).stream(), + getWebhookProperties(config, WebhookProperties.PROJECT_KEY).stream()) + .map( + webHookProperty -> { + String name = config.get(format(WEBHOOK_PROPERTY_FORMAT, webHookProperty, WebhookProperties.NAME_FIELD)).orElse(null); + String url = config.get(format(WEBHOOK_PROPERTY_FORMAT, webHookProperty, WebhookProperties.URL_FIELD)).orElse(null); + if (name != null && url != null) { + return new Webhook(analysis.getProjectUuid(), analysis.getCeTaskUuid(), name, url); + } + return null; + }) + .filter(Objects::nonNull) + .collect(MoreCollectors.toList()); + } + + private static List<String> getWebhookProperties(Configuration config, String propertyKey) { + String[] webhookIds = config.getStringArray(propertyKey); + return Arrays.stream(webhookIds) + .map(webhookId -> format(WEBHOOK_PROPERTY_FORMAT, propertyKey, webhookId)) + .limit(MAX_WEBHOOKS_PER_TYPE) + .collect(MoreCollectors.toList(webhookIds.length)); + } + + private static void log(WebhookDelivery delivery) { + Optional<String> error = delivery.getErrorMessage(); + if (error.isPresent()) { + LOGGER.debug("Failed to send webhook '{}' | url={} | message={}", + delivery.getWebhook().getName(), delivery.getWebhook().getUrl(), error.get()); + } else { + LOGGER.debug("Sent webhook '{}' | url={} | time={}ms | status={}", + delivery.getWebhook().getName(), delivery.getWebhook().getUrl(), delivery.getDurationInMs().orElse(-1), delivery.getHttpStatus().orElse(-1)); + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/Webhook.java b/server/sonar-server/src/main/java/org/sonar/server/webhook/Webhook.java index 03318fc7ba0..3a9f1a3b253 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/Webhook.java +++ b/server/sonar-server/src/main/java/org/sonar/server/webhook/Webhook.java @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.server.computation.task.projectanalysis.webhook; +package org.sonar.server.webhook; import javax.annotation.concurrent.Immutable; diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookCaller.java b/server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookCaller.java index ef71dfb8ec3..2edca12d26c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookCaller.java +++ b/server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookCaller.java @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.server.computation.task.projectanalysis.webhook; +package org.sonar.server.webhook; public interface WebhookCaller { diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookCallerImpl.java b/server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookCallerImpl.java index 3c14782e586..bf31ffdde74 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookCallerImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookCallerImpl.java @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.server.computation.task.projectanalysis.webhook; +package org.sonar.server.webhook; import java.io.IOException; import okhttp3.Credentials; diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookDelivery.java b/server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookDelivery.java index 748ad53db65..2210aa4ddf0 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookDelivery.java +++ b/server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookDelivery.java @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.server.computation.task.projectanalysis.webhook; +package org.sonar.server.webhook; import java.util.Optional; import javax.annotation.Nullable; diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookDeliveryStorage.java b/server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookDeliveryStorage.java index 7a36e32970e..cd2dbe6e315 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookDeliveryStorage.java +++ b/server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookDeliveryStorage.java @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.server.computation.task.projectanalysis.webhook; +package org.sonar.server.webhook; import com.google.common.base.Throwables; import org.sonar.api.ce.ComputeEngineSide; diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookModule.java b/server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookModule.java index 2d377d803ba..6a5da0c597b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookModule.java +++ b/server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookModule.java @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.server.computation.task.projectanalysis.webhook; +package org.sonar.server.webhook; import org.sonar.core.platform.Module; @@ -27,7 +27,6 @@ public class WebhookModule extends Module { add( WebhookCallerImpl.class, WebhookDeliveryStorage.class, - WebhookPayloadFactoryImpl.class, - WebhookPostTask.class); + WebHooksImpl.class); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPayload.java b/server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookPayload.java index c08e99f68fa..fe46db90bf9 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPayload.java +++ b/server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookPayload.java @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.server.computation.task.projectanalysis.webhook; +package org.sonar.server.webhook; import javax.annotation.concurrent.Immutable; diff --git a/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/package-info.java b/server/sonar-server/src/main/java/org/sonar/server/webhook/package-info.java index 0e5fed56bd2..c05f978de48 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/package-info.java +++ b/server/sonar-server/src/main/java/org/sonar/server/webhook/package-info.java @@ -18,7 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @ParametersAreNonnullByDefault -package org.sonar.server.webhook.ws; +package org.sonar.server.webhook; import javax.annotation.ParametersAreNonnullByDefault; diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPayloadFactoryImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPayloadFactoryImplTest.java index 9a51d6d0c84..e4c409f2019 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPayloadFactoryImplTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPayloadFactoryImplTest.java @@ -35,6 +35,7 @@ import org.sonar.api.ce.posttask.ScannerContext; import org.sonar.api.platform.Server; import org.sonar.api.utils.System2; import org.sonar.server.computation.task.projectanalysis.api.posttask.BranchImpl; +import org.sonar.server.webhook.WebhookPayload; import static java.util.Collections.emptyMap; import static org.assertj.core.api.Assertions.assertThat; diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPostTaskTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPostTaskTest.java index 044825700f1..600d7c11335 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPostTaskTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPostTaskTest.java @@ -19,127 +19,73 @@ */ package org.sonar.server.computation.task.projectanalysis.webhook; -import java.io.IOException; import java.util.Date; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import org.junit.Rule; +import java.util.Random; +import java.util.function.Supplier; +import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.sonar.api.ce.posttask.CeTask; +import org.sonar.api.ce.posttask.PostProjectAnalysisTask; import org.sonar.api.ce.posttask.PostProjectAnalysisTaskTester; -import org.sonar.api.config.internal.MapSettings; -import org.sonar.api.utils.log.LogTester; -import org.sonar.api.utils.log.LoggerLevel; +import org.sonar.api.ce.posttask.Project; +import org.sonar.api.config.Configuration; import org.sonar.server.computation.task.projectanalysis.component.ConfigurationRepository; -import org.sonar.server.computation.task.projectanalysis.component.TestSettingsRepository; +import org.sonar.server.webhook.WebHooks; +import org.sonar.server.webhook.WebhookPayload; +import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.same; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; import static org.sonar.api.ce.posttask.PostProjectAnalysisTaskTester.newCeTaskBuilder; import static org.sonar.api.ce.posttask.PostProjectAnalysisTaskTester.newProjectBuilder; import static org.sonar.api.ce.posttask.PostProjectAnalysisTaskTester.newScannerContextBuilder; public class WebhookPostTaskTest { - private static final long NOW = 1_500_000_000_000L; - private static final String PROJECT_UUID = "P1_UUID"; - - @Rule - public LogTester logTester = new LogTester().setLevel(LoggerLevel.DEBUG); - - private final MapSettings settings = new MapSettings(); - private final TestWebhookCaller caller = new TestWebhookCaller(); - private final WebhookPayloadFactory payloadFactory = new TestWebhookPayloadFactory(); - private final WebhookDeliveryStorage deliveryStorage = mock(WebhookDeliveryStorage.class); - - @Test - public void do_nothing_if_no_webhooks() { - execute(); - - assertThat(caller.countSent()).isEqualTo(0); - assertThat(logTester.logs(LoggerLevel.DEBUG)).isEmpty(); - verifyZeroInteractions(deliveryStorage); - } - - @Test - public void send_global_webhooks() { - settings.setProperty("sonar.webhooks.global", "1,2"); - settings.setProperty("sonar.webhooks.global.1.name", "First"); - settings.setProperty("sonar.webhooks.global.1.url", "http://url1"); - settings.setProperty("sonar.webhooks.global.2.name", "Second"); - settings.setProperty("sonar.webhooks.global.2.url", "http://url2"); - caller.enqueueSuccess(NOW, 200, 1_234); - caller.enqueueFailure(NOW, new IOException("Fail to connect")); - - execute(); - - assertThat(caller.countSent()).isEqualTo(2); - assertThat(logTester.logs(LoggerLevel.DEBUG)).contains("Sent webhook 'First' | url=http://url1 | time=1234ms | status=200"); - assertThat(logTester.logs(LoggerLevel.DEBUG)).contains("Failed to send webhook 'Second' | url=http://url2 | message=Fail to connect"); - verify(deliveryStorage, times(2)).persist(any(WebhookDelivery.class)); - verify(deliveryStorage).purge(PROJECT_UUID); + private final Configuration configuration = mock(Configuration.class); + private final WebhookPayload webhookPayload = mock(WebhookPayload.class); + private final WebhookPayloadFactory payloadFactory = mock(WebhookPayloadFactory.class); + private final WebHooks webHooks = mock(WebHooks.class); + private final ConfigurationRepository configurationRepository = mock(ConfigurationRepository.class); + private WebhookPostTask underTest = new WebhookPostTask(configurationRepository, payloadFactory, webHooks); + + @Before + public void wireMocks() throws Exception { + when(payloadFactory.create(any(PostProjectAnalysisTask.ProjectAnalysis.class))).thenReturn(webhookPayload); + when(configurationRepository.getConfiguration()).thenReturn(configuration); } @Test - public void send_project_webhooks() { - settings.setProperty("sonar.webhooks.project", "1"); - settings.setProperty("sonar.webhooks.project.1.name", "First"); - settings.setProperty("sonar.webhooks.project.1.url", "http://url1"); - caller.enqueueSuccess(NOW, 200, 1_234); - - execute(); - - assertThat(caller.countSent()).isEqualTo(1); - assertThat(logTester.logs(LoggerLevel.DEBUG)).contains("Sent webhook 'First' | url=http://url1 | time=1234ms | status=200"); - verify(deliveryStorage).persist(any(WebhookDelivery.class)); - verify(deliveryStorage).purge(PROJECT_UUID); - } - - @Test - public void process_only_the_10_first_global_webhooks() { - testMaxWebhooks("sonar.webhooks.global"); - } - - @Test - public void process_only_the_10_first_project_webhooks() { - testMaxWebhooks("sonar.webhooks.project"); - } + public void call_webhooks() { + Project project = newProjectBuilder() + .setUuid(randomAlphanumeric(3)) + .setKey(randomAlphanumeric(4)) + .setName(randomAlphanumeric(5)) + .build(); + CeTask ceTask = newCeTaskBuilder() + .setStatus(CeTask.Status.values()[new Random().nextInt(CeTask.Status.values().length)]) + .setId(randomAlphanumeric(6)) + .build(); + + PostProjectAnalysisTask.ProjectAnalysis projectAnalysis = PostProjectAnalysisTaskTester.of(underTest) + .at(new Date()) + .withCeTask(ceTask) + .withProject(project) + .withScannerContext(newScannerContextBuilder().build()) + .execute(); - private void testMaxWebhooks(String property) { - IntStream.range(1, 15) - .forEach(i -> { - settings.setProperty(property + "." + i + ".name", "First"); - settings.setProperty(property + "." + i + ".url", "http://url"); - caller.enqueueSuccess(NOW, 200, 1_234); - }); - settings.setProperty(property, IntStream.range(1, 15).mapToObj(String::valueOf).collect(Collectors.joining(","))); + ArgumentCaptor<Supplier> supplierCaptor = ArgumentCaptor.forClass(Supplier.class); + verify(webHooks).sendProjectAnalysisUpdate(same(configuration), eq(new WebHooks.Analysis(project.getUuid(), ceTask.getId())), supplierCaptor.capture()); - execute(); + assertThat(supplierCaptor.getValue().get()).isSameAs(webhookPayload); - assertThat(caller.countSent()).isEqualTo(10); - assertThat(logTester.logs(LoggerLevel.DEBUG).stream().filter(log -> log.contains("Sent"))).hasSize(10); + verify(payloadFactory).create(same(projectAnalysis)); } - private void execute() { - ConfigurationRepository settingsRepository = new TestSettingsRepository(settings.asConfig()); - WebhookPostTask task = new WebhookPostTask(settingsRepository, payloadFactory, caller, deliveryStorage); - - PostProjectAnalysisTaskTester.of(task) - .at(new Date()) - .withCeTask(newCeTaskBuilder() - .setStatus(CeTask.Status.SUCCESS) - .setId("#1") - .build()) - .withProject(newProjectBuilder() - .setUuid(PROJECT_UUID) - .setKey("P1") - .setName("Project One") - .build()) - .withScannerContext(newScannerContextBuilder().build()) - .execute(); - } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/webhook/TestWebhookCaller.java b/server/sonar-server/src/test/java/org/sonar/server/webhook/TestWebhookCaller.java index b63f479bf3d..ebbb42f83f5 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/webhook/TestWebhookCaller.java +++ b/server/sonar-server/src/test/java/org/sonar/server/webhook/TestWebhookCaller.java @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.server.computation.task.projectanalysis.webhook; +package org.sonar.server.webhook; import java.util.LinkedList; import java.util.Queue; diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/webhook/TestWebhookPayloadFactory.java b/server/sonar-server/src/test/java/org/sonar/server/webhook/TestWebhookPayloadFactory.java index 11ef3fef67b..603b8ee3861 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/webhook/TestWebhookPayloadFactory.java +++ b/server/sonar-server/src/test/java/org/sonar/server/webhook/TestWebhookPayloadFactory.java @@ -17,9 +17,10 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.server.computation.task.projectanalysis.webhook; +package org.sonar.server.webhook; import org.sonar.api.ce.posttask.PostProjectAnalysisTask; +import org.sonar.server.computation.task.projectanalysis.webhook.WebhookPayloadFactory; public class TestWebhookPayloadFactory implements WebhookPayloadFactory { diff --git a/server/sonar-server/src/test/java/org/sonar/server/webhook/WebHooksImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/webhook/WebHooksImplTest.java new file mode 100644 index 00000000000..217a1653f59 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/webhook/WebHooksImplTest.java @@ -0,0 +1,120 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.webhook; + +import java.io.IOException; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.config.internal.MapSettings; +import org.sonar.api.utils.log.LogTester; +import org.sonar.api.utils.log.LoggerLevel; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +public class WebHooksImplTest { + + private static final long NOW = 1_500_000_000_000L; + private static final String PROJECT_UUID = "P1_UUID"; + + @Rule + public LogTester logTester = new LogTester(); + + private final MapSettings settings = new MapSettings(); + private final TestWebhookCaller caller = new TestWebhookCaller(); + private final WebhookDeliveryStorage deliveryStorage = mock(WebhookDeliveryStorage.class); + private final WebhookPayload mock = mock(WebhookPayload.class); + private final WebHooksImpl underTest = new WebHooksImpl(caller, deliveryStorage); + + @Test + public void do_nothing_if_no_webhooks() { + underTest.sendProjectAnalysisUpdate(settings.asConfig(), new WebHooks.Analysis(PROJECT_UUID, "#1"), () -> mock); + + assertThat(caller.countSent()).isEqualTo(0); + assertThat(logTester.logs(LoggerLevel.DEBUG)).isEmpty(); + verifyZeroInteractions(deliveryStorage); + } + + @Test + public void send_global_webhooks() { + settings.setProperty("sonar.webhooks.global", "1,2"); + settings.setProperty("sonar.webhooks.global.1.name", "First"); + settings.setProperty("sonar.webhooks.global.1.url", "http://url1"); + settings.setProperty("sonar.webhooks.global.2.name", "Second"); + settings.setProperty("sonar.webhooks.global.2.url", "http://url2"); + caller.enqueueSuccess(NOW, 200, 1_234); + caller.enqueueFailure(NOW, new IOException("Fail to connect")); + + underTest.sendProjectAnalysisUpdate(settings.asConfig(), new WebHooks.Analysis(PROJECT_UUID, "#1"), () -> mock); + + assertThat(caller.countSent()).isEqualTo(2); + assertThat(logTester.logs(LoggerLevel.DEBUG)).contains("Sent webhook 'First' | url=http://url1 | time=1234ms | status=200"); + assertThat(logTester.logs(LoggerLevel.DEBUG)).contains("Failed to send webhook 'Second' | url=http://url2 | message=Fail to connect"); + verify(deliveryStorage, times(2)).persist(any(WebhookDelivery.class)); + verify(deliveryStorage).purge(PROJECT_UUID); + } + + @Test + public void send_project_webhooks() { + settings.setProperty("sonar.webhooks.project", "1"); + settings.setProperty("sonar.webhooks.project.1.name", "First"); + settings.setProperty("sonar.webhooks.project.1.url", "http://url1"); + caller.enqueueSuccess(NOW, 200, 1_234); + + underTest.sendProjectAnalysisUpdate(settings.asConfig(), new WebHooks.Analysis(PROJECT_UUID, "#1"), () -> mock); + + assertThat(caller.countSent()).isEqualTo(1); + assertThat(logTester.logs(LoggerLevel.DEBUG)).contains("Sent webhook 'First' | url=http://url1 | time=1234ms | status=200"); + verify(deliveryStorage).persist(any(WebhookDelivery.class)); + verify(deliveryStorage).purge(PROJECT_UUID); + } + + @Test + public void process_only_the_10_first_global_webhooks() { + testMaxWebhooks("sonar.webhooks.global"); + } + + @Test + public void process_only_the_10_first_project_webhooks() { + testMaxWebhooks("sonar.webhooks.project"); + } + + private void testMaxWebhooks(String property) { + IntStream.range(1, 15) + .forEach(i -> { + settings.setProperty(property + "." + i + ".name", "First"); + settings.setProperty(property + "." + i + ".url", "http://url"); + caller.enqueueSuccess(NOW, 200, 1_234); + }); + settings.setProperty(property, IntStream.range(1, 15).mapToObj(String::valueOf).collect(Collectors.joining(","))); + + underTest.sendProjectAnalysisUpdate(settings.asConfig(), new WebHooks.Analysis(PROJECT_UUID, "#1"), () -> mock); + + assertThat(caller.countSent()).isEqualTo(10); + assertThat(logTester.logs(LoggerLevel.DEBUG).stream().filter(log -> log.contains("Sent"))).hasSize(10); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookCallerImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookCallerImplTest.java index 155ec647963..40712c0f9fc 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookCallerImplTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookCallerImplTest.java @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.server.computation.task.projectanalysis.webhook; +package org.sonar.server.webhook; import okhttp3.Credentials; import okhttp3.HttpUrl; diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookDeliveryStorageTest.java b/server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookDeliveryStorageTest.java index 49faead0747..d64a8f333c7 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookDeliveryStorageTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookDeliveryStorageTest.java @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.server.computation.task.projectanalysis.webhook; +package org.sonar.server.webhook; import java.io.IOException; import org.junit.Rule; @@ -28,6 +28,10 @@ import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.DbTester; import org.sonar.db.webhook.WebhookDeliveryDto; +import org.sonar.server.webhook.Webhook; +import org.sonar.server.webhook.WebhookDelivery; +import org.sonar.server.webhook.WebhookDeliveryStorage; +import org.sonar.server.webhook.WebhookPayload; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookDeliveryTest.java b/server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookDeliveryTest.java index ebcf94541b9..e4662816b23 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookDeliveryTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookDeliveryTest.java @@ -17,10 +17,13 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.server.computation.task.projectanalysis.webhook; +package org.sonar.server.webhook; import java.io.IOException; import org.junit.Test; +import org.sonar.server.webhook.Webhook; +import org.sonar.server.webhook.WebhookDelivery; +import org.sonar.server.webhook.WebhookPayload; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookModuleTest.java index c706e9793a3..5a80a0b7459 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookModuleTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookModuleTest.java @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.server.computation.task.projectanalysis.webhook; +package org.sonar.server.webhook; import org.junit.Rule; import org.junit.Test; @@ -40,6 +40,6 @@ public class WebhookModuleTest { underTest.configure(container); - assertThat(container.size()).isEqualTo(4 + COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER); + assertThat(container.size()).isEqualTo(3 + COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER); } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/ce/posttask/PostProjectAnalysisTaskTester.java b/sonar-plugin-api/src/main/java/org/sonar/api/ce/posttask/PostProjectAnalysisTaskTester.java index 2c9e358071b..2ab390b0cff 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/ce/posttask/PostProjectAnalysisTaskTester.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/ce/posttask/PostProjectAnalysisTaskTester.java @@ -177,60 +177,61 @@ public class PostProjectAnalysisTaskTester { return this; } - public void execute() { + public PostProjectAnalysisTask.ProjectAnalysis execute() { requireNonNull(ceTask, CE_TASK_CAN_NOT_BE_NULL); requireNonNull(project, PROJECT_CAN_NOT_BE_NULL); requireNonNull(date, DATE_CAN_NOT_BE_NULL); - this.underTest.finished( - new PostProjectAnalysisTask.ProjectAnalysis() { - @Override - public ScannerContext getScannerContext() { - return scannerContext; - } - - @Override - public CeTask getCeTask() { - return ceTask; - } - - @Override - public Project getProject() { - return project; - } - - @Override - public Optional<Branch> getBranch() { - return Optional.ofNullable(branch); - } - - @Override - public QualityGate getQualityGate() { - return qualityGate; - } - - @Override - public Date getDate() { - return date; - } - - @Override - public Optional<Date> getAnalysisDate() { - return Optional.of(date); - } - - @Override - public String toString() { - return "ProjectAnalysis{" + + PostProjectAnalysisTask.ProjectAnalysis projectAnalysis = new PostProjectAnalysisTask.ProjectAnalysis() { + @Override + public ScannerContext getScannerContext() { + return scannerContext; + } + + @Override + public CeTask getCeTask() { + return ceTask; + } + + @Override + public Project getProject() { + return project; + } + + @Override + public Optional<Branch> getBranch() { + return Optional.ofNullable(branch); + } + + @Override + public QualityGate getQualityGate() { + return qualityGate; + } + + @Override + public Date getDate() { + return date; + } + + @Override + public Optional<Date> getAnalysisDate() { + return Optional.of(date); + } + + @Override + public String toString() { + return "ProjectAnalysis{" + "ceTask=" + ceTask + ", project=" + project + ", date=" + date.getTime() + ", analysisDate=" + date.getTime() + ", qualityGate=" + qualityGate + '}'; - } - }); + } + }; + this.underTest.finished(projectAnalysis); + return projectAnalysis; } public static final class CeTaskBuilder { |