compile 'com.google.guava:guava'
compile 'org.slf4j:slf4j-api'
- compile project(':sonar-core')
+ compile 'com.squareup.okhttp3:okhttp'
compile project(':server:sonar-db-dao')
+ compile project(':sonar-core')
+ compile project(':sonar-ws')
compileOnly project(path: ':sonar-plugin-api')
compileOnly project(path: ':server:sonar-process')
testCompile 'com.google.code.findbugs:jsr305'
testCompile 'com.h2database:h2'
+ testCompile 'com.squareup.okhttp3:mockwebserver'
testCompile 'com.tngtech.java:junit-dataprovider'
testCompile 'junit:junit'
testCompile 'org.assertj:assertj-core'
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.async;
+
+public interface AsyncExecution {
+ /**
+ * Add the specified {@link Runnable} in queue for asynchronous processing.
+ *
+ * This method returns instantly and {@code r} is executed in another thread.
+ *
+ * @throws NullPointerException if r is {@code null}
+ */
+ void addToQueue(Runnable r);
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.async;
+
+public interface AsyncExecutionExecutorService {
+ void addToQueue(Runnable r);
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.async;
+
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.server.util.AbstractStoppableExecutorService;
+
+import static java.util.concurrent.TimeUnit.MINUTES;
+
+public class AsyncExecutionExecutorServiceImpl
+ extends AbstractStoppableExecutorService<ThreadPoolExecutor>
+ implements AsyncExecutionExecutorService, AsyncExecutionMonitoring {
+ private static final Logger LOG = Loggers.get(AsyncExecutionExecutorServiceImpl.class);
+
+ private static final int MAX_THREAD_COUNT = 10;
+ private static final int UNLIMITED_QUEUE = Integer.MAX_VALUE;
+ private static final long KEEP_ALIVE_TIME_IN_MINUTES = 5L;
+
+ public AsyncExecutionExecutorServiceImpl() {
+ super(createDelegate());
+ }
+
+ private static ThreadPoolExecutor createDelegate() {
+ ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
+ MAX_THREAD_COUNT, MAX_THREAD_COUNT,
+ KEEP_ALIVE_TIME_IN_MINUTES, MINUTES,
+ new LinkedBlockingQueue<>(UNLIMITED_QUEUE),
+ new ThreadFactoryBuilder()
+ .setDaemon(false)
+ .setNameFormat("SQ_async-%d")
+ .setUncaughtExceptionHandler(((t, e) -> LOG.error("Thread " + t + " failed unexpectedly", e)))
+ .build());
+ threadPoolExecutor.allowCoreThreadTimeOut(true);
+ return threadPoolExecutor;
+ }
+
+ @Override
+ public void addToQueue(Runnable r) {
+ this.submit(r);
+ }
+
+ @Override
+ public int getQueueSize() {
+ return delegate.getQueue().size();
+ }
+
+ @Override
+ public int getWorkerCount() {
+ return delegate.getPoolSize();
+ }
+
+ @Override
+ public int getLargestWorkerCount() {
+ return delegate.getLargestPoolSize();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.async;
+
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+
+import static java.util.Objects.requireNonNull;
+
+public class AsyncExecutionImpl implements AsyncExecution {
+ private static final Logger LOG = Loggers.get(AsyncExecutionImpl.class);
+ private final AsyncExecutionExecutorService executorService;
+
+ public AsyncExecutionImpl(AsyncExecutionExecutorService executorService) {
+ this.executorService = executorService;
+ }
+
+ @Override
+ public void addToQueue(Runnable r) {
+ requireNonNull(r);
+ executorService.addToQueue(() -> {
+ try {
+ r.run();
+ } catch (Exception e) {
+ LOG.error("Asynchronous task failed", e);
+ }
+ });
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.async;
+
+public interface AsyncExecutionMBean {
+
+ String OBJECT_NAME = "SonarQube:name=AsyncExecution";
+
+ long getQueueSize();
+
+ long getWorkerCount();
+
+ long getLargestWorkerCount();
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.async;
+
+import org.picocontainer.Startable;
+import org.sonar.process.Jmx;
+
+public class AsyncExecutionMBeanImpl implements AsyncExecutionMBean, Startable {
+
+ private final AsyncExecutionMonitoring asyncExecutionMonitoring;
+
+ public AsyncExecutionMBeanImpl(AsyncExecutionMonitoring asyncExecutionMonitoring) {
+ this.asyncExecutionMonitoring = asyncExecutionMonitoring;
+ }
+
+ @Override
+ public void start() {
+ Jmx.register(OBJECT_NAME, this);
+ }
+
+ @Override
+ public void stop() {
+ Jmx.unregister(OBJECT_NAME);
+ }
+
+ @Override
+ public long getQueueSize() {
+ return asyncExecutionMonitoring.getQueueSize();
+ }
+
+ @Override
+ public long getWorkerCount() {
+ return asyncExecutionMonitoring.getWorkerCount();
+ }
+
+ @Override
+ public long getLargestWorkerCount() {
+ return asyncExecutionMonitoring.getLargestWorkerCount();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.async;
+
+import org.sonar.core.platform.Module;
+
+public class AsyncExecutionModule extends Module {
+ @Override
+ protected void configureModule() {
+ add(
+ AsyncExecutionMBeanImpl.class,
+ AsyncExecutionExecutorServiceImpl.class,
+ AsyncExecutionImpl.class);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.async;
+
+public interface AsyncExecutionMonitoring {
+ int getQueueSize();
+
+ int getWorkerCount();
+
+ int getLargestWorkerCount();
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.async;
+
+import javax.annotation.ParametersAreNonnullByDefault;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.project;
+
+import java.util.Objects;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+import org.sonar.db.component.ComponentDto;
+
+@Immutable
+public class Project {
+
+ private final String uuid;
+ private final String key;
+ private final String name;
+ private final String description;
+
+ public Project(String uuid, String key, String name) {
+ this(uuid, key, name, null);
+ }
+
+ public Project(String uuid, String key, String name, @Nullable String description) {
+ this.uuid = uuid;
+ this.key = key;
+ this.name = name;
+ this.description = description;
+ }
+
+ public static Project from(ComponentDto project) {
+ return new Project(project.uuid(), project.getDbKey(), project.name(), project.description());
+ }
+
+ /**
+ * Always links to a row that exists in database.
+ */
+ public String getUuid() {
+ return uuid;
+ }
+
+ /**
+ * Always links to a row that exists in database.
+ */
+ public String getKey() {
+ return key;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Project project = (Project) o;
+ return uuid.equals(project.uuid)
+ && key.equals(project.key)
+ && name.equals(project.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(uuid, key, name);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("Project{");
+ sb.append("uuid='").append(uuid).append('\'');
+ sb.append(", key='").append(key).append('\'');
+ sb.append(", name='").append(name).append('\'');
+ sb.append(", description=").append(toString(this.description));
+ sb.append('}');
+ return sb.toString();
+ }
+
+ private static String toString(@Nullable String s) {
+ if (s == null) {
+ return null;
+ }
+ return '\'' + s + '\'';
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.project;
+
+import javax.annotation.ParametersAreNonnullByDefault;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.qualitygate;
+
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Stream;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+import org.sonar.db.qualitygate.QualityGateConditionDto;
+
+import static com.google.common.base.Strings.emptyToNull;
+import static java.util.Objects.requireNonNull;
+
+@Immutable
+public class Condition {
+
+ private final String metricKey;
+ private final Operator operator;
+ @CheckForNull
+ private final String warningThreshold;
+ @CheckForNull
+ private final String errorThreshold;
+ private final boolean onLeakPeriod;
+
+ public Condition(String metricKey, Operator operator,
+ @Nullable String errorThreshold, @Nullable String warningThreshold,
+ boolean onLeakPeriod) {
+ this.metricKey = requireNonNull(metricKey, "metricKey can't be null");
+ this.operator = requireNonNull(operator, "operator can't be null");
+ this.onLeakPeriod = onLeakPeriod;
+ this.errorThreshold = emptyToNull(errorThreshold);
+ this.warningThreshold = emptyToNull(warningThreshold);
+ }
+
+ public String getMetricKey() {
+ return metricKey;
+ }
+
+ public boolean isOnLeakPeriod() {
+ return onLeakPeriod;
+ }
+
+ public Operator getOperator() {
+ return operator;
+ }
+
+ public Optional<String> getWarningThreshold() {
+ return Optional.ofNullable(warningThreshold);
+ }
+
+ public Optional<String> getErrorThreshold() {
+ return Optional.ofNullable(errorThreshold);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Condition condition = (Condition) o;
+ return onLeakPeriod == condition.onLeakPeriod &&
+ Objects.equals(metricKey, condition.metricKey) &&
+ operator == condition.operator &&
+ Objects.equals(warningThreshold, condition.warningThreshold) &&
+ Objects.equals(errorThreshold, condition.errorThreshold);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(metricKey, operator, warningThreshold, errorThreshold, onLeakPeriod);
+ }
+
+ @Override
+ public String toString() {
+ return "Condition{" +
+ "metricKey='" + metricKey + '\'' +
+ ", operator=" + operator +
+ ", warningThreshold=" + toString(warningThreshold) +
+ ", errorThreshold=" + toString(errorThreshold) +
+ ", onLeakPeriod=" + onLeakPeriod +
+ '}';
+ }
+
+ private static String toString(@Nullable String errorThreshold) {
+ if (errorThreshold == null) {
+ return null;
+ }
+ return '\'' + errorThreshold + '\'';
+ }
+
+ public enum Operator {
+ EQUALS(QualityGateConditionDto.OPERATOR_EQUALS),
+ NOT_EQUALS(QualityGateConditionDto.OPERATOR_NOT_EQUALS),
+ GREATER_THAN(QualityGateConditionDto.OPERATOR_GREATER_THAN),
+ LESS_THAN(QualityGateConditionDto.OPERATOR_LESS_THAN);
+
+ private final String dbValue;
+
+ Operator(String dbValue) {
+ this.dbValue = dbValue;
+ }
+
+ public String getDbValue() {
+ return dbValue;
+ }
+
+ public static Operator fromDbValue(String s) {
+ return Stream.of(values())
+ .filter(o -> o.getDbValue().equals(s))
+ .findFirst()
+ .orElseThrow(() -> new IllegalArgumentException("Unsupported operator db value: " + s));
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.qualitygate;
+
+import java.util.Objects;
+import java.util.Optional;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+
+import static java.util.Objects.requireNonNull;
+
+@Immutable
+public class EvaluatedCondition {
+ private final Condition condition;
+ private final EvaluationStatus status;
+ @Nullable
+ private final String value;
+
+ public EvaluatedCondition(Condition condition, EvaluationStatus status, @Nullable String value) {
+ this.condition = requireNonNull(condition, "condition can't be null");
+ this.status = requireNonNull(status, "status can't be null");
+ this.value = value;
+ }
+
+ public Condition getCondition() {
+ return condition;
+ }
+
+ public EvaluationStatus getStatus() {
+ return status;
+ }
+
+ public Optional<String> getValue() {
+ return Optional.ofNullable(value);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ EvaluatedCondition that = (EvaluatedCondition) o;
+ return Objects.equals(condition, that.condition) &&
+ status == that.status &&
+ Objects.equals(value, that.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(condition, status, value);
+ }
+
+ @Override
+ public String toString() {
+ return "EvaluatedCondition{" +
+ "condition=" + condition +
+ ", status=" + status +
+ ", value=" + (value == null ? null : ('\'' + value + '\'')) +
+ '}';
+ }
+
+ /**
+ * Quality gate condition evaluation status.
+ */
+ public enum EvaluationStatus {
+ /**
+ * No measure found or measure had no value. The condition has not been evaluated and therefor ignored in
+ * the computation of the Quality Gate status.
+ */
+ NO_VALUE,
+ /**
+ * Condition evaluated as OK, neither error nor warning thresholds have been reached.
+ */
+ OK,
+ /**
+ * Condition evaluated as WARN, only warning thresholds has been reached.
+ */
+ WARN,
+ /**
+ * Condition evaluated as ERROR, error thresholds has been reached (and most likely warning thresholds too).
+ */
+ ERROR
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.qualitygate;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+import org.sonar.api.measures.Metric;
+import org.sonar.server.qualitygate.EvaluatedCondition.EvaluationStatus;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+@Immutable
+public class EvaluatedQualityGate {
+ private final QualityGate qualityGate;
+ private final Metric.Level status;
+ private final Set<EvaluatedCondition> evaluatedConditions;
+ private final boolean ignoredConditionsOnSmallChangeset;
+
+ private EvaluatedQualityGate(QualityGate qualityGate, Metric.Level status, Set<EvaluatedCondition> evaluatedConditions, boolean ignoredConditionsOnSmallChangeset) {
+ this.qualityGate = requireNonNull(qualityGate, "qualityGate can't be null");
+ this.status = requireNonNull(status, "status can't be null");
+ this.evaluatedConditions = evaluatedConditions;
+ this.ignoredConditionsOnSmallChangeset = ignoredConditionsOnSmallChangeset;
+ }
+
+ public QualityGate getQualityGate() {
+ return qualityGate;
+ }
+
+ public Metric.Level getStatus() {
+ return status;
+ }
+
+ public Set<EvaluatedCondition> getEvaluatedConditions() {
+ return evaluatedConditions;
+ }
+
+ public boolean hasIgnoredConditionsOnSmallChangeset() {
+ return ignoredConditionsOnSmallChangeset;
+ }
+
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ EvaluatedQualityGate that = (EvaluatedQualityGate) o;
+ return Objects.equals(qualityGate, that.qualityGate) &&
+ status == that.status &&
+ Objects.equals(evaluatedConditions, that.evaluatedConditions);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(qualityGate, status, evaluatedConditions);
+ }
+
+ @Override
+ public String toString() {
+ return "EvaluatedQualityGate{" +
+ "qualityGate=" + qualityGate +
+ ", status=" + status +
+ ", evaluatedConditions=" + evaluatedConditions +
+ '}';
+ }
+
+ public static final class Builder {
+ private QualityGate qualityGate;
+ private Metric.Level status;
+ private final Map<Condition, EvaluatedCondition> evaluatedConditions = new HashMap<>();
+ private boolean ignoredConditionsOnSmallChangeset = false;
+
+ private Builder() {
+ // use static factory method
+ }
+
+ public Builder setQualityGate(QualityGate qualityGate) {
+ this.qualityGate = qualityGate;
+ return this;
+ }
+
+ public Builder setStatus(Metric.Level status) {
+ this.status = status;
+ return this;
+ }
+
+ public Builder setIgnoredConditionsOnSmallChangeset(boolean b) {
+ this.ignoredConditionsOnSmallChangeset = b;
+ return this;
+ }
+
+ public Builder addCondition(Condition condition, EvaluationStatus status, @Nullable String value) {
+ evaluatedConditions.put(condition, new EvaluatedCondition(condition, status, value));
+ return this;
+ }
+
+ public Builder addCondition(EvaluatedCondition c) {
+ evaluatedConditions.put(c.getCondition(), c);
+ return this;
+ }
+
+ public Set<EvaluatedCondition> getEvaluatedConditions() {
+ return ImmutableSet.copyOf(evaluatedConditions.values());
+ }
+
+ public EvaluatedQualityGate build() {
+ return new EvaluatedQualityGate(
+ this.qualityGate,
+ this.status,
+ checkEvaluatedConditions(qualityGate, evaluatedConditions),
+ ignoredConditionsOnSmallChangeset);
+ }
+
+ private static Set<EvaluatedCondition> checkEvaluatedConditions(QualityGate qualityGate, Map<Condition, EvaluatedCondition> evaluatedConditions) {
+ Set<Condition> conditions = qualityGate.getConditions();
+
+ Set<Condition> conditionsNotEvaluated = conditions.stream()
+ .filter(c -> !evaluatedConditions.containsKey(c))
+ .collect(Collectors.toSet());
+ checkArgument(conditionsNotEvaluated.isEmpty(), "Evaluation missing for the following conditions: %s", conditionsNotEvaluated);
+
+ Set<Condition> unknownConditions = evaluatedConditions.keySet().stream()
+ .filter(c -> !conditions.contains(c))
+ .collect(Collectors.toSet());
+ checkArgument(unknownConditions.isEmpty(), "Evaluation provided for unknown conditions: %s", unknownConditions);
+
+ return ImmutableSet.copyOf(evaluatedConditions.values());
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.qualitygate;
+
+import java.util.Objects;
+import java.util.Set;
+import javax.annotation.concurrent.Immutable;
+
+import static java.util.Objects.requireNonNull;
+import static org.sonar.core.util.stream.MoreCollectors.toSet;
+
+@Immutable
+public class QualityGate {
+ private final String id;
+ private final String name;
+ private final Set<Condition> conditions;
+
+ public QualityGate(String id, String name, Set<Condition> conditions) {
+ this.id = requireNonNull(id, "id can't be null");
+ this.name = requireNonNull(name, "name can't be null");
+ this.conditions = requireNonNull(conditions, "conditions can't be null")
+ .stream()
+ .map(c -> requireNonNull(c, "condition can't be null"))
+ .collect(toSet(conditions.size()));
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Set<Condition> getConditions() {
+ return conditions;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ QualityGate that = (QualityGate) o;
+ return Objects.equals(id, that.id) &&
+ Objects.equals(name, that.name) &&
+ Objects.equals(conditions, that.conditions);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, name, conditions);
+ }
+
+ @Override
+ public String toString() {
+ return "QualityGate{" +
+ "id=" + id +
+ ", name='" + name + '\'' +
+ ", conditions=" + conditions +
+ '}';
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.qualitygate;
+
+import javax.annotation.ParametersAreNonnullByDefault;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.util;
+
+import okhttp3.OkHttpClient;
+import org.picocontainer.injectors.ProviderAdapter;
+import org.sonar.api.SonarRuntime;
+import org.sonar.api.ce.ComputeEngineSide;
+import org.sonar.api.config.Configuration;
+import org.sonar.api.server.ServerSide;
+import org.sonarqube.ws.client.OkHttpClientBuilder;
+
+import static java.lang.String.format;
+import static org.sonar.process.ProcessProperties.Property.HTTP_PROXY_PASSWORD;
+import static org.sonar.process.ProcessProperties.Property.HTTP_PROXY_USER;
+
+/**
+ * Provide a unique instance of {@link OkHttpClient} which configuration:
+ * <ul>
+ * <li>supports HTTPS</li>
+ * <li>supports proxy, including authentication, as defined by the properties like "http.proxyHost" in
+ * conf/sonar.properties</li>
+ * <li>has connect and read timeouts of 10 seconds each</li>
+ * <li>sends automatically the HTTP header "User-Agent" with value "SonarQube/{version}", for instance "SonarQube/6.2"</li>
+ * </ul>
+ */
+@ServerSide
+@ComputeEngineSide
+public class OkHttpClientProvider extends ProviderAdapter {
+
+ private static final int DEFAULT_CONNECT_TIMEOUT_IN_MS = 10_000;
+ private static final int DEFAULT_READ_TIMEOUT_IN_MS = 10_000;
+
+ private okhttp3.OkHttpClient okHttpClient;
+
+ /**
+ * @return a {@link OkHttpClient} singleton
+ */
+ public OkHttpClient provide(Configuration config, SonarRuntime runtime) {
+ if (okHttpClient == null) {
+ OkHttpClientBuilder builder = new OkHttpClientBuilder();
+ builder.setConnectTimeoutMs(DEFAULT_CONNECT_TIMEOUT_IN_MS);
+ builder.setReadTimeoutMs(DEFAULT_READ_TIMEOUT_IN_MS);
+ // no need to define proxy URL as system-wide proxy is used and properly
+ // configured by bootstrap process.
+ builder.setProxyLogin(config.get(HTTP_PROXY_USER.getKey()).orElse(null));
+ builder.setProxyPassword(config.get(HTTP_PROXY_PASSWORD.getKey()).orElse(null));
+ builder.setUserAgent(format("SonarQube/%s", runtime.getApiVersion().toString()));
+ okHttpClient = builder.build();
+ }
+ return okHttpClient;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 com.google.common.base.Objects;
+import java.util.Date;
+
+import static java.util.Objects.requireNonNull;
+
+public final class Analysis {
+ private final String uuid;
+ private final long date;
+
+ public Analysis(String uuid, long date) {
+ requireNonNull(uuid, "uuid must not be null");
+ this.uuid = uuid;
+ this.date = date;
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public Date getDate() {
+ return new Date(date);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof Analysis)) {
+ return false;
+ }
+ Analysis analysis = (Analysis) o;
+ return Objects.equal(uuid, analysis.uuid) &&
+ Objects.equal(date, analysis.date);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(uuid, date);
+ }
+
+ @Override
+ public String toString() {
+ return "Analysis{" +
+ "uuid='" + uuid + '\'' +
+ ", date=" + date +
+ '}';
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.Optional;
+import javax.annotation.Nullable;
+
+import static java.util.Objects.requireNonNull;
+
+public final class Branch {
+ private final boolean main;
+ private final String name;
+ private final Type type;
+
+ public Branch(boolean main, @Nullable String name, Type type) {
+ this.main = main;
+ this.name = name;
+ this.type = requireNonNull(type, "type can't be null");
+ }
+
+ public boolean isMain() {
+ return main;
+ }
+
+ public Optional<String> getName() {
+ return Optional.ofNullable(name);
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ public enum Type {
+ LONG, SHORT, PULL_REQUEST
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Branch branch = (Branch) o;
+ return main == branch.main &&
+ Objects.equals(name, branch.name) &&
+ type == branch.type;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(main, name, type);
+ }
+
+ @Override
+ public String toString() {
+ return "Branch{" +
+ "main=" + main +
+ ", name='" + name + '\'' +
+ ", type=" + type +
+ '}';
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 static java.util.Objects.requireNonNull;
+
+public final class CeTask {
+ private final String id;
+ private final Status status;
+
+ public CeTask(String id, Status status) {
+ this.id = requireNonNull(id, "id can't be null");
+ this.status = requireNonNull(status, "status can't be null");
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public Status getStatus() {
+ return status;
+ }
+
+ public enum Status {
+ SUCCESS, FAILED
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ CeTask task = (CeTask) o;
+ return Objects.equals(id, task.id) &&
+ status == task.status;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, status);
+ }
+
+ @Override
+ public String toString() {
+ return "CeTask{" +
+ "id='" + id + '\'' +
+ ", status=" + status +
+ '}';
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.Map;
+import java.util.Objects;
+import java.util.Optional;
+import javax.annotation.Nullable;
+import org.sonar.server.project.Project;
+import org.sonar.server.qualitygate.EvaluatedQualityGate;
+
+import static com.google.common.collect.ImmutableMap.copyOf;
+import static java.util.Objects.requireNonNull;
+
+public class ProjectAnalysis {
+ private final Project project;
+ private final CeTask ceTask;
+ private final Branch branch;
+ private final EvaluatedQualityGate qualityGate;
+ private final Long updatedAt;
+ private final Map<String, String> properties;
+ private final Analysis analysis;
+
+ public ProjectAnalysis(Project project, @Nullable CeTask ceTask, @Nullable Analysis analysis,
+ @Nullable Branch branch, @Nullable EvaluatedQualityGate qualityGate, @Nullable Long updatedAt,
+ Map<String, String> properties) {
+ this.project = requireNonNull(project, "project can't be null");
+ this.ceTask = ceTask;
+ this.branch = branch;
+ this.qualityGate = qualityGate;
+ this.updatedAt = updatedAt;
+ this.properties = copyOf(requireNonNull(properties, "properties can't be null"));
+ this.analysis = analysis;
+ }
+
+ public Optional<CeTask> getCeTask() {
+ return Optional.ofNullable(ceTask);
+ }
+
+ public Project getProject() {
+ return project;
+ }
+
+ public Optional<Branch> getBranch() {
+ return Optional.ofNullable(branch);
+ }
+
+ public Optional<EvaluatedQualityGate> getQualityGate() {
+ return Optional.ofNullable(qualityGate);
+ }
+
+ public Map<String, String> getProperties() {
+ return properties;
+ }
+
+ public Optional<Analysis> getAnalysis() {
+ return Optional.ofNullable(analysis);
+ }
+
+ public Optional<Long> getUpdatedAt() {
+ return Optional.ofNullable(updatedAt);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ProjectAnalysis that = (ProjectAnalysis) o;
+ return Objects.equals(project, that.project) &&
+ Objects.equals(ceTask, that.ceTask) &&
+ Objects.equals(branch, that.branch) &&
+ Objects.equals(qualityGate, that.qualityGate) &&
+ Objects.equals(updatedAt, that.updatedAt) &&
+ Objects.equals(properties, that.properties) &&
+ Objects.equals(analysis, that.analysis);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(project, ceTask, branch, qualityGate, updatedAt, properties, analysis);
+ }
+
+ @Override
+ public String toString() {
+ return "ProjectAnalysis{" +
+ "project=" + project +
+ ", ceTask=" + ceTask +
+ ", branch=" + branch +
+ ", qualityGate=" + qualityGate +
+ ", updatedAt=" + updatedAt +
+ ", properties=" + properties +
+ ", analysis=" + analysis +
+ '}';
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.sonar.api.config.Configuration;
+import org.sonar.db.component.ComponentDto;
+
+import static java.util.Objects.requireNonNull;
+
+public interface WebHooks {
+
+ /**
+ * Tells whether any webHook is configured for the specified {@link Configuration}.
+ *
+ * <p>
+ * This can be used to not do consuming operations before calling
+ * {@link #sendProjectAnalysisUpdate(Analysis, Supplier)}
+ */
+ boolean isEnabled(ComponentDto projectDto);
+
+ /**
+ * Calls all WebHooks configured in the specified {@link Configuration} for the specified analysis with the
+ * {@link WebhookPayload} provided by the specified Supplier.
+ */
+ void sendProjectAnalysisUpdate(Analysis analysis, Supplier<WebhookPayload> payloadSupplier);
+
+ final class Analysis {
+ private final String projectUuid;
+ private final String ceTaskUuid;
+ private final String analysisUuid;
+
+ public Analysis(String projectUuid, @Nullable String analysisUuid, @Nullable String ceTaskUuid) {
+ this.projectUuid = requireNonNull(projectUuid, "projectUuid can't be null");
+ this.analysisUuid = analysisUuid;
+ this.ceTaskUuid = ceTaskUuid;
+ }
+
+ public String getProjectUuid() {
+ return projectUuid;
+ }
+
+ @CheckForNull
+ public String getCeTaskUuid() {
+ return ceTaskUuid;
+ }
+
+ @CheckForNull
+ public String getAnalysisUuid() {
+ return analysisUuid;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Analysis analysis = (Analysis) o;
+ return Objects.equals(projectUuid, analysis.projectUuid) &&
+ Objects.equals(ceTaskUuid, analysis.ceTaskUuid) &&
+ Objects.equals(analysisUuid, analysis.analysisUuid);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(projectUuid, ceTaskUuid, analysisUuid);
+ }
+
+ @Override
+ public String toString() {
+ return "Analysis{" +
+ "projectUuid='" + projectUuid + '\'' +
+ ", ceTaskUuid='" + ceTaskUuid + '\'' +
+ '}';
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.List;
+import java.util.Optional;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+import org.sonar.api.ce.ComputeEngineSide;
+import org.sonar.api.server.ServerSide;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.core.util.stream.MoreCollectors;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.webhook.WebhookDao;
+import org.sonar.db.webhook.WebhookDto;
+import org.sonar.server.async.AsyncExecution;
+
+import static java.lang.String.format;
+import static java.util.Optional.ofNullable;
+
+@ServerSide
+@ComputeEngineSide
+public class WebHooksImpl implements WebHooks {
+
+ private static final Logger LOGGER = Loggers.get(WebHooksImpl.class);
+
+ private final WebhookCaller caller;
+ private final WebhookDeliveryStorage deliveryStorage;
+ private final AsyncExecution asyncExecution;
+ private final DbClient dbClient;
+
+ public WebHooksImpl(WebhookCaller caller, WebhookDeliveryStorage deliveryStorage, AsyncExecution asyncExecution, DbClient dbClient) {
+ this.caller = caller;
+ this.deliveryStorage = deliveryStorage;
+ this.asyncExecution = asyncExecution;
+ this.dbClient = dbClient;
+ }
+
+ @Override
+ public boolean isEnabled(ComponentDto projectDto) {
+ return readWebHooksFrom(projectDto.uuid())
+ .findAny()
+ .isPresent();
+ }
+
+ private Stream<WebhookDto> readWebHooksFrom(String projectUuid) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+
+ Optional<ComponentDto> optionalComponentDto = ofNullable(dbClient.componentDao().selectByUuid(dbSession, projectUuid).orNull());
+ ComponentDto componentDto = checkStateWithOptional(optionalComponentDto, "the requested project '%s' was not found", projectUuid);
+
+ if (componentDto.getMainBranchProjectUuid() != null && !componentDto.uuid().equals(componentDto.getMainBranchProjectUuid())) {
+ Optional<ComponentDto> mainBranchComponentDto = ofNullable(dbClient.componentDao().selectByUuid(dbSession, componentDto.getMainBranchProjectUuid()).orNull());
+ componentDto = checkStateWithOptional(mainBranchComponentDto, "the requested project '%s' was not found", projectUuid);
+ }
+
+ WebhookDao dao = dbClient.webhookDao();
+ return Stream.concat(
+ dao.selectByProject(dbSession, componentDto).stream(),
+ dao.selectByOrganizationUuid(dbSession, componentDto.getOrganizationUuid()).stream());
+ }
+ }
+
+ private static <T> T checkStateWithOptional(java.util.Optional<T> value, String message, Object... messageArguments) {
+ if (!value.isPresent()) {
+ throw new IllegalStateException(format(message, messageArguments));
+ }
+
+ return value.get();
+ }
+
+ @Override
+ public void sendProjectAnalysisUpdate(Analysis analysis, Supplier<WebhookPayload> payloadSupplier) {
+ List<Webhook> webhooks = readWebHooksFrom(analysis.getProjectUuid())
+ .map(dto -> new Webhook(dto.getUuid(), analysis.getProjectUuid(), analysis.getCeTaskUuid(), analysis.getAnalysisUuid(), dto.getName(), dto.getUrl()))
+ .collect(MoreCollectors.toList());
+ if (webhooks.isEmpty()) {
+ return;
+ }
+
+ WebhookPayload payload = payloadSupplier.get();
+ webhooks.forEach(webhook -> asyncExecution.addToQueue(() -> {
+ WebhookDelivery delivery = caller.call(webhook, payload);
+ log(delivery);
+ deliveryStorage.persist(delivery);
+ }));
+ asyncExecution.addToQueue(() -> deliveryStorage.purge(analysis.getProjectUuid()));
+ }
+
+ 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));
+ }
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.Optional;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+
+import static java.util.Objects.requireNonNull;
+import static java.util.Optional.ofNullable;
+
+@Immutable
+public class Webhook {
+
+ private final String uuid;
+ private final String componentUuid;
+ private final String ceTaskUuid;
+ private final String analysisUuid;
+ private final String name;
+ private final String url;
+
+ public Webhook(String uuid, String componentUuid, @Nullable String ceTaskUuid, @Nullable String analysisUuid, String name, String url) {
+ this.uuid = uuid;
+ this.componentUuid = requireNonNull(componentUuid);
+ this.ceTaskUuid = ceTaskUuid;
+ this.analysisUuid = analysisUuid;
+ this.name = requireNonNull(name);
+ this.url = requireNonNull(url);
+ }
+
+ public String getComponentUuid() {
+ return componentUuid;
+ }
+
+ public Optional<String> getCeTaskUuid() {
+ return ofNullable(ceTaskUuid);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public Optional<String> getAnalysisUuid() {
+ return ofNullable(analysisUuid);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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;
+
+public interface WebhookCaller {
+
+ /**
+ * Call webhook by sending a HTTP(S) POST request containing
+ * the JSON payload.
+ * <br/>
+ * Errors are silently ignored. They don't generate logs or
+ * throw exceptions. The error status is stored in the
+ * returned {@link WebhookDelivery}.
+ */
+ WebhookDelivery call(Webhook webhook, WebhookPayload payload);
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 okhttp3.Credentials;
+import okhttp3.HttpUrl;
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+import org.sonar.api.ce.ComputeEngineSide;
+import org.sonar.api.server.ServerSide;
+import org.sonar.api.utils.System2;
+
+import static java.lang.String.format;
+import static java.net.HttpURLConnection.HTTP_MOVED_PERM;
+import static java.net.HttpURLConnection.HTTP_MOVED_TEMP;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static okhttp3.internal.http.StatusLine.HTTP_PERM_REDIRECT;
+import static okhttp3.internal.http.StatusLine.HTTP_TEMP_REDIRECT;
+import static org.apache.commons.lang.StringUtils.isNotEmpty;
+
+@ServerSide
+@ComputeEngineSide
+public class WebhookCallerImpl implements WebhookCaller {
+
+ private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
+ private static final String PROJECT_KEY_HEADER = "X-SonarQube-Project";
+
+ private final System2 system;
+ private final OkHttpClient okHttpClient;
+
+ public WebhookCallerImpl(System2 system, OkHttpClient okHttpClient) {
+ this.system = system;
+ this.okHttpClient = newClientWithoutRedirect(okHttpClient);
+ }
+
+ @Override
+ public WebhookDelivery call(Webhook webhook, WebhookPayload payload) {
+ WebhookDelivery.Builder builder = new WebhookDelivery.Builder();
+ long startedAt = system.now();
+ builder
+ .setAt(startedAt)
+ .setPayload(payload)
+ .setWebhook(webhook);
+
+ try {
+ Request request = buildHttpRequest(webhook, payload);
+ try (Response response = execute(request)) {
+ builder.setHttpStatus(response.code());
+ }
+ } catch (Exception e) {
+ builder.setError(e);
+ }
+
+ return builder
+ .setDurationInMs((int) (system.now() - startedAt))
+ .build();
+ }
+
+ private static Request buildHttpRequest(Webhook webhook, WebhookPayload payload) {
+ HttpUrl url = HttpUrl.parse(webhook.getUrl());
+ if (url == null) {
+ throw new IllegalArgumentException("Webhook URL is not valid: " + webhook.getUrl());
+ }
+ Request.Builder request = new Request.Builder();
+ request.url(url);
+ request.header(PROJECT_KEY_HEADER, payload.getProjectKey());
+ if (isNotEmpty(url.username())) {
+ request.header("Authorization", Credentials.basic(url.username(), url.password(), UTF_8));
+ }
+
+ RequestBody body = RequestBody.create(JSON, payload.getJson());
+ request.post(body);
+ return request.build();
+ }
+
+ private Response execute(Request request) throws IOException {
+ Response response = okHttpClient.newCall(request).execute();
+ switch (response.code()) {
+ case HTTP_MOVED_PERM:
+ case HTTP_MOVED_TEMP:
+ case HTTP_TEMP_REDIRECT:
+ case HTTP_PERM_REDIRECT:
+ // OkHttpClient does not follow the redirect with the same HTTP method. A POST is
+ // redirected to a GET. Because of that the redirect must be manually
+ // implemented.
+ // See:
+ // https://github.com/square/okhttp/blob/07309c1c7d9e296014268ebd155ebf7ef8679f6c/okhttp/src/main/java/okhttp3/internal/http/RetryAndFollowUpInterceptor.java#L316
+ // https://github.com/square/okhttp/issues/936#issuecomment-266430151
+ return followPostRedirect(response);
+ default:
+ return response;
+ }
+ }
+
+ /**
+ * Inspired by https://github.com/square/okhttp/blob/parent-3.6.0/okhttp/src/main/java/okhttp3/internal/http/RetryAndFollowUpInterceptor.java#L286
+ */
+ private Response followPostRedirect(Response response) throws IOException {
+ String location = response.header("Location");
+ if (location == null) {
+ throw new IllegalStateException(format("Missing HTTP header 'Location' in redirect of %s", response.request().url()));
+ }
+ HttpUrl url = response.request().url().resolve(location);
+
+ // Don't follow redirects to unsupported protocols.
+ if (url == null) {
+ throw new IllegalStateException(format("Unsupported protocol in redirect of %s to %s", response.request().url(), location));
+ }
+
+ Request.Builder redirectRequest = response.request().newBuilder();
+ redirectRequest.post(response.request().body());
+ response.body().close();
+ return okHttpClient.newCall(redirectRequest.url(url).build()).execute();
+ }
+
+ private static OkHttpClient newClientWithoutRedirect(OkHttpClient client) {
+ return client.newBuilder()
+ .followRedirects(false)
+ .followSslRedirects(false)
+ .build();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.Optional;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+
+import static com.google.common.base.Throwables.getRootCause;
+import static java.util.Objects.requireNonNull;
+
+/**
+ * A {@link WebhookDelivery} represents the result of a webhook call.
+ */
+@Immutable
+public class WebhookDelivery {
+
+ private final Webhook webhook;
+ private final WebhookPayload payload;
+ private final Integer httpStatus;
+ private final Integer durationInMs;
+ private final long at;
+ private final Throwable error;
+
+ private WebhookDelivery(Builder builder) {
+ this.webhook = requireNonNull(builder.webhook);
+ this.payload = requireNonNull(builder.payload);
+ this.httpStatus = builder.httpStatus;
+ this.durationInMs = builder.durationInMs;
+ this.at = builder.at;
+ this.error = builder.error;
+ }
+
+ public Webhook getWebhook() {
+ return webhook;
+ }
+
+ public WebhookPayload getPayload() {
+ return payload;
+ }
+
+ /**
+ * @return the HTTP status if {@link #getError()} is empty, else returns
+ * {@link Optional#empty()}
+ */
+ public Optional<Integer> getHttpStatus() {
+ return Optional.ofNullable(httpStatus);
+ }
+
+ /**
+ * @return the duration in milliseconds if {@link #getError()} is empty,
+ * else returns {@link Optional#empty()}
+ */
+ public Optional<Integer> getDurationInMs() {
+ return Optional.ofNullable(durationInMs);
+ }
+
+ /**
+ * @return the date of sending
+ */
+ public long getAt() {
+ return at;
+ }
+
+ /**
+ * @return the error raised if the request could not be executed due to a connectivity
+ * problem or timeout
+ */
+ public Optional<Throwable> getError() {
+ return Optional.ofNullable(error);
+ }
+
+ /**
+ * @return the cause message of {@link #getError()}, Optional.empty() is error is not set.
+ */
+ public Optional<String> getErrorMessage() {
+ return error != null ? Optional.ofNullable(getRootCause(error).getMessage()) : Optional.empty();
+ }
+
+ public boolean isSuccess() {
+ return httpStatus != null && httpStatus >= 200 && httpStatus < 300;
+ }
+
+ public static class Builder {
+ private Webhook webhook;
+ private WebhookPayload payload;
+ private Integer httpStatus;
+ private Integer durationInMs;
+ private long at;
+ private Throwable error;
+
+ public Builder setWebhook(Webhook w) {
+ this.webhook = w;
+ return this;
+ }
+
+ public Builder setPayload(WebhookPayload payload) {
+ this.payload = payload;
+ return this;
+ }
+
+ public Builder setHttpStatus(@Nullable Integer httpStatus) {
+ this.httpStatus = httpStatus;
+ return this;
+ }
+
+ public Builder setDurationInMs(@Nullable Integer durationInMs) {
+ this.durationInMs = durationInMs;
+ return this;
+ }
+
+ public Builder setAt(long at) {
+ this.at = at;
+ return this;
+ }
+
+ public Builder setError(@Nullable Throwable t) {
+ this.error = t;
+ return this;
+ }
+
+ public WebhookDelivery build() {
+ return new WebhookDelivery(this);
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 com.google.common.base.Throwables;
+import org.sonar.api.ce.ComputeEngineSide;
+import org.sonar.api.server.ServerSide;
+import org.sonar.api.utils.System2;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.webhook.WebhookDeliveryDao;
+import org.sonar.db.webhook.WebhookDeliveryDto;
+
+/**
+ * Persist and purge {@link WebhookDelivery} into database
+ */
+@ServerSide
+@ComputeEngineSide
+public class WebhookDeliveryStorage {
+
+ private static final long ALIVE_DELAY_MS = 30L * 24 * 60 * 60 * 1000;
+
+ private final DbClient dbClient;
+ private final System2 system;
+ private final UuidFactory uuidFactory;
+
+ public WebhookDeliveryStorage(DbClient dbClient, System2 system, UuidFactory uuidFactory) {
+ this.dbClient = dbClient;
+ this.system = system;
+ this.uuidFactory = uuidFactory;
+ }
+
+ public void persist(WebhookDelivery delivery) {
+ WebhookDeliveryDao dao = dbClient.webhookDeliveryDao();
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ dao.insert(dbSession, toDto(delivery));
+ dbSession.commit();
+ }
+ }
+
+ public void purge(String componentUuid) {
+ long beforeDate = system.now() - ALIVE_DELAY_MS;
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ dbClient.webhookDeliveryDao().deleteComponentBeforeDate(dbSession, componentUuid, beforeDate);
+ dbSession.commit();
+ }
+ }
+
+ private WebhookDeliveryDto toDto(WebhookDelivery delivery) {
+ WebhookDeliveryDto dto = new WebhookDeliveryDto();
+ dto.setUuid(uuidFactory.create());
+ dto.setWebhookUuid(delivery.getWebhook().getUuid());
+ dto.setComponentUuid(delivery.getWebhook().getComponentUuid());
+ delivery.getWebhook().getCeTaskUuid().ifPresent(dto::setCeTaskUuid);
+ delivery.getWebhook().getAnalysisUuid().ifPresent(dto::setAnalysisUuid);
+ dto.setName(delivery.getWebhook().getName());
+ dto.setUrl(delivery.getWebhook().getUrl());
+ dto.setSuccess(delivery.isSuccess());
+ dto.setHttpStatus(delivery.getHttpStatus().orElse(null));
+ dto.setDurationMs(delivery.getDurationInMs().orElse(null));
+ dto.setErrorStacktrace(delivery.getError().map(Throwables::getStackTraceAsString).orElse(null));
+ dto.setPayload(delivery.getPayload().getJson());
+ dto.setCreatedAt(delivery.getAt());
+ return dto;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 org.sonar.core.platform.Module;
+
+public class WebhookModule extends Module {
+ @Override
+ protected void configureModule() {
+ add(
+ WebhookCallerImpl.class,
+ WebhookDeliveryStorage.class,
+ WebHooksImpl.class,
+ WebhookPayloadFactoryImpl.class);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 javax.annotation.concurrent.Immutable;
+
+import static java.util.Objects.requireNonNull;
+
+@Immutable
+public class WebhookPayload {
+
+ private final String projectKey;
+ private final String json;
+
+ public WebhookPayload(String projectKey, String json) {
+ this.projectKey = requireNonNull(projectKey);
+ this.json = requireNonNull(json);
+ }
+
+ public String getProjectKey() {
+ return projectKey;
+ }
+
+ public String getJson() {
+ return json;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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;
+
+@FunctionalInterface
+public interface WebhookPayloadFactory {
+
+ WebhookPayload create(ProjectAnalysis analysis);
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.net.URLEncoder;
+import java.util.Date;
+import java.util.Map;
+import java.util.Optional;
+import org.sonar.api.ce.ComputeEngineSide;
+import org.sonar.api.platform.Server;
+import org.sonar.api.server.ServerSide;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.server.project.Project;
+import org.sonar.server.qualitygate.Condition;
+import org.sonar.server.qualitygate.EvaluatedCondition;
+import org.sonar.server.qualitygate.EvaluatedQualityGate;
+
+import static java.lang.String.format;
+import static org.sonar.core.config.CorePropertyDefinitions.SONAR_ANALYSIS;
+
+@ServerSide
+@ComputeEngineSide
+public class WebhookPayloadFactoryImpl implements WebhookPayloadFactory {
+
+ private static final String PROPERTY_STATUS = "status";
+ private final Server server;
+ private final System2 system2;
+
+ public WebhookPayloadFactoryImpl(Server server, System2 system2) {
+ this.server = server;
+ this.system2 = system2;
+ }
+
+ @Override
+ public WebhookPayload create(ProjectAnalysis analysis) {
+ Writer string = new StringWriter();
+ try (JsonWriter writer = JsonWriter.of(string)) {
+ writer.beginObject();
+ writeServer(writer);
+ writeTask(writer, analysis.getCeTask());
+ writeDates(writer, analysis, system2);
+ writeProject(analysis, writer, analysis.getProject());
+ analysis.getBranch().ifPresent(b -> writeBranch(writer, analysis.getProject(), b));
+ analysis.getQualityGate().ifPresent(qualityGate -> writeQualityGate(writer, qualityGate));
+ writeAnalysisProperties(writer, analysis.getProperties());
+ writer.endObject().close();
+ return new WebhookPayload(analysis.getProject().getKey(), string.toString());
+ }
+ }
+
+ private void writeServer(JsonWriter writer) {
+ writer.prop("serverUrl", server.getPublicRootUrl());
+ }
+
+ private static void writeDates(JsonWriter writer, ProjectAnalysis analysis, System2 system2) {
+ analysis.getAnalysis().ifPresent(a -> writer.propDateTime("analysedAt", a.getDate()));
+ writer.propDateTime("changedAt", new Date(analysis.getUpdatedAt().orElse(system2.now())));
+ }
+
+ private void writeProject(ProjectAnalysis analysis, JsonWriter writer, Project project) {
+ writer
+ .name("project")
+ .beginObject()
+ .prop("key", project.getKey())
+ .prop("name", analysis.getProject().getName())
+ .prop("url", projectUrlOf(project))
+ .endObject();
+ }
+
+ private static void writeAnalysisProperties(JsonWriter writer, Map<String, String> properties) {
+ writer
+ .name("properties")
+ .beginObject();
+ properties.entrySet()
+ .stream()
+ .filter(prop -> prop.getKey().startsWith(SONAR_ANALYSIS))
+ .forEach(prop -> writer.prop(prop.getKey(), prop.getValue()));
+ writer.endObject();
+ }
+
+ private static void writeTask(JsonWriter writer, Optional<CeTask> ceTask) {
+ ceTask.ifPresent(ceTask1 -> writer.prop("taskId", ceTask1.getId()));
+ writer.prop(PROPERTY_STATUS, ceTask.map(CeTask::getStatus).orElse(CeTask.Status.SUCCESS).toString());
+ }
+
+ private void writeBranch(JsonWriter writer, Project project, Branch branch) {
+ writer
+ .name("branch")
+ .beginObject()
+ .prop("name", branch.getName().orElse(null))
+ .prop("type", branch.getType().name())
+ .prop("isMain", branch.isMain())
+ .prop("url", branchUrlOf(project, branch))
+ .endObject();
+ }
+
+ private String projectUrlOf(Project project) {
+ return format("%s/dashboard?id=%s", server.getPublicRootUrl(), encode(project.getKey()));
+ }
+
+ private String branchUrlOf(Project project, Branch branch) {
+ if (branch.getType() == Branch.Type.LONG) {
+ if (branch.isMain()) {
+ return projectUrlOf(project);
+ }
+ return format("%s/dashboard?branch=%s&id=%s",
+ server.getPublicRootUrl(), encode(branch.getName().orElse("")), encode(project.getKey()));
+ }
+ if (branch.getType() == Branch.Type.SHORT) {
+ return format("%s/project/issues?branch=%s&id=%s&resolved=false",
+ server.getPublicRootUrl(), encode(branch.getName().orElse("")), encode(project.getKey()));
+ }
+ if (branch.getType() == Branch.Type.PULL_REQUEST) {
+ return format("%s/project/issues?pullRequest=%s&id=%s&resolved=false",
+ server.getPublicRootUrl(), encode(branch.getName().orElse("")), encode(project.getKey()));
+ }
+ return projectUrlOf(project);
+ }
+
+ private static void writeQualityGate(JsonWriter writer, EvaluatedQualityGate gate) {
+ writer
+ .name("qualityGate")
+ .beginObject()
+ .prop("name", gate.getQualityGate().getName())
+ .prop(PROPERTY_STATUS, gate.getStatus().toString())
+ .name("conditions")
+ .beginArray();
+ for (EvaluatedCondition evaluatedCondition : gate.getEvaluatedConditions()) {
+ Condition condition = evaluatedCondition.getCondition();
+ writer
+ .beginObject()
+ .prop("metric", condition.getMetricKey())
+ .prop("operator", condition.getOperator().name());
+ evaluatedCondition.getValue().ifPresent(t -> writer.prop("value", t));
+ writer
+ .prop(PROPERTY_STATUS, evaluatedCondition.getStatus().name())
+ .prop("onLeakPeriod", condition.isOnLeakPeriod())
+ .prop("errorThreshold", condition.getErrorThreshold().orElse(null))
+ .prop("warningThreshold", condition.getWarningThreshold().orElse(null))
+ .endObject();
+ }
+ writer
+ .endArray()
+ .endObject();
+ }
+
+ private static String encode(String toEncode) {
+ try {
+ return URLEncoder.encode(toEncode, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new IllegalStateException("Encoding not supported", e);
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.webhook;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.async;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class AsyncExecutionExecutorServiceImplTest {
+ private AsyncExecutionExecutorServiceImpl underTest = new AsyncExecutionExecutorServiceImpl();
+
+ @Test
+ public void submit_executes_runnable_in_another_thread() {
+ try (SlowRunnable slowRunnable = new SlowRunnable()) {
+ underTest.submit(slowRunnable);
+ assertThat(slowRunnable.executed).isFalse();
+ }
+ }
+
+ private static final class SlowRunnable implements Runnable, AutoCloseable {
+ private final CountDownLatch latch = new CountDownLatch(1);
+ private volatile boolean executed = false;
+
+ @Override
+ public void run() {
+ try {
+ latch.await(30, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ executed = true;
+ }
+
+ @Override
+ public void close() {
+ latch.countDown();
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.async;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class AsyncExecutionImplTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ private AsyncExecutionExecutorService synchronousExecutorService = Runnable::run;
+ private AsyncExecutionImpl underTest = new AsyncExecutionImpl(synchronousExecutorService);
+
+ @Test
+ public void addToQueue_fails_with_NPE_if_Runnable_is_null() {
+ expectedException.expect(NullPointerException.class);
+
+ underTest.addToQueue(null);
+ }
+
+ @Test
+ public void addToQueue_submits_runnable_to_executorService_which_does_not_fail_if_Runnable_argument_throws_exception() {
+ underTest.addToQueue(() -> {
+ throw new RuntimeException("Faking an exception thrown by Runnable argument");
+ });
+
+ assertThat(logTester.logs()).hasSize(1);
+ assertThat(logTester.logs(LoggerLevel.ERROR)).containsOnly("Asynchronous task failed");
+ }
+
+ @Test
+ public void addToQueue_submits_runnable_that_fails_if_Runnable_argument_throws_Error() {
+ Error expected = new Error("Faking an exception thrown by Runnable argument");
+ Runnable runnable = () -> {
+ throw expected;
+ };
+
+ expectedException.expect(Error.class);
+ expectedException.expectMessage(expected.getMessage());
+
+ underTest.addToQueue(runnable);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.async;
+
+import java.lang.management.ManagementFactory;
+import javax.annotation.CheckForNull;
+import javax.management.InstanceNotFoundException;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class AsyncExecutionMBeanImplTest {
+ private AsyncExecutionMonitoring asyncExecutionMonitoring = Mockito.mock(AsyncExecutionMonitoring.class);
+
+ private AsyncExecutionMBeanImpl underTest = new AsyncExecutionMBeanImpl(asyncExecutionMonitoring);
+
+ @Test
+ public void register_and_unregister() throws Exception {
+ assertThat(getMBean()).isNull();
+
+ underTest.start();
+ assertThat(getMBean()).isNotNull();
+
+ underTest.stop();
+ assertThat(getMBean()).isNull();
+ }
+
+ @Test
+ public void getQueueSize_delegates_to_AsyncExecutionMonitoring() {
+ when(asyncExecutionMonitoring.getQueueSize()).thenReturn(12);
+
+ assertThat(underTest.getQueueSize()).isEqualTo(12);
+
+ verify(asyncExecutionMonitoring).getQueueSize();
+ }
+
+ @Test
+ public void getWorkerCount_delegates_to_AsyncExecutionMonitoring() {
+ when(asyncExecutionMonitoring.getWorkerCount()).thenReturn(12);
+
+ assertThat(underTest.getWorkerCount()).isEqualTo(12);
+
+ verify(asyncExecutionMonitoring).getWorkerCount();
+ }
+
+ @Test
+ public void getLargestWorkerCount_delegates_to_AsyncExecutionMonitoring() {
+ when(asyncExecutionMonitoring.getLargestWorkerCount()).thenReturn(12);
+
+ assertThat(underTest.getLargestWorkerCount()).isEqualTo(12);
+
+ verify(asyncExecutionMonitoring).getLargestWorkerCount();
+ }
+
+ @CheckForNull
+ private ObjectInstance getMBean() throws Exception {
+ try {
+ return ManagementFactory.getPlatformMBeanServer().getObjectInstance(new ObjectName(AsyncExecutionMBean.OBJECT_NAME));
+ } catch (InstanceNotFoundException e) {
+ return null;
+ }
+ }
+
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.project;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ProjectTest {
+ @Test
+ public void test_bean_without_description() {
+ Project project1 = new Project("U1", "K1", "N1");
+ Project project2 = new Project("U1", "K1", "N1", null);
+
+ assertThat(project1.getUuid()).isEqualTo(project2.getUuid()).isEqualTo("U1");
+ assertThat(project1.getKey()).isEqualTo(project2.getKey()).isEqualTo("K1");
+ assertThat(project1.getName()).isEqualTo(project2.getName()).isEqualTo("N1");
+ assertThat(project1.getDescription()).isEqualTo(project2.getDescription()).isNull();
+
+ assertThat(project1.toString())
+ .isEqualTo(project2.toString())
+ .isEqualTo("Project{uuid='U1', key='K1', name='N1', description=null}");
+ }
+
+ @Test
+ public void test_bean_with_description() {
+ Project project1 = new Project("U1", "K1", "N1", "D1");
+
+ assertThat(project1.getUuid()).isEqualTo("U1");
+ assertThat(project1.getKey()).isEqualTo("K1");
+ assertThat(project1.getName()).isEqualTo("N1");
+ assertThat(project1.getDescription()).isEqualTo("D1");
+
+ assertThat(project1.toString())
+ .isEqualTo(project1.toString())
+ .isEqualTo("Project{uuid='U1', key='K1', name='N1', description='D1'}");
+ }
+
+ @Test
+ public void test_equals_and_hashCode() {
+ Project project1 = new Project("U1", "K1", "N1");
+ Project project2 = new Project("U1", "K1", "N1", "D1");
+
+ assertThat(project1).isEqualTo(project1);
+ assertThat(project1).isNotEqualTo(null);
+ assertThat(project1).isNotEqualTo(new Object());
+ assertThat(project1).isEqualTo(new Project("U1", "K1", "N1", null));
+ assertThat(project1).isNotEqualTo(new Project("U1", "K2", "N1", null));
+ assertThat(project1).isNotEqualTo(new Project("U1", "K1", "N2", null));
+ assertThat(project1).isEqualTo(project2);
+
+ assertThat(project1.hashCode()).isEqualTo(project1.hashCode());
+ assertThat(project1.hashCode()).isNotEqualTo(null);
+ assertThat(project1.hashCode()).isNotEqualTo(new Object().hashCode());
+ assertThat(project1.hashCode()).isEqualTo(new Project("U1", "K1", "N1", null).hashCode());
+ assertThat(project1.hashCode()).isNotEqualTo(new Project("U1", "K2", "N1", null).hashCode());
+ assertThat(project1.hashCode()).isNotEqualTo(new Project("U1", "K1", "N2", null).hashCode());
+ assertThat(project1.hashCode()).isEqualTo(project2.hashCode());
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.qualitygate;
+
+import java.util.Arrays;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ConditionTest {
+ private static final String METRIC_KEY = "metric_key";
+ private static final Condition.Operator OPERATOR = Condition.Operator.EQUALS;
+ private static final String ERROR_THRESHOLD = "2";
+ private static final String WARN_THRESHOLD = "4";
+ private static final boolean ON_LEAK_PERIOD = true;
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private Condition underTest = new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, WARN_THRESHOLD, ON_LEAK_PERIOD);
+
+ @Test
+ public void constructor_throws_NPE_if_metricKey_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("metricKey can't be null");
+
+ new Condition(null, OPERATOR, ERROR_THRESHOLD, WARN_THRESHOLD, ON_LEAK_PERIOD);
+ }
+
+ @Test
+ public void constructor_throws_NPE_if_operator_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("operator can't be null");
+
+ new Condition(METRIC_KEY, null, ERROR_THRESHOLD, WARN_THRESHOLD, ON_LEAK_PERIOD);
+ }
+
+ @Test
+ public void errorThreshold_can_be_null() {
+ Condition underTest = new Condition(METRIC_KEY, OPERATOR, null, WARN_THRESHOLD, ON_LEAK_PERIOD);
+
+ assertThat(underTest.getErrorThreshold()).isEmpty();
+ }
+
+ @Test
+ public void warnThreshold_can_be_null() {
+ Condition underTest = new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, null, ON_LEAK_PERIOD);
+
+ assertThat(underTest.getWarningThreshold()).isEmpty();
+ }
+
+ @Test
+ public void verify_getters() {
+ assertThat(underTest.getMetricKey()).isEqualTo(METRIC_KEY);
+ assertThat(underTest.getOperator()).isEqualTo(OPERATOR);
+ assertThat(underTest.getErrorThreshold()).contains(ERROR_THRESHOLD);
+ assertThat(underTest.getWarningThreshold()).contains(WARN_THRESHOLD);
+ assertThat(underTest.isOnLeakPeriod()).isEqualTo(ON_LEAK_PERIOD);
+ }
+
+ @Test
+ public void toString_is_override() {
+ assertThat(underTest.toString())
+ .isEqualTo("Condition{metricKey='metric_key', operator=EQUALS, warningThreshold='4', errorThreshold='2', onLeakPeriod=true}");
+ }
+
+ @Test
+ public void toString_does_not_quote_nulls() {
+ Condition withNulls = new Condition("metric_key", Condition.Operator.LESS_THAN, null, null, false);
+ assertThat(withNulls.toString())
+ .isEqualTo("Condition{metricKey='metric_key', operator=LESS_THAN, warningThreshold=null, errorThreshold=null, onLeakPeriod=false}");
+ }
+
+ @Test
+ public void equals_is_based_on_all_fields() {
+ assertThat(underTest).isEqualTo(underTest);
+ assertThat(underTest).isNotEqualTo(null);
+ assertThat(underTest).isNotEqualTo(new Object());
+ assertThat(underTest).isEqualTo(new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, WARN_THRESHOLD, ON_LEAK_PERIOD));
+ assertThat(underTest).isNotEqualTo(new Condition("other_metric_key", OPERATOR, ERROR_THRESHOLD, WARN_THRESHOLD, ON_LEAK_PERIOD));
+ Arrays.stream(Condition.Operator.values())
+ .filter(s -> !OPERATOR.equals(s))
+ .forEach(otherOperator -> assertThat(underTest)
+ .isNotEqualTo(new Condition(METRIC_KEY, otherOperator, ERROR_THRESHOLD, WARN_THRESHOLD, ON_LEAK_PERIOD)));
+ assertThat(underTest).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, null, WARN_THRESHOLD, ON_LEAK_PERIOD));
+ assertThat(underTest).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, "other_error_threshold", WARN_THRESHOLD, ON_LEAK_PERIOD));
+ assertThat(underTest).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, null, ON_LEAK_PERIOD));
+ assertThat(underTest).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, "other_warn_threshold", ON_LEAK_PERIOD));
+ assertThat(underTest).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, WARN_THRESHOLD, !ON_LEAK_PERIOD));
+ }
+
+ @Test
+ public void hashcode_is_based_on_all_fields() {
+ assertThat(underTest.hashCode()).isEqualTo(underTest.hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(null);
+ assertThat(underTest.hashCode()).isNotEqualTo(new Object().hashCode());
+ assertThat(underTest.hashCode()).isEqualTo(new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, WARN_THRESHOLD, ON_LEAK_PERIOD).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new Condition("other_metric_key", OPERATOR, ERROR_THRESHOLD, WARN_THRESHOLD, ON_LEAK_PERIOD).hashCode());
+ Arrays.stream(Condition.Operator.values())
+ .filter(s -> !OPERATOR.equals(s))
+ .forEach(otherOperator -> assertThat(underTest.hashCode())
+ .isNotEqualTo(new Condition(METRIC_KEY, otherOperator, ERROR_THRESHOLD, WARN_THRESHOLD, ON_LEAK_PERIOD).hashCode()));
+ assertThat(underTest.hashCode()).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, null, WARN_THRESHOLD, ON_LEAK_PERIOD).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, "other_error_threshold", WARN_THRESHOLD, ON_LEAK_PERIOD).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, null, ON_LEAK_PERIOD).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, "other_warn_threshold", ON_LEAK_PERIOD).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, WARN_THRESHOLD, !ON_LEAK_PERIOD).hashCode());
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.qualitygate;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.server.qualitygate.Condition.Operator.EQUALS;
+import static org.sonar.server.qualitygate.EvaluatedCondition.EvaluationStatus.OK;
+import static org.sonar.server.qualitygate.EvaluatedCondition.EvaluationStatus.WARN;
+
+public class EvaluatedConditionTest {
+ private static final Condition CONDITION_1 = new Condition("metricKey", EQUALS, "2", "4", false);
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private EvaluatedCondition underTest = new EvaluatedCondition(CONDITION_1, WARN, "value");
+
+ @Test
+ public void constructor_throws_NPE_if_condition_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("condition can't be null");
+
+ new EvaluatedCondition(null, WARN, "value");
+ }
+
+ @Test
+ public void constructor_throws_NPE_if_EvaluationStatus_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("status can't be null");
+
+ new EvaluatedCondition(CONDITION_1, null, "value");
+ }
+
+ @Test
+ public void constructor_accepts_null_value() {
+ EvaluatedCondition underTest = new EvaluatedCondition(CONDITION_1, WARN, null);
+
+ assertThat(underTest.getValue()).isEmpty();
+ }
+
+ @Test
+ public void verify_getters() {
+ EvaluatedCondition underTest = new EvaluatedCondition(CONDITION_1, WARN, "value");
+
+ assertThat(underTest.getCondition()).isEqualTo(CONDITION_1);
+ assertThat(underTest.getStatus()).isEqualTo(WARN);
+ assertThat(underTest.getValue()).contains("value");
+ }
+
+ @Test
+ public void override_toString() {
+ assertThat(underTest.toString()).isEqualTo("EvaluatedCondition{condition=" +
+ "Condition{metricKey='metricKey', operator=EQUALS, warningThreshold='4', errorThreshold='2', onLeakPeriod=false}, " +
+ "status=WARN, value='value'}");
+ }
+
+ @Test
+ public void toString_does_not_quote_null_value() {
+ EvaluatedCondition underTest = new EvaluatedCondition(CONDITION_1, WARN, null);
+
+ assertThat(underTest.toString()).isEqualTo("EvaluatedCondition{condition=" +
+ "Condition{metricKey='metricKey', operator=EQUALS, warningThreshold='4', errorThreshold='2', onLeakPeriod=false}, " +
+ "status=WARN, value=null}");
+ }
+
+ @Test
+ public void equals_is_based_on_all_fields() {
+ assertThat(underTest).isEqualTo(underTest);
+ assertThat(underTest).isEqualTo(new EvaluatedCondition(CONDITION_1, WARN, "value"));
+ assertThat(underTest).isNotEqualTo(null);
+ assertThat(underTest).isNotEqualTo(new Object());
+ assertThat(underTest).isNotEqualTo(new EvaluatedCondition(new Condition("other_metric", EQUALS, "a", "b", true), WARN, "value"));
+ assertThat(underTest).isNotEqualTo(new EvaluatedCondition(CONDITION_1, OK, "value"));
+ assertThat(underTest).isNotEqualTo(new EvaluatedCondition(CONDITION_1, WARN, null));
+ assertThat(underTest).isNotEqualTo(new EvaluatedCondition(CONDITION_1, WARN, "other_value"));
+ }
+
+ @Test
+ public void hashcode_is_based_on_all_fields() {
+ assertThat(underTest.hashCode()).isEqualTo(underTest.hashCode());
+ assertThat(underTest.hashCode()).isEqualTo(new EvaluatedCondition(CONDITION_1, WARN, "value").hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(null);
+ assertThat(underTest.hashCode()).isNotEqualTo(new Object().hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new EvaluatedCondition(new Condition("other_metric", EQUALS, "a", "b", true), WARN, "value").hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new EvaluatedCondition(CONDITION_1, OK, "value").hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new EvaluatedCondition(CONDITION_1, WARN, null).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new EvaluatedCondition(CONDITION_1, WARN, "other_value").hashCode());
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.qualitygate;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Random;
+import org.apache.commons.lang.RandomStringUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.measures.Metric.Level;
+
+import static java.util.Collections.emptySet;
+import static java.util.Collections.singleton;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.server.qualitygate.EvaluatedQualityGate.newBuilder;
+
+public class EvaluatedQualityGateTest {
+ private static final String QUALITY_GATE_ID = "qg_id";
+ private static final String QUALITY_GATE_NAME = "qg_name";
+ private static final QualityGate NO_CONDITION_QUALITY_GATE = new QualityGate(QUALITY_GATE_ID, QUALITY_GATE_NAME, emptySet());
+ private static final Condition CONDITION_1 = new Condition("metric_key", Condition.Operator.LESS_THAN, "2", "4", true);
+ private static final Condition CONDITION_2 = new Condition("metric_key_2", Condition.Operator.GREATER_THAN, "6", "12", false);
+ private static final QualityGate ONE_CONDITION_QUALITY_GATE = new QualityGate(QUALITY_GATE_ID, QUALITY_GATE_NAME, singleton(CONDITION_1));
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private final Random random = new Random();
+ private final Level randomStatus = Level.values()[random.nextInt(Level.values().length)];
+ private final EvaluatedCondition.EvaluationStatus randomEvaluationStatus = EvaluatedCondition.EvaluationStatus.values()[random
+ .nextInt(EvaluatedCondition.EvaluationStatus.values().length)];
+ private final String randomValue = random.nextBoolean() ? null : RandomStringUtils.randomAlphanumeric(3);
+
+ private EvaluatedQualityGate.Builder builder = newBuilder();
+
+ @Test
+ public void build_fails_with_NPE_if_status_not_set() {
+ builder.setQualityGate(NO_CONDITION_QUALITY_GATE);
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("status can't be null");
+
+ builder.build();
+ }
+
+ @Test
+ public void addCondition_fails_with_NPE_if_condition_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("condition can't be null");
+
+ builder.addCondition(null, EvaluatedCondition.EvaluationStatus.WARN, "a_value");
+ }
+
+ @Test
+ public void addCondition_fails_with_NPE_if_status_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("status can't be null");
+
+ builder.addCondition(new Condition("metric_key", Condition.Operator.LESS_THAN, "2", "4", true), null, "a_value");
+ }
+
+ @Test
+ public void addCondition_accepts_null_value() {
+ builder.addCondition(CONDITION_1, EvaluatedCondition.EvaluationStatus.NO_VALUE, null);
+
+ assertThat(builder.getEvaluatedConditions())
+ .containsOnly(new EvaluatedCondition(CONDITION_1, EvaluatedCondition.EvaluationStatus.NO_VALUE, null));
+ }
+
+ @Test
+ public void getEvaluatedConditions_returns_empty_with_no_condition_added_to_builder() {
+ assertThat(builder.getEvaluatedConditions()).isEmpty();
+ }
+
+ @Test
+ public void build_fails_with_IAE_if_condition_added_and_no_on_QualityGate() {
+ builder.setQualityGate(NO_CONDITION_QUALITY_GATE)
+ .setStatus(randomStatus)
+ .addCondition(CONDITION_1, randomEvaluationStatus, randomValue);
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Evaluation provided for unknown conditions: [" + CONDITION_1 + "]");
+
+ builder.build();
+ }
+
+ @Test
+ public void build_fails_with_IAE_if_condition_is_missing_for_one_defined_in_QualityGate() {
+ builder.setQualityGate(ONE_CONDITION_QUALITY_GATE)
+ .setStatus(randomStatus);
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Evaluation missing for the following conditions: [" + CONDITION_1 + "]");
+
+ builder.build();
+ }
+
+ @Test
+ public void verify_getters() {
+ EvaluatedQualityGate underTest = builder
+ .setQualityGate(ONE_CONDITION_QUALITY_GATE)
+ .setStatus(randomStatus)
+ .addCondition(CONDITION_1, randomEvaluationStatus, randomValue)
+ .build();
+
+ assertThat(underTest.getQualityGate()).isEqualTo(ONE_CONDITION_QUALITY_GATE);
+ assertThat(underTest.getStatus()).isEqualTo(randomStatus);
+ assertThat(underTest.getEvaluatedConditions())
+ .containsOnly(new EvaluatedCondition(CONDITION_1, randomEvaluationStatus, randomValue));
+ }
+
+ @Test
+ public void verify_getters_when_no_condition() {
+ EvaluatedQualityGate underTest = builder
+ .setQualityGate(NO_CONDITION_QUALITY_GATE)
+ .setStatus(randomStatus)
+ .build();
+
+ assertThat(underTest.getQualityGate()).isEqualTo(NO_CONDITION_QUALITY_GATE);
+ assertThat(underTest.getStatus()).isEqualTo(randomStatus);
+ assertThat(underTest.getEvaluatedConditions()).isEmpty();
+ }
+
+ @Test
+ public void verify_getters_when_multiple_conditions() {
+ QualityGate qualityGate = new QualityGate(QUALITY_GATE_ID, QUALITY_GATE_NAME, ImmutableSet.of(CONDITION_1, CONDITION_2));
+ EvaluatedQualityGate underTest = builder
+ .setQualityGate(qualityGate)
+ .setStatus(randomStatus)
+ .addCondition(CONDITION_1, randomEvaluationStatus, randomValue)
+ .addCondition(CONDITION_2, EvaluatedCondition.EvaluationStatus.WARN, "bad")
+ .build();
+
+ assertThat(underTest.getQualityGate()).isEqualTo(qualityGate);
+ assertThat(underTest.getStatus()).isEqualTo(randomStatus);
+ assertThat(underTest.getEvaluatedConditions()).containsOnly(
+ new EvaluatedCondition(CONDITION_1, randomEvaluationStatus, randomValue),
+ new EvaluatedCondition(CONDITION_2, EvaluatedCondition.EvaluationStatus.WARN, "bad"));
+ }
+
+ @Test
+ public void equals_is_based_on_all_fields() {
+ EvaluatedQualityGate.Builder builder = this.builder
+ .setQualityGate(ONE_CONDITION_QUALITY_GATE)
+ .setStatus(Level.WARN)
+ .addCondition(CONDITION_1, EvaluatedCondition.EvaluationStatus.WARN, "foo");
+
+ EvaluatedQualityGate underTest = builder.build();
+ assertThat(underTest).isEqualTo(builder.build());
+ assertThat(underTest).isNotSameAs(builder.build());
+ assertThat(underTest).isNotEqualTo(null);
+ assertThat(underTest).isNotEqualTo(new Object());
+ assertThat(underTest).isNotEqualTo(builder.setQualityGate(new QualityGate("other_id", QUALITY_GATE_NAME, singleton(CONDITION_1))).build());
+ assertThat(underTest).isNotEqualTo(builder.setQualityGate(ONE_CONDITION_QUALITY_GATE).setStatus(Level.OK).build());
+ assertThat(underTest).isNotEqualTo(newBuilder()
+ .setQualityGate(ONE_CONDITION_QUALITY_GATE)
+ .setStatus(Level.WARN)
+ .addCondition(CONDITION_1, EvaluatedCondition.EvaluationStatus.OK, "foo")
+ .build());
+ }
+
+ @Test
+ public void hashcode_is_based_on_all_fields() {
+ EvaluatedQualityGate.Builder builder = this.builder
+ .setQualityGate(ONE_CONDITION_QUALITY_GATE)
+ .setStatus(Level.WARN)
+ .addCondition(CONDITION_1, EvaluatedCondition.EvaluationStatus.WARN, "foo");
+
+ EvaluatedQualityGate underTest = builder.build();
+ assertThat(underTest.hashCode()).isEqualTo(builder.build().hashCode());
+ assertThat(underTest.hashCode()).isNotSameAs(builder.build().hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(null);
+ assertThat(underTest.hashCode()).isNotEqualTo(new Object().hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(builder.setQualityGate(new QualityGate("other_id", QUALITY_GATE_NAME, singleton(CONDITION_1))).build().hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(builder.setQualityGate(ONE_CONDITION_QUALITY_GATE).setStatus(Level.OK).build().hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(newBuilder()
+ .setQualityGate(ONE_CONDITION_QUALITY_GATE)
+ .setStatus(Level.WARN)
+ .addCondition(CONDITION_1, EvaluatedCondition.EvaluationStatus.OK, "foo")
+ .build().hashCode());
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.qualitygate;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Random;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static java.util.Collections.emptySet;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class QualityGateTest {
+ private static final String QUALIGATE_ID = "qg_id";
+ private static final String QUALIGATE_NAME = "qg_name";
+ private static final Condition CONDITION_1 = new Condition("m1", Condition.Operator.EQUALS, "1", "2", false);
+ private static final Condition CONDITION_2 = new Condition("m2", Condition.Operator.LESS_THAN, "2", "4", true);
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private QualityGate underTest = new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_1, CONDITION_2));
+
+ @Test
+ public void constructor_fails_with_NPE_if_id_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("id can't be null");
+
+ new QualityGate(null, "name", emptySet());
+ }
+
+ @Test
+ public void constructor_fails_with_NPE_if_name_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("name can't be null");
+
+ new QualityGate("id", null, emptySet());
+ }
+
+ @Test
+ public void constructor_fails_with_NPE_if_conditions_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("conditions can't be null");
+
+ new QualityGate("id", "name", null);
+ }
+
+ @Test
+ public void constructor_fails_with_NPE_if_conditions_contains_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("condition can't be null");
+ Random random = new Random();
+ Set<Condition> conditions = Stream.of(
+ IntStream.range(0, random.nextInt(5))
+ .mapToObj(i -> new Condition("m_before_" + i, Condition.Operator.EQUALS, null, null, false)),
+ Stream.of((Condition) null),
+ IntStream.range(0, random.nextInt(5))
+ .mapToObj(i -> new Condition("m_after_" + i, Condition.Operator.EQUALS, null, null, false)))
+ .flatMap(s -> s)
+ .collect(Collectors.toSet());
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("condition can't be null");
+
+ new QualityGate("id", "name", conditions);
+ }
+
+ @Test
+ public void verify_getters() {
+ assertThat(underTest.getId()).isEqualTo(QUALIGATE_ID);
+ assertThat(underTest.getName()).isEqualTo(QUALIGATE_NAME);
+ assertThat(underTest.getConditions()).containsOnly(CONDITION_1, CONDITION_2);
+ }
+
+ @Test
+ public void toString_is_override() {
+ QualityGate underTest = new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_2));
+
+ assertThat(underTest.toString()).isEqualTo("QualityGate{id=qg_id, name='qg_name', conditions=[" +
+ "Condition{metricKey='m2', operator=LESS_THAN, warningThreshold='4', errorThreshold='2', onLeakPeriod=true}" +
+ "]}");
+ }
+
+ @Test
+ public void equals_is_based_on_all_fields() {
+ assertThat(underTest).isEqualTo(underTest);
+ assertThat(underTest).isEqualTo(new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_2, CONDITION_1)));
+ assertThat(underTest).isNotEqualTo(null);
+ assertThat(underTest).isNotEqualTo(new Object());
+ assertThat(underTest).isNotEqualTo(new QualityGate("other_id", QUALIGATE_NAME, ImmutableSet.of(CONDITION_2, CONDITION_1)));
+ assertThat(underTest).isNotEqualTo(new QualityGate(QUALIGATE_ID, "other_name", ImmutableSet.of(CONDITION_2, CONDITION_1)));
+ assertThat(underTest).isNotEqualTo(new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, emptySet()));
+ assertThat(underTest).isNotEqualTo(new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_1)));
+ assertThat(underTest).isNotEqualTo(new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_2)));
+ assertThat(underTest).isNotEqualTo(
+ new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_1, CONDITION_2, new Condition("new", Condition.Operator.GREATER_THAN, "a", "b", false))));
+ }
+
+ @Test
+ public void hashcode_is_based_on_all_fields() {
+ assertThat(underTest.hashCode()).isEqualTo(underTest.hashCode());
+ assertThat(underTest.hashCode()).isEqualTo(new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_2, CONDITION_1)).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(null);
+ assertThat(underTest.hashCode()).isNotEqualTo(new Object().hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new QualityGate("other_id", QUALIGATE_NAME, ImmutableSet.of(CONDITION_2, CONDITION_1)).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new QualityGate(QUALIGATE_ID, "other_name", ImmutableSet.of(CONDITION_2, CONDITION_1)).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, emptySet()).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_1)).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_2)).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(
+ new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_1, CONDITION_2, new Condition("new", Condition.Operator.GREATER_THAN, "a", "b", false))).hashCode());
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.util;
+
+import java.io.IOException;
+import java.util.Base64;
+import okhttp3.OkHttpClient;
+import okhttp3.Protocol;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+import okhttp3.mockwebserver.RecordedRequest;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.SonarQubeSide;
+import org.sonar.api.SonarRuntime;
+import org.sonar.api.config.internal.MapSettings;
+import org.sonar.api.internal.SonarRuntimeImpl;
+import org.sonar.api.utils.Version;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class OkHttpClientProviderTest {
+
+ private MapSettings settings = new MapSettings();
+ private SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(Version.parse("6.2"), SonarQubeSide.SERVER);
+ private final OkHttpClientProvider underTest = new OkHttpClientProvider();
+
+ @Rule
+ public MockWebServer server = new MockWebServer();
+
+ @Test
+ public void get_returns_a_OkHttpClient_with_default_configuration() throws Exception {
+ OkHttpClient client = underTest.provide(settings.asConfig(), runtime);
+
+ assertThat(client.connectTimeoutMillis()).isEqualTo(10_000);
+ assertThat(client.readTimeoutMillis()).isEqualTo(10_000);
+ assertThat(client.proxy()).isNull();
+
+ RecordedRequest recordedRequest = call(client);
+ assertThat(recordedRequest.getHeader("User-Agent")).isEqualTo("SonarQube/6.2");
+ assertThat(recordedRequest.getHeader("Proxy-Authorization")).isNull();
+ }
+
+ @Test
+ public void get_returns_a_OkHttpClient_with_proxy_authentication() throws Exception {
+ settings.setProperty("http.proxyUser", "the-login");
+ settings.setProperty("http.proxyPassword", "the-password");
+
+ OkHttpClient client = underTest.provide(settings.asConfig(), runtime);
+ Response response = new Response.Builder().protocol(Protocol.HTTP_1_1).request(new Request.Builder().url("http://foo").build()).code(407).build();
+ Request request = client.proxyAuthenticator().authenticate(null, response);
+
+ assertThat(request.header("Proxy-Authorization")).isEqualTo("Basic " + Base64.getEncoder().encodeToString("the-login:the-password".getBytes()));
+ }
+
+ @Test
+ public void get_returns_a_singleton() {
+ OkHttpClient client1 = underTest.provide(settings.asConfig(), runtime);
+ OkHttpClient client2 = underTest.provide(settings.asConfig(), runtime);
+ assertThat(client2).isNotNull().isSameAs(client1);
+ }
+
+ private RecordedRequest call(OkHttpClient client) throws IOException, InterruptedException {
+ server.enqueue(new MockResponse().setBody("pong"));
+ client.newCall(new Request.Builder().url(server.url("/ping")).build()).execute();
+
+ return server.takeRequest();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.Date;
+import java.util.Random;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class AnalysisTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void constructor_throws_NPE_when_uuid_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("uuid must not be null");
+
+ new Analysis(null, 1_990L);
+ }
+
+ @Test
+ public void test_equality() {
+ String uuid = randomAlphanumeric(35);
+ long date = new Random().nextLong();
+ Analysis underTest = new Analysis(uuid, date);
+
+ assertThat(underTest).isEqualTo(underTest);
+ assertThat(underTest.getUuid()).isEqualTo(uuid);
+ assertThat(underTest.getDate()).isEqualTo(new Date(date));
+
+ assertThat(underTest).isNotEqualTo(null);
+ assertThat(underTest).isNotEqualTo(new Analysis(uuid + "1", date));
+ assertThat(underTest).isNotEqualTo(new Analysis(uuid, date + 1_000L));
+ }
+
+ @Test
+ public void test_hashcode() {
+ String uuid = randomAlphanumeric(35);
+ long date = new Random().nextLong();
+ Analysis underTest = new Analysis(uuid, date);
+
+ assertThat(underTest.hashCode()).isEqualTo(underTest.hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new Analysis(uuid + "1", date).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new Analysis(uuid, date + 1_000).hashCode());
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.ArrayList;
+import java.util.List;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.organization.OrganizationDbTester;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.webhook.WebhookDbTester;
+import org.sonar.server.async.AsyncExecution;
+import org.sonar.server.organization.DefaultOrganizationProvider;
+
+import static java.util.Objects.requireNonNull;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.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;
+import static org.sonar.db.DbTester.create;
+import static org.sonar.db.webhook.WebhookTesting.newWebhook;
+import static org.sonar.server.organization.TestDefaultOrganizationProvider.from;
+
+public class AsynchronousWebHooksImplTest {
+
+ private System2 system2 = mock(System2.class);
+
+ @Rule
+ public DbTester db = create(system2);
+ private WebhookDbTester webhookDbTester = db.webhooks();
+ private ComponentDbTester componentDbTester = db.components();
+ private OrganizationDbTester organizationDbTester = db.organizations();
+ private DefaultOrganizationProvider defaultOrganizationProvider = from(db);
+
+ private static final long NOW = 1_500_000_000_000L;
+
+
+ private final TestWebhookCaller caller = new TestWebhookCaller();
+ private final WebhookDeliveryStorage deliveryStorage = mock(WebhookDeliveryStorage.class);
+ private final WebhookPayload mock = mock(WebhookPayload.class);
+ private final RecordingAsyncExecution asyncExecution = new RecordingAsyncExecution();
+
+ private final WebHooksImpl underTest = new WebHooksImpl(caller, deliveryStorage, asyncExecution, db.getDbClient());
+
+ @Test
+ public void send_global_webhooks() {
+
+ OrganizationDto organizationDto = db.getDefaultOrganization() ;
+ ComponentDto project = componentDbTester.insertPrivateProject(componentDto -> componentDto.setOrganizationUuid(organizationDto.getUuid()));
+ webhookDbTester.insert(newWebhook(organizationDto).setName("First").setUrl("http://url1"));
+ webhookDbTester.insert(newWebhook(organizationDto).setName("Second").setUrl("http://url2"));
+
+ caller.enqueueSuccess(NOW, 200, 1_234);
+ caller.enqueueFailure(NOW, new IOException("Fail to connect"));
+
+ underTest.sendProjectAnalysisUpdate(new WebHooks.Analysis(project.uuid(), "1", "#1"), () -> mock);
+
+ assertThat(caller.countSent()).isZero();
+ verifyZeroInteractions(deliveryStorage);
+
+ asyncExecution.executeRecorded();
+
+ assertThat(caller.countSent()).isEqualTo(2);
+ verify(deliveryStorage, times(2)).persist(any(WebhookDelivery.class));
+ verify(deliveryStorage).purge(project.uuid());
+ }
+
+ private static class RecordingAsyncExecution implements AsyncExecution {
+ private final List<Runnable> runnableList = new ArrayList<>();
+
+ @Override
+ public void addToQueue(Runnable r) {
+ runnableList.add(requireNonNull(r));
+ }
+
+ public void executeRecorded() {
+ ArrayList<Runnable> runnables = new ArrayList<>(runnableList);
+ runnableList.clear();
+ runnables.forEach(Runnable::run);
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.Random;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class BranchTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private Branch underTest = new Branch(true, "b", Branch.Type.SHORT);
+
+ @Test
+ public void constructor_throws_NPE_if_type_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("type can't be null");
+
+ new Branch(new Random().nextBoolean(), "s", null);
+ }
+
+ @Test
+ public void verify_getters() {
+ assertThat(underTest.isMain()).isTrue();
+ assertThat(underTest.getName()).contains("b");
+ assertThat(underTest.getType()).isEqualTo(Branch.Type.SHORT);
+
+
+ Branch underTestWithNull = new Branch(false, null, Branch.Type.LONG);
+ assertThat(underTestWithNull.isMain()).isFalse();
+ assertThat(underTestWithNull.getName()).isEmpty();
+ assertThat(underTestWithNull.getType()).isEqualTo(Branch.Type.LONG);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CeTaskTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private CeTask underTest = new CeTask("A", CeTask.Status.SUCCESS);
+
+ @Test
+ public void constructor_throws_NPE_if_id_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("id can't be null");
+
+ new CeTask(null, CeTask.Status.SUCCESS);
+ }
+
+ @Test
+ public void constructor_throws_NPE_if_status_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("status can't be null");
+
+ new CeTask("B", null);
+ }
+
+ @Test
+ public void verify_getters() {
+ assertThat(underTest.getId()).isEqualTo("A");
+ assertThat(underTest.getStatus()).isEqualTo(CeTask.Status.SUCCESS);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 com.google.common.collect.ImmutableMap;
+import java.util.Map;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.measures.Metric;
+import org.sonar.server.project.Project;
+import org.sonar.server.qualitygate.EvaluatedQualityGate;
+import org.sonar.server.qualitygate.QualityGate;
+
+import static java.util.Collections.emptyMap;
+import static java.util.Collections.emptySet;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ProjectAnalysisTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private final CeTask ceTask = new CeTask("id", CeTask.Status.SUCCESS);
+ private final Project project = new Project("uuid", "key", "name");
+ private final Analysis analysis = new Analysis("analysis_uuid", 1_500L);
+ private final Branch branch = new Branch(true, "name", Branch.Type.SHORT);
+ private final EvaluatedQualityGate qualityGate = EvaluatedQualityGate.newBuilder()
+ .setQualityGate(new QualityGate("id", "name", emptySet()))
+ .setStatus(Metric.Level.WARN)
+ .build();
+ private final Map<String, String> properties = ImmutableMap.of("a", "b");
+ private ProjectAnalysis underTest = new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, 1L, properties);
+
+ @Test
+ public void constructor_throws_NPE_if_project_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("project can't be null");
+
+ new ProjectAnalysis(null,
+ ceTask,
+ analysis,
+ branch,
+ qualityGate,
+ 1L,
+ emptyMap());
+ }
+
+ @Test
+ public void constructor_throws_NPE_if_properties_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("properties can't be null");
+
+ new ProjectAnalysis(project,
+ ceTask,
+ analysis,
+ branch,
+ qualityGate,
+ 1L,
+ null);
+ }
+
+ @Test
+ public void verify_getters() {
+ assertThat(underTest.getCeTask().get()).isSameAs(ceTask);
+ assertThat(underTest.getProject()).isSameAs(project);
+ assertThat(underTest.getBranch().get()).isSameAs(branch);
+ assertThat(underTest.getQualityGate().get()).isSameAs(qualityGate);
+ assertThat(underTest.getProperties()).isEqualTo(properties);
+ assertThat(underTest.getAnalysis().get()).isEqualTo(analysis);
+
+ ProjectAnalysis underTestWithNulls = new ProjectAnalysis(project, null, null, null, null, null, emptyMap());
+ assertThat(underTestWithNulls.getCeTask()).isEmpty();
+ assertThat(underTestWithNulls.getBranch()).isEmpty();
+ assertThat(underTestWithNulls.getQualityGate()).isEmpty();
+ assertThat(underTestWithNulls.getProperties()).isEmpty();
+ assertThat(underTestWithNulls.getAnalysis()).isEmpty();
+ }
+
+ @Test
+ public void defines_equals_based_on_all_fields() {
+ assertThat(underTest).isEqualTo(underTest);
+ assertThat(underTest).isEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, 1L, properties));
+ assertThat(underTest).isNotEqualTo(null);
+ assertThat(underTest).isNotEqualTo(new Object());
+ assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, new CeTask("2", CeTask.Status.SUCCESS), analysis, branch, qualityGate, 1L, properties));
+ assertThat(underTest).isNotEqualTo(new ProjectAnalysis(new Project("A", "B", "C"), ceTask, analysis, branch, qualityGate, 1L, properties));
+ assertThat(underTest).isNotEqualTo(new ProjectAnalysis(new Project("A", "B", "C"), ceTask, analysis, branch, qualityGate, 1L, properties));
+ assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, null, null, null, qualityGate, 1L, properties));
+ assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, null, null, qualityGate, 1L, properties));
+ assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, new Analysis("foo", 1_500L), null, qualityGate, 1L, properties));
+ assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, null, qualityGate, 1L, properties));
+ assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, new Branch(false, "B", Branch.Type.SHORT), qualityGate, 1L, properties));
+ assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, null, 1L, properties));
+ EvaluatedQualityGate otherQualityGate = EvaluatedQualityGate.newBuilder()
+ .setQualityGate(new QualityGate("A", "B", emptySet()))
+ .setStatus(Metric.Level.WARN)
+ .build();
+ assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, otherQualityGate, 1L, properties));
+ assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, null, properties));
+ assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, 2L, properties));
+ assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, 1L, emptyMap()));
+ assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, 1L, ImmutableMap.of("A", "B")));
+ }
+
+ @Test
+ public void defines_hashcode_based_on_all_fields() {
+ assertThat(underTest.hashCode()).isEqualTo(underTest.hashCode());
+ assertThat(underTest.hashCode()).isEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, 1L, properties).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new Object().hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, new CeTask("2", CeTask.Status.SUCCESS), analysis, branch, qualityGate, 1L, properties).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(new Project("A", "B", "C"), ceTask, analysis, branch, qualityGate, 1L, properties).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(new Project("A", "B", "C"), ceTask, analysis, branch, qualityGate, 1L, properties).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, null, null, null, qualityGate, 1L, properties).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, null, null, qualityGate, 1L, properties).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, new Analysis("foo", 1_500L), null, qualityGate, 1L, properties).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, null, qualityGate, 1L, properties).hashCode());
+ assertThat(underTest.hashCode())
+ .isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, new Branch(false, "B", Branch.Type.SHORT), qualityGate, 1L, properties).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, null, 1L, properties).hashCode());
+ EvaluatedQualityGate otherQualityGate = EvaluatedQualityGate.newBuilder()
+ .setQualityGate(new QualityGate("A", "B", emptySet()))
+ .setStatus(Metric.Level.WARN)
+ .build();
+ assertThat(underTest.hashCode())
+ .isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, otherQualityGate, 1L, properties).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, this.qualityGate, null, properties).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, this.qualityGate, 2L, properties).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, this.qualityGate, 1L, emptyMap()).hashCode());
+ assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, this.qualityGate, 1L, ImmutableMap.of("B", "C")).hashCode());
+ }
+
+ @Test
+ public void verify_toString() {
+ assertThat(underTest.toString()).isEqualTo(
+ "ProjectAnalysis{project=Project{uuid='uuid', key='key', name='name', description=null}, ceTask=CeTask{id='id', status=SUCCESS}, branch=Branch{main=true, name='name', type=SHORT}, qualityGate=EvaluatedQualityGate{qualityGate=QualityGate{id=id, name='name', conditions=[]}, status=WARN, evaluatedConditions=[]}, updatedAt=1, properties={a=b}, analysis=Analysis{uuid='analysis_uuid', date=1500}}");
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.webhook.WebhookDbTester;
+import org.sonar.server.async.AsyncExecution;
+import org.sonar.server.organization.DefaultOrganizationProvider;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.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;
+import static org.sonar.api.utils.log.LoggerLevel.DEBUG;
+import static org.sonar.db.DbTester.create;
+import static org.sonar.db.webhook.WebhookTesting.newWebhook;
+import static org.sonar.server.organization.TestDefaultOrganizationProvider.from;
+
+public class SynchronousWebHooksImplTest {
+
+ private static final long NOW = 1_500_000_000_000L;
+
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ @Rule
+ public DbTester db = create();
+ private DbClient dbClient = db.getDbClient();
+
+ private WebhookDbTester webhookDbTester = db.webhooks();
+ private ComponentDbTester componentDbTester = db.components();
+ private DefaultOrganizationProvider defaultOrganizationProvider = from(db);
+
+ private final TestWebhookCaller caller = new TestWebhookCaller();
+ private final WebhookDeliveryStorage deliveryStorage = mock(WebhookDeliveryStorage.class);
+ private final WebhookPayload mock = mock(WebhookPayload.class);
+ private final AsyncExecution synchronousAsyncExecution = Runnable::run;
+ private final WebHooksImpl underTest = new WebHooksImpl(caller, deliveryStorage, synchronousAsyncExecution, dbClient);
+
+ @Test
+ public void isEnabled_returns_false_if_no_webhooks() {
+ ComponentDto componentDto = componentDbTester.insertPrivateProject();
+
+ assertThat(underTest.isEnabled(componentDto)).isFalse();
+ }
+
+ @Test
+ public void isEnabled_returns_true_if_one_valid_global_webhook() {
+ ComponentDto componentDto = componentDbTester.insertPrivateProject();
+ webhookDbTester.insert(newWebhook(componentDto).setName("First").setUrl("http://url1"));
+
+ assertThat(underTest.isEnabled(componentDto)).isTrue();
+ }
+
+ @Test
+ public void isEnabled_returns_true_if_one_valid_project_webhook() {
+ String organizationUuid = defaultOrganizationProvider.get().getUuid();
+ ComponentDto componentDto = componentDbTester.insertPrivateProject().setOrganizationUuid(organizationUuid);
+ webhookDbTester.insert(newWebhook(componentDto).setName("First").setUrl("http://url1"));
+
+ assertThat(underTest.isEnabled(componentDto)).isTrue();
+ }
+
+
+ @Test
+ public void do_nothing_if_no_webhooks() {
+ ComponentDto componentDto = componentDbTester.insertPrivateProject().setOrganizationUuid(defaultOrganizationProvider.get().getUuid());
+
+ underTest.sendProjectAnalysisUpdate(new WebHooks.Analysis(componentDto.uuid(), "1", "#1"), () -> mock);
+
+ assertThat(caller.countSent()).isEqualTo(0);
+ assertThat(logTester.logs(DEBUG)).isEmpty();
+ verifyZeroInteractions(deliveryStorage);
+ }
+
+ @Test
+ public void send_global_webhooks() {
+
+ ComponentDto componentDto = componentDbTester.insertPrivateProject();
+ webhookDbTester.insert(newWebhook(componentDto).setName("First").setUrl("http://url1"));
+ webhookDbTester.insert(newWebhook(componentDto).setName("Second").setUrl("http://url2"));
+ caller.enqueueSuccess(NOW, 200, 1_234);
+ caller.enqueueFailure(NOW, new IOException("Fail to connect"));
+
+ underTest.sendProjectAnalysisUpdate(new WebHooks.Analysis(componentDto.uuid(), "1", "#1"), () -> mock);
+
+ assertThat(caller.countSent()).isEqualTo(2);
+ assertThat(logTester.logs(DEBUG)).contains("Sent webhook 'First' | url=http://url1 | time=1234ms | status=200");
+ assertThat(logTester.logs(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(componentDto.uuid());
+
+ }
+
+ @Test
+ public void send_project_webhooks() {
+
+ String organizationUuid = defaultOrganizationProvider.get().getUuid();
+ ComponentDto componentDto = componentDbTester.insertPrivateProject().setOrganizationUuid(organizationUuid);
+ webhookDbTester.insert(newWebhook(componentDto).setName("First").setUrl("http://url1"));
+ caller.enqueueSuccess(NOW, 200, 1_234);
+
+ underTest.sendProjectAnalysisUpdate(new WebHooks.Analysis(componentDto.uuid(), "1", "#1"), () -> mock);
+
+ assertThat(caller.countSent()).isEqualTo(1);
+ assertThat(logTester.logs(DEBUG)).contains("Sent webhook 'First' | url=http://url1 | time=1234ms | status=200");
+ verify(deliveryStorage).persist(any(WebhookDelivery.class));
+ verify(deliveryStorage).purge(componentDto.uuid());
+
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.LinkedList;
+import java.util.Queue;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.annotation.Nullable;
+
+import static java.util.Objects.requireNonNull;
+
+public class TestWebhookCaller implements WebhookCaller {
+
+ private final Queue<Item> deliveries = new LinkedList<>();
+ private final AtomicInteger countSent = new AtomicInteger(0);
+
+ public TestWebhookCaller enqueueSuccess(long at, int httpCode, int durationMs) {
+ deliveries.add(new Item(at, httpCode, durationMs, null));
+ return this;
+ }
+
+ public TestWebhookCaller enqueueFailure(long at, Throwable t) {
+ deliveries.add(new Item(at, null, null, t));
+ return this;
+ }
+
+ @Override
+ public WebhookDelivery call(Webhook webhook, WebhookPayload payload) {
+ Item item = requireNonNull(deliveries.poll(), "Queue is empty");
+ countSent.incrementAndGet();
+ return new WebhookDelivery.Builder()
+ .setAt(item.at)
+ .setHttpStatus(item.httpCode)
+ .setDurationInMs(item.durationMs)
+ .setError(item.throwable)
+ .setPayload(payload)
+ .setWebhook(webhook)
+ .build();
+ }
+
+ public int countSent() {
+ return countSent.get();
+ }
+
+ private static class Item {
+ final long at;
+ final Integer httpCode;
+ final Integer durationMs;
+ final Throwable throwable;
+
+ Item(long at, @Nullable Integer httpCode, @Nullable Integer durationMs, @Nullable Throwable throwable) {
+ this.at = at;
+ this.httpCode = httpCode;
+ this.durationMs = durationMs;
+ this.throwable = throwable;
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 okhttp3.Credentials;
+import okhttp3.HttpUrl;
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+import okhttp3.mockwebserver.RecordedRequest;
+import org.apache.commons.lang.RandomStringUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.DisableOnDebug;
+import org.junit.rules.TestRule;
+import org.junit.rules.Timeout;
+import org.sonar.api.SonarQubeSide;
+import org.sonar.api.SonarRuntime;
+import org.sonar.api.config.internal.MapSettings;
+import org.sonar.api.internal.SonarRuntimeImpl;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.Version;
+import org.sonar.api.utils.internal.TestSystem2;
+import org.sonar.server.util.OkHttpClientProvider;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class WebhookCallerImplTest {
+
+ private static final long NOW = 1_500_000_000_000L;
+ private static final String PROJECT_UUID = "P_UUID1";
+ private static final String WEBHOOK_UUID = "WH_UUID1";
+ private static final String CE_TASK_UUID = "CE_UUID1";
+ private static final String SOME_JSON = "{\"payload\": {}}";
+ private static final WebhookPayload PAYLOAD = new WebhookPayload("P1", SOME_JSON);
+
+ @Rule
+ public MockWebServer server = new MockWebServer();
+
+ @Rule
+ public TestRule safeguardTimeout = new DisableOnDebug(Timeout.seconds(60));
+
+ private System2 system = new TestSystem2().setNow(NOW);
+
+ @Test
+ public void send_posts_payload_to_http_server() throws Exception {
+ Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", server.url("/ping").toString());
+
+ server.enqueue(new MockResponse().setBody("pong").setResponseCode(201));
+ WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
+
+ assertThat(delivery.getHttpStatus()).hasValue(201);
+ assertThat(delivery.getWebhook().getUuid()).isEqualTo(WEBHOOK_UUID);
+ assertThat(delivery.getDurationInMs().get()).isGreaterThanOrEqualTo(0);
+ assertThat(delivery.getError()).isEmpty();
+ assertThat(delivery.getAt()).isEqualTo(NOW);
+ assertThat(delivery.getWebhook()).isSameAs(webhook);
+ assertThat(delivery.getPayload()).isSameAs(PAYLOAD);
+
+ RecordedRequest recordedRequest = server.takeRequest();
+ assertThat(recordedRequest.getMethod()).isEqualTo("POST");
+ assertThat(recordedRequest.getPath()).isEqualTo("/ping");
+ assertThat(recordedRequest.getBody().readUtf8()).isEqualTo(PAYLOAD.getJson());
+ assertThat(recordedRequest.getHeader("User-Agent")).isEqualTo("SonarQube/6.2");
+ assertThat(recordedRequest.getHeader("Content-Type")).isEqualTo("application/json; charset=utf-8");
+ assertThat(recordedRequest.getHeader("X-SonarQube-Project")).isEqualTo(PAYLOAD.getProjectKey());
+ }
+
+ @Test
+ public void silently_catch_error_when_external_server_does_not_answer() throws Exception {
+ Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", server.url("/ping").toString());
+
+ server.shutdown();
+ WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
+
+ assertThat(delivery.getHttpStatus()).isEmpty();
+ assertThat(delivery.getDurationInMs().get()).isGreaterThanOrEqualTo(0);
+ // message can be "Connection refused" or "connect timed out"
+ assertThat(delivery.getErrorMessage().get()).matches("(.*Connection refused.*)|(.*connect timed out.*)");
+ assertThat(delivery.getAt()).isEqualTo(NOW);
+ assertThat(delivery.getWebhook()).isSameAs(webhook);
+ assertThat(delivery.getPayload()).isSameAs(PAYLOAD);
+ }
+
+ @Test
+ public void silently_catch_error_when_url_is_incorrect() {
+ Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", "this_is_not_an_url");
+
+ WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
+
+ assertThat(delivery.getHttpStatus()).isEmpty();
+ assertThat(delivery.getDurationInMs().get()).isGreaterThanOrEqualTo(0);
+ assertThat(delivery.getError().get()).isInstanceOf(IllegalArgumentException.class);
+ assertThat(delivery.getErrorMessage().get()).isEqualTo("Webhook URL is not valid: this_is_not_an_url");
+ assertThat(delivery.getAt()).isEqualTo(NOW);
+ assertThat(delivery.getWebhook()).isSameAs(webhook);
+ assertThat(delivery.getPayload()).isSameAs(PAYLOAD);
+ }
+
+ /**
+ * SONAR-8799
+ */
+ @Test
+ public void redirects_should_be_followed_with_POST_method() throws Exception {
+ Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", server.url("/redirect").toString());
+
+ // /redirect redirects to /target
+ server.enqueue(new MockResponse().setResponseCode(307).setHeader("Location", server.url("target")));
+ server.enqueue(new MockResponse().setResponseCode(200));
+
+ WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
+
+ assertThat(delivery.getHttpStatus().get()).isEqualTo(200);
+ assertThat(delivery.getDurationInMs().get()).isGreaterThanOrEqualTo(0);
+ assertThat(delivery.getError()).isEmpty();
+ assertThat(delivery.getAt()).isEqualTo(NOW);
+ assertThat(delivery.getWebhook()).isSameAs(webhook);
+ assertThat(delivery.getPayload()).isSameAs(PAYLOAD);
+
+ takeAndVerifyPostRequest("/redirect");
+ takeAndVerifyPostRequest("/target");
+ }
+
+ @Test
+ public void credentials_are_propagated_to_POST_redirects() throws Exception {
+ HttpUrl url = server.url("/redirect").newBuilder().username("theLogin").password("thePassword").build();
+ Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", url.toString());
+
+ // /redirect redirects to /target
+ server.enqueue(new MockResponse().setResponseCode(307).setHeader("Location", server.url("target")));
+ server.enqueue(new MockResponse().setResponseCode(200));
+
+ WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
+
+ assertThat(delivery.getHttpStatus().get()).isEqualTo(200);
+
+ RecordedRequest redirectedRequest = takeAndVerifyPostRequest("/redirect");
+ assertThat(redirectedRequest.getHeader("Authorization")).isEqualTo(Credentials.basic(url.username(), url.password()));
+
+ RecordedRequest targetRequest = takeAndVerifyPostRequest("/target");
+ assertThat(targetRequest.getHeader("Authorization")).isEqualTo(Credentials.basic(url.username(), url.password()));
+ }
+
+ @Test
+ public void redirects_throws_ISE_if_header_Location_is_missing() {
+ HttpUrl url = server.url("/redirect");
+ Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", url.toString());
+
+ server.enqueue(new MockResponse().setResponseCode(307));
+
+ WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
+
+ Throwable error = delivery.getError().get();
+ assertThat(error)
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("Missing HTTP header 'Location' in redirect of " + url);
+ }
+
+ @Test
+ public void redirects_throws_ISE_if_header_Location_does_not_relate_to_a_supported_protocol() {
+ HttpUrl url = server.url("/redirect");
+ Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", url.toString());
+
+ server.enqueue(new MockResponse().setResponseCode(307).setHeader("Location", "ftp://foo"));
+
+ WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
+
+ Throwable error = delivery.getError().get();
+ assertThat(error)
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("Unsupported protocol in redirect of " + url + " to ftp://foo");
+ }
+
+ @Test
+ public void send_basic_authentication_header_if_url_contains_credentials() throws Exception {
+ HttpUrl url = server.url("/ping").newBuilder().username("theLogin").password("thePassword").build();
+ Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", url.toString());
+ server.enqueue(new MockResponse().setBody("pong"));
+
+ WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
+
+ assertThat(delivery.getWebhook().getUrl())
+ .isEqualTo(url.toString())
+ .contains("://theLogin:thePassword@");
+ RecordedRequest recordedRequest = takeAndVerifyPostRequest("/ping");
+ assertThat(recordedRequest.getHeader("Authorization")).isEqualTo(Credentials.basic(url.username(), url.password()));
+ }
+
+ private RecordedRequest takeAndVerifyPostRequest(String expectedPath) throws Exception {
+ RecordedRequest request = server.takeRequest();
+
+ assertThat(request.getMethod()).isEqualTo("POST");
+ assertThat(request.getPath()).isEqualTo(expectedPath);
+ assertThat(request.getHeader("User-Agent")).isEqualTo("SonarQube/6.2");
+ return request;
+ }
+
+ private WebhookCaller newSender() {
+ SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(Version.parse("6.2"), SonarQubeSide.SERVER);
+ return new WebhookCallerImpl(system, new OkHttpClientProvider().provide(new MapSettings().asConfig(), runtime));
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 org.apache.commons.lang.RandomStringUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.webhook.WebhookDbTesting;
+import org.sonar.db.webhook.WebhookDeliveryDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.db.webhook.WebhookDbTesting.selectAllDeliveryUuids;
+
+public class WebhookDeliveryStorageTest {
+
+ private static final String DELIVERY_UUID = "abcde1234";
+ private static final long NOW = 1_500_000_000_000L;
+ private static final long TWO_MONTHS_AGO = NOW - 60L * 24 * 60 * 60 * 1000;
+ private static final long TWO_WEEKS_AGO = NOW - 14L * 24 * 60 * 60 * 1000;
+
+ private final System2 system = mock(System2.class);
+
+ @Rule
+ public final DbTester dbTester = DbTester.create(system).setDisableDefaultOrganization(true);
+
+ private DbClient dbClient = dbTester.getDbClient();
+ private DbSession dbSession = dbTester.getSession();
+ private UuidFactory uuidFactory = mock(UuidFactory.class);
+ private WebhookDeliveryStorage underTest = new WebhookDeliveryStorage(dbClient, system, uuidFactory);
+
+ @Test
+ public void persist_generates_uuid_then_inserts_record() {
+ when(uuidFactory.create()).thenReturn(DELIVERY_UUID);
+ WebhookDelivery delivery = newBuilderTemplate().build();
+
+ underTest.persist(delivery);
+
+ WebhookDeliveryDto dto = dbClient.webhookDeliveryDao().selectByUuid(dbSession, DELIVERY_UUID).get();
+ assertThat(dto.getUuid()).isEqualTo(DELIVERY_UUID);
+ assertThat(dto.getWebhookUuid()).isEqualTo("WEBHOOK_UUID_1");
+ assertThat(dto.getComponentUuid()).isEqualTo(delivery.getWebhook().getComponentUuid());
+ assertThat(dto.getCeTaskUuid()).isEqualTo(delivery.getWebhook().getCeTaskUuid().get());
+ assertThat(dto.getName()).isEqualTo(delivery.getWebhook().getName());
+ assertThat(dto.getUrl()).isEqualTo(delivery.getWebhook().getUrl());
+ assertThat(dto.getCreatedAt()).isEqualTo(delivery.getAt());
+ assertThat(dto.getHttpStatus()).isEqualTo(delivery.getHttpStatus().get());
+ assertThat(dto.getDurationMs()).isEqualTo(delivery.getDurationInMs().get());
+ assertThat(dto.getPayload()).isEqualTo(delivery.getPayload().getJson());
+ assertThat(dto.getErrorStacktrace()).isNull();
+ }
+
+ @Test
+ public void persist_error_stacktrace() {
+ when(uuidFactory.create()).thenReturn(DELIVERY_UUID);
+ WebhookDelivery delivery = newBuilderTemplate()
+ .setError(new IOException("fail to connect"))
+ .build();
+
+ underTest.persist(delivery);
+
+ WebhookDeliveryDto dto = dbClient.webhookDeliveryDao().selectByUuid(dbSession, DELIVERY_UUID).get();
+ assertThat(dto.getErrorStacktrace()).contains("java.io.IOException", "fail to connect");
+ }
+
+ @Test
+ public void purge_deletes_records_older_than_one_month_on_the_project() {
+ when(system.now()).thenReturn(NOW);
+ dbClient.webhookDeliveryDao().insert(dbSession, newDto("D1", "PROJECT_1", TWO_MONTHS_AGO));
+ dbClient.webhookDeliveryDao().insert(dbSession, newDto("D2", "PROJECT_1", TWO_WEEKS_AGO));
+ dbClient.webhookDeliveryDao().insert(dbSession, newDto("D3", "PROJECT_2", TWO_MONTHS_AGO));
+ dbSession.commit();
+
+ underTest.purge("PROJECT_1");
+
+ // do not purge another project PROJECT_2
+ assertThat(selectAllDeliveryUuids(dbTester, dbSession)).containsOnly("D2", "D3");
+ }
+
+ private static WebhookDelivery.Builder newBuilderTemplate() {
+ return new WebhookDelivery.Builder()
+ .setWebhook(new Webhook("WEBHOOK_UUID_1", "COMPONENT1", "TASK1", RandomStringUtils.randomAlphanumeric(40),"Jenkins", "http://jenkins"))
+ .setPayload(new WebhookPayload("my-project", "{json}"))
+ .setAt(1_000_000L)
+ .setHttpStatus(200)
+ .setDurationInMs(1_000);
+ }
+
+ private static WebhookDeliveryDto newDto(String uuid, String componentUuid, long at) {
+ return WebhookDbTesting.newDto()
+ .setUuid(uuid)
+ .setComponentUuid(componentUuid)
+ .setCreatedAt(at);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+
+public class WebhookDeliveryTest {
+
+ @Test
+ public void isSuccess_returns_false_if_failed_to_send_http_request() {
+ WebhookDelivery delivery = newBuilderTemplate()
+ .setError(new IOException("Fail to connect"))
+ .build();
+
+ assertThat(delivery.isSuccess()).isFalse();
+ }
+
+ @Test
+ public void isSuccess_returns_false_if_http_response_returns_error_status() {
+ WebhookDelivery delivery = newBuilderTemplate()
+ .setHttpStatus(404)
+ .build();
+
+ assertThat(delivery.isSuccess()).isFalse();
+ }
+
+ @Test
+ public void isSuccess_returns_true_if_http_response_returns_2xx_code() {
+ WebhookDelivery delivery = newBuilderTemplate()
+ .setHttpStatus(204)
+ .build();
+
+ assertThat(delivery.isSuccess()).isTrue();
+ }
+
+ @Test
+ public void getErrorMessage_returns_empty_if_no_error() {
+ WebhookDelivery delivery = newBuilderTemplate().build();
+
+ assertThat(delivery.getErrorMessage()).isEmpty();
+ }
+
+ @Test
+ public void getErrorMessage_returns_root_cause_message_if_error() {
+ Exception rootCause = new IOException("fail to connect");
+ Exception cause = new IOException("nested", rootCause);
+ WebhookDelivery delivery = newBuilderTemplate()
+ .setError(cause)
+ .build();
+
+ assertThat(delivery.getErrorMessage().get()).isEqualTo("fail to connect");
+ }
+
+ private static WebhookDelivery.Builder newBuilderTemplate() {
+ return new WebhookDelivery.Builder()
+ .setWebhook(mock(Webhook.class))
+ .setPayload(mock(WebhookPayload.class))
+ .setAt(1_000L);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.core.platform.ComponentContainer;
+import org.sonar.server.webhook.WebhookModule;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.core.platform.ComponentContainer.COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER;
+
+public class WebhookModuleTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private WebhookModule underTest = new WebhookModule();
+
+ @Test
+ public void verify_count_of_added_components() {
+ ComponentContainer container = new ComponentContainer();
+
+ underTest.configure(container);
+
+ assertThat(container.size()).isEqualTo(4 + COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 com.google.common.collect.ImmutableMap;
+import java.util.Map;
+import javax.annotation.Nullable;
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.platform.Server;
+import org.sonar.api.utils.System2;
+import org.sonar.server.project.Project;
+import org.sonar.server.qualitygate.Condition;
+import org.sonar.server.qualitygate.EvaluatedCondition;
+import org.sonar.server.qualitygate.EvaluatedQualityGate;
+import org.sonar.server.qualitygate.QualityGate;
+
+import static java.util.Collections.emptyMap;
+import static java.util.Collections.emptySet;
+import static java.util.Collections.singleton;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.test.JsonAssert.assertJson;
+
+public class WebhookPayloadFactoryImplTest {
+
+ private static final String PROJECT_KEY = "P1";
+
+ private Server server = mock(Server.class);
+ private System2 system2 = mock(System2.class);
+ private WebhookPayloadFactory underTest = new WebhookPayloadFactoryImpl(server, system2);
+
+ @Before
+ public void setUp() throws Exception {
+ when(server.getPublicRootUrl()).thenReturn("http://foo");
+ when(system2.now()).thenReturn(1_500_999L);
+ }
+
+ @Test
+ public void create_payload_for_successful_analysis() {
+ CeTask task = new CeTask("#1", CeTask.Status.SUCCESS);
+ Condition condition = new Condition("coverage", Condition.Operator.GREATER_THAN, "70.0", "75.0", true);
+ EvaluatedQualityGate gate = EvaluatedQualityGate.newBuilder()
+ .setQualityGate(new QualityGate("G1", "Gate One", singleton(condition)))
+ .setStatus(Metric.Level.WARN)
+ .addCondition(condition, EvaluatedCondition.EvaluationStatus.WARN, "74.0")
+ .build();
+ ProjectAnalysis analysis = newAnalysis(task, gate, null, 1_500_000_000_000L, emptyMap());
+
+ WebhookPayload payload = underTest.create(analysis);
+ assertThat(payload.getProjectKey()).isEqualTo(PROJECT_KEY);
+ assertJson(payload.getJson())
+ .isSimilarTo("{" +
+ " \"serverUrl\": \"http://foo\"," +
+ " \"taskId\": \"#1\"," +
+ " \"status\": \"SUCCESS\"," +
+ " \"analysedAt\": \"2017-07-14T04:40:00+0200\"," +
+ " \"changedAt\": \"2017-07-14T04:40:00+0200\"," +
+ " \"project\": {" +
+ " \"key\": \"P1\"," +
+ " \"name\": \"Project One\"," +
+ " \"url\": \"http://foo/dashboard?id=P1\"" +
+ " }," +
+ " \"qualityGate\": {" +
+ " \"name\": \"Gate One\"," +
+ " \"status\": \"WARN\"," +
+ " \"conditions\": [" +
+ " {" +
+ " \"metric\": \"coverage\"," +
+ " \"operator\": \"GREATER_THAN\"," +
+ " \"value\": \"74.0\"," +
+ " \"status\": \"WARN\"," +
+ " \"onLeakPeriod\": true," +
+ " \"errorThreshold\": \"70.0\"," +
+ " \"warningThreshold\": \"75.0\"" +
+ " }" +
+ " ]" +
+ " }," +
+ " \"properties\": {" +
+ " }" +
+ "}");
+ }
+
+ @Test
+ public void create_payload_with_gate_conditions_without_value() {
+ CeTask task = new CeTask("#1", CeTask.Status.SUCCESS);
+
+ Condition condition = new Condition("coverage", Condition.Operator.GREATER_THAN, "70.0", "75.0", false);
+ EvaluatedQualityGate gate = EvaluatedQualityGate.newBuilder()
+ .setQualityGate(new QualityGate("G1", "Gate One", singleton(condition)))
+ .setStatus(Metric.Level.WARN)
+ .addCondition(condition, EvaluatedCondition.EvaluationStatus.NO_VALUE, null)
+ .build();
+ ProjectAnalysis analysis = newAnalysis(task, gate, null, 1_500_000_000_000L, emptyMap());
+
+ WebhookPayload payload = underTest.create(analysis);
+ assertThat(payload.getProjectKey()).isEqualTo(PROJECT_KEY);
+ assertJson(payload.getJson())
+ .isSimilarTo("{" +
+ " \"serverUrl\": \"http://foo\"," +
+ " \"taskId\": \"#1\"," +
+ " \"status\": \"SUCCESS\"," +
+ " \"analysedAt\": \"2017-07-14T04:40:00+0200\"," +
+ " \"changedAt\": \"2017-07-14T04:40:00+0200\"," +
+ " \"project\": {" +
+ " \"key\": \"P1\"," +
+ " \"name\": \"Project One\"," +
+ " \"url\": \"http://foo/dashboard?id=P1\"" +
+ " }," +
+ " \"qualityGate\": {" +
+ " \"name\": \"Gate One\"," +
+ " \"status\": \"WARN\"," +
+ " \"conditions\": [" +
+ " {" +
+ " \"metric\": \"coverage\"," +
+ " \"operator\": \"GREATER_THAN\"," +
+ " \"status\": \"NO_VALUE\"," +
+ " \"onLeakPeriod\": false," +
+ " \"errorThreshold\": \"70.0\"," +
+ " \"warningThreshold\": \"75.0\"" +
+ " }" +
+ " ]" +
+ " }" +
+ "}");
+ }
+
+ @Test
+ public void create_payload_with_analysis_properties() {
+ CeTask task = new CeTask("#1", CeTask.Status.SUCCESS);
+ EvaluatedQualityGate gate = EvaluatedQualityGate.newBuilder()
+ .setQualityGate(new QualityGate("G1", "Gate One", emptySet()))
+ .setStatus(Metric.Level.WARN)
+ .build();
+ Map<String, String> scannerProperties = ImmutableMap.of(
+ "sonar.analysis.revision", "ab45d24",
+ "sonar.analysis.buildNumber", "B123",
+ "not.prefixed.with.sonar.analysis", "should be ignored",
+ "ignored", "should be ignored too");
+ ProjectAnalysis analysis = newAnalysis(task, gate, null, 1_500_000_000_000L, scannerProperties);
+
+ WebhookPayload payload = underTest.create(analysis);
+ assertJson(payload.getJson())
+ .isSimilarTo("{" +
+ " \"serverUrl\": \"http://foo\"," +
+ " \"taskId\": \"#1\"," +
+ " \"status\": \"SUCCESS\"," +
+ " \"analysedAt\": \"2017-07-14T04:40:00+0200\"," +
+ " \"changedAt\": \"2017-07-14T04:40:00+0200\"," +
+ " \"project\": {" +
+ " \"key\": \"P1\"," +
+ " \"name\": \"Project One\"," +
+ " \"url\": \"http://foo/dashboard?id=P1\"" +
+ " }," +
+ " \"qualityGate\": {" +
+ " \"name\": \"Gate One\"," +
+ " \"status\": \"WARN\"," +
+ " \"conditions\": [" +
+ " ]" +
+ " }," +
+ " \"properties\": {" +
+ " \"sonar.analysis.revision\": \"ab45d24\"," +
+ " \"sonar.analysis.buildNumber\": \"B123\"" +
+ " }" +
+ "}");
+ assertThat(payload.getJson())
+ .doesNotContain("not.prefixed.with.sonar.analysis")
+ .doesNotContain("ignored");
+ }
+
+ @Test
+ public void create_payload_for_failed_analysis() {
+ CeTask ceTask = new CeTask("#1", CeTask.Status.FAILED);
+ ProjectAnalysis analysis = newAnalysis(ceTask, null, null, 1_500_000_000_000L, emptyMap());
+
+ WebhookPayload payload = underTest.create(analysis);
+
+ assertThat(payload.getProjectKey()).isEqualTo(PROJECT_KEY);
+ assertJson(payload.getJson())
+ .isSimilarTo("{" +
+ " \"serverUrl\": \"http://foo\"," +
+ " \"taskId\": \"#1\"," +
+ " \"status\": \"FAILED\"," +
+ " \"changedAt\": \"2017-07-14T04:40:00+0200\"," +
+ " \"project\": {" +
+ " \"key\": \"P1\"," +
+ " \"name\": \"Project One\"," +
+ " \"url\": \"http://foo/dashboard?id=P1\"" +
+ " }," +
+ " \"properties\": {" +
+ " }" +
+ "}");
+ }
+
+ @Test
+ public void create_payload_for_no_analysis_date() {
+ CeTask ceTask = new CeTask("#1", CeTask.Status.FAILED);
+ ProjectAnalysis analysis = newAnalysis(ceTask, null, null, null, emptyMap());
+
+ WebhookPayload payload = underTest.create(analysis);
+
+ assertThat(payload.getProjectKey()).isEqualTo(PROJECT_KEY);
+ assertJson(payload.getJson())
+ .isSimilarTo("{" +
+ " \"serverUrl\": \"http://foo\"," +
+ " \"taskId\": \"#1\"," +
+ " \"status\": \"FAILED\"," +
+ " \"changedAt\": \"1970-01-01T01:25:00+0100\"," +
+ " \"project\": {" +
+ " \"key\": \"P1\"," +
+ " \"name\": \"Project One\"" +
+ " }," +
+ " \"properties\": {" +
+ " }" +
+ "}");
+ }
+
+ @Test
+ public void create_payload_on_short_branch() {
+ CeTask task = new CeTask("#1", CeTask.Status.SUCCESS);
+ ProjectAnalysis analysis = newAnalysis(task, null, new Branch(false, "feature/foo", Branch.Type.SHORT), 1_500_000_000_000L, emptyMap());
+
+ WebhookPayload payload = underTest.create(analysis);
+ assertJson(payload.getJson())
+ .isSimilarTo("{" +
+ "\"branch\": {" +
+ " \"name\": \"feature/foo\"," +
+ " \"type\": \"SHORT\"," +
+ " \"isMain\": false," +
+ " \"url\": \"http://foo/project/issues?branch=feature%2Ffoo&id=P1&resolved=false\"" +
+ "}" +
+ "}");
+ }
+
+ @Test
+ public void create_payload_on_pull_request() {
+ CeTask task = new CeTask("#1", CeTask.Status.SUCCESS);
+ ProjectAnalysis analysis = newAnalysis(task, null, new Branch(false, "pr/foo", Branch.Type.PULL_REQUEST), 1_500_000_000_000L, emptyMap());
+
+ WebhookPayload payload = underTest.create(analysis);
+ assertJson(payload.getJson())
+ .isSimilarTo("{" +
+ "\"branch\": {" +
+ " \"name\": \"pr/foo\"," +
+ " \"type\": \"PULL_REQUEST\"," +
+ " \"isMain\": false," +
+ " \"url\": \"http://foo/project/issues?pullRequest=pr%2Ffoo&id=P1&resolved=false\"" +
+ "}" +
+ "}");
+ }
+
+ @Test
+ public void create_without_ce_task() {
+ ProjectAnalysis analysis = newAnalysis(null, null, null, null, emptyMap());
+
+ WebhookPayload payload = underTest.create(analysis);
+ String json = payload.getJson();
+ assertThat(json).doesNotContain("taskId");
+ assertJson(json)
+ .isSimilarTo("{" +
+ " \"serverUrl\": \"http://foo\"," +
+ " \"status\": \"SUCCESS\"," +
+ " \"changedAt\": \"1970-01-01T01:25:00+0100\"," +
+ " \"project\": {" +
+ " \"key\": \"P1\"," +
+ " \"name\": \"Project One\"" +
+ " }," +
+ " \"properties\": {" +
+ " }" +
+ "}");
+ }
+
+ @Test
+ public void create_payload_on_long_branch() {
+ CeTask task = new CeTask("#1", CeTask.Status.SUCCESS);
+ ProjectAnalysis analysis = newAnalysis(task, null, new Branch(false, "feature/foo", Branch.Type.LONG), 1_500_000_000_000L, emptyMap());
+
+ WebhookPayload payload = underTest.create(analysis);
+ assertJson(payload.getJson())
+ .isSimilarTo("{" +
+ "\"branch\": {" +
+ " \"name\": \"feature/foo\"" +
+ " \"type\": \"LONG\"" +
+ " \"isMain\": false," +
+ " \"url\": \"http://foo/dashboard?branch=feature%2Ffoo&id=P1\"" +
+ "}" +
+ "}");
+ }
+
+ @Test
+ public void create_payload_on_main_branch_without_name() {
+ CeTask task = new CeTask("#1", CeTask.Status.SUCCESS);
+ ProjectAnalysis analysis = newAnalysis(task, null, new Branch(true, null, Branch.Type.LONG), 1_500_000_000_000L, emptyMap());
+
+ WebhookPayload payload = underTest.create(analysis);
+ assertJson(payload.getJson())
+ .isSimilarTo("{" +
+ "\"branch\": {" +
+ " \"type\": \"LONG\"" +
+ " \"isMain\": true," +
+ " \"url\": \"http://foo/dashboard?id=P1\"" +
+ "}" +
+ "}");
+ }
+
+ private static ProjectAnalysis newAnalysis(@Nullable CeTask task, @Nullable EvaluatedQualityGate gate,
+ @Nullable Branch branch, @Nullable Long analysisDate, Map<String, String> scannerProperties) {
+ return new ProjectAnalysis(new Project("P1_UUID", PROJECT_KEY, "Project One"), task, analysisDate == null ? null : new Analysis("A_UUID1", analysisDate), branch,
+ gate, analysisDate, scannerProperties);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.Optional;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class WebhookTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void constructor_with_null_componentUuid_should_throw_NPE() {
+ expectedException.expect(NullPointerException.class);
+
+ new Webhook(randomAlphanumeric(40), null, null, null, randomAlphanumeric(10), randomAlphanumeric(10));
+ }
+
+ @Test
+ public void constructor_with_null_name_should_throw_NPE() {
+ expectedException.expect(NullPointerException.class);
+
+ new Webhook(randomAlphanumeric(40), randomAlphanumeric(10), null, null, null, randomAlphanumeric(10));
+ }
+
+ @Test
+ public void constructor_with_null_url_should_throw_NPE() {
+ expectedException.expect(NullPointerException.class);
+
+ new Webhook(randomAlphanumeric(40), randomAlphanumeric(10), null, null, randomAlphanumeric(10), null);
+ }
+
+ @Test
+ public void constructor_with_null_ceTaskUuid_or_analysisUuidurl_should_return_Optional_empty() {
+ String componentUuid = randomAlphanumeric(10);
+ String name = randomAlphanumeric(10);
+ String url = randomAlphanumeric(10);
+ Webhook underTest = new Webhook(randomAlphanumeric(40), componentUuid, null, null, name, url);
+
+ assertThat(underTest.getComponentUuid()).isEqualTo(componentUuid);
+ assertThat(underTest.getName()).isEqualTo(name);
+ assertThat(underTest.getUrl()).isEqualTo(url);
+ assertThat(underTest.getCeTaskUuid()).isEqualTo(Optional.empty());
+ assertThat(underTest.getAnalysisUuid()).isEqualTo(Optional.empty());
+
+ String ceTaskUuid = randomAlphanumeric(10);
+ String analysisUuid = randomAlphanumeric(10);
+ underTest = new Webhook(randomAlphanumeric(40), componentUuid, ceTaskUuid, analysisUuid, name, url);
+ assertThat(underTest.getComponentUuid()).isEqualTo(componentUuid);
+ assertThat(underTest.getName()).isEqualTo(name);
+ assertThat(underTest.getUrl()).isEqualTo(url);
+ assertThat(underTest.getCeTaskUuid().get()).isEqualTo(ceTaskUuid);
+ assertThat(underTest.getAnalysisUuid().get()).isEqualTo(analysisUuid);
+ }
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.async;
-
-public interface AsyncExecution {
- /**
- * Add the specified {@link Runnable} in queue for asynchronous processing.
- *
- * This method returns instantly and {@code r} is executed in another thread.
- *
- * @throws NullPointerException if r is {@code null}
- */
- void addToQueue(Runnable r);
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.async;
-
-public interface AsyncExecutionExecutorService {
- void addToQueue(Runnable r);
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.async;
-
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.server.util.AbstractStoppableExecutorService;
-
-import static java.util.concurrent.TimeUnit.MINUTES;
-
-public class AsyncExecutionExecutorServiceImpl
- extends AbstractStoppableExecutorService<ThreadPoolExecutor>
- implements AsyncExecutionExecutorService, AsyncExecutionMonitoring {
- private static final Logger LOG = Loggers.get(AsyncExecutionExecutorServiceImpl.class);
-
- private static final int MAX_THREAD_COUNT = 10;
- private static final int UNLIMITED_QUEUE = Integer.MAX_VALUE;
- private static final long KEEP_ALIVE_TIME_IN_MINUTES = 5L;
-
- public AsyncExecutionExecutorServiceImpl() {
- super(createDelegate());
- }
-
- private static ThreadPoolExecutor createDelegate() {
- ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
- MAX_THREAD_COUNT, MAX_THREAD_COUNT,
- KEEP_ALIVE_TIME_IN_MINUTES, MINUTES,
- new LinkedBlockingQueue<>(UNLIMITED_QUEUE),
- new ThreadFactoryBuilder()
- .setDaemon(false)
- .setNameFormat("SQ_async-%d")
- .setUncaughtExceptionHandler(((t, e) -> LOG.error("Thread " + t + " failed unexpectedly", e)))
- .build());
- threadPoolExecutor.allowCoreThreadTimeOut(true);
- return threadPoolExecutor;
- }
-
- @Override
- public void addToQueue(Runnable r) {
- this.submit(r);
- }
-
- @Override
- public int getQueueSize() {
- return delegate.getQueue().size();
- }
-
- @Override
- public int getWorkerCount() {
- return delegate.getPoolSize();
- }
-
- @Override
- public int getLargestWorkerCount() {
- return delegate.getLargestPoolSize();
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.async;
-
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-
-import static java.util.Objects.requireNonNull;
-
-public class AsyncExecutionImpl implements AsyncExecution {
- private static final Logger LOG = Loggers.get(AsyncExecutionImpl.class);
- private final AsyncExecutionExecutorService executorService;
-
- public AsyncExecutionImpl(AsyncExecutionExecutorService executorService) {
- this.executorService = executorService;
- }
-
- @Override
- public void addToQueue(Runnable r) {
- requireNonNull(r);
- executorService.addToQueue(() -> {
- try {
- r.run();
- } catch (Exception e) {
- LOG.error("Asynchronous task failed", e);
- }
- });
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.async;
-
-public interface AsyncExecutionMBean {
-
- String OBJECT_NAME = "SonarQube:name=AsyncExecution";
-
- long getQueueSize();
-
- long getWorkerCount();
-
- long getLargestWorkerCount();
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.async;
-
-import org.picocontainer.Startable;
-import org.sonar.process.Jmx;
-
-public class AsyncExecutionMBeanImpl implements AsyncExecutionMBean, Startable {
-
- private final AsyncExecutionMonitoring asyncExecutionMonitoring;
-
- public AsyncExecutionMBeanImpl(AsyncExecutionMonitoring asyncExecutionMonitoring) {
- this.asyncExecutionMonitoring = asyncExecutionMonitoring;
- }
-
- @Override
- public void start() {
- Jmx.register(OBJECT_NAME, this);
- }
-
- @Override
- public void stop() {
- Jmx.unregister(OBJECT_NAME);
- }
-
- @Override
- public long getQueueSize() {
- return asyncExecutionMonitoring.getQueueSize();
- }
-
- @Override
- public long getWorkerCount() {
- return asyncExecutionMonitoring.getWorkerCount();
- }
-
- @Override
- public long getLargestWorkerCount() {
- return asyncExecutionMonitoring.getLargestWorkerCount();
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.async;
-
-import org.sonar.core.platform.Module;
-
-public class AsyncExecutionModule extends Module {
- @Override
- protected void configureModule() {
- add(
- AsyncExecutionMBeanImpl.class,
- AsyncExecutionExecutorServiceImpl.class,
- AsyncExecutionImpl.class);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.async;
-
-public interface AsyncExecutionMonitoring {
- int getQueueSize();
-
- int getWorkerCount();
-
- int getLargestWorkerCount();
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.
- */
-@ParametersAreNonnullByDefault
-package org.sonar.server.async;
-
-import javax.annotation.ParametersAreNonnullByDefault;
import org.sonar.api.rules.AnnotationRuleParser;
import org.sonar.api.rules.XMLRuleParser;
import org.sonar.api.server.rule.RulesDefinitionXmlLoader;
-import org.sonar.server.ce.CeModule;
import org.sonar.ce.notification.ReportAnalysisFailureNotificationModule;
import org.sonar.core.component.DefaultResourceTypes;
import org.sonar.core.extension.CoreExtensionsInstaller;
import org.sonar.server.branch.BranchFeatureProxyImpl;
import org.sonar.server.branch.pr.ws.PullRequestWsModule;
import org.sonar.server.branch.ws.BranchWsModule;
+import org.sonar.server.ce.CeModule;
import org.sonar.server.ce.ws.CeWsModule;
import org.sonar.server.component.ComponentCleanerService;
import org.sonar.server.component.ComponentFinder;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.project;
-
-import java.util.Objects;
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.Immutable;
-import org.sonar.db.component.ComponentDto;
-
-@Immutable
-public class Project {
-
- private final String uuid;
- private final String key;
- private final String name;
- private final String description;
-
- public Project(String uuid, String key, String name) {
- this(uuid, key, name, null);
- }
-
- public Project(String uuid, String key, String name, @Nullable String description) {
- this.uuid = uuid;
- this.key = key;
- this.name = name;
- this.description = description;
- }
-
- public static Project from(ComponentDto project) {
- return new Project(project.uuid(), project.getDbKey(), project.name(), project.description());
- }
-
- /**
- * Always links to a row that exists in database.
- */
- public String getUuid() {
- return uuid;
- }
-
- /**
- * Always links to a row that exists in database.
- */
- public String getKey() {
- return key;
- }
-
- public String getName() {
- return name;
- }
-
- public String getDescription() {
- return description;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- Project project = (Project) o;
- return uuid.equals(project.uuid)
- && key.equals(project.key)
- && name.equals(project.name);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(uuid, key, name);
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder("Project{");
- sb.append("uuid='").append(uuid).append('\'');
- sb.append(", key='").append(key).append('\'');
- sb.append(", name='").append(name).append('\'');
- sb.append(", description=").append(toString(this.description));
- sb.append('}');
- return sb.toString();
- }
-
- private static String toString(@Nullable String s) {
- if (s == null) {
- return null;
- }
- return '\'' + s + '\'';
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.qualitygate;
-
-import java.util.Objects;
-import java.util.Optional;
-import java.util.stream.Stream;
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.Immutable;
-import org.sonar.db.qualitygate.QualityGateConditionDto;
-
-import static com.google.common.base.Strings.emptyToNull;
-import static java.util.Objects.requireNonNull;
-
-@Immutable
-public class Condition {
-
- private final String metricKey;
- private final Operator operator;
- @CheckForNull
- private final String warningThreshold;
- @CheckForNull
- private final String errorThreshold;
- private final boolean onLeakPeriod;
-
- public Condition(String metricKey, Operator operator,
- @Nullable String errorThreshold, @Nullable String warningThreshold,
- boolean onLeakPeriod) {
- this.metricKey = requireNonNull(metricKey, "metricKey can't be null");
- this.operator = requireNonNull(operator, "operator can't be null");
- this.onLeakPeriod = onLeakPeriod;
- this.errorThreshold = emptyToNull(errorThreshold);
- this.warningThreshold = emptyToNull(warningThreshold);
- }
-
- public String getMetricKey() {
- return metricKey;
- }
-
- public boolean isOnLeakPeriod() {
- return onLeakPeriod;
- }
-
- public Operator getOperator() {
- return operator;
- }
-
- public Optional<String> getWarningThreshold() {
- return Optional.ofNullable(warningThreshold);
- }
-
- public Optional<String> getErrorThreshold() {
- return Optional.ofNullable(errorThreshold);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- Condition condition = (Condition) o;
- return onLeakPeriod == condition.onLeakPeriod &&
- Objects.equals(metricKey, condition.metricKey) &&
- operator == condition.operator &&
- Objects.equals(warningThreshold, condition.warningThreshold) &&
- Objects.equals(errorThreshold, condition.errorThreshold);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(metricKey, operator, warningThreshold, errorThreshold, onLeakPeriod);
- }
-
- @Override
- public String toString() {
- return "Condition{" +
- "metricKey='" + metricKey + '\'' +
- ", operator=" + operator +
- ", warningThreshold=" + toString(warningThreshold) +
- ", errorThreshold=" + toString(errorThreshold) +
- ", onLeakPeriod=" + onLeakPeriod +
- '}';
- }
-
- private static String toString(@Nullable String errorThreshold) {
- if (errorThreshold == null) {
- return null;
- }
- return '\'' + errorThreshold + '\'';
- }
-
- public enum Operator {
- EQUALS(QualityGateConditionDto.OPERATOR_EQUALS),
- NOT_EQUALS(QualityGateConditionDto.OPERATOR_NOT_EQUALS),
- GREATER_THAN(QualityGateConditionDto.OPERATOR_GREATER_THAN),
- LESS_THAN(QualityGateConditionDto.OPERATOR_LESS_THAN);
-
- private final String dbValue;
-
- Operator(String dbValue) {
- this.dbValue = dbValue;
- }
-
- public String getDbValue() {
- return dbValue;
- }
-
- public static Operator fromDbValue(String s) {
- return Stream.of(values())
- .filter(o -> o.getDbValue().equals(s))
- .findFirst()
- .orElseThrow(() -> new IllegalArgumentException("Unsupported operator db value: " + s));
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.qualitygate;
-
-import java.util.Objects;
-import java.util.Optional;
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.Immutable;
-
-import static java.util.Objects.requireNonNull;
-
-@Immutable
-public class EvaluatedCondition {
- private final Condition condition;
- private final EvaluationStatus status;
- @Nullable
- private final String value;
-
- public EvaluatedCondition(Condition condition, EvaluationStatus status, @Nullable String value) {
- this.condition = requireNonNull(condition, "condition can't be null");
- this.status = requireNonNull(status, "status can't be null");
- this.value = value;
- }
-
- public Condition getCondition() {
- return condition;
- }
-
- public EvaluationStatus getStatus() {
- return status;
- }
-
- public Optional<String> getValue() {
- return Optional.ofNullable(value);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- EvaluatedCondition that = (EvaluatedCondition) o;
- return Objects.equals(condition, that.condition) &&
- status == that.status &&
- Objects.equals(value, that.value);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(condition, status, value);
- }
-
- @Override
- public String toString() {
- return "EvaluatedCondition{" +
- "condition=" + condition +
- ", status=" + status +
- ", value=" + (value == null ? null : ('\'' + value + '\'')) +
- '}';
- }
-
- /**
- * Quality gate condition evaluation status.
- */
- public enum EvaluationStatus {
- /**
- * No measure found or measure had no value. The condition has not been evaluated and therefor ignored in
- * the computation of the Quality Gate status.
- */
- NO_VALUE,
- /**
- * Condition evaluated as OK, neither error nor warning thresholds have been reached.
- */
- OK,
- /**
- * Condition evaluated as WARN, only warning thresholds has been reached.
- */
- WARN,
- /**
- * Condition evaluated as ERROR, error thresholds has been reached (and most likely warning thresholds too).
- */
- ERROR
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.qualitygate;
-
-import com.google.common.collect.ImmutableSet;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.stream.Collectors;
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.Immutable;
-import org.sonar.api.measures.Metric;
-import org.sonar.server.qualitygate.EvaluatedCondition.EvaluationStatus;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static java.util.Objects.requireNonNull;
-
-@Immutable
-public class EvaluatedQualityGate {
- private final QualityGate qualityGate;
- private final Metric.Level status;
- private final Set<EvaluatedCondition> evaluatedConditions;
- private final boolean ignoredConditionsOnSmallChangeset;
-
- private EvaluatedQualityGate(QualityGate qualityGate, Metric.Level status, Set<EvaluatedCondition> evaluatedConditions, boolean ignoredConditionsOnSmallChangeset) {
- this.qualityGate = requireNonNull(qualityGate, "qualityGate can't be null");
- this.status = requireNonNull(status, "status can't be null");
- this.evaluatedConditions = evaluatedConditions;
- this.ignoredConditionsOnSmallChangeset = ignoredConditionsOnSmallChangeset;
- }
-
- public QualityGate getQualityGate() {
- return qualityGate;
- }
-
- public Metric.Level getStatus() {
- return status;
- }
-
- public Set<EvaluatedCondition> getEvaluatedConditions() {
- return evaluatedConditions;
- }
-
- public boolean hasIgnoredConditionsOnSmallChangeset() {
- return ignoredConditionsOnSmallChangeset;
- }
-
- public static Builder newBuilder() {
- return new Builder();
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- EvaluatedQualityGate that = (EvaluatedQualityGate) o;
- return Objects.equals(qualityGate, that.qualityGate) &&
- status == that.status &&
- Objects.equals(evaluatedConditions, that.evaluatedConditions);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(qualityGate, status, evaluatedConditions);
- }
-
- @Override
- public String toString() {
- return "EvaluatedQualityGate{" +
- "qualityGate=" + qualityGate +
- ", status=" + status +
- ", evaluatedConditions=" + evaluatedConditions +
- '}';
- }
-
- public static final class Builder {
- private QualityGate qualityGate;
- private Metric.Level status;
- private final Map<Condition, EvaluatedCondition> evaluatedConditions = new HashMap<>();
- private boolean ignoredConditionsOnSmallChangeset = false;
-
- private Builder() {
- // use static factory method
- }
-
- public Builder setQualityGate(QualityGate qualityGate) {
- this.qualityGate = qualityGate;
- return this;
- }
-
- public Builder setStatus(Metric.Level status) {
- this.status = status;
- return this;
- }
-
- public Builder setIgnoredConditionsOnSmallChangeset(boolean b) {
- this.ignoredConditionsOnSmallChangeset = b;
- return this;
- }
-
- public Builder addCondition(Condition condition, EvaluationStatus status, @Nullable String value) {
- evaluatedConditions.put(condition, new EvaluatedCondition(condition, status, value));
- return this;
- }
-
- public Builder addCondition(EvaluatedCondition c) {
- evaluatedConditions.put(c.getCondition(), c);
- return this;
- }
-
- public Set<EvaluatedCondition> getEvaluatedConditions() {
- return ImmutableSet.copyOf(evaluatedConditions.values());
- }
-
- public EvaluatedQualityGate build() {
- return new EvaluatedQualityGate(
- this.qualityGate,
- this.status,
- checkEvaluatedConditions(qualityGate, evaluatedConditions),
- ignoredConditionsOnSmallChangeset);
- }
-
- private static Set<EvaluatedCondition> checkEvaluatedConditions(QualityGate qualityGate, Map<Condition, EvaluatedCondition> evaluatedConditions) {
- Set<Condition> conditions = qualityGate.getConditions();
-
- Set<Condition> conditionsNotEvaluated = conditions.stream()
- .filter(c -> !evaluatedConditions.containsKey(c))
- .collect(Collectors.toSet());
- checkArgument(conditionsNotEvaluated.isEmpty(), "Evaluation missing for the following conditions: %s", conditionsNotEvaluated);
-
- Set<Condition> unknownConditions = evaluatedConditions.keySet().stream()
- .filter(c -> !conditions.contains(c))
- .collect(Collectors.toSet());
- checkArgument(unknownConditions.isEmpty(), "Evaluation provided for unknown conditions: %s", unknownConditions);
-
- return ImmutableSet.copyOf(evaluatedConditions.values());
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.qualitygate;
-
-import java.util.Objects;
-import java.util.Set;
-import javax.annotation.concurrent.Immutable;
-
-import static java.util.Objects.requireNonNull;
-import static org.sonar.core.util.stream.MoreCollectors.toSet;
-
-@Immutable
-public class QualityGate {
- private final String id;
- private final String name;
- private final Set<Condition> conditions;
-
- public QualityGate(String id, String name, Set<Condition> conditions) {
- this.id = requireNonNull(id, "id can't be null");
- this.name = requireNonNull(name, "name can't be null");
- this.conditions = requireNonNull(conditions, "conditions can't be null")
- .stream()
- .map(c -> requireNonNull(c, "condition can't be null"))
- .collect(toSet(conditions.size()));
- }
-
- public String getId() {
- return id;
- }
-
- public String getName() {
- return name;
- }
-
- public Set<Condition> getConditions() {
- return conditions;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- QualityGate that = (QualityGate) o;
- return Objects.equals(id, that.id) &&
- Objects.equals(name, that.name) &&
- Objects.equals(conditions, that.conditions);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(id, name, conditions);
- }
-
- @Override
- public String toString() {
- return "QualityGate{" +
- "id=" + id +
- ", name='" + name + '\'' +
- ", conditions=" + conditions +
- '}';
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.util;
-
-import okhttp3.OkHttpClient;
-import org.picocontainer.injectors.ProviderAdapter;
-import org.sonar.api.SonarRuntime;
-import org.sonar.api.ce.ComputeEngineSide;
-import org.sonar.api.config.Configuration;
-import org.sonar.api.server.ServerSide;
-import org.sonarqube.ws.client.OkHttpClientBuilder;
-
-import static java.lang.String.format;
-import static org.sonar.process.ProcessProperties.Property.HTTP_PROXY_PASSWORD;
-import static org.sonar.process.ProcessProperties.Property.HTTP_PROXY_USER;
-
-/**
- * Provide a unique instance of {@link OkHttpClient} which configuration:
- * <ul>
- * <li>supports HTTPS</li>
- * <li>supports proxy, including authentication, as defined by the properties like "http.proxyHost" in
- * conf/sonar.properties</li>
- * <li>has connect and read timeouts of 10 seconds each</li>
- * <li>sends automatically the HTTP header "User-Agent" with value "SonarQube/{version}", for instance "SonarQube/6.2"</li>
- * </ul>
- */
-@ServerSide
-@ComputeEngineSide
-public class OkHttpClientProvider extends ProviderAdapter {
-
- private static final int DEFAULT_CONNECT_TIMEOUT_IN_MS = 10_000;
- private static final int DEFAULT_READ_TIMEOUT_IN_MS = 10_000;
-
- private okhttp3.OkHttpClient okHttpClient;
-
- /**
- * @return a {@link OkHttpClient} singleton
- */
- public OkHttpClient provide(Configuration config, SonarRuntime runtime) {
- if (okHttpClient == null) {
- OkHttpClientBuilder builder = new OkHttpClientBuilder();
- builder.setConnectTimeoutMs(DEFAULT_CONNECT_TIMEOUT_IN_MS);
- builder.setReadTimeoutMs(DEFAULT_READ_TIMEOUT_IN_MS);
- // no need to define proxy URL as system-wide proxy is used and properly
- // configured by bootstrap process.
- builder.setProxyLogin(config.get(HTTP_PROXY_USER.getKey()).orElse(null));
- builder.setProxyPassword(config.get(HTTP_PROXY_PASSWORD.getKey()).orElse(null));
- builder.setUserAgent(format("SonarQube/%s", runtime.getApiVersion().toString()));
- okHttpClient = builder.build();
- }
- return okHttpClient;
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 com.google.common.base.Objects;
-import java.util.Date;
-
-import static java.util.Objects.requireNonNull;
-
-public final class Analysis {
- private final String uuid;
- private final long date;
-
- public Analysis(String uuid, long date) {
- requireNonNull(uuid, "uuid must not be null");
- this.uuid = uuid;
- this.date = date;
- }
-
- public String getUuid() {
- return uuid;
- }
-
- public Date getDate() {
- return new Date(date);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (!(o instanceof Analysis)) {
- return false;
- }
- Analysis analysis = (Analysis) o;
- return Objects.equal(uuid, analysis.uuid) &&
- Objects.equal(date, analysis.date);
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(uuid, date);
- }
-
- @Override
- public String toString() {
- return "Analysis{" +
- "uuid='" + uuid + '\'' +
- ", date=" + date +
- '}';
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.Optional;
-import javax.annotation.Nullable;
-
-import static java.util.Objects.requireNonNull;
-
-public final class Branch {
- private final boolean main;
- private final String name;
- private final Type type;
-
- public Branch(boolean main, @Nullable String name, Type type) {
- this.main = main;
- this.name = name;
- this.type = requireNonNull(type, "type can't be null");
- }
-
- public boolean isMain() {
- return main;
- }
-
- public Optional<String> getName() {
- return Optional.ofNullable(name);
- }
-
- public Type getType() {
- return type;
- }
-
- public enum Type {
- LONG, SHORT, PULL_REQUEST
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- Branch branch = (Branch) o;
- return main == branch.main &&
- Objects.equals(name, branch.name) &&
- type == branch.type;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(main, name, type);
- }
-
- @Override
- public String toString() {
- return "Branch{" +
- "main=" + main +
- ", name='" + name + '\'' +
- ", type=" + type +
- '}';
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 static java.util.Objects.requireNonNull;
-
-public final class CeTask {
- private final String id;
- private final Status status;
-
- public CeTask(String id, Status status) {
- this.id = requireNonNull(id, "id can't be null");
- this.status = requireNonNull(status, "status can't be null");
- }
-
- public String getId() {
- return id;
- }
-
- public Status getStatus() {
- return status;
- }
-
- public enum Status {
- SUCCESS, FAILED
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- CeTask task = (CeTask) o;
- return Objects.equals(id, task.id) &&
- status == task.status;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(id, status);
- }
-
- @Override
- public String toString() {
- return "CeTask{" +
- "id='" + id + '\'' +
- ", status=" + status +
- '}';
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.Map;
-import java.util.Objects;
-import java.util.Optional;
-import javax.annotation.Nullable;
-import org.sonar.server.project.Project;
-import org.sonar.server.qualitygate.EvaluatedQualityGate;
-
-import static com.google.common.collect.ImmutableMap.copyOf;
-import static java.util.Objects.requireNonNull;
-
-public class ProjectAnalysis {
- private final Project project;
- private final CeTask ceTask;
- private final Branch branch;
- private final EvaluatedQualityGate qualityGate;
- private final Long updatedAt;
- private final Map<String, String> properties;
- private final Analysis analysis;
-
- public ProjectAnalysis(Project project, @Nullable CeTask ceTask, @Nullable Analysis analysis,
- @Nullable Branch branch, @Nullable EvaluatedQualityGate qualityGate, @Nullable Long updatedAt,
- Map<String, String> properties) {
- this.project = requireNonNull(project, "project can't be null");
- this.ceTask = ceTask;
- this.branch = branch;
- this.qualityGate = qualityGate;
- this.updatedAt = updatedAt;
- this.properties = copyOf(requireNonNull(properties, "properties can't be null"));
- this.analysis = analysis;
- }
-
- public Optional<CeTask> getCeTask() {
- return Optional.ofNullable(ceTask);
- }
-
- public Project getProject() {
- return project;
- }
-
- public Optional<Branch> getBranch() {
- return Optional.ofNullable(branch);
- }
-
- public Optional<EvaluatedQualityGate> getQualityGate() {
- return Optional.ofNullable(qualityGate);
- }
-
- public Map<String, String> getProperties() {
- return properties;
- }
-
- public Optional<Analysis> getAnalysis() {
- return Optional.ofNullable(analysis);
- }
-
- public Optional<Long> getUpdatedAt() {
- return Optional.ofNullable(updatedAt);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- ProjectAnalysis that = (ProjectAnalysis) o;
- return Objects.equals(project, that.project) &&
- Objects.equals(ceTask, that.ceTask) &&
- Objects.equals(branch, that.branch) &&
- Objects.equals(qualityGate, that.qualityGate) &&
- Objects.equals(updatedAt, that.updatedAt) &&
- Objects.equals(properties, that.properties) &&
- Objects.equals(analysis, that.analysis);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(project, ceTask, branch, qualityGate, updatedAt, properties, analysis);
- }
-
- @Override
- public String toString() {
- return "ProjectAnalysis{" +
- "project=" + project +
- ", ceTask=" + ceTask +
- ", branch=" + branch +
- ", qualityGate=" + qualityGate +
- ", updatedAt=" + updatedAt +
- ", properties=" + properties +
- ", analysis=" + analysis +
- '}';
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-import org.sonar.api.config.Configuration;
-import org.sonar.db.component.ComponentDto;
-
-import static java.util.Objects.requireNonNull;
-
-public interface WebHooks {
-
- /**
- * Tells whether any webHook is configured for the specified {@link Configuration}.
- *
- * <p>
- * This can be used to not do consuming operations before calling
- * {@link #sendProjectAnalysisUpdate(Analysis, Supplier)}
- */
- boolean isEnabled(ComponentDto projectDto);
-
- /**
- * Calls all WebHooks configured in the specified {@link Configuration} for the specified analysis with the
- * {@link WebhookPayload} provided by the specified Supplier.
- */
- void sendProjectAnalysisUpdate(Analysis analysis, Supplier<WebhookPayload> payloadSupplier);
-
- final class Analysis {
- private final String projectUuid;
- private final String ceTaskUuid;
- private final String analysisUuid;
-
- public Analysis(String projectUuid, @Nullable String analysisUuid, @Nullable String ceTaskUuid) {
- this.projectUuid = requireNonNull(projectUuid, "projectUuid can't be null");
- this.analysisUuid = analysisUuid;
- this.ceTaskUuid = ceTaskUuid;
- }
-
- public String getProjectUuid() {
- return projectUuid;
- }
-
- @CheckForNull
- public String getCeTaskUuid() {
- return ceTaskUuid;
- }
-
- @CheckForNull
- public String getAnalysisUuid() {
- return analysisUuid;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- Analysis analysis = (Analysis) o;
- return Objects.equals(projectUuid, analysis.projectUuid) &&
- Objects.equals(ceTaskUuid, analysis.ceTaskUuid) &&
- Objects.equals(analysisUuid, analysis.analysisUuid);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(projectUuid, ceTaskUuid, analysisUuid);
- }
-
- @Override
- public String toString() {
- return "Analysis{" +
- "projectUuid='" + projectUuid + '\'' +
- ", ceTaskUuid='" + ceTaskUuid + '\'' +
- '}';
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.List;
-import java.util.Optional;
-import java.util.function.Supplier;
-import java.util.stream.Stream;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.core.util.stream.MoreCollectors;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.db.webhook.WebhookDao;
-import org.sonar.db.webhook.WebhookDto;
-import org.sonar.server.async.AsyncExecution;
-
-import static java.util.Optional.ofNullable;
-import static org.sonar.server.ws.WsUtils.checkStateWithOptional;
-
-public class WebHooksImpl implements WebHooks {
-
- private static final Logger LOGGER = Loggers.get(WebHooksImpl.class);
-
- private final WebhookCaller caller;
- private final WebhookDeliveryStorage deliveryStorage;
- private final AsyncExecution asyncExecution;
- private final DbClient dbClient;
-
- public WebHooksImpl(WebhookCaller caller, WebhookDeliveryStorage deliveryStorage, AsyncExecution asyncExecution, DbClient dbClient) {
- this.caller = caller;
- this.deliveryStorage = deliveryStorage;
- this.asyncExecution = asyncExecution;
- this.dbClient = dbClient;
- }
-
- @Override
- public boolean isEnabled(ComponentDto projectDto) {
- return readWebHooksFrom(projectDto.uuid())
- .findAny()
- .isPresent();
- }
-
- private Stream<WebhookDto> readWebHooksFrom(String projectUuid) {
- try (DbSession dbSession = dbClient.openSession(false)) {
-
- Optional<ComponentDto> optionalComponentDto = ofNullable(dbClient.componentDao().selectByUuid(dbSession, projectUuid).orNull());
- ComponentDto componentDto = checkStateWithOptional(optionalComponentDto, "the requested project '%s' was not found", projectUuid);
-
- if (componentDto.getMainBranchProjectUuid() != null && !componentDto.uuid().equals(componentDto.getMainBranchProjectUuid())) {
- Optional<ComponentDto> mainBranchComponentDto = ofNullable(dbClient.componentDao().selectByUuid(dbSession, componentDto.getMainBranchProjectUuid()).orNull());
- componentDto = checkStateWithOptional(mainBranchComponentDto, "the requested project '%s' was not found", projectUuid);
- }
-
- WebhookDao dao = dbClient.webhookDao();
- return Stream.concat(
- dao.selectByProject(dbSession, componentDto).stream(),
- dao.selectByOrganizationUuid(dbSession, componentDto.getOrganizationUuid()).stream());
- }
- }
-
- @Override
- public void sendProjectAnalysisUpdate(Analysis analysis, Supplier<WebhookPayload> payloadSupplier) {
- List<Webhook> webhooks = readWebHooksFrom(analysis.getProjectUuid())
- .map(dto -> new Webhook(dto.getUuid(), analysis.getProjectUuid(), analysis.getCeTaskUuid(), analysis.getAnalysisUuid(), dto.getName(), dto.getUrl()))
- .collect(MoreCollectors.toList());
- if (webhooks.isEmpty()) {
- return;
- }
-
- WebhookPayload payload = payloadSupplier.get();
- webhooks.forEach(webhook -> asyncExecution.addToQueue(() -> {
- WebhookDelivery delivery = caller.call(webhook, payload);
- log(delivery);
- deliveryStorage.persist(delivery);
- }));
- asyncExecution.addToQueue(() -> deliveryStorage.purge(analysis.getProjectUuid()));
- }
-
- 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));
- }
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.Optional;
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.Immutable;
-
-import static java.util.Objects.requireNonNull;
-import static java.util.Optional.ofNullable;
-
-@Immutable
-public class Webhook {
-
- private final String uuid;
- private final String componentUuid;
- private final String ceTaskUuid;
- private final String analysisUuid;
- private final String name;
- private final String url;
-
- public Webhook(String uuid, String componentUuid, @Nullable String ceTaskUuid, @Nullable String analysisUuid, String name, String url) {
- this.uuid = uuid;
- this.componentUuid = requireNonNull(componentUuid);
- this.ceTaskUuid = ceTaskUuid;
- this.analysisUuid = analysisUuid;
- this.name = requireNonNull(name);
- this.url = requireNonNull(url);
- }
-
- public String getComponentUuid() {
- return componentUuid;
- }
-
- public Optional<String> getCeTaskUuid() {
- return ofNullable(ceTaskUuid);
- }
-
- public String getName() {
- return name;
- }
-
- public String getUrl() {
- return url;
- }
-
- public String getUuid() {
- return uuid;
- }
-
- public Optional<String> getAnalysisUuid() {
- return ofNullable(analysisUuid);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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;
-
-public interface WebhookCaller {
-
- /**
- * Call webhook by sending a HTTP(S) POST request containing
- * the JSON payload.
- * <br/>
- * Errors are silently ignored. They don't generate logs or
- * throw exceptions. The error status is stored in the
- * returned {@link WebhookDelivery}.
- */
- WebhookDelivery call(Webhook webhook, WebhookPayload payload);
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 okhttp3.Credentials;
-import okhttp3.HttpUrl;
-import okhttp3.MediaType;
-import okhttp3.OkHttpClient;
-import okhttp3.Request;
-import okhttp3.RequestBody;
-import okhttp3.Response;
-import org.sonar.api.ce.ComputeEngineSide;
-import org.sonar.api.utils.System2;
-
-import static java.lang.String.format;
-import static java.net.HttpURLConnection.HTTP_MOVED_PERM;
-import static java.net.HttpURLConnection.HTTP_MOVED_TEMP;
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static okhttp3.internal.http.StatusLine.HTTP_PERM_REDIRECT;
-import static okhttp3.internal.http.StatusLine.HTTP_TEMP_REDIRECT;
-import static org.apache.commons.lang.StringUtils.isNotEmpty;
-
-@ComputeEngineSide
-public class WebhookCallerImpl implements WebhookCaller {
-
- private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
- private static final String PROJECT_KEY_HEADER = "X-SonarQube-Project";
-
- private final System2 system;
- private final OkHttpClient okHttpClient;
-
- public WebhookCallerImpl(System2 system, OkHttpClient okHttpClient) {
- this.system = system;
- this.okHttpClient = newClientWithoutRedirect(okHttpClient);
- }
-
- @Override
- public WebhookDelivery call(Webhook webhook, WebhookPayload payload) {
- WebhookDelivery.Builder builder = new WebhookDelivery.Builder();
- long startedAt = system.now();
- builder
- .setAt(startedAt)
- .setPayload(payload)
- .setWebhook(webhook);
-
- try {
- Request request = buildHttpRequest(webhook, payload);
- try (Response response = execute(request)) {
- builder.setHttpStatus(response.code());
- }
- } catch (Exception e) {
- builder.setError(e);
- }
-
- return builder
- .setDurationInMs((int) (system.now() - startedAt))
- .build();
- }
-
- private static Request buildHttpRequest(Webhook webhook, WebhookPayload payload) {
- HttpUrl url = HttpUrl.parse(webhook.getUrl());
- if (url == null) {
- throw new IllegalArgumentException("Webhook URL is not valid: " + webhook.getUrl());
- }
- Request.Builder request = new Request.Builder();
- request.url(url);
- request.header(PROJECT_KEY_HEADER, payload.getProjectKey());
- if (isNotEmpty(url.username())) {
- request.header("Authorization", Credentials.basic(url.username(), url.password(), UTF_8));
- }
-
- RequestBody body = RequestBody.create(JSON, payload.getJson());
- request.post(body);
- return request.build();
- }
-
- private Response execute(Request request) throws IOException {
- Response response = okHttpClient.newCall(request).execute();
- switch (response.code()) {
- case HTTP_MOVED_PERM:
- case HTTP_MOVED_TEMP:
- case HTTP_TEMP_REDIRECT:
- case HTTP_PERM_REDIRECT:
- // OkHttpClient does not follow the redirect with the same HTTP method. A POST is
- // redirected to a GET. Because of that the redirect must be manually
- // implemented.
- // See:
- // https://github.com/square/okhttp/blob/07309c1c7d9e296014268ebd155ebf7ef8679f6c/okhttp/src/main/java/okhttp3/internal/http/RetryAndFollowUpInterceptor.java#L316
- // https://github.com/square/okhttp/issues/936#issuecomment-266430151
- return followPostRedirect(response);
- default:
- return response;
- }
- }
-
- /**
- * Inspired by https://github.com/square/okhttp/blob/parent-3.6.0/okhttp/src/main/java/okhttp3/internal/http/RetryAndFollowUpInterceptor.java#L286
- */
- private Response followPostRedirect(Response response) throws IOException {
- String location = response.header("Location");
- if (location == null) {
- throw new IllegalStateException(format("Missing HTTP header 'Location' in redirect of %s", response.request().url()));
- }
- HttpUrl url = response.request().url().resolve(location);
-
- // Don't follow redirects to unsupported protocols.
- if (url == null) {
- throw new IllegalStateException(format("Unsupported protocol in redirect of %s to %s", response.request().url(), location));
- }
-
- Request.Builder redirectRequest = response.request().newBuilder();
- redirectRequest.post(response.request().body());
- response.body().close();
- return okHttpClient.newCall(redirectRequest.url(url).build()).execute();
- }
-
- private static OkHttpClient newClientWithoutRedirect(OkHttpClient client) {
- return client.newBuilder()
- .followRedirects(false)
- .followSslRedirects(false)
- .build();
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.Optional;
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.Immutable;
-
-import static com.google.common.base.Throwables.getRootCause;
-import static java.util.Objects.requireNonNull;
-
-/**
- * A {@link WebhookDelivery} represents the result of a webhook call.
- */
-@Immutable
-public class WebhookDelivery {
-
- private final Webhook webhook;
- private final WebhookPayload payload;
- private final Integer httpStatus;
- private final Integer durationInMs;
- private final long at;
- private final Throwable error;
-
- private WebhookDelivery(Builder builder) {
- this.webhook = requireNonNull(builder.webhook);
- this.payload = requireNonNull(builder.payload);
- this.httpStatus = builder.httpStatus;
- this.durationInMs = builder.durationInMs;
- this.at = builder.at;
- this.error = builder.error;
- }
-
- public Webhook getWebhook() {
- return webhook;
- }
-
- public WebhookPayload getPayload() {
- return payload;
- }
-
- /**
- * @return the HTTP status if {@link #getError()} is empty, else returns
- * {@link Optional#empty()}
- */
- public Optional<Integer> getHttpStatus() {
- return Optional.ofNullable(httpStatus);
- }
-
- /**
- * @return the duration in milliseconds if {@link #getError()} is empty,
- * else returns {@link Optional#empty()}
- */
- public Optional<Integer> getDurationInMs() {
- return Optional.ofNullable(durationInMs);
- }
-
- /**
- * @return the date of sending
- */
- public long getAt() {
- return at;
- }
-
- /**
- * @return the error raised if the request could not be executed due to a connectivity
- * problem or timeout
- */
- public Optional<Throwable> getError() {
- return Optional.ofNullable(error);
- }
-
- /**
- * @return the cause message of {@link #getError()}, Optional.empty() is error is not set.
- */
- public Optional<String> getErrorMessage() {
- return error != null ? Optional.ofNullable(getRootCause(error).getMessage()) : Optional.empty();
- }
-
- public boolean isSuccess() {
- return httpStatus != null && httpStatus >= 200 && httpStatus < 300;
- }
-
- public static class Builder {
- private Webhook webhook;
- private WebhookPayload payload;
- private Integer httpStatus;
- private Integer durationInMs;
- private long at;
- private Throwable error;
-
- public Builder setWebhook(Webhook w) {
- this.webhook = w;
- return this;
- }
-
- public Builder setPayload(WebhookPayload payload) {
- this.payload = payload;
- return this;
- }
-
- public Builder setHttpStatus(@Nullable Integer httpStatus) {
- this.httpStatus = httpStatus;
- return this;
- }
-
- public Builder setDurationInMs(@Nullable Integer durationInMs) {
- this.durationInMs = durationInMs;
- return this;
- }
-
- public Builder setAt(long at) {
- this.at = at;
- return this;
- }
-
- public Builder setError(@Nullable Throwable t) {
- this.error = t;
- return this;
- }
-
- public WebhookDelivery build() {
- return new WebhookDelivery(this);
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 com.google.common.base.Throwables;
-import org.sonar.api.ce.ComputeEngineSide;
-import org.sonar.api.utils.System2;
-import org.sonar.core.util.UuidFactory;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.webhook.WebhookDeliveryDao;
-import org.sonar.db.webhook.WebhookDeliveryDto;
-
-/**
- * Persist and purge {@link WebhookDelivery} into database
- */
-@ComputeEngineSide
-public class WebhookDeliveryStorage {
-
- private static final long ALIVE_DELAY_MS = 30L * 24 * 60 * 60 * 1000;
-
- private final DbClient dbClient;
- private final System2 system;
- private final UuidFactory uuidFactory;
-
- public WebhookDeliveryStorage(DbClient dbClient, System2 system, UuidFactory uuidFactory) {
- this.dbClient = dbClient;
- this.system = system;
- this.uuidFactory = uuidFactory;
- }
-
- public void persist(WebhookDelivery delivery) {
- WebhookDeliveryDao dao = dbClient.webhookDeliveryDao();
- try (DbSession dbSession = dbClient.openSession(false)) {
- dao.insert(dbSession, toDto(delivery));
- dbSession.commit();
- }
- }
-
- public void purge(String componentUuid) {
- long beforeDate = system.now() - ALIVE_DELAY_MS;
- try (DbSession dbSession = dbClient.openSession(false)) {
- dbClient.webhookDeliveryDao().deleteComponentBeforeDate(dbSession, componentUuid, beforeDate);
- dbSession.commit();
- }
- }
-
- private WebhookDeliveryDto toDto(WebhookDelivery delivery) {
- WebhookDeliveryDto dto = new WebhookDeliveryDto();
- dto.setUuid(uuidFactory.create());
- dto.setWebhookUuid(delivery.getWebhook().getUuid());
- dto.setComponentUuid(delivery.getWebhook().getComponentUuid());
- delivery.getWebhook().getCeTaskUuid().ifPresent(dto::setCeTaskUuid);
- delivery.getWebhook().getAnalysisUuid().ifPresent(dto::setAnalysisUuid);
- dto.setName(delivery.getWebhook().getName());
- dto.setUrl(delivery.getWebhook().getUrl());
- dto.setSuccess(delivery.isSuccess());
- dto.setHttpStatus(delivery.getHttpStatus().orElse(null));
- dto.setDurationMs(delivery.getDurationInMs().orElse(null));
- dto.setErrorStacktrace(delivery.getError().map(Throwables::getStackTraceAsString).orElse(null));
- dto.setPayload(delivery.getPayload().getJson());
- dto.setCreatedAt(delivery.getAt());
- return dto;
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 org.sonar.core.platform.Module;
-
-public class WebhookModule extends Module {
- @Override
- protected void configureModule() {
- add(
- WebhookCallerImpl.class,
- WebhookDeliveryStorage.class,
- WebHooksImpl.class,
- WebhookPayloadFactoryImpl.class);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 javax.annotation.concurrent.Immutable;
-
-import static java.util.Objects.requireNonNull;
-
-@Immutable
-public class WebhookPayload {
-
- private final String projectKey;
- private final String json;
-
- public WebhookPayload(String projectKey, String json) {
- this.projectKey = requireNonNull(projectKey);
- this.json = requireNonNull(json);
- }
-
- public String getProjectKey() {
- return projectKey;
- }
-
- public String getJson() {
- return json;
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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;
-
-@FunctionalInterface
-public interface WebhookPayloadFactory {
-
- WebhookPayload create(ProjectAnalysis analysis);
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.StringWriter;
-import java.io.UnsupportedEncodingException;
-import java.io.Writer;
-import java.net.URLEncoder;
-import java.util.Date;
-import java.util.Map;
-import java.util.Optional;
-import org.sonar.api.ce.ComputeEngineSide;
-import org.sonar.api.platform.Server;
-import org.sonar.api.utils.System2;
-import org.sonar.api.utils.text.JsonWriter;
-import org.sonar.server.project.Project;
-import org.sonar.server.qualitygate.Condition;
-import org.sonar.server.qualitygate.EvaluatedCondition;
-import org.sonar.server.qualitygate.EvaluatedQualityGate;
-
-import static java.lang.String.format;
-import static org.sonar.core.config.CorePropertyDefinitions.SONAR_ANALYSIS;
-
-@ComputeEngineSide
-public class WebhookPayloadFactoryImpl implements WebhookPayloadFactory {
-
- private static final String PROPERTY_STATUS = "status";
- private final Server server;
- private final System2 system2;
-
- public WebhookPayloadFactoryImpl(Server server, System2 system2) {
- this.server = server;
- this.system2 = system2;
- }
-
- @Override
- public WebhookPayload create(ProjectAnalysis analysis) {
- Writer string = new StringWriter();
- try (JsonWriter writer = JsonWriter.of(string)) {
- writer.beginObject();
- writeServer(writer);
- writeTask(writer, analysis.getCeTask());
- writeDates(writer, analysis, system2);
- writeProject(analysis, writer, analysis.getProject());
- analysis.getBranch().ifPresent(b -> writeBranch(writer, analysis.getProject(), b));
- analysis.getQualityGate().ifPresent(qualityGate -> writeQualityGate(writer, qualityGate));
- writeAnalysisProperties(writer, analysis.getProperties());
- writer.endObject().close();
- return new WebhookPayload(analysis.getProject().getKey(), string.toString());
- }
- }
-
- private void writeServer(JsonWriter writer) {
- writer.prop("serverUrl", server.getPublicRootUrl());
- }
-
- private static void writeDates(JsonWriter writer, ProjectAnalysis analysis, System2 system2) {
- analysis.getAnalysis().ifPresent(a -> writer.propDateTime("analysedAt", a.getDate()));
- writer.propDateTime("changedAt", new Date(analysis.getUpdatedAt().orElse(system2.now())));
- }
-
- private void writeProject(ProjectAnalysis analysis, JsonWriter writer, Project project) {
- writer
- .name("project")
- .beginObject()
- .prop("key", project.getKey())
- .prop("name", analysis.getProject().getName())
- .prop("url", projectUrlOf(project))
- .endObject();
- }
-
- private static void writeAnalysisProperties(JsonWriter writer, Map<String, String> properties) {
- writer
- .name("properties")
- .beginObject();
- properties.entrySet()
- .stream()
- .filter(prop -> prop.getKey().startsWith(SONAR_ANALYSIS))
- .forEach(prop -> writer.prop(prop.getKey(), prop.getValue()));
- writer.endObject();
- }
-
- private static void writeTask(JsonWriter writer, Optional<CeTask> ceTask) {
- ceTask.ifPresent(ceTask1 -> writer.prop("taskId", ceTask1.getId()));
- writer.prop(PROPERTY_STATUS, ceTask.map(CeTask::getStatus).orElse(CeTask.Status.SUCCESS).toString());
- }
-
- private void writeBranch(JsonWriter writer, Project project, Branch branch) {
- writer
- .name("branch")
- .beginObject()
- .prop("name", branch.getName().orElse(null))
- .prop("type", branch.getType().name())
- .prop("isMain", branch.isMain())
- .prop("url", branchUrlOf(project, branch))
- .endObject();
- }
-
- private String projectUrlOf(Project project) {
- return format("%s/dashboard?id=%s", server.getPublicRootUrl(), encode(project.getKey()));
- }
-
- private String branchUrlOf(Project project, Branch branch) {
- if (branch.getType() == Branch.Type.LONG) {
- if (branch.isMain()) {
- return projectUrlOf(project);
- }
- return format("%s/dashboard?branch=%s&id=%s",
- server.getPublicRootUrl(), encode(branch.getName().orElse("")), encode(project.getKey()));
- }
- if (branch.getType() == Branch.Type.SHORT) {
- return format("%s/project/issues?branch=%s&id=%s&resolved=false",
- server.getPublicRootUrl(), encode(branch.getName().orElse("")), encode(project.getKey()));
- }
- if (branch.getType() == Branch.Type.PULL_REQUEST) {
- return format("%s/project/issues?pullRequest=%s&id=%s&resolved=false",
- server.getPublicRootUrl(), encode(branch.getName().orElse("")), encode(project.getKey()));
- }
- return projectUrlOf(project);
- }
-
- private static void writeQualityGate(JsonWriter writer, EvaluatedQualityGate gate) {
- writer
- .name("qualityGate")
- .beginObject()
- .prop("name", gate.getQualityGate().getName())
- .prop(PROPERTY_STATUS, gate.getStatus().toString())
- .name("conditions")
- .beginArray();
- for (EvaluatedCondition evaluatedCondition : gate.getEvaluatedConditions()) {
- Condition condition = evaluatedCondition.getCondition();
- writer
- .beginObject()
- .prop("metric", condition.getMetricKey())
- .prop("operator", condition.getOperator().name());
- evaluatedCondition.getValue().ifPresent(t -> writer.prop("value", t));
- writer
- .prop(PROPERTY_STATUS, evaluatedCondition.getStatus().name())
- .prop("onLeakPeriod", condition.isOnLeakPeriod())
- .prop("errorThreshold", condition.getErrorThreshold().orElse(null))
- .prop("warningThreshold", condition.getWarningThreshold().orElse(null))
- .endObject();
- }
- writer
- .endArray()
- .endObject();
- }
-
- private static String encode(String toEncode) {
- try {
- return URLEncoder.encode(toEncode, "UTF-8");
- } catch (UnsupportedEncodingException e) {
- throw new IllegalStateException("Encoding not supported", e);
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.
- */
-@ParametersAreNonnullByDefault
-package org.sonar.server.webhook;
-
-import javax.annotation.ParametersAreNonnullByDefault;
-
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.async;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import org.junit.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class AsyncExecutionExecutorServiceImplTest {
- private AsyncExecutionExecutorServiceImpl underTest = new AsyncExecutionExecutorServiceImpl();
-
- @Test
- public void submit_executes_runnable_in_another_thread() {
- try (SlowRunnable slowRunnable = new SlowRunnable()) {
- underTest.submit(slowRunnable);
- assertThat(slowRunnable.executed).isFalse();
- }
- }
-
- private static final class SlowRunnable implements Runnable, AutoCloseable {
- private final CountDownLatch latch = new CountDownLatch(1);
- private volatile boolean executed = false;
-
- @Override
- public void run() {
- try {
- latch.await(30, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- // ignore
- }
- executed = true;
- }
-
- @Override
- public void close() {
- latch.countDown();
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.async;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.api.utils.log.LogTester;
-import org.sonar.api.utils.log.LoggerLevel;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class AsyncExecutionImplTest {
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
- @Rule
- public LogTester logTester = new LogTester();
-
- private AsyncExecutionExecutorService synchronousExecutorService = Runnable::run;
- private AsyncExecutionImpl underTest = new AsyncExecutionImpl(synchronousExecutorService);
-
- @Test
- public void addToQueue_fails_with_NPE_if_Runnable_is_null() {
- expectedException.expect(NullPointerException.class);
-
- underTest.addToQueue(null);
- }
-
- @Test
- public void addToQueue_submits_runnable_to_executorService_which_does_not_fail_if_Runnable_argument_throws_exception() {
- underTest.addToQueue(() -> {
- throw new RuntimeException("Faking an exception thrown by Runnable argument");
- });
-
- assertThat(logTester.logs()).hasSize(1);
- assertThat(logTester.logs(LoggerLevel.ERROR)).containsOnly("Asynchronous task failed");
- }
-
- @Test
- public void addToQueue_submits_runnable_that_fails_if_Runnable_argument_throws_Error() {
- Error expected = new Error("Faking an exception thrown by Runnable argument");
- Runnable runnable = () -> {
- throw expected;
- };
-
- expectedException.expect(Error.class);
- expectedException.expectMessage(expected.getMessage());
-
- underTest.addToQueue(runnable);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.async;
-
-import java.lang.management.ManagementFactory;
-import javax.annotation.CheckForNull;
-import javax.management.InstanceNotFoundException;
-import javax.management.ObjectInstance;
-import javax.management.ObjectName;
-import org.junit.Test;
-import org.mockito.Mockito;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class AsyncExecutionMBeanImplTest {
- private AsyncExecutionMonitoring asyncExecutionMonitoring = Mockito.mock(AsyncExecutionMonitoring.class);
-
- private AsyncExecutionMBeanImpl underTest = new AsyncExecutionMBeanImpl(asyncExecutionMonitoring);
-
- @Test
- public void register_and_unregister() throws Exception {
- assertThat(getMBean()).isNull();
-
- underTest.start();
- assertThat(getMBean()).isNotNull();
-
- underTest.stop();
- assertThat(getMBean()).isNull();
- }
-
- @Test
- public void getQueueSize_delegates_to_AsyncExecutionMonitoring() {
- when(asyncExecutionMonitoring.getQueueSize()).thenReturn(12);
-
- assertThat(underTest.getQueueSize()).isEqualTo(12);
-
- verify(asyncExecutionMonitoring).getQueueSize();
- }
-
- @Test
- public void getWorkerCount_delegates_to_AsyncExecutionMonitoring() {
- when(asyncExecutionMonitoring.getWorkerCount()).thenReturn(12);
-
- assertThat(underTest.getWorkerCount()).isEqualTo(12);
-
- verify(asyncExecutionMonitoring).getWorkerCount();
- }
-
- @Test
- public void getLargestWorkerCount_delegates_to_AsyncExecutionMonitoring() {
- when(asyncExecutionMonitoring.getLargestWorkerCount()).thenReturn(12);
-
- assertThat(underTest.getLargestWorkerCount()).isEqualTo(12);
-
- verify(asyncExecutionMonitoring).getLargestWorkerCount();
- }
-
- @CheckForNull
- private ObjectInstance getMBean() throws Exception {
- try {
- return ManagementFactory.getPlatformMBeanServer().getObjectInstance(new ObjectName(AsyncExecutionMBean.OBJECT_NAME));
- } catch (InstanceNotFoundException e) {
- return null;
- }
- }
-
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.project;
-
-import org.junit.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class ProjectTest {
- @Test
- public void test_bean_without_description() {
- Project project1 = new Project("U1", "K1", "N1");
- Project project2 = new Project("U1", "K1", "N1", null);
-
- assertThat(project1.getUuid()).isEqualTo(project2.getUuid()).isEqualTo("U1");
- assertThat(project1.getKey()).isEqualTo(project2.getKey()).isEqualTo("K1");
- assertThat(project1.getName()).isEqualTo(project2.getName()).isEqualTo("N1");
- assertThat(project1.getDescription()).isEqualTo(project2.getDescription()).isNull();
-
- assertThat(project1.toString())
- .isEqualTo(project2.toString())
- .isEqualTo("Project{uuid='U1', key='K1', name='N1', description=null}");
- }
-
- @Test
- public void test_bean_with_description() {
- Project project1 = new Project("U1", "K1", "N1", "D1");
-
- assertThat(project1.getUuid()).isEqualTo("U1");
- assertThat(project1.getKey()).isEqualTo("K1");
- assertThat(project1.getName()).isEqualTo("N1");
- assertThat(project1.getDescription()).isEqualTo("D1");
-
- assertThat(project1.toString())
- .isEqualTo(project1.toString())
- .isEqualTo("Project{uuid='U1', key='K1', name='N1', description='D1'}");
- }
-
- @Test
- public void test_equals_and_hashCode() {
- Project project1 = new Project("U1", "K1", "N1");
- Project project2 = new Project("U1", "K1", "N1", "D1");
-
- assertThat(project1).isEqualTo(project1);
- assertThat(project1).isNotEqualTo(null);
- assertThat(project1).isNotEqualTo(new Object());
- assertThat(project1).isEqualTo(new Project("U1", "K1", "N1", null));
- assertThat(project1).isNotEqualTo(new Project("U1", "K2", "N1", null));
- assertThat(project1).isNotEqualTo(new Project("U1", "K1", "N2", null));
- assertThat(project1).isEqualTo(project2);
-
- assertThat(project1.hashCode()).isEqualTo(project1.hashCode());
- assertThat(project1.hashCode()).isNotEqualTo(null);
- assertThat(project1.hashCode()).isNotEqualTo(new Object().hashCode());
- assertThat(project1.hashCode()).isEqualTo(new Project("U1", "K1", "N1", null).hashCode());
- assertThat(project1.hashCode()).isNotEqualTo(new Project("U1", "K2", "N1", null).hashCode());
- assertThat(project1.hashCode()).isNotEqualTo(new Project("U1", "K1", "N2", null).hashCode());
- assertThat(project1.hashCode()).isEqualTo(project2.hashCode());
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.qualitygate;
-
-import java.util.Arrays;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class ConditionTest {
- private static final String METRIC_KEY = "metric_key";
- private static final Condition.Operator OPERATOR = Condition.Operator.EQUALS;
- private static final String ERROR_THRESHOLD = "2";
- private static final String WARN_THRESHOLD = "4";
- private static final boolean ON_LEAK_PERIOD = true;
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private Condition underTest = new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, WARN_THRESHOLD, ON_LEAK_PERIOD);
-
- @Test
- public void constructor_throws_NPE_if_metricKey_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("metricKey can't be null");
-
- new Condition(null, OPERATOR, ERROR_THRESHOLD, WARN_THRESHOLD, ON_LEAK_PERIOD);
- }
-
- @Test
- public void constructor_throws_NPE_if_operator_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("operator can't be null");
-
- new Condition(METRIC_KEY, null, ERROR_THRESHOLD, WARN_THRESHOLD, ON_LEAK_PERIOD);
- }
-
- @Test
- public void errorThreshold_can_be_null() {
- Condition underTest = new Condition(METRIC_KEY, OPERATOR, null, WARN_THRESHOLD, ON_LEAK_PERIOD);
-
- assertThat(underTest.getErrorThreshold()).isEmpty();
- }
-
- @Test
- public void warnThreshold_can_be_null() {
- Condition underTest = new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, null, ON_LEAK_PERIOD);
-
- assertThat(underTest.getWarningThreshold()).isEmpty();
- }
-
- @Test
- public void verify_getters() {
- assertThat(underTest.getMetricKey()).isEqualTo(METRIC_KEY);
- assertThat(underTest.getOperator()).isEqualTo(OPERATOR);
- assertThat(underTest.getErrorThreshold()).contains(ERROR_THRESHOLD);
- assertThat(underTest.getWarningThreshold()).contains(WARN_THRESHOLD);
- assertThat(underTest.isOnLeakPeriod()).isEqualTo(ON_LEAK_PERIOD);
- }
-
- @Test
- public void toString_is_override() {
- assertThat(underTest.toString())
- .isEqualTo("Condition{metricKey='metric_key', operator=EQUALS, warningThreshold='4', errorThreshold='2', onLeakPeriod=true}");
- }
-
- @Test
- public void toString_does_not_quote_nulls() {
- Condition withNulls = new Condition("metric_key", Condition.Operator.LESS_THAN, null, null, false);
- assertThat(withNulls.toString())
- .isEqualTo("Condition{metricKey='metric_key', operator=LESS_THAN, warningThreshold=null, errorThreshold=null, onLeakPeriod=false}");
- }
-
- @Test
- public void equals_is_based_on_all_fields() {
- assertThat(underTest).isEqualTo(underTest);
- assertThat(underTest).isNotEqualTo(null);
- assertThat(underTest).isNotEqualTo(new Object());
- assertThat(underTest).isEqualTo(new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, WARN_THRESHOLD, ON_LEAK_PERIOD));
- assertThat(underTest).isNotEqualTo(new Condition("other_metric_key", OPERATOR, ERROR_THRESHOLD, WARN_THRESHOLD, ON_LEAK_PERIOD));
- Arrays.stream(Condition.Operator.values())
- .filter(s -> !OPERATOR.equals(s))
- .forEach(otherOperator -> assertThat(underTest)
- .isNotEqualTo(new Condition(METRIC_KEY, otherOperator, ERROR_THRESHOLD, WARN_THRESHOLD, ON_LEAK_PERIOD)));
- assertThat(underTest).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, null, WARN_THRESHOLD, ON_LEAK_PERIOD));
- assertThat(underTest).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, "other_error_threshold", WARN_THRESHOLD, ON_LEAK_PERIOD));
- assertThat(underTest).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, null, ON_LEAK_PERIOD));
- assertThat(underTest).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, "other_warn_threshold", ON_LEAK_PERIOD));
- assertThat(underTest).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, WARN_THRESHOLD, !ON_LEAK_PERIOD));
- }
-
- @Test
- public void hashcode_is_based_on_all_fields() {
- assertThat(underTest.hashCode()).isEqualTo(underTest.hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(null);
- assertThat(underTest.hashCode()).isNotEqualTo(new Object().hashCode());
- assertThat(underTest.hashCode()).isEqualTo(new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, WARN_THRESHOLD, ON_LEAK_PERIOD).hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new Condition("other_metric_key", OPERATOR, ERROR_THRESHOLD, WARN_THRESHOLD, ON_LEAK_PERIOD).hashCode());
- Arrays.stream(Condition.Operator.values())
- .filter(s -> !OPERATOR.equals(s))
- .forEach(otherOperator -> assertThat(underTest.hashCode())
- .isNotEqualTo(new Condition(METRIC_KEY, otherOperator, ERROR_THRESHOLD, WARN_THRESHOLD, ON_LEAK_PERIOD).hashCode()));
- assertThat(underTest.hashCode()).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, null, WARN_THRESHOLD, ON_LEAK_PERIOD).hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, "other_error_threshold", WARN_THRESHOLD, ON_LEAK_PERIOD).hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, null, ON_LEAK_PERIOD).hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, "other_warn_threshold", ON_LEAK_PERIOD).hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new Condition(METRIC_KEY, OPERATOR, ERROR_THRESHOLD, WARN_THRESHOLD, !ON_LEAK_PERIOD).hashCode());
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.qualitygate;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.server.qualitygate.Condition.Operator.EQUALS;
-import static org.sonar.server.qualitygate.EvaluatedCondition.EvaluationStatus.OK;
-import static org.sonar.server.qualitygate.EvaluatedCondition.EvaluationStatus.WARN;
-
-public class EvaluatedConditionTest {
- private static final Condition CONDITION_1 = new Condition("metricKey", EQUALS, "2", "4", false);
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private EvaluatedCondition underTest = new EvaluatedCondition(CONDITION_1, WARN, "value");
-
- @Test
- public void constructor_throws_NPE_if_condition_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("condition can't be null");
-
- new EvaluatedCondition(null, WARN, "value");
- }
-
- @Test
- public void constructor_throws_NPE_if_EvaluationStatus_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("status can't be null");
-
- new EvaluatedCondition(CONDITION_1, null, "value");
- }
-
- @Test
- public void constructor_accepts_null_value() {
- EvaluatedCondition underTest = new EvaluatedCondition(CONDITION_1, WARN, null);
-
- assertThat(underTest.getValue()).isEmpty();
- }
-
- @Test
- public void verify_getters() {
- EvaluatedCondition underTest = new EvaluatedCondition(CONDITION_1, WARN, "value");
-
- assertThat(underTest.getCondition()).isEqualTo(CONDITION_1);
- assertThat(underTest.getStatus()).isEqualTo(WARN);
- assertThat(underTest.getValue()).contains("value");
- }
-
- @Test
- public void override_toString() {
- assertThat(underTest.toString()).isEqualTo("EvaluatedCondition{condition=" +
- "Condition{metricKey='metricKey', operator=EQUALS, warningThreshold='4', errorThreshold='2', onLeakPeriod=false}, " +
- "status=WARN, value='value'}");
- }
-
- @Test
- public void toString_does_not_quote_null_value() {
- EvaluatedCondition underTest = new EvaluatedCondition(CONDITION_1, WARN, null);
-
- assertThat(underTest.toString()).isEqualTo("EvaluatedCondition{condition=" +
- "Condition{metricKey='metricKey', operator=EQUALS, warningThreshold='4', errorThreshold='2', onLeakPeriod=false}, " +
- "status=WARN, value=null}");
- }
-
- @Test
- public void equals_is_based_on_all_fields() {
- assertThat(underTest).isEqualTo(underTest);
- assertThat(underTest).isEqualTo(new EvaluatedCondition(CONDITION_1, WARN, "value"));
- assertThat(underTest).isNotEqualTo(null);
- assertThat(underTest).isNotEqualTo(new Object());
- assertThat(underTest).isNotEqualTo(new EvaluatedCondition(new Condition("other_metric", EQUALS, "a", "b", true), WARN, "value"));
- assertThat(underTest).isNotEqualTo(new EvaluatedCondition(CONDITION_1, OK, "value"));
- assertThat(underTest).isNotEqualTo(new EvaluatedCondition(CONDITION_1, WARN, null));
- assertThat(underTest).isNotEqualTo(new EvaluatedCondition(CONDITION_1, WARN, "other_value"));
- }
-
- @Test
- public void hashcode_is_based_on_all_fields() {
- assertThat(underTest.hashCode()).isEqualTo(underTest.hashCode());
- assertThat(underTest.hashCode()).isEqualTo(new EvaluatedCondition(CONDITION_1, WARN, "value").hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(null);
- assertThat(underTest.hashCode()).isNotEqualTo(new Object().hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new EvaluatedCondition(new Condition("other_metric", EQUALS, "a", "b", true), WARN, "value").hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new EvaluatedCondition(CONDITION_1, OK, "value").hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new EvaluatedCondition(CONDITION_1, WARN, null).hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new EvaluatedCondition(CONDITION_1, WARN, "other_value").hashCode());
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.qualitygate;
-
-import com.google.common.collect.ImmutableSet;
-import java.util.Random;
-import org.apache.commons.lang.RandomStringUtils;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.api.measures.Metric.Level;
-
-import static java.util.Collections.emptySet;
-import static java.util.Collections.singleton;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.server.qualitygate.EvaluatedQualityGate.newBuilder;
-
-public class EvaluatedQualityGateTest {
- private static final String QUALITY_GATE_ID = "qg_id";
- private static final String QUALITY_GATE_NAME = "qg_name";
- private static final QualityGate NO_CONDITION_QUALITY_GATE = new QualityGate(QUALITY_GATE_ID, QUALITY_GATE_NAME, emptySet());
- private static final Condition CONDITION_1 = new Condition("metric_key", Condition.Operator.LESS_THAN, "2", "4", true);
- private static final Condition CONDITION_2 = new Condition("metric_key_2", Condition.Operator.GREATER_THAN, "6", "12", false);
- private static final QualityGate ONE_CONDITION_QUALITY_GATE = new QualityGate(QUALITY_GATE_ID, QUALITY_GATE_NAME, singleton(CONDITION_1));
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private final Random random = new Random();
- private final Level randomStatus = Level.values()[random.nextInt(Level.values().length)];
- private final EvaluatedCondition.EvaluationStatus randomEvaluationStatus = EvaluatedCondition.EvaluationStatus.values()[random
- .nextInt(EvaluatedCondition.EvaluationStatus.values().length)];
- private final String randomValue = random.nextBoolean() ? null : RandomStringUtils.randomAlphanumeric(3);
-
- private EvaluatedQualityGate.Builder builder = newBuilder();
-
- @Test
- public void build_fails_with_NPE_if_status_not_set() {
- builder.setQualityGate(NO_CONDITION_QUALITY_GATE);
-
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("status can't be null");
-
- builder.build();
- }
-
- @Test
- public void addCondition_fails_with_NPE_if_condition_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("condition can't be null");
-
- builder.addCondition(null, EvaluatedCondition.EvaluationStatus.WARN, "a_value");
- }
-
- @Test
- public void addCondition_fails_with_NPE_if_status_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("status can't be null");
-
- builder.addCondition(new Condition("metric_key", Condition.Operator.LESS_THAN, "2", "4", true), null, "a_value");
- }
-
- @Test
- public void addCondition_accepts_null_value() {
- builder.addCondition(CONDITION_1, EvaluatedCondition.EvaluationStatus.NO_VALUE, null);
-
- assertThat(builder.getEvaluatedConditions())
- .containsOnly(new EvaluatedCondition(CONDITION_1, EvaluatedCondition.EvaluationStatus.NO_VALUE, null));
- }
-
- @Test
- public void getEvaluatedConditions_returns_empty_with_no_condition_added_to_builder() {
- assertThat(builder.getEvaluatedConditions()).isEmpty();
- }
-
- @Test
- public void build_fails_with_IAE_if_condition_added_and_no_on_QualityGate() {
- builder.setQualityGate(NO_CONDITION_QUALITY_GATE)
- .setStatus(randomStatus)
- .addCondition(CONDITION_1, randomEvaluationStatus, randomValue);
-
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("Evaluation provided for unknown conditions: [" + CONDITION_1 + "]");
-
- builder.build();
- }
-
- @Test
- public void build_fails_with_IAE_if_condition_is_missing_for_one_defined_in_QualityGate() {
- builder.setQualityGate(ONE_CONDITION_QUALITY_GATE)
- .setStatus(randomStatus);
-
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("Evaluation missing for the following conditions: [" + CONDITION_1 + "]");
-
- builder.build();
- }
-
- @Test
- public void verify_getters() {
- EvaluatedQualityGate underTest = builder
- .setQualityGate(ONE_CONDITION_QUALITY_GATE)
- .setStatus(randomStatus)
- .addCondition(CONDITION_1, randomEvaluationStatus, randomValue)
- .build();
-
- assertThat(underTest.getQualityGate()).isEqualTo(ONE_CONDITION_QUALITY_GATE);
- assertThat(underTest.getStatus()).isEqualTo(randomStatus);
- assertThat(underTest.getEvaluatedConditions())
- .containsOnly(new EvaluatedCondition(CONDITION_1, randomEvaluationStatus, randomValue));
- }
-
- @Test
- public void verify_getters_when_no_condition() {
- EvaluatedQualityGate underTest = builder
- .setQualityGate(NO_CONDITION_QUALITY_GATE)
- .setStatus(randomStatus)
- .build();
-
- assertThat(underTest.getQualityGate()).isEqualTo(NO_CONDITION_QUALITY_GATE);
- assertThat(underTest.getStatus()).isEqualTo(randomStatus);
- assertThat(underTest.getEvaluatedConditions()).isEmpty();
- }
-
- @Test
- public void verify_getters_when_multiple_conditions() {
- QualityGate qualityGate = new QualityGate(QUALITY_GATE_ID, QUALITY_GATE_NAME, ImmutableSet.of(CONDITION_1, CONDITION_2));
- EvaluatedQualityGate underTest = builder
- .setQualityGate(qualityGate)
- .setStatus(randomStatus)
- .addCondition(CONDITION_1, randomEvaluationStatus, randomValue)
- .addCondition(CONDITION_2, EvaluatedCondition.EvaluationStatus.WARN, "bad")
- .build();
-
- assertThat(underTest.getQualityGate()).isEqualTo(qualityGate);
- assertThat(underTest.getStatus()).isEqualTo(randomStatus);
- assertThat(underTest.getEvaluatedConditions()).containsOnly(
- new EvaluatedCondition(CONDITION_1, randomEvaluationStatus, randomValue),
- new EvaluatedCondition(CONDITION_2, EvaluatedCondition.EvaluationStatus.WARN, "bad"));
- }
-
- @Test
- public void equals_is_based_on_all_fields() {
- EvaluatedQualityGate.Builder builder = this.builder
- .setQualityGate(ONE_CONDITION_QUALITY_GATE)
- .setStatus(Level.WARN)
- .addCondition(CONDITION_1, EvaluatedCondition.EvaluationStatus.WARN, "foo");
-
- EvaluatedQualityGate underTest = builder.build();
- assertThat(underTest).isEqualTo(builder.build());
- assertThat(underTest).isNotSameAs(builder.build());
- assertThat(underTest).isNotEqualTo(null);
- assertThat(underTest).isNotEqualTo(new Object());
- assertThat(underTest).isNotEqualTo(builder.setQualityGate(new QualityGate("other_id", QUALITY_GATE_NAME, singleton(CONDITION_1))).build());
- assertThat(underTest).isNotEqualTo(builder.setQualityGate(ONE_CONDITION_QUALITY_GATE).setStatus(Level.OK).build());
- assertThat(underTest).isNotEqualTo(newBuilder()
- .setQualityGate(ONE_CONDITION_QUALITY_GATE)
- .setStatus(Level.WARN)
- .addCondition(CONDITION_1, EvaluatedCondition.EvaluationStatus.OK, "foo")
- .build());
- }
-
- @Test
- public void hashcode_is_based_on_all_fields() {
- EvaluatedQualityGate.Builder builder = this.builder
- .setQualityGate(ONE_CONDITION_QUALITY_GATE)
- .setStatus(Level.WARN)
- .addCondition(CONDITION_1, EvaluatedCondition.EvaluationStatus.WARN, "foo");
-
- EvaluatedQualityGate underTest = builder.build();
- assertThat(underTest.hashCode()).isEqualTo(builder.build().hashCode());
- assertThat(underTest.hashCode()).isNotSameAs(builder.build().hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(null);
- assertThat(underTest.hashCode()).isNotEqualTo(new Object().hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(builder.setQualityGate(new QualityGate("other_id", QUALITY_GATE_NAME, singleton(CONDITION_1))).build().hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(builder.setQualityGate(ONE_CONDITION_QUALITY_GATE).setStatus(Level.OK).build().hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(newBuilder()
- .setQualityGate(ONE_CONDITION_QUALITY_GATE)
- .setStatus(Level.WARN)
- .addCondition(CONDITION_1, EvaluatedCondition.EvaluationStatus.OK, "foo")
- .build().hashCode());
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.qualitygate;
-
-import com.google.common.collect.ImmutableSet;
-import java.util.Random;
-import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-import java.util.stream.Stream;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-import static java.util.Collections.emptySet;
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class QualityGateTest {
- private static final String QUALIGATE_ID = "qg_id";
- private static final String QUALIGATE_NAME = "qg_name";
- private static final Condition CONDITION_1 = new Condition("m1", Condition.Operator.EQUALS, "1", "2", false);
- private static final Condition CONDITION_2 = new Condition("m2", Condition.Operator.LESS_THAN, "2", "4", true);
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private QualityGate underTest = new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_1, CONDITION_2));
-
- @Test
- public void constructor_fails_with_NPE_if_id_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("id can't be null");
-
- new QualityGate(null, "name", emptySet());
- }
-
- @Test
- public void constructor_fails_with_NPE_if_name_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("name can't be null");
-
- new QualityGate("id", null, emptySet());
- }
-
- @Test
- public void constructor_fails_with_NPE_if_conditions_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("conditions can't be null");
-
- new QualityGate("id", "name", null);
- }
-
- @Test
- public void constructor_fails_with_NPE_if_conditions_contains_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("condition can't be null");
- Random random = new Random();
- Set<Condition> conditions = Stream.of(
- IntStream.range(0, random.nextInt(5))
- .mapToObj(i -> new Condition("m_before_" + i, Condition.Operator.EQUALS, null, null, false)),
- Stream.of((Condition) null),
- IntStream.range(0, random.nextInt(5))
- .mapToObj(i -> new Condition("m_after_" + i, Condition.Operator.EQUALS, null, null, false)))
- .flatMap(s -> s)
- .collect(Collectors.toSet());
-
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("condition can't be null");
-
- new QualityGate("id", "name", conditions);
- }
-
- @Test
- public void verify_getters() {
- assertThat(underTest.getId()).isEqualTo(QUALIGATE_ID);
- assertThat(underTest.getName()).isEqualTo(QUALIGATE_NAME);
- assertThat(underTest.getConditions()).containsOnly(CONDITION_1, CONDITION_2);
- }
-
- @Test
- public void toString_is_override() {
- QualityGate underTest = new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_2));
-
- assertThat(underTest.toString()).isEqualTo("QualityGate{id=qg_id, name='qg_name', conditions=[" +
- "Condition{metricKey='m2', operator=LESS_THAN, warningThreshold='4', errorThreshold='2', onLeakPeriod=true}" +
- "]}");
- }
-
- @Test
- public void equals_is_based_on_all_fields() {
- assertThat(underTest).isEqualTo(underTest);
- assertThat(underTest).isEqualTo(new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_2, CONDITION_1)));
- assertThat(underTest).isNotEqualTo(null);
- assertThat(underTest).isNotEqualTo(new Object());
- assertThat(underTest).isNotEqualTo(new QualityGate("other_id", QUALIGATE_NAME, ImmutableSet.of(CONDITION_2, CONDITION_1)));
- assertThat(underTest).isNotEqualTo(new QualityGate(QUALIGATE_ID, "other_name", ImmutableSet.of(CONDITION_2, CONDITION_1)));
- assertThat(underTest).isNotEqualTo(new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, emptySet()));
- assertThat(underTest).isNotEqualTo(new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_1)));
- assertThat(underTest).isNotEqualTo(new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_2)));
- assertThat(underTest).isNotEqualTo(
- new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_1, CONDITION_2, new Condition("new", Condition.Operator.GREATER_THAN, "a", "b", false))));
- }
-
- @Test
- public void hashcode_is_based_on_all_fields() {
- assertThat(underTest.hashCode()).isEqualTo(underTest.hashCode());
- assertThat(underTest.hashCode()).isEqualTo(new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_2, CONDITION_1)).hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(null);
- assertThat(underTest.hashCode()).isNotEqualTo(new Object().hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new QualityGate("other_id", QUALIGATE_NAME, ImmutableSet.of(CONDITION_2, CONDITION_1)).hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new QualityGate(QUALIGATE_ID, "other_name", ImmutableSet.of(CONDITION_2, CONDITION_1)).hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, emptySet()).hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_1)).hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_2)).hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(
- new QualityGate(QUALIGATE_ID, QUALIGATE_NAME, ImmutableSet.of(CONDITION_1, CONDITION_2, new Condition("new", Condition.Operator.GREATER_THAN, "a", "b", false))).hashCode());
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.util;
-
-import java.io.IOException;
-import java.util.Base64;
-import okhttp3.OkHttpClient;
-import okhttp3.Protocol;
-import okhttp3.Request;
-import okhttp3.Response;
-import okhttp3.mockwebserver.MockResponse;
-import okhttp3.mockwebserver.MockWebServer;
-import okhttp3.mockwebserver.RecordedRequest;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.SonarQubeSide;
-import org.sonar.api.SonarRuntime;
-import org.sonar.api.config.internal.MapSettings;
-import org.sonar.api.internal.SonarRuntimeImpl;
-import org.sonar.api.utils.Version;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class OkHttpClientProviderTest {
-
- private MapSettings settings = new MapSettings();
- private SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(Version.parse("6.2"), SonarQubeSide.SERVER);
- private final OkHttpClientProvider underTest = new OkHttpClientProvider();
-
- @Rule
- public MockWebServer server = new MockWebServer();
-
- @Test
- public void get_returns_a_OkHttpClient_with_default_configuration() throws Exception {
- OkHttpClient client = underTest.provide(settings.asConfig(), runtime);
-
- assertThat(client.connectTimeoutMillis()).isEqualTo(10_000);
- assertThat(client.readTimeoutMillis()).isEqualTo(10_000);
- assertThat(client.proxy()).isNull();
-
- RecordedRequest recordedRequest = call(client);
- assertThat(recordedRequest.getHeader("User-Agent")).isEqualTo("SonarQube/6.2");
- assertThat(recordedRequest.getHeader("Proxy-Authorization")).isNull();
- }
-
- @Test
- public void get_returns_a_OkHttpClient_with_proxy_authentication() throws Exception {
- settings.setProperty("http.proxyUser", "the-login");
- settings.setProperty("http.proxyPassword", "the-password");
-
- OkHttpClient client = underTest.provide(settings.asConfig(), runtime);
- Response response = new Response.Builder().protocol(Protocol.HTTP_1_1).request(new Request.Builder().url("http://foo").build()).code(407).build();
- Request request = client.proxyAuthenticator().authenticate(null, response);
-
- assertThat(request.header("Proxy-Authorization")).isEqualTo("Basic " + Base64.getEncoder().encodeToString("the-login:the-password".getBytes()));
- }
-
- @Test
- public void get_returns_a_singleton() {
- OkHttpClient client1 = underTest.provide(settings.asConfig(), runtime);
- OkHttpClient client2 = underTest.provide(settings.asConfig(), runtime);
- assertThat(client2).isNotNull().isSameAs(client1);
- }
-
- private RecordedRequest call(OkHttpClient client) throws IOException, InterruptedException {
- server.enqueue(new MockResponse().setBody("pong"));
- client.newCall(new Request.Builder().url(server.url("/ping")).build()).execute();
-
- return server.takeRequest();
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.Date;
-import java.util.Random;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class AnalysisTest {
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- @Test
- public void constructor_throws_NPE_when_uuid_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("uuid must not be null");
-
- new Analysis(null, 1_990L);
- }
-
- @Test
- public void test_equality() {
- String uuid = randomAlphanumeric(35);
- long date = new Random().nextLong();
- Analysis underTest = new Analysis(uuid, date);
-
- assertThat(underTest).isEqualTo(underTest);
- assertThat(underTest.getUuid()).isEqualTo(uuid);
- assertThat(underTest.getDate()).isEqualTo(new Date(date));
-
- assertThat(underTest).isNotEqualTo(null);
- assertThat(underTest).isNotEqualTo(new Analysis(uuid + "1", date));
- assertThat(underTest).isNotEqualTo(new Analysis(uuid, date + 1_000L));
- }
-
- @Test
- public void test_hashcode() {
- String uuid = randomAlphanumeric(35);
- long date = new Random().nextLong();
- Analysis underTest = new Analysis(uuid, date);
-
- assertThat(underTest.hashCode()).isEqualTo(underTest.hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new Analysis(uuid + "1", date).hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new Analysis(uuid, date + 1_000).hashCode());
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.ArrayList;
-import java.util.List;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.utils.System2;
-import org.sonar.db.DbTester;
-import org.sonar.db.component.ComponentDbTester;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.db.organization.OrganizationDbTester;
-import org.sonar.db.organization.OrganizationDto;
-import org.sonar.db.webhook.WebhookDbTester;
-import org.sonar.server.async.AsyncExecution;
-import org.sonar.server.organization.DefaultOrganizationProvider;
-
-import static java.util.Objects.requireNonNull;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.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;
-import static org.sonar.db.DbTester.create;
-import static org.sonar.db.webhook.WebhookTesting.newWebhook;
-import static org.sonar.server.organization.TestDefaultOrganizationProvider.from;
-
-public class AsynchronousWebHooksImplTest {
-
- private System2 system2 = mock(System2.class);
-
- @Rule
- public DbTester db = create(system2);
- private WebhookDbTester webhookDbTester = db.webhooks();
- private ComponentDbTester componentDbTester = db.components();
- private OrganizationDbTester organizationDbTester = db.organizations();
- private DefaultOrganizationProvider defaultOrganizationProvider = from(db);
-
- private static final long NOW = 1_500_000_000_000L;
-
-
- private final TestWebhookCaller caller = new TestWebhookCaller();
- private final WebhookDeliveryStorage deliveryStorage = mock(WebhookDeliveryStorage.class);
- private final WebhookPayload mock = mock(WebhookPayload.class);
- private final RecordingAsyncExecution asyncExecution = new RecordingAsyncExecution();
-
- private final WebHooksImpl underTest = new WebHooksImpl(caller, deliveryStorage, asyncExecution, db.getDbClient());
-
- @Test
- public void send_global_webhooks() {
-
- OrganizationDto organizationDto = db.getDefaultOrganization() ;
- ComponentDto project = componentDbTester.insertPrivateProject(componentDto -> componentDto.setOrganizationUuid(organizationDto.getUuid()));
- webhookDbTester.insert(newWebhook(organizationDto).setName("First").setUrl("http://url1"));
- webhookDbTester.insert(newWebhook(organizationDto).setName("Second").setUrl("http://url2"));
-
- caller.enqueueSuccess(NOW, 200, 1_234);
- caller.enqueueFailure(NOW, new IOException("Fail to connect"));
-
- underTest.sendProjectAnalysisUpdate(new WebHooks.Analysis(project.uuid(), "1", "#1"), () -> mock);
-
- assertThat(caller.countSent()).isZero();
- verifyZeroInteractions(deliveryStorage);
-
- asyncExecution.executeRecorded();
-
- assertThat(caller.countSent()).isEqualTo(2);
- verify(deliveryStorage, times(2)).persist(any(WebhookDelivery.class));
- verify(deliveryStorage).purge(project.uuid());
- }
-
- private static class RecordingAsyncExecution implements AsyncExecution {
- private final List<Runnable> runnableList = new ArrayList<>();
-
- @Override
- public void addToQueue(Runnable r) {
- runnableList.add(requireNonNull(r));
- }
-
- public void executeRecorded() {
- ArrayList<Runnable> runnables = new ArrayList<>(runnableList);
- runnableList.clear();
- runnables.forEach(Runnable::run);
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.Random;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class BranchTest {
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private Branch underTest = new Branch(true, "b", Branch.Type.SHORT);
-
- @Test
- public void constructor_throws_NPE_if_type_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("type can't be null");
-
- new Branch(new Random().nextBoolean(), "s", null);
- }
-
- @Test
- public void verify_getters() {
- assertThat(underTest.isMain()).isTrue();
- assertThat(underTest.getName()).contains("b");
- assertThat(underTest.getType()).isEqualTo(Branch.Type.SHORT);
-
-
- Branch underTestWithNull = new Branch(false, null, Branch.Type.LONG);
- assertThat(underTestWithNull.isMain()).isFalse();
- assertThat(underTestWithNull.getName()).isEmpty();
- assertThat(underTestWithNull.getType()).isEqualTo(Branch.Type.LONG);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class CeTaskTest {
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private CeTask underTest = new CeTask("A", CeTask.Status.SUCCESS);
-
- @Test
- public void constructor_throws_NPE_if_id_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("id can't be null");
-
- new CeTask(null, CeTask.Status.SUCCESS);
- }
-
- @Test
- public void constructor_throws_NPE_if_status_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("status can't be null");
-
- new CeTask("B", null);
- }
-
- @Test
- public void verify_getters() {
- assertThat(underTest.getId()).isEqualTo("A");
- assertThat(underTest.getStatus()).isEqualTo(CeTask.Status.SUCCESS);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 com.google.common.collect.ImmutableMap;
-import java.util.Map;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.api.measures.Metric;
-import org.sonar.server.project.Project;
-import org.sonar.server.qualitygate.EvaluatedQualityGate;
-import org.sonar.server.qualitygate.QualityGate;
-
-import static java.util.Collections.emptyMap;
-import static java.util.Collections.emptySet;
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class ProjectAnalysisTest {
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private final CeTask ceTask = new CeTask("id", CeTask.Status.SUCCESS);
- private final Project project = new Project("uuid", "key", "name");
- private final Analysis analysis = new Analysis("analysis_uuid", 1_500L);
- private final Branch branch = new Branch(true, "name", Branch.Type.SHORT);
- private final EvaluatedQualityGate qualityGate = EvaluatedQualityGate.newBuilder()
- .setQualityGate(new QualityGate("id", "name", emptySet()))
- .setStatus(Metric.Level.WARN)
- .build();
- private final Map<String, String> properties = ImmutableMap.of("a", "b");
- private ProjectAnalysis underTest = new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, 1L, properties);
-
- @Test
- public void constructor_throws_NPE_if_project_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("project can't be null");
-
- new ProjectAnalysis(null,
- ceTask,
- analysis,
- branch,
- qualityGate,
- 1L,
- emptyMap());
- }
-
- @Test
- public void constructor_throws_NPE_if_properties_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("properties can't be null");
-
- new ProjectAnalysis(project,
- ceTask,
- analysis,
- branch,
- qualityGate,
- 1L,
- null);
- }
-
- @Test
- public void verify_getters() {
- assertThat(underTest.getCeTask().get()).isSameAs(ceTask);
- assertThat(underTest.getProject()).isSameAs(project);
- assertThat(underTest.getBranch().get()).isSameAs(branch);
- assertThat(underTest.getQualityGate().get()).isSameAs(qualityGate);
- assertThat(underTest.getProperties()).isEqualTo(properties);
- assertThat(underTest.getAnalysis().get()).isEqualTo(analysis);
-
- ProjectAnalysis underTestWithNulls = new ProjectAnalysis(project, null, null, null, null, null, emptyMap());
- assertThat(underTestWithNulls.getCeTask()).isEmpty();
- assertThat(underTestWithNulls.getBranch()).isEmpty();
- assertThat(underTestWithNulls.getQualityGate()).isEmpty();
- assertThat(underTestWithNulls.getProperties()).isEmpty();
- assertThat(underTestWithNulls.getAnalysis()).isEmpty();
- }
-
- @Test
- public void defines_equals_based_on_all_fields() {
- assertThat(underTest).isEqualTo(underTest);
- assertThat(underTest).isEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, 1L, properties));
- assertThat(underTest).isNotEqualTo(null);
- assertThat(underTest).isNotEqualTo(new Object());
- assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, new CeTask("2", CeTask.Status.SUCCESS), analysis, branch, qualityGate, 1L, properties));
- assertThat(underTest).isNotEqualTo(new ProjectAnalysis(new Project("A", "B", "C"), ceTask, analysis, branch, qualityGate, 1L, properties));
- assertThat(underTest).isNotEqualTo(new ProjectAnalysis(new Project("A", "B", "C"), ceTask, analysis, branch, qualityGate, 1L, properties));
- assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, null, null, null, qualityGate, 1L, properties));
- assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, null, null, qualityGate, 1L, properties));
- assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, new Analysis("foo", 1_500L), null, qualityGate, 1L, properties));
- assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, null, qualityGate, 1L, properties));
- assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, new Branch(false, "B", Branch.Type.SHORT), qualityGate, 1L, properties));
- assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, null, 1L, properties));
- EvaluatedQualityGate otherQualityGate = EvaluatedQualityGate.newBuilder()
- .setQualityGate(new QualityGate("A", "B", emptySet()))
- .setStatus(Metric.Level.WARN)
- .build();
- assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, otherQualityGate, 1L, properties));
- assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, null, properties));
- assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, 2L, properties));
- assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, 1L, emptyMap()));
- assertThat(underTest).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, 1L, ImmutableMap.of("A", "B")));
- }
-
- @Test
- public void defines_hashcode_based_on_all_fields() {
- assertThat(underTest.hashCode()).isEqualTo(underTest.hashCode());
- assertThat(underTest.hashCode()).isEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, 1L, properties).hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new Object().hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, new CeTask("2", CeTask.Status.SUCCESS), analysis, branch, qualityGate, 1L, properties).hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(new Project("A", "B", "C"), ceTask, analysis, branch, qualityGate, 1L, properties).hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(new Project("A", "B", "C"), ceTask, analysis, branch, qualityGate, 1L, properties).hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, null, null, null, qualityGate, 1L, properties).hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, null, null, qualityGate, 1L, properties).hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, new Analysis("foo", 1_500L), null, qualityGate, 1L, properties).hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, null, qualityGate, 1L, properties).hashCode());
- assertThat(underTest.hashCode())
- .isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, new Branch(false, "B", Branch.Type.SHORT), qualityGate, 1L, properties).hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, null, 1L, properties).hashCode());
- EvaluatedQualityGate otherQualityGate = EvaluatedQualityGate.newBuilder()
- .setQualityGate(new QualityGate("A", "B", emptySet()))
- .setStatus(Metric.Level.WARN)
- .build();
- assertThat(underTest.hashCode())
- .isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, otherQualityGate, 1L, properties).hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, this.qualityGate, null, properties).hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, this.qualityGate, 2L, properties).hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, this.qualityGate, 1L, emptyMap()).hashCode());
- assertThat(underTest.hashCode()).isNotEqualTo(new ProjectAnalysis(project, ceTask, analysis, branch, this.qualityGate, 1L, ImmutableMap.of("B", "C")).hashCode());
- }
-
- @Test
- public void verify_toString() {
- assertThat(underTest.toString()).isEqualTo(
- "ProjectAnalysis{project=Project{uuid='uuid', key='key', name='name', description=null}, ceTask=CeTask{id='id', status=SUCCESS}, branch=Branch{main=true, name='name', type=SHORT}, qualityGate=EvaluatedQualityGate{qualityGate=QualityGate{id=id, name='name', conditions=[]}, status=WARN, evaluatedConditions=[]}, updatedAt=1, properties={a=b}, analysis=Analysis{uuid='analysis_uuid', date=1500}}");
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.utils.log.LogTester;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbTester;
-import org.sonar.db.component.ComponentDbTester;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.db.webhook.WebhookDbTester;
-import org.sonar.server.async.AsyncExecution;
-import org.sonar.server.organization.DefaultOrganizationProvider;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.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;
-import static org.sonar.api.utils.log.LoggerLevel.DEBUG;
-import static org.sonar.db.DbTester.create;
-import static org.sonar.db.webhook.WebhookTesting.newWebhook;
-import static org.sonar.server.organization.TestDefaultOrganizationProvider.from;
-
-public class SynchronousWebHooksImplTest {
-
- private static final long NOW = 1_500_000_000_000L;
-
- @Rule
- public LogTester logTester = new LogTester();
-
- @Rule
- public DbTester db = create();
- private DbClient dbClient = db.getDbClient();
-
- private WebhookDbTester webhookDbTester = db.webhooks();
- private ComponentDbTester componentDbTester = db.components();
- private DefaultOrganizationProvider defaultOrganizationProvider = from(db);
-
- private final TestWebhookCaller caller = new TestWebhookCaller();
- private final WebhookDeliveryStorage deliveryStorage = mock(WebhookDeliveryStorage.class);
- private final WebhookPayload mock = mock(WebhookPayload.class);
- private final AsyncExecution synchronousAsyncExecution = Runnable::run;
- private final WebHooksImpl underTest = new WebHooksImpl(caller, deliveryStorage, synchronousAsyncExecution, dbClient);
-
- @Test
- public void isEnabled_returns_false_if_no_webhooks() {
- ComponentDto componentDto = componentDbTester.insertPrivateProject();
-
- assertThat(underTest.isEnabled(componentDto)).isFalse();
- }
-
- @Test
- public void isEnabled_returns_true_if_one_valid_global_webhook() {
- ComponentDto componentDto = componentDbTester.insertPrivateProject();
- webhookDbTester.insert(newWebhook(componentDto).setName("First").setUrl("http://url1"));
-
- assertThat(underTest.isEnabled(componentDto)).isTrue();
- }
-
- @Test
- public void isEnabled_returns_true_if_one_valid_project_webhook() {
- String organizationUuid = defaultOrganizationProvider.get().getUuid();
- ComponentDto componentDto = componentDbTester.insertPrivateProject().setOrganizationUuid(organizationUuid);
- webhookDbTester.insert(newWebhook(componentDto).setName("First").setUrl("http://url1"));
-
- assertThat(underTest.isEnabled(componentDto)).isTrue();
- }
-
-
- @Test
- public void do_nothing_if_no_webhooks() {
- ComponentDto componentDto = componentDbTester.insertPrivateProject().setOrganizationUuid(defaultOrganizationProvider.get().getUuid());
-
- underTest.sendProjectAnalysisUpdate(new WebHooks.Analysis(componentDto.uuid(), "1", "#1"), () -> mock);
-
- assertThat(caller.countSent()).isEqualTo(0);
- assertThat(logTester.logs(DEBUG)).isEmpty();
- verifyZeroInteractions(deliveryStorage);
- }
-
- @Test
- public void send_global_webhooks() {
-
- ComponentDto componentDto = componentDbTester.insertPrivateProject();
- webhookDbTester.insert(newWebhook(componentDto).setName("First").setUrl("http://url1"));
- webhookDbTester.insert(newWebhook(componentDto).setName("Second").setUrl("http://url2"));
- caller.enqueueSuccess(NOW, 200, 1_234);
- caller.enqueueFailure(NOW, new IOException("Fail to connect"));
-
- underTest.sendProjectAnalysisUpdate(new WebHooks.Analysis(componentDto.uuid(), "1", "#1"), () -> mock);
-
- assertThat(caller.countSent()).isEqualTo(2);
- assertThat(logTester.logs(DEBUG)).contains("Sent webhook 'First' | url=http://url1 | time=1234ms | status=200");
- assertThat(logTester.logs(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(componentDto.uuid());
-
- }
-
- @Test
- public void send_project_webhooks() {
-
- String organizationUuid = defaultOrganizationProvider.get().getUuid();
- ComponentDto componentDto = componentDbTester.insertPrivateProject().setOrganizationUuid(organizationUuid);
- webhookDbTester.insert(newWebhook(componentDto).setName("First").setUrl("http://url1"));
- caller.enqueueSuccess(NOW, 200, 1_234);
-
- underTest.sendProjectAnalysisUpdate(new WebHooks.Analysis(componentDto.uuid(), "1", "#1"), () -> mock);
-
- assertThat(caller.countSent()).isEqualTo(1);
- assertThat(logTester.logs(DEBUG)).contains("Sent webhook 'First' | url=http://url1 | time=1234ms | status=200");
- verify(deliveryStorage).persist(any(WebhookDelivery.class));
- verify(deliveryStorage).purge(componentDto.uuid());
-
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.LinkedList;
-import java.util.Queue;
-import java.util.concurrent.atomic.AtomicInteger;
-import javax.annotation.Nullable;
-
-import static java.util.Objects.requireNonNull;
-
-public class TestWebhookCaller implements WebhookCaller {
-
- private final Queue<Item> deliveries = new LinkedList<>();
- private final AtomicInteger countSent = new AtomicInteger(0);
-
- public TestWebhookCaller enqueueSuccess(long at, int httpCode, int durationMs) {
- deliveries.add(new Item(at, httpCode, durationMs, null));
- return this;
- }
-
- public TestWebhookCaller enqueueFailure(long at, Throwable t) {
- deliveries.add(new Item(at, null, null, t));
- return this;
- }
-
- @Override
- public WebhookDelivery call(Webhook webhook, WebhookPayload payload) {
- Item item = requireNonNull(deliveries.poll(), "Queue is empty");
- countSent.incrementAndGet();
- return new WebhookDelivery.Builder()
- .setAt(item.at)
- .setHttpStatus(item.httpCode)
- .setDurationInMs(item.durationMs)
- .setError(item.throwable)
- .setPayload(payload)
- .setWebhook(webhook)
- .build();
- }
-
- public int countSent() {
- return countSent.get();
- }
-
- private static class Item {
- final long at;
- final Integer httpCode;
- final Integer durationMs;
- final Throwable throwable;
-
- Item(long at, @Nullable Integer httpCode, @Nullable Integer durationMs, @Nullable Throwable throwable) {
- this.at = at;
- this.httpCode = httpCode;
- this.durationMs = durationMs;
- this.throwable = throwable;
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 okhttp3.Credentials;
-import okhttp3.HttpUrl;
-import okhttp3.mockwebserver.MockResponse;
-import okhttp3.mockwebserver.MockWebServer;
-import okhttp3.mockwebserver.RecordedRequest;
-import org.apache.commons.lang.RandomStringUtils;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.DisableOnDebug;
-import org.junit.rules.TestRule;
-import org.junit.rules.Timeout;
-import org.sonar.api.SonarQubeSide;
-import org.sonar.api.SonarRuntime;
-import org.sonar.api.config.internal.MapSettings;
-import org.sonar.api.internal.SonarRuntimeImpl;
-import org.sonar.api.utils.System2;
-import org.sonar.api.utils.Version;
-import org.sonar.api.utils.internal.TestSystem2;
-import org.sonar.server.util.OkHttpClientProvider;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class WebhookCallerImplTest {
-
- private static final long NOW = 1_500_000_000_000L;
- private static final String PROJECT_UUID = "P_UUID1";
- private static final String WEBHOOK_UUID = "WH_UUID1";
- private static final String CE_TASK_UUID = "CE_UUID1";
- private static final String SOME_JSON = "{\"payload\": {}}";
- private static final WebhookPayload PAYLOAD = new WebhookPayload("P1", SOME_JSON);
-
- @Rule
- public MockWebServer server = new MockWebServer();
-
- @Rule
- public TestRule safeguardTimeout = new DisableOnDebug(Timeout.seconds(60));
-
- private System2 system = new TestSystem2().setNow(NOW);
-
- @Test
- public void send_posts_payload_to_http_server() throws Exception {
- Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", server.url("/ping").toString());
-
- server.enqueue(new MockResponse().setBody("pong").setResponseCode(201));
- WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
-
- assertThat(delivery.getHttpStatus()).hasValue(201);
- assertThat(delivery.getWebhook().getUuid()).isEqualTo(WEBHOOK_UUID);
- assertThat(delivery.getDurationInMs().get()).isGreaterThanOrEqualTo(0);
- assertThat(delivery.getError()).isEmpty();
- assertThat(delivery.getAt()).isEqualTo(NOW);
- assertThat(delivery.getWebhook()).isSameAs(webhook);
- assertThat(delivery.getPayload()).isSameAs(PAYLOAD);
-
- RecordedRequest recordedRequest = server.takeRequest();
- assertThat(recordedRequest.getMethod()).isEqualTo("POST");
- assertThat(recordedRequest.getPath()).isEqualTo("/ping");
- assertThat(recordedRequest.getBody().readUtf8()).isEqualTo(PAYLOAD.getJson());
- assertThat(recordedRequest.getHeader("User-Agent")).isEqualTo("SonarQube/6.2");
- assertThat(recordedRequest.getHeader("Content-Type")).isEqualTo("application/json; charset=utf-8");
- assertThat(recordedRequest.getHeader("X-SonarQube-Project")).isEqualTo(PAYLOAD.getProjectKey());
- }
-
- @Test
- public void silently_catch_error_when_external_server_does_not_answer() throws Exception {
- Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", server.url("/ping").toString());
-
- server.shutdown();
- WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
-
- assertThat(delivery.getHttpStatus()).isEmpty();
- assertThat(delivery.getDurationInMs().get()).isGreaterThanOrEqualTo(0);
- // message can be "Connection refused" or "connect timed out"
- assertThat(delivery.getErrorMessage().get()).matches("(.*Connection refused.*)|(.*connect timed out.*)");
- assertThat(delivery.getAt()).isEqualTo(NOW);
- assertThat(delivery.getWebhook()).isSameAs(webhook);
- assertThat(delivery.getPayload()).isSameAs(PAYLOAD);
- }
-
- @Test
- public void silently_catch_error_when_url_is_incorrect() {
- Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", "this_is_not_an_url");
-
- WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
-
- assertThat(delivery.getHttpStatus()).isEmpty();
- assertThat(delivery.getDurationInMs().get()).isGreaterThanOrEqualTo(0);
- assertThat(delivery.getError().get()).isInstanceOf(IllegalArgumentException.class);
- assertThat(delivery.getErrorMessage().get()).isEqualTo("Webhook URL is not valid: this_is_not_an_url");
- assertThat(delivery.getAt()).isEqualTo(NOW);
- assertThat(delivery.getWebhook()).isSameAs(webhook);
- assertThat(delivery.getPayload()).isSameAs(PAYLOAD);
- }
-
- /**
- * SONAR-8799
- */
- @Test
- public void redirects_should_be_followed_with_POST_method() throws Exception {
- Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", server.url("/redirect").toString());
-
- // /redirect redirects to /target
- server.enqueue(new MockResponse().setResponseCode(307).setHeader("Location", server.url("target")));
- server.enqueue(new MockResponse().setResponseCode(200));
-
- WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
-
- assertThat(delivery.getHttpStatus().get()).isEqualTo(200);
- assertThat(delivery.getDurationInMs().get()).isGreaterThanOrEqualTo(0);
- assertThat(delivery.getError()).isEmpty();
- assertThat(delivery.getAt()).isEqualTo(NOW);
- assertThat(delivery.getWebhook()).isSameAs(webhook);
- assertThat(delivery.getPayload()).isSameAs(PAYLOAD);
-
- takeAndVerifyPostRequest("/redirect");
- takeAndVerifyPostRequest("/target");
- }
-
- @Test
- public void credentials_are_propagated_to_POST_redirects() throws Exception {
- HttpUrl url = server.url("/redirect").newBuilder().username("theLogin").password("thePassword").build();
- Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", url.toString());
-
- // /redirect redirects to /target
- server.enqueue(new MockResponse().setResponseCode(307).setHeader("Location", server.url("target")));
- server.enqueue(new MockResponse().setResponseCode(200));
-
- WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
-
- assertThat(delivery.getHttpStatus().get()).isEqualTo(200);
-
- RecordedRequest redirectedRequest = takeAndVerifyPostRequest("/redirect");
- assertThat(redirectedRequest.getHeader("Authorization")).isEqualTo(Credentials.basic(url.username(), url.password()));
-
- RecordedRequest targetRequest = takeAndVerifyPostRequest("/target");
- assertThat(targetRequest.getHeader("Authorization")).isEqualTo(Credentials.basic(url.username(), url.password()));
- }
-
- @Test
- public void redirects_throws_ISE_if_header_Location_is_missing() {
- HttpUrl url = server.url("/redirect");
- Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", url.toString());
-
- server.enqueue(new MockResponse().setResponseCode(307));
-
- WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
-
- Throwable error = delivery.getError().get();
- assertThat(error)
- .isInstanceOf(IllegalStateException.class)
- .hasMessage("Missing HTTP header 'Location' in redirect of " + url);
- }
-
- @Test
- public void redirects_throws_ISE_if_header_Location_does_not_relate_to_a_supported_protocol() {
- HttpUrl url = server.url("/redirect");
- Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", url.toString());
-
- server.enqueue(new MockResponse().setResponseCode(307).setHeader("Location", "ftp://foo"));
-
- WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
-
- Throwable error = delivery.getError().get();
- assertThat(error)
- .isInstanceOf(IllegalStateException.class)
- .hasMessage("Unsupported protocol in redirect of " + url + " to ftp://foo");
- }
-
- @Test
- public void send_basic_authentication_header_if_url_contains_credentials() throws Exception {
- HttpUrl url = server.url("/ping").newBuilder().username("theLogin").password("thePassword").build();
- Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID, RandomStringUtils.randomAlphanumeric(40),"my-webhook", url.toString());
- server.enqueue(new MockResponse().setBody("pong"));
-
- WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
-
- assertThat(delivery.getWebhook().getUrl())
- .isEqualTo(url.toString())
- .contains("://theLogin:thePassword@");
- RecordedRequest recordedRequest = takeAndVerifyPostRequest("/ping");
- assertThat(recordedRequest.getHeader("Authorization")).isEqualTo(Credentials.basic(url.username(), url.password()));
- }
-
- private RecordedRequest takeAndVerifyPostRequest(String expectedPath) throws Exception {
- RecordedRequest request = server.takeRequest();
-
- assertThat(request.getMethod()).isEqualTo("POST");
- assertThat(request.getPath()).isEqualTo(expectedPath);
- assertThat(request.getHeader("User-Agent")).isEqualTo("SonarQube/6.2");
- return request;
- }
-
- private WebhookCaller newSender() {
- SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(Version.parse("6.2"), SonarQubeSide.SERVER);
- return new WebhookCallerImpl(system, new OkHttpClientProvider().provide(new MapSettings().asConfig(), runtime));
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 org.apache.commons.lang.RandomStringUtils;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.utils.System2;
-import org.sonar.core.util.UuidFactory;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.DbTester;
-import org.sonar.db.webhook.WebhookDbTesting;
-import org.sonar.db.webhook.WebhookDeliveryDto;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.db.webhook.WebhookDbTesting.newDto;
-import static org.sonar.db.webhook.WebhookDbTesting.selectAllDeliveryUuids;
-
-public class WebhookDeliveryStorageTest {
-
- private static final String DELIVERY_UUID = "abcde1234";
- private static final long NOW = 1_500_000_000_000L;
- private static final long TWO_MONTHS_AGO = NOW - 60L * 24 * 60 * 60 * 1000;
- private static final long TWO_WEEKS_AGO = NOW - 14L * 24 * 60 * 60 * 1000;
-
- private final System2 system = mock(System2.class);
-
- @Rule
- public final DbTester dbTester = DbTester.create(system).setDisableDefaultOrganization(true);
-
- private DbClient dbClient = dbTester.getDbClient();
- private DbSession dbSession = dbTester.getSession();
- private UuidFactory uuidFactory = mock(UuidFactory.class);
- private WebhookDeliveryStorage underTest = new WebhookDeliveryStorage(dbClient, system, uuidFactory);
-
- @Test
- public void persist_generates_uuid_then_inserts_record() {
- when(uuidFactory.create()).thenReturn(DELIVERY_UUID);
- WebhookDelivery delivery = newBuilderTemplate().build();
-
- underTest.persist(delivery);
-
- WebhookDeliveryDto dto = dbClient.webhookDeliveryDao().selectByUuid(dbSession, DELIVERY_UUID).get();
- assertThat(dto.getUuid()).isEqualTo(DELIVERY_UUID);
- assertThat(dto.getWebhookUuid()).isEqualTo("WEBHOOK_UUID_1");
- assertThat(dto.getComponentUuid()).isEqualTo(delivery.getWebhook().getComponentUuid());
- assertThat(dto.getCeTaskUuid()).isEqualTo(delivery.getWebhook().getCeTaskUuid().get());
- assertThat(dto.getName()).isEqualTo(delivery.getWebhook().getName());
- assertThat(dto.getUrl()).isEqualTo(delivery.getWebhook().getUrl());
- assertThat(dto.getCreatedAt()).isEqualTo(delivery.getAt());
- assertThat(dto.getHttpStatus()).isEqualTo(delivery.getHttpStatus().get());
- assertThat(dto.getDurationMs()).isEqualTo(delivery.getDurationInMs().get());
- assertThat(dto.getPayload()).isEqualTo(delivery.getPayload().getJson());
- assertThat(dto.getErrorStacktrace()).isNull();
- }
-
- @Test
- public void persist_error_stacktrace() {
- when(uuidFactory.create()).thenReturn(DELIVERY_UUID);
- WebhookDelivery delivery = newBuilderTemplate()
- .setError(new IOException("fail to connect"))
- .build();
-
- underTest.persist(delivery);
-
- WebhookDeliveryDto dto = dbClient.webhookDeliveryDao().selectByUuid(dbSession, DELIVERY_UUID).get();
- assertThat(dto.getErrorStacktrace()).contains("java.io.IOException", "fail to connect");
- }
-
- @Test
- public void purge_deletes_records_older_than_one_month_on_the_project() {
- when(system.now()).thenReturn(NOW);
- dbClient.webhookDeliveryDao().insert(dbSession, newDto("D1", "PROJECT_1", TWO_MONTHS_AGO));
- dbClient.webhookDeliveryDao().insert(dbSession, newDto("D2", "PROJECT_1", TWO_WEEKS_AGO));
- dbClient.webhookDeliveryDao().insert(dbSession, newDto("D3", "PROJECT_2", TWO_MONTHS_AGO));
- dbSession.commit();
-
- underTest.purge("PROJECT_1");
-
- // do not purge another project PROJECT_2
- assertThat(selectAllDeliveryUuids(dbTester, dbSession)).containsOnly("D2", "D3");
- }
-
- private static WebhookDelivery.Builder newBuilderTemplate() {
- return new WebhookDelivery.Builder()
- .setWebhook(new Webhook("WEBHOOK_UUID_1", "COMPONENT1", "TASK1", RandomStringUtils.randomAlphanumeric(40),"Jenkins", "http://jenkins"))
- .setPayload(new WebhookPayload("my-project", "{json}"))
- .setAt(1_000_000L)
- .setHttpStatus(200)
- .setDurationInMs(1_000);
- }
-
- private static WebhookDeliveryDto newDto(String uuid, String componentUuid, long at) {
- return WebhookDbTesting.newDto()
- .setUuid(uuid)
- .setComponentUuid(componentUuid)
- .setCreatedAt(at);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 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;
-
-
-public class WebhookDeliveryTest {
-
- @Test
- public void isSuccess_returns_false_if_failed_to_send_http_request() {
- WebhookDelivery delivery = newBuilderTemplate()
- .setError(new IOException("Fail to connect"))
- .build();
-
- assertThat(delivery.isSuccess()).isFalse();
- }
-
- @Test
- public void isSuccess_returns_false_if_http_response_returns_error_status() {
- WebhookDelivery delivery = newBuilderTemplate()
- .setHttpStatus(404)
- .build();
-
- assertThat(delivery.isSuccess()).isFalse();
- }
-
- @Test
- public void isSuccess_returns_true_if_http_response_returns_2xx_code() {
- WebhookDelivery delivery = newBuilderTemplate()
- .setHttpStatus(204)
- .build();
-
- assertThat(delivery.isSuccess()).isTrue();
- }
-
- @Test
- public void getErrorMessage_returns_empty_if_no_error() {
- WebhookDelivery delivery = newBuilderTemplate().build();
-
- assertThat(delivery.getErrorMessage()).isEmpty();
- }
-
- @Test
- public void getErrorMessage_returns_root_cause_message_if_error() {
- Exception rootCause = new IOException("fail to connect");
- Exception cause = new IOException("nested", rootCause);
- WebhookDelivery delivery = newBuilderTemplate()
- .setError(cause)
- .build();
-
- assertThat(delivery.getErrorMessage().get()).isEqualTo("fail to connect");
- }
-
- private static WebhookDelivery.Builder newBuilderTemplate() {
- return new WebhookDelivery.Builder()
- .setWebhook(mock(Webhook.class))
- .setPayload(mock(WebhookPayload.class))
- .setAt(1_000L);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.core.platform.ComponentContainer;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.core.platform.ComponentContainer.COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER;
-
-public class WebhookModuleTest {
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private WebhookModule underTest = new WebhookModule();
-
- @Test
- public void verify_count_of_added_components() {
- ComponentContainer container = new ComponentContainer();
-
- underTest.configure(container);
-
- assertThat(container.size()).isEqualTo(4 + COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 com.google.common.collect.ImmutableMap;
-import java.util.Map;
-import javax.annotation.Nullable;
-import org.junit.Before;
-import org.junit.Test;
-import org.sonar.api.measures.Metric;
-import org.sonar.api.platform.Server;
-import org.sonar.api.utils.System2;
-import org.sonar.server.project.Project;
-import org.sonar.server.qualitygate.Condition;
-import org.sonar.server.qualitygate.EvaluatedCondition;
-import org.sonar.server.qualitygate.EvaluatedQualityGate;
-import org.sonar.server.qualitygate.QualityGate;
-
-import static java.util.Collections.emptyMap;
-import static java.util.Collections.emptySet;
-import static java.util.Collections.singleton;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.test.JsonAssert.assertJson;
-
-public class WebhookPayloadFactoryImplTest {
-
- private static final String PROJECT_KEY = "P1";
-
- private Server server = mock(Server.class);
- private System2 system2 = mock(System2.class);
- private WebhookPayloadFactory underTest = new WebhookPayloadFactoryImpl(server, system2);
-
- @Before
- public void setUp() throws Exception {
- when(server.getPublicRootUrl()).thenReturn("http://foo");
- when(system2.now()).thenReturn(1_500_999L);
- }
-
- @Test
- public void create_payload_for_successful_analysis() {
- CeTask task = new CeTask("#1", CeTask.Status.SUCCESS);
- Condition condition = new Condition("coverage", Condition.Operator.GREATER_THAN, "70.0", "75.0", true);
- EvaluatedQualityGate gate = EvaluatedQualityGate.newBuilder()
- .setQualityGate(new QualityGate("G1", "Gate One", singleton(condition)))
- .setStatus(Metric.Level.WARN)
- .addCondition(condition, EvaluatedCondition.EvaluationStatus.WARN, "74.0")
- .build();
- ProjectAnalysis analysis = newAnalysis(task, gate, null, 1_500_000_000_000L, emptyMap());
-
- WebhookPayload payload = underTest.create(analysis);
- assertThat(payload.getProjectKey()).isEqualTo(PROJECT_KEY);
- assertJson(payload.getJson())
- .isSimilarTo("{" +
- " \"serverUrl\": \"http://foo\"," +
- " \"taskId\": \"#1\"," +
- " \"status\": \"SUCCESS\"," +
- " \"analysedAt\": \"2017-07-14T04:40:00+0200\"," +
- " \"changedAt\": \"2017-07-14T04:40:00+0200\"," +
- " \"project\": {" +
- " \"key\": \"P1\"," +
- " \"name\": \"Project One\"," +
- " \"url\": \"http://foo/dashboard?id=P1\"" +
- " }," +
- " \"qualityGate\": {" +
- " \"name\": \"Gate One\"," +
- " \"status\": \"WARN\"," +
- " \"conditions\": [" +
- " {" +
- " \"metric\": \"coverage\"," +
- " \"operator\": \"GREATER_THAN\"," +
- " \"value\": \"74.0\"," +
- " \"status\": \"WARN\"," +
- " \"onLeakPeriod\": true," +
- " \"errorThreshold\": \"70.0\"," +
- " \"warningThreshold\": \"75.0\"" +
- " }" +
- " ]" +
- " }," +
- " \"properties\": {" +
- " }" +
- "}");
- }
-
- @Test
- public void create_payload_with_gate_conditions_without_value() {
- CeTask task = new CeTask("#1", CeTask.Status.SUCCESS);
-
- Condition condition = new Condition("coverage", Condition.Operator.GREATER_THAN, "70.0", "75.0", false);
- EvaluatedQualityGate gate = EvaluatedQualityGate.newBuilder()
- .setQualityGate(new QualityGate("G1", "Gate One", singleton(condition)))
- .setStatus(Metric.Level.WARN)
- .addCondition(condition, EvaluatedCondition.EvaluationStatus.NO_VALUE, null)
- .build();
- ProjectAnalysis analysis = newAnalysis(task, gate, null, 1_500_000_000_000L, emptyMap());
-
- WebhookPayload payload = underTest.create(analysis);
- assertThat(payload.getProjectKey()).isEqualTo(PROJECT_KEY);
- assertJson(payload.getJson())
- .isSimilarTo("{" +
- " \"serverUrl\": \"http://foo\"," +
- " \"taskId\": \"#1\"," +
- " \"status\": \"SUCCESS\"," +
- " \"analysedAt\": \"2017-07-14T04:40:00+0200\"," +
- " \"changedAt\": \"2017-07-14T04:40:00+0200\"," +
- " \"project\": {" +
- " \"key\": \"P1\"," +
- " \"name\": \"Project One\"," +
- " \"url\": \"http://foo/dashboard?id=P1\"" +
- " }," +
- " \"qualityGate\": {" +
- " \"name\": \"Gate One\"," +
- " \"status\": \"WARN\"," +
- " \"conditions\": [" +
- " {" +
- " \"metric\": \"coverage\"," +
- " \"operator\": \"GREATER_THAN\"," +
- " \"status\": \"NO_VALUE\"," +
- " \"onLeakPeriod\": false," +
- " \"errorThreshold\": \"70.0\"," +
- " \"warningThreshold\": \"75.0\"" +
- " }" +
- " ]" +
- " }" +
- "}");
- }
-
- @Test
- public void create_payload_with_analysis_properties() {
- CeTask task = new CeTask("#1", CeTask.Status.SUCCESS);
- EvaluatedQualityGate gate = EvaluatedQualityGate.newBuilder()
- .setQualityGate(new QualityGate("G1", "Gate One", emptySet()))
- .setStatus(Metric.Level.WARN)
- .build();
- Map<String, String> scannerProperties = ImmutableMap.of(
- "sonar.analysis.revision", "ab45d24",
- "sonar.analysis.buildNumber", "B123",
- "not.prefixed.with.sonar.analysis", "should be ignored",
- "ignored", "should be ignored too");
- ProjectAnalysis analysis = newAnalysis(task, gate, null, 1_500_000_000_000L, scannerProperties);
-
- WebhookPayload payload = underTest.create(analysis);
- assertJson(payload.getJson())
- .isSimilarTo("{" +
- " \"serverUrl\": \"http://foo\"," +
- " \"taskId\": \"#1\"," +
- " \"status\": \"SUCCESS\"," +
- " \"analysedAt\": \"2017-07-14T04:40:00+0200\"," +
- " \"changedAt\": \"2017-07-14T04:40:00+0200\"," +
- " \"project\": {" +
- " \"key\": \"P1\"," +
- " \"name\": \"Project One\"," +
- " \"url\": \"http://foo/dashboard?id=P1\"" +
- " }," +
- " \"qualityGate\": {" +
- " \"name\": \"Gate One\"," +
- " \"status\": \"WARN\"," +
- " \"conditions\": [" +
- " ]" +
- " }," +
- " \"properties\": {" +
- " \"sonar.analysis.revision\": \"ab45d24\"," +
- " \"sonar.analysis.buildNumber\": \"B123\"" +
- " }" +
- "}");
- assertThat(payload.getJson())
- .doesNotContain("not.prefixed.with.sonar.analysis")
- .doesNotContain("ignored");
- }
-
- @Test
- public void create_payload_for_failed_analysis() {
- CeTask ceTask = new CeTask("#1", CeTask.Status.FAILED);
- ProjectAnalysis analysis = newAnalysis(ceTask, null, null, 1_500_000_000_000L, emptyMap());
-
- WebhookPayload payload = underTest.create(analysis);
-
- assertThat(payload.getProjectKey()).isEqualTo(PROJECT_KEY);
- assertJson(payload.getJson())
- .isSimilarTo("{" +
- " \"serverUrl\": \"http://foo\"," +
- " \"taskId\": \"#1\"," +
- " \"status\": \"FAILED\"," +
- " \"changedAt\": \"2017-07-14T04:40:00+0200\"," +
- " \"project\": {" +
- " \"key\": \"P1\"," +
- " \"name\": \"Project One\"," +
- " \"url\": \"http://foo/dashboard?id=P1\"" +
- " }," +
- " \"properties\": {" +
- " }" +
- "}");
- }
-
- @Test
- public void create_payload_for_no_analysis_date() {
- CeTask ceTask = new CeTask("#1", CeTask.Status.FAILED);
- ProjectAnalysis analysis = newAnalysis(ceTask, null, null, null, emptyMap());
-
- WebhookPayload payload = underTest.create(analysis);
-
- assertThat(payload.getProjectKey()).isEqualTo(PROJECT_KEY);
- assertJson(payload.getJson())
- .isSimilarTo("{" +
- " \"serverUrl\": \"http://foo\"," +
- " \"taskId\": \"#1\"," +
- " \"status\": \"FAILED\"," +
- " \"changedAt\": \"1970-01-01T01:25:00+0100\"," +
- " \"project\": {" +
- " \"key\": \"P1\"," +
- " \"name\": \"Project One\"" +
- " }," +
- " \"properties\": {" +
- " }" +
- "}");
- }
-
- @Test
- public void create_payload_on_short_branch() {
- CeTask task = new CeTask("#1", CeTask.Status.SUCCESS);
- ProjectAnalysis analysis = newAnalysis(task, null, new Branch(false, "feature/foo", Branch.Type.SHORT), 1_500_000_000_000L, emptyMap());
-
- WebhookPayload payload = underTest.create(analysis);
- assertJson(payload.getJson())
- .isSimilarTo("{" +
- "\"branch\": {" +
- " \"name\": \"feature/foo\"," +
- " \"type\": \"SHORT\"," +
- " \"isMain\": false," +
- " \"url\": \"http://foo/project/issues?branch=feature%2Ffoo&id=P1&resolved=false\"" +
- "}" +
- "}");
- }
-
- @Test
- public void create_payload_on_pull_request() {
- CeTask task = new CeTask("#1", CeTask.Status.SUCCESS);
- ProjectAnalysis analysis = newAnalysis(task, null, new Branch(false, "pr/foo", Branch.Type.PULL_REQUEST), 1_500_000_000_000L, emptyMap());
-
- WebhookPayload payload = underTest.create(analysis);
- assertJson(payload.getJson())
- .isSimilarTo("{" +
- "\"branch\": {" +
- " \"name\": \"pr/foo\"," +
- " \"type\": \"PULL_REQUEST\"," +
- " \"isMain\": false," +
- " \"url\": \"http://foo/project/issues?pullRequest=pr%2Ffoo&id=P1&resolved=false\"" +
- "}" +
- "}");
- }
-
- @Test
- public void create_without_ce_task() {
- ProjectAnalysis analysis = newAnalysis(null, null, null, null, emptyMap());
-
- WebhookPayload payload = underTest.create(analysis);
- String json = payload.getJson();
- assertThat(json).doesNotContain("taskId");
- assertJson(json)
- .isSimilarTo("{" +
- " \"serverUrl\": \"http://foo\"," +
- " \"status\": \"SUCCESS\"," +
- " \"changedAt\": \"1970-01-01T01:25:00+0100\"," +
- " \"project\": {" +
- " \"key\": \"P1\"," +
- " \"name\": \"Project One\"" +
- " }," +
- " \"properties\": {" +
- " }" +
- "}");
- }
-
- @Test
- public void create_payload_on_long_branch() {
- CeTask task = new CeTask("#1", CeTask.Status.SUCCESS);
- ProjectAnalysis analysis = newAnalysis(task, null, new Branch(false, "feature/foo", Branch.Type.LONG), 1_500_000_000_000L, emptyMap());
-
- WebhookPayload payload = underTest.create(analysis);
- assertJson(payload.getJson())
- .isSimilarTo("{" +
- "\"branch\": {" +
- " \"name\": \"feature/foo\"" +
- " \"type\": \"LONG\"" +
- " \"isMain\": false," +
- " \"url\": \"http://foo/dashboard?branch=feature%2Ffoo&id=P1\"" +
- "}" +
- "}");
- }
-
- @Test
- public void create_payload_on_main_branch_without_name() {
- CeTask task = new CeTask("#1", CeTask.Status.SUCCESS);
- ProjectAnalysis analysis = newAnalysis(task, null, new Branch(true, null, Branch.Type.LONG), 1_500_000_000_000L, emptyMap());
-
- WebhookPayload payload = underTest.create(analysis);
- assertJson(payload.getJson())
- .isSimilarTo("{" +
- "\"branch\": {" +
- " \"type\": \"LONG\"" +
- " \"isMain\": true," +
- " \"url\": \"http://foo/dashboard?id=P1\"" +
- "}" +
- "}");
- }
-
- private static ProjectAnalysis newAnalysis(@Nullable CeTask task, @Nullable EvaluatedQualityGate gate,
- @Nullable Branch branch, @Nullable Long analysisDate, Map<String, String> scannerProperties) {
- return new ProjectAnalysis(new Project("P1_UUID", PROJECT_KEY, "Project One"), task, analysisDate == null ? null : new Analysis("A_UUID1", analysisDate), branch,
- gate, analysisDate, scannerProperties);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.Optional;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class WebhookTest {
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- @Test
- public void constructor_with_null_componentUuid_should_throw_NPE() {
- expectedException.expect(NullPointerException.class);
-
- new Webhook(randomAlphanumeric(40), null, null, null, randomAlphanumeric(10), randomAlphanumeric(10));
- }
-
- @Test
- public void constructor_with_null_name_should_throw_NPE() {
- expectedException.expect(NullPointerException.class);
-
- new Webhook(randomAlphanumeric(40), randomAlphanumeric(10), null, null, null, randomAlphanumeric(10));
- }
-
- @Test
- public void constructor_with_null_url_should_throw_NPE() {
- expectedException.expect(NullPointerException.class);
-
- new Webhook(randomAlphanumeric(40), randomAlphanumeric(10), null, null, randomAlphanumeric(10), null);
- }
-
- @Test
- public void constructor_with_null_ceTaskUuid_or_analysisUuidurl_should_return_Optional_empty() {
- String componentUuid = randomAlphanumeric(10);
- String name = randomAlphanumeric(10);
- String url = randomAlphanumeric(10);
- Webhook underTest = new Webhook(randomAlphanumeric(40), componentUuid, null, null, name, url);
-
- assertThat(underTest.getComponentUuid()).isEqualTo(componentUuid);
- assertThat(underTest.getName()).isEqualTo(name);
- assertThat(underTest.getUrl()).isEqualTo(url);
- assertThat(underTest.getCeTaskUuid()).isEqualTo(Optional.empty());
- assertThat(underTest.getAnalysisUuid()).isEqualTo(Optional.empty());
-
- String ceTaskUuid = randomAlphanumeric(10);
- String analysisUuid = randomAlphanumeric(10);
- underTest = new Webhook(randomAlphanumeric(40), componentUuid, ceTaskUuid, analysisUuid, name, url);
- assertThat(underTest.getComponentUuid()).isEqualTo(componentUuid);
- assertThat(underTest.getName()).isEqualTo(name);
- assertThat(underTest.getUrl()).isEqualTo(url);
- assertThat(underTest.getCeTaskUuid().get()).isEqualTo(ceTaskUuid);
- assertThat(underTest.getAnalysisUuid().get()).isEqualTo(analysisUuid);
- }
-}