Showing preview only (687K chars total). Download the full file or copy to clipboard to get everything.
Repository: AmadeusITGroup/sonar-stash
Branch: master
Commit: 924610151ea5
Files: 79
Total size: 651.2 KB
Directory structure:
gitextract_2kj9ve84/
├── .gitignore
├── .travis/
│ ├── runSonarQubeAnalysis.sh
│ └── script.sh
├── .travis.yml
├── LICENSE.md
├── README.md
├── dependency-check-suppression.xml
├── pom.xml
└── src/
├── main/
│ ├── java/
│ │ └── org/
│ │ └── sonar/
│ │ └── plugins/
│ │ └── stash/
│ │ ├── IssuePathResolver.java
│ │ ├── PeekableInputStream.java
│ │ ├── PluginInfo.java
│ │ ├── PullRequestRef.java
│ │ ├── StashIssueReportingPostJob.java
│ │ ├── StashPlugin.java
│ │ ├── StashPluginConfiguration.java
│ │ ├── StashPluginUtils.java
│ │ ├── StashProjectBuilder.java
│ │ ├── StashRequestFacade.java
│ │ ├── client/
│ │ │ ├── ContentType.java
│ │ │ ├── StashClient.java
│ │ │ └── StashCredentials.java
│ │ ├── exceptions/
│ │ │ ├── StashClientException.java
│ │ │ ├── StashConfigurationException.java
│ │ │ ├── StashException.java
│ │ │ └── StashReportExtractionException.java
│ │ └── issue/
│ │ ├── MarkdownPrinter.java
│ │ ├── StashComment.java
│ │ ├── StashCommentReport.java
│ │ ├── StashDiff.java
│ │ ├── StashDiffReport.java
│ │ ├── StashPullRequest.java
│ │ ├── StashTask.java
│ │ ├── StashUser.java
│ │ └── collector/
│ │ ├── SonarQubeCollector.java
│ │ └── StashCollector.java
│ └── resources/
│ └── org/
│ └── sonar/
│ └── plugins/
│ └── stash/
│ └── sonar-stash.properties
└── test/
├── java/
│ └── org/
│ └── sonar/
│ └── plugins/
│ └── stash/
│ ├── CompleteITCase.java
│ ├── DefaultIssue.java
│ ├── DummyStashProjectBuilder.java
│ ├── JavaUtilLoggingCapture.java
│ ├── PeekableInputStreamTest.java
│ ├── PluginInfoTest.java
│ ├── StashIssueReportingPostJobTest.java
│ ├── StashPluginConfigurationTest.java
│ ├── StashPluginUtilsTest.java
│ ├── StashRequestFacadeTest.java
│ ├── StashTest.java
│ ├── TestUtils.java
│ ├── TestWatcherExtension.java
│ ├── client/
│ │ ├── ContentTypeTest.java
│ │ └── StashClientTest.java
│ ├── end2end/
│ │ ├── EndToEndTest.java
│ │ └── Issue194.java
│ ├── fixtures/
│ │ ├── DummyIssuePathResolver.java
│ │ ├── DummyPostJobContext.java
│ │ ├── DummyPostJobIssue.java
│ │ ├── DummyServer.java
│ │ ├── DummyStashServer.java
│ │ ├── MavenSonarFixtures.java
│ │ ├── SonarQube.java
│ │ ├── SonarQubeRule.java
│ │ ├── SonarScanner.java
│ │ ├── WireMockExtension.java
│ │ └── WireMockResponseCallback.java
│ └── issue/
│ ├── MarkdownPrinterTest.java
│ ├── StashCommentReportTest.java
│ ├── StashCommentTest.java
│ ├── StashDiffReportTest.java
│ ├── StashDiffTest.java
│ ├── StashPullRequestTest.java
│ ├── StashTaskTest.java
│ ├── StashUserTest.java
│ └── collector/
│ ├── DiffReportSample.java
│ ├── SonarQubeCollectorTest.java
│ └── StashCollectorTest.java
└── resources/
├── fixtures/
│ └── issue194_stash_diff.json
└── foo/
├── module1/
│ └── src/
│ └── main/
│ └── java/
│ └── Foo.java
├── module2/
│ └── src/
│ └── main/
│ └── java/
│ └── Bar.java
└── sonar-project.properties
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
target
.idea
.project
.classpath
.settings
*.iml
/bin/
/release.properties
/pom.xml.releaseBackup
/dependency-reduced-pom.xml
================================================
FILE: .travis/runSonarQubeAnalysis.sh
================================================
#!/bin/sh
# Exit on failure
set -e
# We don't want to run X times the same analysis because of the matrix configuration
if [ "${SQ_RUN}" != "yes" ]; then
echo "Duplicated run detected, skipping the SonarQube analysis..."
exit 0
fi
echo "Starting analysis by SonarQube..."
mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent install sonar:sonar -B -e -V
================================================
FILE: .travis/script.sh
================================================
#!/bin/bash
set -ev
if [ -z "${TEST_SUITE}" ]; then
echo "No \$TEST_SUITE specified, aborting!"
exit 1
elif [ "unit" = "${TEST_SUITE}" ]; then
mvn -e test -Pcoverage-per-test
elif [ "integration" = "${TEST_SUITE}" ]; then
if [ -z "${SONARQUBE_VERSION}" ]; then
echo "No \$SONARQUBE_VERSION specified, aborting!"
exit 1
fi
tail -F "target/fixtures/sonarqube/sonarqube-${SONARQUBE_VERSION}/logs/sonar.log" &
# otherwise the rails bundled with sonarqube tries to load test.yml which does
# not exist
export RAILS_ENV=production
env -u SONAR_TOKEN mvn -e verify -Dtest.sonarqube.dist.version="${SONARQUBE_VERSION}"
elif [ "dependency-check" = "${TEST_SUITE}" ]; then
mvn -e org.owasp:dependency-check-maven:check
fi
================================================
FILE: .travis.yml
================================================
language: java
matrix:
fast_finish: true
include:
# The basic unit-tests environments
- jdk: oraclejdk8
env:
- TEST_SUITE=unit
- SQ_RUN=yes
# It is necessary only to run the analysis once ;)
# The integration tests environments
- jdk: openjdk8
env:
- TEST_SUITE=integration
- SONARQUBE_VERSION=7.6
- jdk: openjdk8
env:
- TEST_SUITE=integration
- SONARQUBE_VERSION=6.7
- jdk: openjdk8
env:
- TEST_SUITE=dependency-check
addons:
sonarcloud:
organization: "default"
install:
- mvn dependency:go-offline
script:
- ./.travis/script.sh
- ./.travis/runSonarQubeAnalysis.sh
sudo: false
dist: trusty
git:
depth: false
notifications:
email: false
================================================
FILE: LICENSE.md
================================================
The MIT License (MIT)
Copyright (c) 2015 Amadeus S.A.S., Antoine Copet, Mathieu-Antoine Simon
Copyright (c) 2016 Amadeus Germany GmbH, Thomas Weißschuh
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
# SonarQube Stash (BitBucket) plugin
## Important note
Version 7.7 of SonarQube [dropped support for the extension point sonar-stash uses](https://jira.sonarsource.com/browse/SONAR-11670).
This means this plugin *can not work* on SonarQube versions >= 7.7.
Therefore the 1.6.0 release is the last feature-release of `sonar-stash`.
[](https://travis-ci.org/AmadeusITGroup/sonar-stash/branches)
[](https://sonarcloud.io/dashboard?id=org.sonar%3Asonar-stash-plugin)
[](https://sonarcloud.io/dashboard?id=org.sonar%3Asonar-stash-plugin)
[](https://sonarcloud.io/dashboard?id=org.sonar%3Asonar-stash-plugin)
[](https://sonarcloud.io/dashboard?id=org.sonar%3Asonar-stash-plugin)
[](https://sonarcloud.io/dashboard?id=org.sonar%3Asonar-stash-plugin)
**SonarQube is now a real reviewer!**
SonarQube Stash (BitBucket) plugin is a pull-request decorator which allows to integrate SonarQube violations directly into your pull-request.

After every run, in addition of the diff view, you may access to an overview of your SQ analysis:

## Getting started
#### Prerequisites
- Git client to checkout the code
- Maven 3.0.5+
- JDK 1.8+
- SonarQube 6.7 (LTS) and 7.6
- Stash (BitBucket) REST API 1.0 (3.x, 4.x)
Note: these are the versions where the plugin has been tested. Other versions may or may not work, YMMV.
#### To build the plugin
This command generates a jar file:
```
mvn clean package
```
#### To deploy the plugin
Just copy the sonar-stash-plugin jar file to the plugin folder of the expected SonarQube server and restart the SonarQube server. For instance, on Linux platform:
```
cp target/sonar-stash-plugin-1.0.jar $SONARQUBE_HOME/extensions/plugins
```
#### Configuration on SonarQube server
Go to Stash general settings screen on SonarQube server to fill:

**Stash base URL** (sonar.stash.url): To define Stash instance.
**Stash base user** (sonar.stash.login): To define user to push violations on Stash pull-request. User must have **REPO_READ permission** for the repository. **Please notice Stash password needs to be provided to sonar-runner through sonar.stash.password on the commandline**.
**Stash user slug** (sonar.stash.user.slug): If the user username contains special characters the API requires the use of a different slug.
**Stash issue threshold** (sonar.stash.issue.threshold): To limit the number of issue pushed to Stash.
**Stash issue severity threshold** (sonar.stash.issue.severity.threshold): Defines minimum issue severity to create diff-view comments for. Overview comment will still contain all severities. By default, all issues are pushed to Stash.
**Stash timeout** (sonar.stash.timeout): To timeout when Stash Rest api does not replied with expected.
**Stash reviewer approval** (sonar.stash.reviewer.approval): SonarQube is able to approve the pull-request if there is no new issue introduced by the change. By default, this feature is deactivated: if activated, **Stash base user must have REPO_WRITE permission for the repositories.**
**Approval severity** (sonar.stash.reviewer.approval.severity.threshold): Only approve the pull-request if no issues higher than this threshold are detected.
**Include Analysis Overview Comment** (sonar.stash.include.overview): Toggles whether a comment with overview information should be created.

**Stash tasks severity threshold** (sonar.stash.task.issue.severity.threshold): SonarQube is able to create tasks for all issues with a severity higher to the threshold. By default, this feature is deactivated (threshold: NONE).

**Include existing issues** (sonar.stash.include.existing.issues): Toggles whether already existing issues should also be reported.
**Include Vicinity Issues Range** (sonar.stash.include.vicinity.issues.range): Specifies in which area (in lines) around the current diff issues should be reported
**Excluded Rules** (sonar.stash.exclude.rules): Comma separated list of rules for which no comments should be created.
**File names in overview comment**(sonar.stash.overview.filenames): Amount of filenames listed in overview comments
## How to run the plugin?
#### Plugin activation for an analysis
To activate the plugin, just add the following options to the SonarQube launcher (for instance with sonar-runner):
For SonarQube 5.2+:
```
sonar-runner -Dsonar.analysis.mode=issues \
-Dsonar.stash.notification=true -Dsonar.stash.project=<PROJECT> -Dsonar.stash.repository=<REPO> \
-Dsonar.stash.pullrequest.id=<PR_ID> -Dsonar.stash.password=<STASH_PASSWORD>...
```
#### Repository source configuration
To tell the plugin about the root directory of your repository use the `sonar.stash.repository.root` property.
This is necessary to correlate the the file locations between SonarQube and Stash.
```
sonar-runner -Dsonar.stash.repository.root="$PWD" -Dsonar.stash.notification
```

#### Reset comments of previous SonarQube analysis
If needed, you can reset comments published during the previous SonarQube analysis of your pull-request. Please add **sonar.stash.comments.reset** option to your SonarQube analysis. Please notice only comments linked to the **sonar.stash.login** user will be deleted. This reset will be the first action performed by the plugin.
```
sonar-runner -Dsonar.analysis.mode=incremental -Dsonar.stash.notification -Dsonar.stash.comments.reset -Dsonar.stash.project=<PROJECT> -Dsonar.stash.repository=<REPO> -Dsonar.stash.pullrequest.id=<PR_ID> -Dsonar.stash.password=<STASH_PASSWORD>...
```
## How to activate the coverage inside the pull-request
*This functionality has been moved to its own plugin: https://github.com/AmadeusITGroup/sonar-coverage-evolution*
## Protect passwords
The plugin can also read the password from an environment variable.
This is configured by setting `sonar.stash.password.variable` to the name of
the environment variable to read.
The prevents the password from leaking into the process table.
# How to contribute
* Before developing a major feature please open a ticket and announce it.
Maybe the maintainers have strong opinions or useful hints about it.
* Add unit and for major features integration tests.
* Use the [Google Java Style Guide](https://google.github.io/styleguide/javaguide.html) for new development.
================================================
FILE: dependency-check-suppression.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.1.xsd">
<suppress>
<gav regex="true">org\.asynchttpclient:.*</gav>
<cpe>cpe:/a:netty_project:netty</cpe>
</suppress>
<suppress>
<gav regex="true">com\.typesafe\.netty:netty-reactive-streams:.*</gav>
<cve>CVE-2015-2156</cve>
<cve>CVE-2014-3488</cve>
</suppress>
</suppressions>
================================================
FILE: pom.xml
================================================
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.sonar</groupId>
<artifactId>sonar-stash-plugin</artifactId>
<version>1.7.0-SNAPSHOT</version>
<packaging>sonar-plugin</packaging>
<description>Integration between Atlassian Stash (BitBucket) and SonarQube</description>
<name>Stash</name>
<url>https://github.com/AmadeusITGroup/sonar-stash</url>
<licenses>
<license>
<name>MIT</name>
<url>https://opensource.org/licenses/MIT</url>
<distribution>repo</distribution>
</license>
</licenses>
<scm>
<connection>scm:git:git@github.com:AmadeusITGroup/sonar-stash.git</connection>
<developerConnection>scm:git:git@github.com:AmadeusITGroup/sonar-stash.git</developerConnection>
<url>https://github.com/AmadeusITGroup/sonar-stash</url>
<tag>HEAD</tag>
</scm>
<issueManagement>
<system>GitHub Issues</system>
<url>https://github.com/AmadeusITGroup/sonar-stash/issues</url>
</issueManagement>
<ciManagement>
<system>Travis</system>
<url>https://travis-ci.org/AmadeusITGroup/sonar-stash</url>
</ciManagement>
<properties>
<sonar.version>6.7</sonar.version>
<sonar.pluginName>Stash</sonar.pluginName>
<sonar.pluginClass>org.sonar.plugins.stash.StashPlugin</sonar.pluginClass>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<test.sonarqube.dist.groupId>fixme.fixme</test.sonarqube.dist.groupId>
<test.sonarqube.dist.artifactId>sonarqube-dist</test.sonarqube.dist.artifactId>
<test.sonarqube.dist.version>7.6</test.sonarqube.dist.version>
<test.sonarqube.dist.outputdir>${project.build.directory}/fixtures/sonarqube</test.sonarqube.dist.outputdir>
<test.sonarscanner.dist.groupId>fixme.fixme</test.sonarscanner.dist.groupId>
<test.sonarscanner.dist.artifactId>sonarscanner-dist</test.sonarscanner.dist.artifactId>
<test.sonarscanner.dist.version>3.3.0.1492</test.sonarscanner.dist.version>
<test.sonarscanner.dist.outputdir>${project.build.directory}/fixtures/sonarscanner</test.sonarscanner.dist.outputdir>
<test.url.binaries.repo>https://binaries.sonarsource.com/Distribution</test.url.binaries.repo>
<test.plugin.archive>${project.build.directory}/${project.artifactId}-${project.version}.jar</test.plugin.archive>
<test.sources.dir>${project.build.directory}/fixtures/sources</test.sources.dir>
<sonar.coverage.exclusions>src/main/java/org/sonar/plugins/stash/StashPlugin.java,src/main/java/org/sonar/plugins/stash/StashPluginConfiguration.java</sonar.coverage.exclusions>
</properties>
<organization>
<name>Amadeus</name>
<url>http://www.amadeus.com</url>
</organization>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>5.4.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.sonarsource.sonarqube</groupId>
<artifactId>sonar-plugin-api</artifactId>
<version>${sonar.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.cliftonlabs</groupId>
<artifactId>json-simple</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.asynchttpclient</groupId>
<artifactId>async-http-client</artifactId>
<version>2.8.1</version>
<exclusions>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-epoll</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>27.0.1-jre</version>
</dependency>
<!-- unit tests -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.11.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.25.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>2.25.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>3.1.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock</artifactId>
<version>2.21.0</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.picocontainer</groupId>
<artifactId>picocontainer</artifactId>
<version>2.15</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources/</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
<artifactId>sonar-packaging-maven-plugin</artifactId>
<version>1.18.0.372</version>
<extensions>true</extensions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.5.3</version>
<configuration>
<tagNameFormat>@{project.version}</tagNameFormat>
<pushChanges>false</pushChanges>
</configuration>
</plugin>
<plugin>
<groupId>com.googlecode.maven-download-plugin</groupId>
<artifactId>download-maven-plugin</artifactId>
<version>1.3.0</version>
<executions>
<execution>
<id>install-sonarqube</id>
<phase>pre-integration-test</phase>
<goals>
<goal>wget</goal>
</goals>
<configuration>
<url>${test.url.binaries.repo}/sonarqube/sonarqube-${test.sonarqube.dist.version}.zip</url>
<unpack>true</unpack>
<outputDirectory>${test.sonarqube.dist.outputdir}</outputDirectory>
</configuration>
</execution>
<execution>
<id>install-sonarscanner</id>
<phase>pre-integration-test</phase>
<goals>
<goal>wget</goal>
</goals>
<configuration>
<url>${test.url.binaries.repo}/sonar-scanner-cli/sonar-scanner-cli-${test.sonarscanner.dist.version}.zip</url>
<unpack>true</unpack>
<outputDirectory>${test.sonarscanner.dist.outputdir}</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.0.0-M3</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
<configuration>
<trimStackTrace>false</trimStackTrace>
<systemPropertyVariables>
<test.sonarqube.dist.outputdir>${test.sonarqube.dist.outputdir}</test.sonarqube.dist.outputdir>
<test.sonarqube.dist.version>${test.sonarqube.dist.version}</test.sonarqube.dist.version>
<test.sonarscanner.dist.outputdir>${test.sonarscanner.dist.outputdir}</test.sonarscanner.dist.outputdir>
<test.sonarscanner.dist.version>${test.sonarscanner.dist.version}</test.sonarscanner.dist.version>
<test.plugin.archive>${test.plugin.archive}</test.plugin.archive>
<test.sources.dir>${test.sources.dir}</test.sources.dir>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>4.0.2</version>
<configuration>
<skipProvidedScope>true</skipProvidedScope>
<failBuildOnAnyVulnerability>true</failBuildOnAnyVulnerability>
<suppressionFiles>
<suppressionFile>dependency-check-suppression.xml</suppressionFile>
</suppressionFiles>
</configuration>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M3</version>
</plugin>
</plugins>
</pluginManagement>
</build>
<!-- Profile to activate the code coverage -->
<profiles>
<profile>
<id>coverage-per-test</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<properties>
<property>
<name>listener</name>
<value>org.sonar.java.jacoco.JUnitListener</value>
</property>
</properties>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.3</version>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.sonarsource.java</groupId>
<artifactId>sonar-jacoco-listeners</artifactId>
<version>5.11.0.17289</version>
<scope>test</scope>
</dependency>
</dependencies>
</profile>
</profiles>
</project>
================================================
FILE: src/main/java/org/sonar/plugins/stash/IssuePathResolver.java
================================================
package org.sonar.plugins.stash;
import org.sonar.api.batch.postjob.issue.PostJobIssue;
@FunctionalInterface
public interface IssuePathResolver {
String getIssuePath(PostJobIssue issue);
}
================================================
FILE: src/main/java/org/sonar/plugins/stash/PeekableInputStream.java
================================================
package org.sonar.plugins.stash;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.util.Optional;
public class PeekableInputStream extends PushbackInputStream {
public PeekableInputStream(InputStream in) {
super(in);
}
public Optional<Character> peek() throws IOException {
int next = read();
if (next == -1) {
return Optional.empty();
} else {
unread(next);
return Optional.of(Character.valueOf((char)next));
}
}
}
================================================
FILE: src/main/java/org/sonar/plugins/stash/PluginInfo.java
================================================
package org.sonar.plugins.stash;
public class PluginInfo {
private String name;
private String version;
public PluginInfo(String name, String version) {
this.name = name;
this.version = version;
}
public String getVersion() {
return version;
}
public String getName() {
return name;
}
}
================================================
FILE: src/main/java/org/sonar/plugins/stash/PullRequestRef.java
================================================
package org.sonar.plugins.stash;
public final class PullRequestRef {
private String project;
private String repository;
private int pullRequestId;
private PullRequestRef(String project, String repository, int pullRequestId) {
this.project = project;
this.repository = repository;
this.pullRequestId = pullRequestId;
}
public String project() {
return project;
}
public String repository() {
return repository;
}
public int pullRequestId() {
return pullRequestId;
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private String project;
private String repository;
private int pullRequestId;
public Builder setProject(String value) {
project = value;
return this;
}
public Builder setRepository(String value) {
repository = value;
return this;
}
public Builder setPullRequestId(int value) {
pullRequestId = value;
return this;
}
public PullRequestRef build() {
return new PullRequestRef(project, repository, pullRequestId);
}
}
}
================================================
FILE: src/main/java/org/sonar/plugins/stash/StashIssueReportingPostJob.java
================================================
package org.sonar.plugins.stash;
import com.google.common.annotations.VisibleForTesting;
import java.util.Optional;
import java.util.List;
import org.sonar.api.batch.InstantiationStrategy;
import org.sonar.api.batch.ScannerSide;
import org.sonar.api.batch.postjob.PostJob;
import org.sonar.api.batch.postjob.PostJobContext;
import org.sonar.api.batch.postjob.PostJobDescriptor;
import org.sonar.api.batch.postjob.issue.PostJobIssue;
import org.sonar.api.batch.rule.Severity;
import org.sonar.api.platform.Server;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.plugins.stash.client.StashClient;
import org.sonar.plugins.stash.client.StashCredentials;
import org.sonar.plugins.stash.exceptions.StashConfigurationException;
import org.sonar.plugins.stash.exceptions.StashException;
import org.sonar.plugins.stash.issue.StashDiffReport;
import org.sonar.plugins.stash.issue.StashUser;
@ScannerSide
@InstantiationStrategy(InstantiationStrategy.PER_BATCH)
public class StashIssueReportingPostJob implements PostJob {
private static final Logger LOGGER = Loggers.get(StashIssueReportingPostJob.class);
private final StashPluginConfiguration config;
private final StashRequestFacade stashRequestFacade;
private final Server sonarQubeServer;
public StashIssueReportingPostJob(StashPluginConfiguration stashPluginConfiguration,
StashRequestFacade stashRequestFacade,
Server sonarQubeServer) {
this.config = stashPluginConfiguration;
this.stashRequestFacade = stashRequestFacade;
this.sonarQubeServer = sonarQubeServer;
}
@Override
public void execute(PostJobContext context) {
if (!config.hasToNotifyStash()) {
LOGGER.info("{} not enabled, skipping", this);
return;
}
try {
executeThrowing(context);
} catch (StashException e) {
LOGGER.error("Unable to push SonarQube report to Stash", e);
}
}
@VisibleForTesting
public void executeThrowing(PostJobContext context) {
String stashURL = stashRequestFacade.getStashURL();
int stashTimeout = config.getStashTimeout();
StashCredentials stashCredentials = stashRequestFacade.getCredentials();
try (StashClient stashClient = new StashClient(stashURL,
stashCredentials,
stashTimeout,
sonarQubeServer.getVersion())) {
// Down the rabbit hole...
updateStashWithSonarInfo(stashClient, stashCredentials, context.issues());
}
}
/*
* Second part of the code necessary for the executeOn() -- squid:S134
*/
private void updateStashWithSonarInfo(StashClient stashClient,
StashCredentials stashCredentials, Iterable<PostJobIssue> issues) {
int issueThreshold = stashRequestFacade.getIssueThreshold();
PullRequestRef pr = stashRequestFacade.getPullRequest();
// SonarQube objects
List<PostJobIssue> issueReport = stashRequestFacade.extractIssueReport(issues);
StashUser stashUser = stashRequestFacade
.getSonarQubeReviewer(stashCredentials.getUserSlug(), stashClient);
if (stashUser == null) {
throw new StashConfigurationException(
"No SonarQube reviewer identified to publish to Stash the SQ analysis");
}
// Get all changes exposed from Stash differential view of the pull-request
StashDiffReport diffReport = stashRequestFacade.getPullRequestDiffReport(pr, stashClient);
if (diffReport == null) {
throw new StashConfigurationException(
"No Stash differential report available to process the SQ analysis");
}
// if requested, reset all comments linked to the pull-request
if (config.resetComments()) {
stashRequestFacade.resetComments(pr, diffReport, stashUser, stashClient);
}
boolean canApprovePullrequest = config.canApprovePullRequest();
if (canApprovePullrequest) {
stashRequestFacade.addPullRequestReviewer(pr, stashCredentials.getUserSlug(), stashClient);
}
postInfoAndPRsActions(pr, issueReport, issueThreshold, diffReport, stashClient);
}
/*
* Second part of the code necessary for the updateStashWithSonarInfo() method
* and third part of the executeOn() method (call of a call) -- squid:MethodCyclomaticComplexity
*/
private void postInfoAndPRsActions(
PullRequestRef pr, List<PostJobIssue> issueReport, int issueThreshold,
StashDiffReport diffReport, StashClient stashClient
) {
int issueTotal = issueReport.size();
// if threshold exceeded, do not push issue list to Stash
if (issueTotal >= issueThreshold) {
LOGGER.warn("Too many issues detected ({}/{}): Issues cannot be displayed in Diff view",
issueTotal, issueThreshold);
} else {
stashRequestFacade.postSonarQubeReport(pr, issueReport, diffReport, stashClient);
}
if (config.includeAnalysisOverview()) {
stashRequestFacade.postAnalysisOverview(pr, issueReport, stashClient);
}
if (config.canApprovePullRequest()) {
if (shouldApprovePullRequest(config.getApprovalSeverityThreshold(), issueReport)) {
stashRequestFacade.approvePullRequest(pr, stashClient);
} else {
stashRequestFacade.resetPullRequestApproval(pr, stashClient);
}
}
}
static boolean shouldApprovePullRequest(Optional<Severity> approvalSeverityThreshold, List<PostJobIssue> report) {
if (approvalSeverityThreshold.isPresent()) {
return report.stream().noneMatch(issue ->
issue.severity().compareTo(approvalSeverityThreshold.get()) > 0
);
}
return report.isEmpty();
}
@Override
public void describe(PostJobDescriptor descriptor) {
descriptor.requireProperty(StashPlugin.STASH_NOTIFICATION);
descriptor.name("Stash/Bitbucket notification");
}
}
================================================
FILE: src/main/java/org/sonar/plugins/stash/StashPlugin.java
================================================
package org.sonar.plugins.stash;
import com.google.common.collect.Lists;
import java.util.Arrays;
import java.util.stream.Collectors;
import org.sonar.api.Plugin;
import org.sonar.api.Properties;
import org.sonar.api.Property;
import org.sonar.api.PropertyType;
import org.sonar.api.batch.rule.Severity;
import org.sonar.api.config.PropertyDefinition;
import org.sonar.api.resources.Qualifiers;
import java.util.List;
import org.sonar.plugins.stash.issue.StashDiffReport;
@Properties({
@Property(key = StashPlugin.STASH_NOTIFICATION,
name = "Stash Notification",
defaultValue = "false",
description = "Analysis result will be issued in Stash pull request",
global = false),
@Property(key = StashPlugin.STASH_PROJECT,
name = "Stash Project",
description = "Stash project of current pull-request",
global = false),
@Property(key = StashPlugin.STASH_REPOSITORY,
name = "Stash Repository",
description = "Stash project of current pull-request",
global = false),
@Property(key = StashPlugin.STASH_PULL_REQUEST_ID,
name = "Stash Pull-request Id",
description = "Stash pull-request Id",
global = false)})
public class StashPlugin implements Plugin {
private static final String DEFAULT_STASH_TIMEOUT_VALUE = "10000";
private static final String DEFAULT_STASH_THRESHOLD_VALUE = "100";
private static final boolean DEFAULT_STASH_ANALYSIS_OVERVIEW = true;
private static final boolean DEFAULT_STASH_INCLUDE_EXISTING_ISSUES = false;
private static final int DEFAULT_STASH_FILES_IN_OVERVIEW = 0;
private static final int DEFAULT_STASH_INCLUDE_VICINITY_RANGE = StashDiffReport.VICINITY_RANGE_NONE;
private static final String DEFAULT_STASH_EXCLUDE_RULES = "";
private static final String CONFIG_PAGE_SUB_CATEGORY_STASH = "Stash";
public static final String SEVERITY_NONE = "NONE";
private static final List<String> SEVERITY_LIST = Arrays.stream(Severity.values())
.map(Severity::name).collect(Collectors.toList());
private static final List<String> SEVERITY_LIST_WITH_NONE = Lists
.asList(SEVERITY_NONE, SEVERITY_LIST.toArray(new String[]{}));
public enum IssueType {
CONTEXT,
REMOVED,
ADDED,
}
public static final String STASH_NOTIFICATION = "sonar.stash.notification";
public static final String STASH_PROJECT = "sonar.stash.project";
public static final String STASH_REPOSITORY = "sonar.stash.repository";
public static final String STASH_PULL_REQUEST_ID = "sonar.stash.pullrequest.id";
public static final String STASH_RESET_COMMENTS = "sonar.stash.comments.reset";
public static final String STASH_URL = "sonar.stash.url";
public static final String STASH_LOGIN = "sonar.stash.login";
public static final String STASH_USER_SLUG = "sonar.stash.user.slug";
public static final String STASH_PASSWORD = "sonar.stash.password";
public static final String STASH_PASSWORD_ENVIRONMENT_VARIABLE = "sonar.stash.password.variable";
public static final String STASH_REVIEWER_APPROVAL = "sonar.stash.reviewer.approval";
public static final String STASH_REVIEWER_APPROVAL_SEVERITY_THRESHOLD = "sonar.stash.reviewer.approval.severity.threshold";
public static final String STASH_ISSUE_THRESHOLD = "sonar.stash.issue.threshold";
public static final String STASH_ISSUE_SEVERITY_THRESHOLD = "sonar.stash.issue.severity.threshold";
public static final String STASH_TIMEOUT = "sonar.stash.timeout";
public static final String STASH_TASK_SEVERITY_THRESHOLD = "sonar.stash.task.issue.severity.threshold";
public static final String STASH_INCLUDE_ANALYSIS_OVERVIEW = "sonar.stash.include.overview";
public static final String STASH_REPOSITORY_ROOT = "sonar.stash.repository.root";
public static final String STASH_INCLUDE_EXISTING_ISSUES = "sonar.stash.include.existing.issues";
public static final String STASH_FILES_LIMIT_IN_OVERVIEW = "sonar.stash.overview.filenames";
public static final String STASH_INCLUDE_VICINITY_RANGE = "sonar.stash.include.vicinity.issues.range";
public static final String STASH_EXCLUDE_RULES = "sonar.stash.exclude.rules";
@Override
public void define(Context context) {
context.addExtensions(
StashIssueReportingPostJob.class,
StashPluginConfiguration.class,
StashRequestFacade.class,
StashProjectBuilder.class,
PropertyDefinition.builder(STASH_URL)
.name("Stash base URL")
.description("HTTP URL of Stash instance, such as http://yourhost.yourdomain/stash")
.subCategory(CONFIG_PAGE_SUB_CATEGORY_STASH)
.onQualifiers(Qualifiers.PROJECT).build(),
PropertyDefinition.builder(STASH_LOGIN)
.name("Stash base User")
.description("User to push data on Stash instance")
.subCategory(CONFIG_PAGE_SUB_CATEGORY_STASH)
.onQualifiers(Qualifiers.PROJECT).build(),
PropertyDefinition.builder(STASH_USER_SLUG)
.name("Stash base user slug")
.description("If the username has special characters this setting has also to be specified")
.subCategory(CONFIG_PAGE_SUB_CATEGORY_STASH)
.onlyOnQualifiers(Qualifiers.PROJECT).build(),
PropertyDefinition.builder(STASH_PASSWORD)
.name("Stash base Password")
.description("Password for Stash base User (Do NOT use in production, passwords are public"
+ " for everyone with UNAUTHENTICATED HTTP access to SonarQube")
.type(PropertyType.PASSWORD)
.subCategory(CONFIG_PAGE_SUB_CATEGORY_STASH)
.onQualifiers(Qualifiers.PROJECT).build(),
PropertyDefinition.builder(STASH_TIMEOUT)
.name("Stash issue Timeout")
.description("Timeout when pushing a new issue to Stash (in ms)")
.subCategory(CONFIG_PAGE_SUB_CATEGORY_STASH)
.onQualifiers(Qualifiers.PROJECT)
.defaultValue(DEFAULT_STASH_TIMEOUT_VALUE).build(),
PropertyDefinition.builder(STASH_REVIEWER_APPROVAL)
.name("Stash reviewer approval")
.description("Does SonarQube approve the pull-request if there is no new issues?")
.subCategory(CONFIG_PAGE_SUB_CATEGORY_STASH)
.onQualifiers(Qualifiers.PROJECT)
.type(PropertyType.BOOLEAN)
.defaultValue("false").build(),
PropertyDefinition.builder(STASH_ISSUE_THRESHOLD)
.name("Stash issue Threshold")
.description("Threshold to limit the number of issues pushed to Stash server")
.subCategory(CONFIG_PAGE_SUB_CATEGORY_STASH)
.onQualifiers(Qualifiers.PROJECT)
.defaultValue(DEFAULT_STASH_THRESHOLD_VALUE).build(),
PropertyDefinition.builder(STASH_ISSUE_SEVERITY_THRESHOLD)
.name("Stash issue severity Threshold")
.description("Defines minimum issue severity to create diff-view comments for."
+ " Overview comment will still contain all severities."
+ " By default, all issues are pushed to Stash.")
.type(PropertyType.SINGLE_SELECT_LIST)
.subCategory(CONFIG_PAGE_SUB_CATEGORY_STASH)
.onQualifiers(Qualifiers.PROJECT)
.defaultValue(SEVERITY_LIST.get(0))
.options(SEVERITY_LIST).build(),
PropertyDefinition.builder(STASH_REVIEWER_APPROVAL_SEVERITY_THRESHOLD)
.name("Threshold tie the approval to the severity of the found issues")
.description("Maximum severity of an issue for approval to complete")
.type(PropertyType.SINGLE_SELECT_LIST)
.subCategory(CONFIG_PAGE_SUB_CATEGORY_STASH)
.onQualifiers(Qualifiers.PROJECT)
.defaultValue(SEVERITY_NONE)
.options(SEVERITY_LIST_WITH_NONE).build(),
PropertyDefinition.builder(STASH_TASK_SEVERITY_THRESHOLD)
.name("Stash tasks severity threshold")
.description("Only create tasks for issues with the same or higher severity")
.type(PropertyType.SINGLE_SELECT_LIST)
.subCategory(CONFIG_PAGE_SUB_CATEGORY_STASH)
.onQualifiers(Qualifiers.PROJECT)
.defaultValue(SEVERITY_NONE)
.options(SEVERITY_LIST_WITH_NONE).build(),
PropertyDefinition.builder(STASH_INCLUDE_ANALYSIS_OVERVIEW)
.name("Include Analysis Overview Comment")
.description("Create a comment to the Pull Request providing a overview of the results")
.type(PropertyType.BOOLEAN)
.subCategory(CONFIG_PAGE_SUB_CATEGORY_STASH)
.onQualifiers(Qualifiers.PROJECT)
.defaultValue(Boolean.toString(DEFAULT_STASH_ANALYSIS_OVERVIEW)).build(),
PropertyDefinition.builder(STASH_FILES_LIMIT_IN_OVERVIEW)
.name("Include Files in Overview")
.description("Will extend the Analysis Overview comment to include the files where the " +
"issues where found. Set to any positive number to limit how many files per issue " +
"will be shown. Set to 0 to disable this feature.")
.type(PropertyType.INTEGER)
.subCategory(CONFIG_PAGE_SUB_CATEGORY_STASH)
.onQualifiers(Qualifiers.PROJECT)
.defaultValue(String.valueOf(DEFAULT_STASH_FILES_IN_OVERVIEW)).build(),
PropertyDefinition.builder(STASH_INCLUDE_EXISTING_ISSUES)
.name("Include Existing Issues")
.description("Set to true to include already existing issues on modified lines.")
.type(PropertyType.BOOLEAN)
.subCategory(CONFIG_PAGE_SUB_CATEGORY_STASH)
.onQualifiers(Qualifiers.PROJECT)
.defaultValue(Boolean.toString(DEFAULT_STASH_INCLUDE_EXISTING_ISSUES)).build(),
PropertyDefinition.builder(STASH_INCLUDE_VICINITY_RANGE)
.name("Include Vicinity Issues Range")
.description("Specifies the range around the actual changes for which issues are reported. (In lines)")
.type(PropertyType.INTEGER)
.subCategory(CONFIG_PAGE_SUB_CATEGORY_STASH)
.onQualifiers(Qualifiers.PROJECT)
.defaultValue(String.valueOf(DEFAULT_STASH_INCLUDE_VICINITY_RANGE)).build(),
PropertyDefinition.builder(STASH_EXCLUDE_RULES)
.name("Excluded Rules")
.description("Comma separated list of rules for which no comments should be created.")
.type(PropertyType.STRING)
.subCategory(CONFIG_PAGE_SUB_CATEGORY_STASH)
.onQualifiers(Qualifiers.PROJECT)
.defaultValue(DEFAULT_STASH_EXCLUDE_RULES).build()
);
}
}
================================================
FILE: src/main/java/org/sonar/plugins/stash/StashPluginConfiguration.java
================================================
package org.sonar.plugins.stash;
import com.google.common.collect.Sets;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import org.sonar.api.CoreProperties;
import org.sonar.api.batch.InstantiationStrategy;
import org.sonar.api.batch.ScannerSide;
import org.sonar.api.batch.rule.Severity;
import org.sonar.api.config.Settings;
import java.io.File;
import java.util.Optional;
import org.sonar.api.platform.Server;
import org.sonar.api.rule.RuleKey;
@ScannerSide
@InstantiationStrategy(InstantiationStrategy.PER_BATCH)
public class StashPluginConfiguration {
private Settings settings;
private Server server;
public StashPluginConfiguration(Settings settings, Server server) {
this.settings = settings;
this.server = server;
}
public boolean hasToNotifyStash() {
return settings.getBoolean(StashPlugin.STASH_NOTIFICATION);
}
public String getStashProject() {
return settings.getString(StashPlugin.STASH_PROJECT);
}
public String getStashRepository() {
return settings.getString(StashPlugin.STASH_REPOSITORY);
}
public Integer getPullRequestId() {
return settings.getInt(StashPlugin.STASH_PULL_REQUEST_ID);
}
public String getStashURL() {
return settings.getString(StashPlugin.STASH_URL);
}
public String getStashLogin() {
return settings.getString(StashPlugin.STASH_LOGIN);
}
public String getStashUserSlug() {
return settings.getString(StashPlugin.STASH_USER_SLUG);
}
public String getStashPassword() {
return settings.getString(StashPlugin.STASH_PASSWORD);
}
public String getStashPasswordEnvironmentVariable() {
return settings.getString(StashPlugin.STASH_PASSWORD_ENVIRONMENT_VARIABLE);
}
public String getSonarQubeURL() {
return server.getURL();
}
public String getSonarQubeLogin() {
return settings.getString(CoreProperties.LOGIN);
}
public String getSonarQubePassword() {
return settings.getString(CoreProperties.PASSWORD);
}
public int getIssueThreshold() {
return settings.getInt(StashPlugin.STASH_ISSUE_THRESHOLD);
}
public Severity getIssueSeverityThreshold() {
return Severity.valueOf(
Objects.requireNonNull(settings.getString(StashPlugin.STASH_ISSUE_SEVERITY_THRESHOLD)));
}
public int getStashTimeout() {
return settings.getInt(StashPlugin.STASH_TIMEOUT);
}
public boolean canApprovePullRequest() {
return settings.getBoolean(StashPlugin.STASH_REVIEWER_APPROVAL);
}
public boolean resetComments() {
return settings.getBoolean(StashPlugin.STASH_RESET_COMMENTS);
}
public Optional<Severity> getTaskIssueSeverityThreshold() {
return getOptionalSeveritySetting(StashPlugin.STASH_TASK_SEVERITY_THRESHOLD);
}
public Optional<Severity> getApprovalSeverityThreshold() {
return getOptionalSeveritySetting(StashPlugin.STASH_REVIEWER_APPROVAL_SEVERITY_THRESHOLD);
}
public boolean includeAnalysisOverview() {
return settings.getBoolean(StashPlugin.STASH_INCLUDE_ANALYSIS_OVERVIEW);
}
public Optional<File> getRepositoryRoot() {
return Optional.ofNullable(settings.getString(StashPlugin.STASH_REPOSITORY_ROOT)).map(File::new);
}
public boolean includeExistingIssues() {
return settings.getBoolean(StashPlugin.STASH_INCLUDE_EXISTING_ISSUES);
}
public int getFilesLimitInOverview() {
return settings.getInt(StashPlugin.STASH_FILES_LIMIT_IN_OVERVIEW);
}
public int issueVicinityRange() {
return settings.getInt(StashPlugin.STASH_INCLUDE_VICINITY_RANGE);
}
public Set<RuleKey> excludedRules() {
return Sets.newHashSet(
settings.getStringArray(StashPlugin.STASH_EXCLUDE_RULES)
).stream()
.map(String::trim)
.map(RuleKey::parse)
.collect(Collectors.toSet());
}
private Optional<Severity> getOptionalSeveritySetting(String key) {
String setting = settings.getString(key);
if (StashPlugin.SEVERITY_NONE.equals(setting)) {
return Optional.empty();
}
if (setting == null) {
return Optional.empty();
}
return Optional.of(Severity.valueOf(setting));
}
}
================================================
FILE: src/main/java/org/sonar/plugins/stash/StashPluginUtils.java
================================================
package org.sonar.plugins.stash;
import com.google.common.base.CharMatcher;
import java.io.IOException;
import java.util.Collection;
import java.util.Properties;
import org.sonar.api.batch.fs.InputComponent;
import org.sonar.api.batch.fs.InputModule;
import org.sonar.api.batch.postjob.issue.PostJobIssue;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import org.sonar.api.batch.rule.Severity;
public final class StashPluginUtils {
private StashPluginUtils() {}
public static String formatPercentage(double d) {
// Defining that our percentage is precise down to 0.1%
DecimalFormat df = new DecimalFormat("0.0");
// Protecting this method against non-US locales that would not use '.' as decimal separation
DecimalFormatSymbols decimalFormatSymbols = new DecimalFormatSymbols();
decimalFormatSymbols.setDecimalSeparator('.');
df.setDecimalFormatSymbols(decimalFormatSymbols);
// Making sure that we round the 0.1% properly out of the double value
df.setRoundingMode(RoundingMode.HALF_UP);
return df.format(d);
}
public static boolean roundedPercentageGreaterThan(double left, double right) {
return (left > right) && !formatPercentage(left).equals(formatPercentage(right));
}
public static long countIssuesBySeverity(Collection<PostJobIssue> issues, final Severity severity) {
return issues.stream().filter(i -> severity.equals(i.severity())).count();
}
public static boolean isProjectWide(PostJobIssue issue) {
InputComponent ic = issue.inputComponent();
if (!(ic instanceof InputModule)) {
return false;
}
InputModule im = (InputModule) ic;
if (im.key() == null) {
return false;
}
return CharMatcher.is(':').countIn(im.key()) == 0;
}
public static PluginInfo getPluginInfo() {
Properties props = new Properties();
try {
props.load(StashPluginUtils.class.getClassLoader().getResourceAsStream("org/sonar/plugins/stash/sonar-stash.properties"));
} catch (IOException e) {
throw new IllegalStateException(e);
}
return new PluginInfo(
props.getProperty("project.name"),
props.getProperty("project.version")
);
}
public static String removeEnd(String s, String suffix) {
if (s.endsWith(suffix)) {
return removeEnd(s.substring(0, s.length() - suffix.length()), suffix);
}
return s;
}
}
================================================
FILE: src/main/java/org/sonar/plugins/stash/StashProjectBuilder.java
================================================
package org.sonar.plugins.stash;
import org.sonar.api.batch.bootstrap.ProjectBuilder;
import java.io.File;
// FIXME when we depend on 7.0 and use ScmProvider.relativePathFromScmRoot
public class StashProjectBuilder extends ProjectBuilder {
private File projectBaseDir;
@Override
public void build(Context context) {
projectBaseDir = context.projectReactor().getRoot().getBaseDir();
}
public File getProjectBaseDir() {
return projectBaseDir;
}
}
================================================
FILE: src/main/java/org/sonar/plugins/stash/StashRequestFacade.java
================================================
package org.sonar.plugins.stash;
import java.io.File;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Optional;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.sonar.api.batch.InstantiationStrategy;
import org.sonar.api.batch.ScannerSide;
import org.sonar.api.batch.fs.InputComponent;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.postjob.issue.PostJobIssue;
import org.sonar.api.batch.rule.Severity;
import org.sonar.api.scan.filesystem.PathResolver;
import org.sonar.api.utils.System2;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.plugins.stash.StashPlugin.IssueType;
import org.sonar.plugins.stash.client.StashClient;
import org.sonar.plugins.stash.client.StashCredentials;
import org.sonar.plugins.stash.exceptions.StashConfigurationException;
import org.sonar.plugins.stash.issue.MarkdownPrinter;
import org.sonar.plugins.stash.issue.StashComment;
import org.sonar.plugins.stash.issue.StashCommentReport;
import org.sonar.plugins.stash.issue.StashDiffReport;
import org.sonar.plugins.stash.issue.StashPullRequest;
import org.sonar.plugins.stash.issue.StashTask;
import org.sonar.plugins.stash.issue.StashUser;
import org.sonar.plugins.stash.issue.collector.SonarQubeCollector;
@ScannerSide
@InstantiationStrategy(InstantiationStrategy.PER_BATCH)
public class StashRequestFacade implements IssuePathResolver {
private static final Logger LOGGER = Loggers.get(StashRequestFacade.class);
private static final String EXCEPTION_STASH_CONF = "Unable to get {0} from plugin configuration (value is null)";
private final StashPluginConfiguration config;
private final File projectBaseDir;
private final System2 system;
public StashRequestFacade(
StashPluginConfiguration stashPluginConfiguration,
StashProjectBuilder projectBuilder,
System2 system
) {
this.config = stashPluginConfiguration;
this.projectBaseDir = projectBuilder.getProjectBaseDir();
this.system = system;
}
public List<PostJobIssue> extractIssueReport(Iterable<PostJobIssue> issues) {
return SonarQubeCollector.extractIssueReport(
issues, this, config.includeExistingIssues(), config.excludedRules()
);
}
private MarkdownPrinter getMarkdownPrinter() {
return new MarkdownPrinter(getIssueThreshold(), config.getSonarQubeURL(), config.getFilesLimitInOverview(), this);
}
/**
* Post SQ analysis overview on Stash
*/
public void postAnalysisOverview(PullRequestRef pr,
Collection<PostJobIssue> issueReport,
StashClient stashClient) {
String report = getMarkdownPrinter().printReportMarkdown(issueReport);
stashClient.postCommentOnPullRequest(pr, report);
LOGGER.info("SonarQube analysis overview has been reported to Stash.");
}
/**
* Approve pull-request
*/
public void approvePullRequest(PullRequestRef pr, StashClient stashClient) {
stashClient.approvePullRequest(pr);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Pull-request {} ({}/{}) APPROVED by user \"{}\"",
pr.pullRequestId(), pr.project(), pr.repository(), stashClient.getLogin());
}
}
/**
* Reset pull-request approval
*/
public void resetPullRequestApproval(PullRequestRef pr, StashClient stashClient) {
stashClient.resetPullRequestApproval(pr);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Pull-request {} ({}/{}) NOT APPROVED by user \"{}\"",
pr.pullRequestId(), pr.project(), pr.repository(), stashClient.getLogin());
}
}
/**
* Add a reviewer to the current pull-request.
*/
public void addPullRequestReviewer(PullRequestRef pr, String userSlug, StashClient stashClient) {
StashPullRequest pullRequest = stashClient.getPullRequest(pr);
// user not yet in reviewer list
StashUser reviewer = pullRequest.getReviewer(userSlug);
if (reviewer == null) {
List<StashUser> reviewers = pullRequest.getReviewers();
reviewers.add(stashClient.getUser(userSlug));
stashClient.addPullRequestReviewer(pr, pullRequest.getVersion(), reviewers);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("User \"{}\" is now a reviewer of the pull-request #{} in {}/{}",
userSlug, pr.pullRequestId(), pr.project(), pr.repository());
}
}
}
/**
* Push SonarQube report into the pull-request as comments.
*/
public void postSonarQubeReport(PullRequestRef pr,
Iterable<PostJobIssue> issueReport,
StashDiffReport diffReport,
StashClient stashClient) {
postCommentPerIssue(pr, issueReport, diffReport, stashClient);
LOGGER.info("New SonarQube issues (if any) have been reported to Stash.");
}
/**
* Post one comment by found issue on Stash.
*/
void postCommentPerIssue(PullRequestRef pr, Iterable<PostJobIssue> issues,
StashDiffReport diffReport, StashClient stashClient) {
// to optimize request to Stash, builds comment match ordered by filepath
Map<String, StashCommentReport> commentsByFile = new HashMap<>();
for (PostJobIssue issue : issues) {
String path = getIssuePath(issue);
commentsByFile.computeIfAbsent(path, p -> {
StashCommentReport comments = stashClient.getPullRequestComments(pr, p);
// According to the type of the comment
// if type == CONTEXT, comment.line is set to source line instead of destination line
comments.applyDiffReport(diffReport);
return comments;
});
}
Severity issueSeverityThreshold = config.getIssueSeverityThreshold();
for (PostJobIssue issue : issues) {
if (issue.severity().compareTo(issueSeverityThreshold) >= 0) {
postIssueComment(pr, issue, commentsByFile, diffReport, stashClient, config.getTaskIssueSeverityThreshold());
}
}
}
private void postIssueComment(PullRequestRef pr,
PostJobIssue issue,
Map<String, StashCommentReport> commentsByFile,
StashDiffReport diffReport,
StashClient stashClient,
Optional<Severity> taskSeverityThreshold
) {
String issueKey = issue.key();
String path = getIssuePath(issue);
StashCommentReport comments = commentsByFile.get(path);
String commentContent = getMarkdownPrinter().printIssueMarkdown(issue);
Integer issueLine = issue.line();
if (issueLine == null) {
issueLine = 0;
}
// Surprisingly this syntax does not trigger the squid:NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE rule
// but it does if you transform that into a ternary operator at the assignment level :/
// if comment not already pushed to Stash
if (comments != null && comments.contains(commentContent, path, issueLine)) {
LOGGER.debug("Comment \"{}\" already pushed on file {} ({})", issueKey, path, issueLine);
return;
}
// check if issue belongs to the Stash diff view
IssueType type = diffReport.getType(path, issueLine, config.issueVicinityRange());
if (type == null) {
LOGGER.info(
"Comment \"{}\" cannot be pushed to Stash like it does not belong to diff view - {} (line: {})",
issueKey, path, issueLine);
return;
}
long line = diffReport.getLine(path, issueLine);
StashComment comment = stashClient
.postCommentLineOnPullRequest(pr, commentContent, path, line, type);
LOGGER
.info("Comment \"{}\" has been created ({}) on file {} ({})", issueKey, type, path, line);
// Create task linked to the comment if configured
if (
taskSeverityThreshold.isPresent()
&& issue.severity().compareTo(taskSeverityThreshold.get()) >= 0
) {
stashClient.postTaskOnComment(issue.message(), comment.getId());
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Comment \"{}\" has been linked to a Stash task", comment.getId());
}
}
}
public StashCredentials getCredentials() {
String passwordEnvVariable = config.getStashPasswordEnvironmentVariable();
String password = config.getStashPassword();
String userSlug = config.getStashUserSlug();
if (passwordEnvVariable != null) {
password = system.envVariable(passwordEnvVariable);
if (password == null) {
throw new StashConfigurationException(
"Unable to retrieve password from configured environment variable "
+ StashPlugin.STASH_PASSWORD_ENVIRONMENT_VARIABLE);
}
}
if (userSlug == null) {
userSlug = config.getStashLogin();
}
return new StashCredentials(config.getStashLogin(), password, userSlug);
}
/**
* Mandatory Issue Threshold option.
*
* @throws StashConfigurationException if unable to get parameter as Integer
*/
public int getIssueThreshold() {
int result = 0;
try {
result = config.getIssueThreshold();
} catch (NumberFormatException e) {
throw new StashConfigurationException("Unable to get " + StashPlugin.STASH_ISSUE_THRESHOLD
+ " from plugin configuration", e);
}
return result;
}
/**
* Mandatory Stash URL option.
*
* @throws StashConfigurationException if unable to get parameter
*/
public String getStashURL() {
String result = config.getStashURL();
if (result == null) {
throw new StashConfigurationException(
MessageFormat.format(EXCEPTION_STASH_CONF, StashPlugin.STASH_URL));
}
if (result.endsWith("/")) {
LOGGER.warn("Stripping trailing slash from {}, as it leads to invalid URLs",
StashPlugin.STASH_URL);
result = StashPluginUtils.removeEnd(result, "/");
}
return result;
}
public PullRequestRef getPullRequest() {
return PullRequestRef.builder()
.setProject(getStashProject())
.setRepository(getStashRepository())
.setPullRequestId(getStashPullRequestId())
.build();
}
/**
* Mandatory Stash Project option.
*
* @throws StashConfigurationException if unable to get parameter
*/
public String getStashProject() {
String result = config.getStashProject();
if (result == null) {
throw new StashConfigurationException(
MessageFormat.format(EXCEPTION_STASH_CONF, StashPlugin.STASH_PROJECT));
}
return result;
}
/**
* Mandatory Stash Repository option.
*
* @throws StashConfigurationException if unable to get parameter
*/
public String getStashRepository() {
String result = config.getStashRepository();
if (result == null) {
throw new StashConfigurationException(
MessageFormat.format(EXCEPTION_STASH_CONF, StashPlugin.STASH_REPOSITORY));
}
return result;
}
/**
* Mandatory Stash pull-request ID option.
*
* @throws StashConfigurationException if unable to get parameter
*/
public int getStashPullRequestId() {
Integer result = config.getPullRequestId();
if (result == null) {
throw new StashConfigurationException(MessageFormat.format(EXCEPTION_STASH_CONF,
StashPlugin.STASH_PULL_REQUEST_ID));
}
return result;
}
/**
* Get user who published the SQ analysis in Stash.
*/
public StashUser getSonarQubeReviewer(String userSlug, StashClient stashClient) {
StashUser result = null;
result = stashClient.getUser(userSlug);
LOGGER.debug("SonarQube reviewer {} identified in Stash", userSlug);
return result;
}
/**
* Get all changes exposed through the Stash pull-request.
*/
public StashDiffReport getPullRequestDiffReport(PullRequestRef pr, StashClient stashClient) {
StashDiffReport result = stashClient.getPullRequestDiffs(pr);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Stash differential report retrieved from pull request {} #{}",
pr.repository(), pr.pullRequestId());
}
return result;
}
/**
* Reset all comments linked to a pull-request.
*/
public void resetComments(PullRequestRef pr,
StashDiffReport diffReport,
StashUser sonarUser,
StashClient stashClient) {
// Let's call this "diffRep_loop"
for (StashComment comment : diffReport.getComments()) {
// delete comment only if published by the current SQ user
if (sonarUser.getId() != comment.getAuthor().getId()) {
continue;
// Next element in "diffRep_loop"
// comment contains tasks which cannot be deleted => do nothing
} else if (comment.containsPermanentTasks()) {
LOGGER.debug("Comment \"{}\" (path:\"{}\", line:\"{}\")"
+ "CANNOT be deleted because one of its tasks is not deletable.", comment.getId(),
comment.getPath(),
comment.getLine());
continue; // Next element in "diffRep_loop"
}
// delete tasks linked to the current comment
for (StashTask task : comment.getTasks()) {
stashClient.deleteTaskOnComment(task);
}
stashClient.deletePullRequestComment(pr, comment);
}
LOGGER.info("SonarQube issues reported to Stash by user \"{}\" have been reset",
sonarUser.getName());
}
@Override
public String getIssuePath(PostJobIssue issue) {
InputComponent ip = issue.inputComponent();
if (ip == null || !ip.isFile()) {
return null;
}
InputFile inputFile = (InputFile) ip;
Path baseDir = config
.getRepositoryRoot()
.orElse(projectBaseDir).toPath();
return new PathResolver().relativePath(baseDir, inputFile.path());
}
}
================================================
FILE: src/main/java/org/sonar/plugins/stash/client/ContentType.java
================================================
package org.sonar.plugins.stash.client;
import static java.util.Objects.requireNonNull;
/*
* basic implementation of RFC 7231, section 3.1.1.1
* subset of javax.mail.internet.ContentType
*/
public class ContentType {
private String primaryType;
private String subType;
public static final int CONTENTTYPE_ELEM_NUM = 2;
public ContentType(String primaryType, String subType, Object list) {
if (list != null) {
throw new IllegalArgumentException();
}
this.primaryType = requireNonNull(primaryType);
this.subType = requireNonNull(subType);
}
public boolean match(String s) {
String[] parts = s.split(";", CONTENTTYPE_ELEM_NUM);
// we ignore the parameters, match() does not care and we can't have our own
String[] types = parts[0].trim().split("/", CONTENTTYPE_ELEM_NUM);
if (types.length < CONTENTTYPE_ELEM_NUM) {
return false;
}
return primaryType.equalsIgnoreCase(types[0])
&& subType.equalsIgnoreCase(types[1]);
}
@Override
public String toString() {
return primaryType + "/" + subType;
}
}
================================================
FILE: src/main/java/org/sonar/plugins/stash/client/StashClient.java
================================================
package org.sonar.plugins.stash.client;
import com.github.cliftonlabs.json_simple.JsonArray;
import com.github.cliftonlabs.json_simple.JsonException;
import com.github.cliftonlabs.json_simple.JsonObject;
import com.github.cliftonlabs.json_simple.Jsoner;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
import org.asynchttpclient.AsyncHttpClient;
import org.asynchttpclient.BoundRequestBuilder;
import org.asynchttpclient.DefaultAsyncHttpClient;
import org.asynchttpclient.DefaultAsyncHttpClientConfig;
import org.asynchttpclient.Realm;
import org.asynchttpclient.Request;
import org.asynchttpclient.Response;
import org.asynchttpclient.config.AsyncHttpClientConfigDefaults;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.plugins.stash.PeekableInputStream;
import org.sonar.plugins.stash.PluginInfo;
import org.sonar.plugins.stash.PullRequestRef;
import org.sonar.plugins.stash.StashPlugin.IssueType;
import org.sonar.plugins.stash.StashPluginUtils;
import org.sonar.plugins.stash.exceptions.StashClientException;
import org.sonar.plugins.stash.issue.StashComment;
import org.sonar.plugins.stash.issue.StashCommentReport;
import org.sonar.plugins.stash.issue.StashDiffReport;
import org.sonar.plugins.stash.issue.StashPullRequest;
import org.sonar.plugins.stash.issue.StashTask;
import org.sonar.plugins.stash.issue.StashUser;
import org.sonar.plugins.stash.issue.collector.StashCollector;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class StashClient implements AutoCloseable {
private static final Logger LOGGER = Loggers.get(StashClient.class);
private final String baseUrl;
private final StashCredentials credentials;
private final int stashTimeout;
private AsyncHttpClient httpClient;
private static final String REST_API = "/rest/api/1.0/";
private static final String USER_API = "{0}" + REST_API + "users/{1}";
private static final String REPO_API = "{0}" + REST_API + "projects/{1}/repos/{2}/";
private static final String TASKS_API = REST_API + "tasks";
private static final String API_ALL_PR = REPO_API + "pull-requests/";
private static final String API_ONE_PR = API_ALL_PR + "{3,number,#}";
private static final String API_ONE_PR_ALL_COMMENTS = API_ONE_PR + "/comments";
private static final String API_ONE_PR_DIFF = API_ONE_PR + "/diff?withComments=true";
private static final String API_ONE_PR_APPROVAL = API_ONE_PR + "/approve";
private static final String API_ONE_PR_COMMENT_PATH = API_ONE_PR + "/comments?path={4}&start={5,number,#}";
private static final String API_ONE_PR_ONE_COMMENT = API_ONE_PR_ALL_COMMENTS + "/{4}?version={5}";
private static final String PULL_REQUEST_APPROVAL_POST_ERROR_MESSAGE = "Unable to change status of pull-request {0}"
+ " #{1,number,#}.";
private static final String PULL_REQUEST_GET_ERROR_MESSAGE = "Unable to retrieve pull-request {0} #{1,number,#}.";
private static final String PULL_REQUEST_PUT_ERROR_MESSAGE = "Unable to update pull-request {0} #{1,number,#}.";
private static final String USER_GET_ERROR_MESSAGE = "Unable to retrieve user {0}.";
private static final String COMMENT_POST_ERROR_MESSAGE = "Unable to post a comment to {0} #{1,number,#}.";
private static final String COMMENT_GET_ERROR_MESSAGE = "Unable to get comment linked to {0} #{1,number,#}.";
private static final String COMMENT_DELETION_ERROR_MESSAGE = "Unable to delete comment {0,number,#}"
+ " from pull-request {1} #{2,number,#}.";
private static final String TASK_POST_ERROR_MESSAGE = "Unable to post a task on comment {0,number,#}.";
private static final String TASK_DELETION_ERROR_MESSAGE = "Unable to delete task {0,number,#}.";
private static final ContentType JSON = new ContentType("application", "json", null);
public StashClient(String url, StashCredentials credentials, int stashTimeout, String sonarQubeVersion) {
this.baseUrl = url;
this.credentials = credentials;
this.stashTimeout = stashTimeout;
this.httpClient = createHttpClient(sonarQubeVersion);
}
public String getBaseUrl() {
return baseUrl;
}
public String getLogin() {
return credentials.getLogin();
}
public void postCommentOnPullRequest(PullRequestRef pr, String report) {
String request = MessageFormat.format(API_ONE_PR_ALL_COMMENTS,
baseUrl,
pr.project(),
pr.repository(),
pr.pullRequestId());
JsonObject json = new JsonObject();
json.put("text", report);
postCreate(request, json, MessageFormat.format(COMMENT_POST_ERROR_MESSAGE, pr.repository(), pr.pullRequestId()));
}
public StashCommentReport getPullRequestComments(PullRequestRef pr, String path) {
StashCommentReport result = new StashCommentReport();
long start = 0;
boolean isLastPage = false;
while (!isLastPage) {
String request = MessageFormat.format(API_ONE_PR_COMMENT_PATH,
baseUrl,
pr.project(),
pr.repository(),
pr.pullRequestId(),
path,
start);
JsonObject jsonComments = get(request,
MessageFormat.format(COMMENT_GET_ERROR_MESSAGE,
pr.repository(),
pr.pullRequestId()));
result.add(StashCollector.extractComments(jsonComments));
// Stash pagination: check if you get all comments linked to the pull-request
isLastPage = StashCollector.isLastPage(jsonComments);
start = StashCollector.getNextPageStart(jsonComments);
}
return result;
}
public void deletePullRequestComment(PullRequestRef pr, StashComment comment) {
String request = MessageFormat.format(API_ONE_PR_ONE_COMMENT,
baseUrl,
pr.project(),
pr.repository(),
pr.pullRequestId(),
Long.toString(comment.getId()),
Long.toString(comment.getVersion()));
delete(request,
MessageFormat.format(COMMENT_DELETION_ERROR_MESSAGE, comment.getId(), pr.repository(), pr.pullRequestId()));
}
public StashDiffReport getPullRequestDiffs(PullRequestRef pr) {
String request = MessageFormat.format(API_ONE_PR_DIFF,
baseUrl,
pr.project(),
pr.repository(),
pr.pullRequestId());
JsonObject jsonDiffs = get(request,
MessageFormat.format(COMMENT_GET_ERROR_MESSAGE, pr.repository(), pr.pullRequestId()));
return StashCollector.extractDiffs(jsonDiffs);
}
public StashComment postCommentLineOnPullRequest(PullRequestRef pr,
String message,
String path,
long line,
IssueType type) {
String request = MessageFormat.format(API_ONE_PR_ALL_COMMENTS,
baseUrl,
pr.project(),
pr.repository(),
pr.pullRequestId());
JsonObject anchor = new JsonObject();
if (line != 0L) {
anchor.put("line", line);
anchor.put("lineType", type.name());
}
String fileType = "TO";
if (type == IssueType.CONTEXT) {
fileType = "FROM";
}
anchor.put("fileType", fileType);
anchor.put("path", path);
JsonObject json = new JsonObject();
json.put("text", message);
json.put("anchor", anchor);
JsonObject response = postCreate(request, json,
MessageFormat.format(COMMENT_POST_ERROR_MESSAGE,
pr.repository(),
pr.pullRequestId()));
return StashCollector.extractComment(response, path, line);
}
public StashUser getUser(String userSlug) {
String request = MessageFormat.format(USER_API, baseUrl, userSlug);
JsonObject response = get(request, MessageFormat.format(USER_GET_ERROR_MESSAGE, userSlug));
return StashCollector.extractUser(response);
}
public StashPullRequest getPullRequest(PullRequestRef pr) {
String request = MessageFormat.format(API_ONE_PR, baseUrl, pr.project(), pr.repository(), pr.pullRequestId());
JsonObject response = get(request,
MessageFormat.format(PULL_REQUEST_GET_ERROR_MESSAGE,
pr.repository(),
pr.pullRequestId()));
return StashCollector.extractPullRequest(pr, response);
}
public void addPullRequestReviewer(PullRequestRef pr, long pullRequestVersion, List<StashUser> reviewers) {
String request = MessageFormat.format(API_ONE_PR, baseUrl, pr.project(), pr.repository(), pr.pullRequestId());
JsonObject json = new JsonObject();
JsonArray jsonReviewers = new JsonArray();
for (StashUser reviewer : reviewers) {
JsonObject reviewerName = new JsonObject();
reviewerName.put("name", reviewer.getName());
JsonObject user = new JsonObject();
user.put("user", reviewerName);
jsonReviewers.add(user);
}
json.put("reviewers", jsonReviewers);
json.put("id", pr.pullRequestId());
json.put("version", pullRequestVersion);
put(request, json, MessageFormat.format(PULL_REQUEST_PUT_ERROR_MESSAGE, pr.repository(), pr.pullRequestId()));
}
public void approvePullRequest(PullRequestRef pr) {
String request = MessageFormat.format(API_ONE_PR_APPROVAL,
baseUrl,
pr.project(),
pr.repository(),
pr.pullRequestId());
post(request,
null,
MessageFormat.format(PULL_REQUEST_APPROVAL_POST_ERROR_MESSAGE, pr.repository(), pr.pullRequestId()));
}
public void resetPullRequestApproval(PullRequestRef pr) {
String request = MessageFormat.format(API_ONE_PR_APPROVAL,
baseUrl,
pr.project(),
pr.repository(),
pr.pullRequestId());
delete(request,
HttpURLConnection.HTTP_OK,
MessageFormat.format(PULL_REQUEST_APPROVAL_POST_ERROR_MESSAGE, pr.repository(), pr.pullRequestId()));
}
public void postTaskOnComment(String message, Long commentId) {
String request = baseUrl + TASKS_API;
JsonObject anchor = new JsonObject();
anchor.put("id", commentId);
anchor.put("type", "COMMENT");
JsonObject json = new JsonObject();
json.put("anchor", anchor);
json.put("text", message);
postCreate(request, json, MessageFormat.format(TASK_POST_ERROR_MESSAGE, commentId));
}
public void deleteTaskOnComment(StashTask task) {
String request = baseUrl + TASKS_API + "/" + task.getId();
delete(request, MessageFormat.format(TASK_DELETION_ERROR_MESSAGE, task.getId()));
}
@Override
public void close() {
try {
httpClient.close();
} catch (IOException e) {
LOGGER.debug("Ignoring exception while closing StashClient: {}", e, e);
}
}
private JsonObject get(String url, String errorMessage) {
return performRequest(httpClient.prepareGet(url), null, HttpURLConnection.HTTP_OK, errorMessage);
}
private JsonObject post(String url, JsonObject body, String errorMessage) {
return performRequest(httpClient.preparePost(url), body, HttpURLConnection.HTTP_OK, errorMessage);
}
private JsonObject postCreate(String url, JsonObject body, String errorMessage) {
return performRequest(httpClient.preparePost(url), body, HttpURLConnection.HTTP_CREATED, errorMessage);
}
private JsonObject delete(String url, int expectedStatusCode, String errorMessage) {
return performRequest(httpClient.prepareDelete(url), null, expectedStatusCode, errorMessage);
}
private JsonObject delete(String url, String errorMessage) {
return delete(url, HttpURLConnection.HTTP_NO_CONTENT, errorMessage);
}
private JsonObject put(String url, JsonObject body, String errorMessage) {
return performRequest(httpClient.preparePut(url), body, HttpURLConnection.HTTP_OK, errorMessage);
}
private void addAuth(BoundRequestBuilder requestBuilder) {
if (credentials != null && credentials.getLogin() != null) {
String password = Optional.ofNullable(credentials.getPassword()).orElse("");
Realm realm = new Realm.Builder(credentials.getLogin(), password).setUsePreemptiveAuth(true)
.setScheme(Realm.AuthScheme.BASIC).build();
requestBuilder.setRealm(realm);
}
}
private JsonObject performRequest(BoundRequestBuilder requestBuilder,
JsonObject body,
int expectedStatusCode,
String errorMessage) {
if (body != null) {
requestBuilder.setBody(body.toJson());
}
addAuth(requestBuilder);
requestBuilder.setFollowRedirect(true);
requestBuilder.addHeader("Content-Type", JSON.toString());
requestBuilder.addHeader("Accept", JSON.toString());
requestBuilder.setCharset(StandardCharsets.UTF_8);
Request request = requestBuilder.build();
try {
Response response = httpClient.executeRequest(request).get(stashTimeout, TimeUnit.MILLISECONDS);
validateResponse(response, expectedStatusCode, errorMessage);
return extractResponse(response);
} catch (ExecutionException | TimeoutException e) {
throw new StashClientException(request.getUrl(), e);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new StashClientException(request.getUrl(), e);
}
}
private static void validateResponse(Response response, int expectedStatusCode, String message) {
int responseCode = response.getStatusCode();
if (responseCode != expectedStatusCode) {
throw new StashClientException(message + " Received " + responseCode + ": " + formatStashApiError(response));
}
}
private static JsonObject extractResponse(Response response) {
PeekableInputStream bodyStream = new PeekableInputStream(response.getResponseBodyAsStream());
try {
if (!bodyStream.peek().isPresent()) {
return null;
}
String contentType = response.getHeader("Content-Type");
if (!JSON.match(contentType.trim())) {
throw new StashClientException("Received response with type " + contentType + " instead of JSON");
}
Reader body = new InputStreamReader(bodyStream);
Object obj = Jsoner.deserialize(body);
return (JsonObject)obj;
} catch (JsonException | ClassCastException | IOException e) {
throw new StashClientException("Could not parse JSON response", e);
}
}
private static String formatStashApiError(Response response) {
JsonObject responseJson = extractResponse(response);
// squid:S2259: making sure that we do not have a null value that would make a NullPointerException when used
if (responseJson == null) {
throw new StashClientException("The responseJson could not be extracted from the response !");
}
JsonArray errors = (JsonArray)responseJson.get("errors");
if (errors == null) {
throw new StashClientException("Error response did not contain an errors object '" + responseJson + "'");
}
List<String> errorParts = new ArrayList<>();
for (Object o : errors) {
try {
JsonObject error = (JsonObject)o;
errorParts.add((String)error.get("exceptionName") + ": " + (String)error.get("message"));
} catch (ClassCastException e) {
throw new StashClientException("Error response contained invalid error", e);
}
}
return String.join(", ", errorParts);
}
// We can't test this, as the manifest can only be loaded when deployed from a JAR-archive.
// During unit testing this is not the case
private static String getUserAgent(String sonarQubeVersion) {
PluginInfo info = StashPluginUtils.getPluginInfo();
String name;
String version;
name = version = "unknown";
if (info != null) {
name = info.getName();
version = info.getVersion();
}
return MessageFormat.format("SonarQube/{0} {1}/{2} {3}",
sonarQubeVersion == null ? "unknown" : sonarQubeVersion,
name,
version,
AsyncHttpClientConfigDefaults.defaultUserAgent());
}
AsyncHttpClient createHttpClient(String sonarQubeVersion) {
return new DefaultAsyncHttpClient(
new DefaultAsyncHttpClientConfig.Builder()
.setUserAgent(getUserAgent(sonarQubeVersion))
.setCompressionEnforced(true)
.setUseProxySelector(true)
.setUseProxyProperties(true)
.build()
);
}
}
================================================
FILE: src/main/java/org/sonar/plugins/stash/client/StashCredentials.java
================================================
package org.sonar.plugins.stash.client;
public class StashCredentials {
private final String login;
private final String password;
private final String userSlug;
public StashCredentials(String login, String password, String userSlug) {
this.login = login;
this.password = password;
this.userSlug = userSlug;
}
public String getLogin() {
return login;
}
public String getPassword() {
return password;
}
public String getUserSlug() {
return userSlug;
}
}
================================================
FILE: src/main/java/org/sonar/plugins/stash/exceptions/StashClientException.java
================================================
package org.sonar.plugins.stash.exceptions;
public class StashClientException extends StashException {
private static final long serialVersionUID = -9071818008552452736L;
public StashClientException(String message) {
super(message);
}
public StashClientException(Throwable cause) {
super(cause);
}
public StashClientException(String message, Throwable cause) {
super(message, cause);
}
}
================================================
FILE: src/main/java/org/sonar/plugins/stash/exceptions/StashConfigurationException.java
================================================
package org.sonar.plugins.stash.exceptions;
public class StashConfigurationException extends StashException {
private static final long serialVersionUID = 8423412434061160213L;
public StashConfigurationException(String message) {
super(message);
}
public StashConfigurationException(Throwable cause) {
super(cause);
}
public StashConfigurationException(String message, Throwable cause) {
super(message, cause);
}
}
================================================
FILE: src/main/java/org/sonar/plugins/stash/exceptions/StashException.java
================================================
package org.sonar.plugins.stash.exceptions;
public class StashException extends RuntimeException {
private static final long serialVersionUID = -4529815924057418321L;
public StashException(String message) {
super(message);
}
public StashException(Throwable cause) {
super(cause);
}
public StashException(String message, Throwable cause) {
super(message, cause);
}
}
================================================
FILE: src/main/java/org/sonar/plugins/stash/exceptions/StashReportExtractionException.java
================================================
package org.sonar.plugins.stash.exceptions;
public class StashReportExtractionException extends StashException {
private static final long serialVersionUID = -8105232303389875137L;
public StashReportExtractionException(String message) {
super(message);
}
public StashReportExtractionException(Throwable cause) {
super(cause);
}
public StashReportExtractionException(String message, Throwable cause) {
super(message, cause);
}
}
================================================
FILE: src/main/java/org/sonar/plugins/stash/issue/MarkdownPrinter.java
================================================
package org.sonar.plugins.stash.issue;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Multimaps;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Arrays;
import java.util.function.Function;
import java.util.stream.Collectors;
import com.google.common.collect.ArrayListMultimap;
import org.sonar.api.batch.postjob.issue.PostJobIssue;
import org.sonar.api.batch.rule.Severity;
import org.sonar.api.rule.RuleKey;
import org.sonar.plugins.stash.IssuePathResolver;
import static org.sonar.plugins.stash.StashPluginUtils.countIssuesBySeverity;
public final class MarkdownPrinter {
private static final String NEW_LINE = "\n";
private static final String CODING_RULES_RULE_KEY = "coding_rules#rule_key=";
private int issueThreshold;
private String sonarQubeURL;
private int includeFilesInOverview;
private IssuePathResolver issuePathResolver;
private Severity[] orderedSeverities;
public MarkdownPrinter(
int issueThreshold, String sonarQubeURL, int includeFilesInOverview, IssuePathResolver issuePathResolver
) {
this.issueThreshold = issueThreshold;
this.sonarQubeURL = sonarQubeURL;
this.includeFilesInOverview = includeFilesInOverview;
this.issuePathResolver = issuePathResolver;
orderedSeverities = Severity.values();
Arrays.sort(orderedSeverities, Comparator.reverseOrder());
}
public static String printSeverityMarkdown(org.sonar.api.batch.rule.Severity severity) {
StringBuilder sb = new StringBuilder();
sb.append("*").append(severity.name().toUpperCase()).append("*").append(" - ");
return sb.toString();
}
public static String printIssueNumberBySeverityMarkdown(Collection<PostJobIssue> report, Severity severity) {
StringBuilder sb = new StringBuilder();
long issueNumber = countIssuesBySeverity(report, severity);
sb.append("| ").append(severity).append(" | ").append(issueNumber).append(" |")
.append(NEW_LINE);
return sb.toString();
}
public String printIssueMarkdown(PostJobIssue issue) {
StringBuilder sb = new StringBuilder();
String message = issue.message();
if (message == null) {
return "No message";
}
sb.append(MarkdownPrinter.printSeverityMarkdown(issue.severity()))
.append(message)
.append(" [")
.append(link(issue.ruleKey().toString(),
sonarQubeURL + "/" + CODING_RULES_RULE_KEY + issue.ruleKey()))
.append("]");
return sb.toString();
}
public String printReportMarkdown(Collection<PostJobIssue> allIssues) {
StringBuilder sb = new StringBuilder("## SonarQube analysis Overview");
sb.append(NEW_LINE);
if (allIssues.isEmpty()) {
sb.append("### No new issues detected!");
sb.append(NEW_LINE).append(NEW_LINE);
} else {
int issueNumber = allIssues.size();
if (issueNumber >= issueThreshold) {
sb.append("### Too many issues detected ");
sb.append("(").append(issueNumber).append("/").append(issueThreshold).append(")");
sb.append(": Issues cannot be displayed in Diff view.").append(NEW_LINE).append(NEW_LINE);
}
sb.append("| Total New Issues | ").append(issueNumber).append(" |").append(NEW_LINE);
sb.append("|-----------------|------|").append(NEW_LINE);
for (Severity severity : orderedSeverities) {
sb.append(printIssueNumberBySeverityMarkdown(allIssues, severity));
}
ListMultimap<RuleKey, PostJobIssue> uniqueInformation = allIssues.stream().collect(Multimaps.toMultimap(PostJobIssue::ruleKey, Function.identity(), ArrayListMultimap::create));
List<Map.Entry<RuleKey, List<PostJobIssue>>> uniqueSortedInformation = Multimaps.asMap(uniqueInformation)
.entrySet().stream()
.sorted(bySeverity.reversed())
.collect(Collectors.toList());
sb.append(
formatTableList(uniqueSortedInformation));
}
return sb.toString();
}
private String formatTableList(List<Map.Entry<RuleKey, List<PostJobIssue>>> items) {
if (items.isEmpty()) {
return "";
}
String caption = "Issues list";
StringBuilder sb = new StringBuilder();
sb.append(NEW_LINE).append(NEW_LINE);
sb.append("| ").append(caption).append(" |").append(NEW_LINE);
sb.append("|");
sb.append(repeatString(caption.length() + 2, "-"));
sb.append("|");
sb.append(NEW_LINE);
for (Map.Entry<RuleKey, List<PostJobIssue>> item : items) {
List<PostJobIssue> issues = item.getValue();
sb.append("| ").append(printIssueMarkdown(issues.get(0))).append(" |").append(NEW_LINE);
if (this.includeFilesInOverview > 0) {
sb.append("| *Files: ").append(fileNameList(issues)).append("* |").append(NEW_LINE);
}
}
return sb.toString();
}
private String fileNameList(List<PostJobIssue> issues) {
List<String> names = new ArrayList<>();
issues.sort(issueFormatComparator);
for (PostJobIssue issue: issues.subList(0, Math.min(includeFilesInOverview, issues.size()))) {
String path = issuePathResolver.getIssuePath(issue);
Integer line = issue.line();
if (line == null) {
names.add(path);
} else {
names.add(String.format("%s:%s", path, line));
}
}
if (issues.size() > includeFilesInOverview) {
names.add("...");
}
return String.join(", ", names);
}
private String repeatString(int n, String s) {
return String.join("", Collections.nCopies(n, s));
}
private static String link(String title, String target) {
return "[" + title + "](" + target + ")";
}
private static Comparator<? super Entry<RuleKey, ? extends Collection<PostJobIssue>>> bySeverity = Comparator.comparing(
// List invariant is guaranteed by docs
e -> ((List<PostJobIssue>) e.getValue()).get(0).severity());
private Comparator<PostJobIssue> fileNameLength = Comparator
.comparing(i -> issuePathResolver.getIssuePath(i).length());
private Comparator<PostJobIssue> issueLine = Comparator
// -1 sorts before all issues with lines
.comparing(issue -> firstNonNull(issue.line(), -1));
private Comparator<PostJobIssue> fileNameLexical = Comparator
.comparing(i -> issuePathResolver.getIssuePath(i));
private Comparator<PostJobIssue> issueFormatComparator =
fileNameLength
.thenComparing(issueLine)
.thenComparing(fileNameLexical)
;
@SafeVarargs
private static <T> T firstNonNull(T... args) {
for (T t: args) {
if (t != null) {
return t;
}
}
throw new IllegalStateException("At least one of the arguments should have been non-null");
}
}
================================================
FILE: src/main/java/org/sonar/plugins/stash/issue/StashComment.java
================================================
package org.sonar.plugins.stash.issue;
import com.google.common.base.MoreObjects;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class StashComment {
private final long id;
private final String message;
private final StashUser author;
private final long version;
private long line;
private String path;
private List<StashTask> tasks;
public StashComment(long id, String message, String path, Long line, StashUser author, long version) {
this.id = id;
this.message = message;
this.path = path;
this.author = author;
this.version = version;
// Stash comment can be null if comment is global to all the file
if (line == null) {
this.line = 0;
} else {
this.line = line.longValue();
}
tasks = new ArrayList<>();
}
public long getId() {
return id;
}
public void setLine(long line) {
this.line = line;
}
public void setPath(String path) {
this.path = path;
}
public String getMessage() {
return message;
}
public String getPath() {
return path;
}
public long getLine() {
return line;
}
public StashUser getAuthor() {
return author;
}
public long getVersion() {
return version;
}
public List<StashTask> getTasks() {
return Collections.unmodifiableList(tasks);
}
public void addTask(StashTask task) {
tasks.add(task);
}
public boolean containsPermanentTasks() {
boolean result = false;
for (StashTask task : tasks) {
if (!task.isDeletable()) {
result = true;
break;
}
}
return result;
}
@Override
public boolean equals(Object object) {
boolean result = false;
if (object instanceof StashComment) {
StashComment stashComment = (StashComment)object;
result = this.getId() == stashComment.getId();
}
return result;
}
@Override
public int hashCode() {
return (int)this.getId();
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("id", id)
.add("message", message)
.add("author", author)
.add("version", version)
.add("line", line)
.add("path", path)
.add("tasks", tasks)
.toString();
}
}
================================================
FILE: src/main/java/org/sonar/plugins/stash/issue/StashCommentReport.java
================================================
package org.sonar.plugins.stash.issue;
import java.util.Objects;
import java.util.ArrayList;
import java.util.List;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.plugins.stash.StashPlugin.IssueType;
public class StashCommentReport {
private static final Logger LOGGER = Loggers.get(StashCommentReport.class);
private List<StashComment> comments;
public StashCommentReport() {
this.comments = new ArrayList<>();
}
public List<StashComment> getComments() {
return comments;
}
public void add(StashComment comment) {
comments.add(comment);
}
public void add(StashCommentReport report) {
for (StashComment comment : report.getComments()) {
comments.add(comment);
}
}
public boolean contains(String message, String path, long line) {
boolean result = false;
for (StashComment comment : comments) {
if (Objects.equals(comment.getMessage(), message)
&& Objects.equals(comment.getPath(), path)
&& comment.getLine() == line) {
result = true;
break;
}
}
return result;
}
public StashCommentReport applyDiffReport(StashDiffReport diffReport) {
for (StashComment comment : comments) {
StashDiff diff = diffReport.getDiffByComment(comment.getId());
if ((diff != null) && diff.getType() == IssueType.CONTEXT) {
// By default comment line, with type == CONTEXT, is set to FROM value.
// Set comment line to TO value to be compared with SonarQube issue.
long destination = diff.getDestination();
comment.setLine(destination);
LOGGER.debug("Update Stash comment \"{}\": set comment line to destination diff line ({})",
comment.getId(),
comment.getLine());
}
}
return this;
}
public int size() {
return comments.size();
}
}
================================================
FILE: src/main/java/org/sonar/plugins/stash/issue/StashDiff.java
================================================
package org.sonar.plugins.stash.issue;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.sonar.plugins.stash.StashPlugin.IssueType;
public class StashDiff {
private final IssueType type;
private final String path;
private final long source;
private final long destination;
private final List<StashComment> comments;
public StashDiff(IssueType type, String path, long source, long destination) {
this.type = type;
this.path = path;
this.source = source;
this.destination = destination;
this.comments = new ArrayList<>();
}
public void addComment(StashComment comment) {
this.comments.add(comment);
}
public String getPath() {
return path;
}
public long getSource() {
return source;
}
public long getDestination() {
return destination;
}
public IssueType getType() {
return type;
}
public List<StashComment> getComments() {
return Collections.unmodifiableList(comments);
}
public boolean containsComment(long commentId) {
return comments.stream().anyMatch(c -> c.getId() == commentId);
}
}
================================================
FILE: src/main/java/org/sonar/plugins/stash/issue/StashDiffReport.java
================================================
package org.sonar.plugins.stash.issue;
import com.google.common.collect.Range;
import java.util.Collections;
import java.util.Objects;
import java.util.ArrayList;
import java.util.List;
import org.sonar.plugins.stash.StashPlugin.IssueType;
/**
* This class is a representation of the Stash Diff view.
* <p>
* Purpose is to check if a SonarQube issue belongs to the Stash diff view before posting.
* Indeed, Stash Diff view displays only comments which belong to this view.
*/
public class StashDiffReport {
public static final int VICINITY_RANGE_NONE = 0;
private List<StashDiff> diffs;
public StashDiffReport() {
this.diffs = new ArrayList<>();
}
public List<StashDiff> getDiffs() {
return Collections.unmodifiableList(diffs);
}
public void add(StashDiff diff) {
diffs.add(diff);
}
public void add(StashDiffReport report) {
diffs.addAll(report.getDiffs());
}
private static boolean inVicinityOfChangedDiff(StashDiff diff, long destination, int range) {
if (range <= 0) {
return false;
}
long lower = Math.min(diff.getSource(), diff.getDestination());
long upper = Math.max(diff.getSource(), diff.getDestination());
return Range.closed(lower - range, upper + range).contains(destination);
}
private static boolean isChangedDiff(StashDiff diff, long destination) {
return diff.getDestination() == destination;
}
private static boolean lineIsChangedDiff(StashDiff diff) {
return !diff.getType().equals(IssueType.CONTEXT);
}
public IssueType getType(String path, long destination, int vicinityRange) {
boolean isInContextDiff = false;
for (StashDiff diff : diffs) {
if (Objects.equals(diff.getPath(), path)) {
// Line 0 never belongs to Stash Diff view.
// It is a global comment with a type set to CONTEXT.
if (destination == 0) {
return IssueType.CONTEXT;
} else if (!lineIsChangedDiff(diff)) {
// We only care about changed diff
continue;
} else if (isChangedDiff(diff, destination)) {
return diff.getType();
} else if (inVicinityOfChangedDiff(diff, destination, vicinityRange)) {
isInContextDiff = true;
}
}
}
return isInContextDiff ? IssueType.CONTEXT : null;
}
/**
* Depends on the type of the diff.
* If type == "CONTEXT", return the source line of the diff.
* If type == "ADDED", return the destination line of the diff.
*/
public long getLine(String path, long destination) {
for (StashDiff diff : diffs) {
if (Objects.equals(diff.getPath(), path) && (diff.getDestination() == destination)) {
if (diff.getType() == IssueType.CONTEXT) {
return diff.getSource();
} else {
return diff.getDestination();
}
}
}
return 0;
}
public StashDiff getDiffByComment(long commentId) {
for (StashDiff diff : diffs) {
if (diff.containsComment(commentId)) {
return diff;
}
}
return null;
}
/**
* Get all comments from the Stash differential report.
*/
public List<StashComment> getComments() {
List<StashComment> result = new ArrayList<>();
for (StashDiff diff : this.diffs) {
List<StashComment> comments = diff.getComments();
for (StashComment comment : comments) {
if (!result.contains(comment)) {
result.add(comment);
}
}
}
return result;
}
}
================================================
FILE: src/main/java/org/sonar/plugins/stash/issue/StashPullRequest.java
================================================
package org.sonar.plugins.stash.issue;
import org.sonar.plugins.stash.PullRequestRef;
import java.util.ArrayList;
import java.util.List;
public class StashPullRequest {
private final PullRequestRef ref;
private long version;
private final ArrayList<StashUser> reviewers;
public int getId() {
return ref.pullRequestId();
}
public String getProject() {
return ref.project();
}
public String getRepository() {
return ref.repository();
}
public StashPullRequest(PullRequestRef pr) {
this.ref = pr;
this.reviewers = new ArrayList<>();
}
public PullRequestRef getRef() {
return ref;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
public void addReviewer(StashUser reviewer) {
this.reviewers.add(reviewer);
}
public List<StashUser> getReviewers() {
return reviewers;
}
public StashUser getReviewer(String user) {
StashUser result = null;
for (StashUser stashReviewer : reviewers) {
if (user.equals(stashReviewer.getSlug())) {
result = stashReviewer;
break;
}
}
return result;
}
public boolean containsReviewer(StashUser reviewer) {
boolean result = false;
for (StashUser stashReviewer : reviewers) {
if (reviewer.getId() == stashReviewer.getId()) {
result = true;
break;
}
}
return result;
}
}
================================================
FILE: src/main/java/org/sonar/plugins/stash/issue/StashTask.java
================================================
package org.sonar.plugins.stash.issue;
public class StashTask {
private final Long id;
private final String text;
private final String state;
private final boolean deletable;
public StashTask(Long id, String text, String state, boolean deletable) {
this.id = id;
this.text = text;
this.state = state;
this.deletable = deletable;
}
public Long getId() {
return id;
}
public String getText() {
return text;
}
public String getState() {
return state;
}
public boolean isDeletable() {
return deletable;
}
}
================================================
FILE: src/main/java/org/sonar/plugins/stash/issue/StashUser.java
================================================
package org.sonar.plugins.stash.issue;
public class StashUser {
private final long id;
private final String name;
private final String slug;
private final String email;
public StashUser(long id, String name, String slug, String email) {
this.id = id;
this.name = name;
this.slug = slug;
this.email = email;
}
public long getId() {
return id;
}
public String getName() {
return name;
}
public String getSlug() {
return slug;
}
public String getEmail() {
return email;
}
}
================================================
FILE: src/main/java/org/sonar/plugins/stash/issue/collector/SonarQubeCollector.java
================================================
package org.sonar.plugins.stash.issue.collector;
import java.util.Set;
import static org.sonar.plugins.stash.StashPluginUtils.isProjectWide;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.sonar.api.batch.postjob.issue.PostJobIssue;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.plugins.stash.IssuePathResolver;
public final class SonarQubeCollector {
private static final Logger LOGGER = Loggers.get(SonarQubeCollector.class);
private SonarQubeCollector() {
// Hiding implicit public constructor with an explicit private one (squid:S1118)
}
/**
* Create issue report according to issue list generated during SonarQube
* analysis.
*/
public static List<PostJobIssue> extractIssueReport(
Iterable<PostJobIssue> issues, IssuePathResolver issuePathResolver,
boolean includeExistingIssues, Set<RuleKey> excludedRules) {
return StreamSupport.stream(
issues.spliterator(), false)
.filter(issue -> shouldIncludeIssue(
issue, issuePathResolver,
includeExistingIssues, excludedRules
))
.collect(Collectors.toList());
}
static boolean shouldIncludeIssue(
PostJobIssue issue, IssuePathResolver issuePathResolver,
boolean includeExistingIssues, Set<RuleKey> excludedRules
) {
if (!includeExistingIssues && !issue.isNew()) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Issue {} is not a new issue and so, not added to the report", issue.key());
}
return false;
}
if (excludedRules.contains(issue.ruleKey())) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Issue {} is ignored, not added to the report", issue.key());
}
return false;
}
String path = issuePathResolver.getIssuePath(issue);
if (!isProjectWide(issue) && path == null) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Issue {} is not linked to a file, not added to the report", issue.key());
}
return false;
}
return true;
}
}
================================================
FILE: src/main/java/org/sonar/plugins/stash/issue/collector/StashCollector.java
================================================
package org.sonar.plugins.stash.issue.collector;
import com.github.cliftonlabs.json_simple.JsonArray;
import com.github.cliftonlabs.json_simple.JsonObject;
import java.math.BigDecimal;
import org.sonar.plugins.stash.PullRequestRef;
import org.sonar.plugins.stash.StashPlugin.IssueType;
import org.sonar.plugins.stash.exceptions.StashReportExtractionException;
import org.sonar.plugins.stash.issue.StashComment;
import org.sonar.plugins.stash.issue.StashCommentReport;
import org.sonar.plugins.stash.issue.StashDiff;
import org.sonar.plugins.stash.issue.StashDiffReport;
import org.sonar.plugins.stash.issue.StashPullRequest;
import org.sonar.plugins.stash.issue.StashTask;
import org.sonar.plugins.stash.issue.StashUser;
public final class StashCollector {
private static final String AUTHOR = "author";
private static final String VERSION = "version";
private StashCollector() {
// Hiding implicit public constructor with an explicit private one (squid:S1118)
}
public static StashCommentReport extractComments(JsonObject jsonComments) {
StashCommentReport result = new StashCommentReport();
JsonArray jsonValues = (JsonArray)jsonComments.get("values");
if (jsonValues != null) {
for (Object obj : jsonValues.toArray()) {
JsonObject jsonComment = (JsonObject)obj;
StashComment comment = extractComment(jsonComment);
result.add(comment);
}
}
return result;
}
public static StashComment extractComment(JsonObject jsonComment, String path, Long line) {
long id = getLong(jsonComment, "id");
String message = (String)jsonComment.get("text");
long version = getLong(jsonComment, VERSION);
JsonObject jsonAuthor = (JsonObject)jsonComment.get(AUTHOR);
StashUser stashUser = extractUser(jsonAuthor);
return new StashComment(id, message, path, line, stashUser, version);
}
public static StashComment extractComment(JsonObject jsonComment) {
JsonObject jsonAnchor = (JsonObject)jsonComment.get("anchor");
if (jsonAnchor == null) {
throw new StashReportExtractionException("JSON Comment does not contain any \"anchor\" tag"
+ " to describe comment \"line\" and \"path\"");
}
String path = (String)jsonAnchor.get("path");
// can be null if comment is attached to the global file
Long line = getLong(jsonAnchor, "line");
return extractComment(jsonComment, path, line);
}
public static StashPullRequest extractPullRequest(PullRequestRef pr,
JsonObject jsonPullRequest) {
StashPullRequest result = new StashPullRequest(pr);
long version = getLong(jsonPullRequest, VERSION);
result.setVersion(version);
JsonArray jsonReviewers = (JsonArray)jsonPullRequest.get("reviewers");
if (jsonReviewers != null) {
for (Object objReviewer : jsonReviewers.toArray()) {
JsonObject jsonReviewer = (JsonObject)objReviewer;
JsonObject jsonUser = (JsonObject)jsonReviewer.get("user");
if (jsonUser != null) {
StashUser reviewer = extractUser(jsonUser);
result.addReviewer(reviewer);
}
}
}
return result;
}
public static StashUser extractUser(JsonObject jsonUser) {
long id = getLong(jsonUser, "id");
String name = (String)jsonUser.get("name");
String slug = (String)jsonUser.get("slug");
String email = (String)jsonUser.get("email");
return new StashUser(id, name, slug, email);
}
public static StashDiffReport extractDiffs(JsonObject jsonObject) {
StashDiffReport result = new StashDiffReport();
JsonArray jsonDiffs = (JsonArray)jsonObject.get("diffs");
if (jsonDiffs == null) {
return null;
}
// Let's call this for loop "objdiff_loop"
for (Object objDiff : jsonDiffs.toArray()) {
JsonObject jsonDiff = (JsonObject)objDiff;
// destination path in diff view
// if status of the file is deleted, destination == null
JsonObject destinationPath = (JsonObject)jsonDiff.get("destination");
if (destinationPath == null) {
continue; // Let's process the next item in "objdiff_loop"
}
String path = (String)destinationPath.get("toString");
JsonArray jsonHunks = (JsonArray)jsonDiff.get("hunks");
if (jsonHunks == null) {
continue; // Let's process the next item in "objdiff_loop"
}
// calling the extracted section to scan the jsonHunks & jsonDiff into usable diffs
result.add(parseHunksIntoDiffs(path, jsonHunks, jsonDiff));
// Extract File Comments: this kind of comment will be attached to line 0
JsonArray jsonLineComments = (JsonArray)jsonDiff.get("fileComments");
if (jsonLineComments == null) {
continue; // Let's process the next item in "objdiff_loop"
}
StashDiff initialDiff = new StashDiff(IssueType.CONTEXT, path, 0, 0);
// Let's call this for loop "objlinc_loop"
for (Object objLineComment : jsonLineComments.toArray()) {
JsonObject jsonLineComment = (JsonObject)objLineComment;
long lineCommentId = getLong(jsonLineComment, "id");
String lineCommentMessage = (String)jsonLineComment.get("text");
long lineCommentVersion = getLong(jsonLineComment, VERSION);
JsonObject objAuthor = (JsonObject)jsonLineComment.get(AUTHOR);
if (objAuthor == null) {
continue; // Let's process the next item in "objlinc_loop"
}
StashUser author = extractUser(objAuthor);
StashComment comment = new StashComment(lineCommentId, lineCommentMessage, path,
(long)0, author, lineCommentVersion);
initialDiff.addComment(comment);
}
result.add(initialDiff);
}
return result;
}
private static StashDiffReport parseHunksIntoDiffs(String path, JsonArray jsonHunks, JsonObject jsonDiff) {
StashDiffReport result = new StashDiffReport();
// Let's call this for loop "objhunk_loop"
for (Object objHunk : jsonHunks.toArray()) {
JsonObject jsonHunk = (JsonObject)objHunk;
JsonArray jsonSegments = (JsonArray)jsonHunk.get("segments");
if (jsonSegments == null) {
continue; // Let's process the next item in "objhunk_loop"
}
// Let's call this for loop "objsegm_loop"
for (Object objSegment : jsonSegments.toArray()) {
JsonObject jsonSegment = (JsonObject)objSegment;
// type of the diff in diff view
// We filter REMOVED type, like useless for SQ analysis
IssueType type = IssueType.valueOf((String)jsonSegment.get("type"));
//
JsonArray jsonLines = (JsonArray)jsonSegment.get("lines");
if (type == IssueType.REMOVED || jsonLines == null) {
continue; // Let's process the next item in "objsegm_loop"
}
// Let's call this for loop "objline_loop"
for (Object objLine : jsonLines.toArray()) {
JsonObject jsonLine = (JsonObject)objLine;
// destination line in diff view
long source = getLong(jsonLine, "source");
long destination = getLong(jsonLine,"destination");
StashDiff diff = new StashDiff(type, path, source, destination);
// Add comment attached to the current line
JsonArray jsonCommentIds = (JsonArray)jsonLine.get("commentIds");
// To keep this method depth under control (squid:S134), we outsourced the comments extraction
result.add(extractCommentsForDiff(diff, jsonDiff, jsonCommentIds));
}
}
}
return result;
}
private static StashDiff extractCommentsForDiff(StashDiff diff, JsonObject jsonDiff, JsonArray jsonCommentIds) {
// If there is no comments, we just return the diff as-is
if (jsonCommentIds == null) {
return diff;
}
// Let's call this for loop "objcomm_loop"
for (Object objCommentId : jsonCommentIds.toArray()) {
long commentId = getLong(objCommentId);
JsonArray jsonLineComments = (JsonArray)jsonDiff.get("lineComments");
if (jsonLineComments == null) {
continue; // Let's process the next item in "objcomm_loop"
}
// Let's call this for loop "objlico_loop"
for (Object objLineComment : jsonLineComments.toArray()) {
JsonObject jsonLineComment = (JsonObject)objLineComment;
long lineCommentId = getLong(jsonLineComment, "id");
if (lineCommentId != commentId) {
continue; // Let's process the next item in "objcomm_loop"
}
// Sending the JSON for processing into a nice comment
StashComment comment = buildCommentFromJSON(jsonLineComment, diff);
// If there is no valid comment in the JSON, we just consider the next element in "objlico_loop"
if (comment == null) {
continue;
}
// At this point, we can save the comment and add any relevant task to it
diff.addComment(comment);
// get the tasks linked to the current comment
updateCommentTasks(comment, (JsonArray)jsonLineComment.get("tasks"));
}
}
return diff;
}
private static StashComment buildCommentFromJSON(JsonObject jsonLineComment, StashDiff diff) {
long lineCommentId = getLong(jsonLineComment, "id");
String lineCommentMessage = (String)jsonLineComment.get("text");
long lineCommentVersion = getLong(jsonLineComment, VERSION);
JsonObject objAuthor = (JsonObject)jsonLineComment.get(AUTHOR);
if (objAuthor == null) {
return null;
}
StashUser author = extractUser(objAuthor);
return new StashComment(lineCommentId, lineCommentMessage, diff.getPath(),
diff.getDestination(), author, lineCommentVersion);
}
private static void updateCommentTasks(StashComment comment, JsonArray jsonTasks) {
// No need to fail on NullPointerException but we want to keep caller's complexity down
if (jsonTasks == null) {
return;
}
for (Object objTask : jsonTasks.toArray()) {
JsonObject jsonTask = (JsonObject)objTask;
comment.addTask(extractTask(jsonTask));
}
}
public static StashTask extractTask(JsonObject jsonTask) {
long taskId = getLong(jsonTask, "id");
String taskText = (String)jsonTask.get("text");
String taskState = (String)jsonTask.get("state");
boolean deletable = true;
JsonObject objPermission = (JsonObject)jsonTask.get("permittedOperations");
if (objPermission != null) {
deletable = (boolean)objPermission.get("deletable");
}
return new StashTask(taskId, taskText, taskState, deletable);
}
public static boolean isLastPage(JsonObject jsonObject) {
if (jsonObject.get("isLastPage") != null) {
return (Boolean)jsonObject.get("isLastPage");
}
return true;
}
public static long getNextPageStart(JsonObject jsonObject) {
if (jsonObject.get("nextPageStart") != null) {
return getLong(jsonObject,"nextPageStart");
}
return 0;
}
private static final Long getLong(JsonObject o, String name) {
return getLong(o.get(name));
}
private static final Long getLong(Object o) {
BigDecimal bd = (BigDecimal) o;
if (bd == null) {
return null;
}
return bd.longValue();
}
}
================================================
FILE: src/main/resources/org/sonar/plugins/stash/sonar-stash.properties
================================================
project.version=${project.version}
project.name=${project.name}
================================================
FILE: src/test/java/org/sonar/plugins/stash/CompleteITCase.java
================================================
package org.sonar.plugins.stash;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.sonar.plugins.stash.TestUtils.primeWireMock;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
import com.google.gson.JsonObject;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.sonar.plugins.stash.fixtures.DummyStashServer;
import org.sonar.plugins.stash.fixtures.MavenSonarFixtures;
import org.sonar.plugins.stash.fixtures.SonarQubeRule;
import org.sonar.plugins.stash.fixtures.SonarScanner;
import org.sonar.plugins.stash.fixtures.WireMockExtension;
import org.sonar.plugins.stash.issue.StashUser;
import org.sonar.plugins.stash.issue.collector.DiffReportSample;
public class CompleteITCase {
protected static SonarScanner sonarScanner;
protected File sourcesDir;
protected static final StashUser user = new StashUser(
// has to match data in fixture
1,"sonarqube", "sonarqube", "sq@example.com"
);
protected static final String stashPassword = "myPassword";
protected static final String stashProject = "PROJ";
protected static final String stashRepo = "REPO";
protected String projectKey;
protected String projectName;
protected static final int stashPullRequest = 42;
protected DummyStashServer stash;
protected static final PullRequestRef pr = new PullRequestRef.Builder()
.setProject(stashProject)
.setRepository(stashRepo)
.setPullRequestId(stashPullRequest)
.build();
@RegisterExtension
public WireMockExtension wireMock = new WireMockExtension(
DummyStashServer.extend(WireMockConfiguration.options().dynamicPort()), true);
@RegisterExtension
public static SonarQubeRule sonarqube = new SonarQubeRule(MavenSonarFixtures.getSonarqube(9000));
@BeforeAll
public static void setUpClass() throws Exception {
sonarqube.get().installPlugin(new File(System.getProperty("test.plugin.archive")));
sonarqube.start();
sonarScanner = MavenSonarFixtures.getSonarScanner();
}
@BeforeEach
public void setUp(TestInfo testInfo) throws Exception {
sourcesDir = Paths.get(
System.getProperty("test.sources.dir"),
testInfo.getTestMethod().get().getName()
).toFile();
projectKey = testInfo.getTestMethod().get().getName();
projectName = testInfo.getDisplayName();
sourcesDir.mkdirs();
sonarqube.get().createProject(projectKey, projectName);
primeWireMock(wireMock);
stash = new DummyStashServer(wireMock);
stash.mockUser(user);
}
@Test
public void testBasic() throws Exception {
stash.mockPrDiff(pr, DiffReportSample.baseReport);
stash.noCommentsFor(pr);
stash.expectCommentsUpdateFor(pr);
Properties props = new Properties();
props.put("sonar.sources", ".");
scan(true, true, props);
wireMock.verify(WireMock.getRequestedFor(WireMock.urlPathMatching(".*" + user.getSlug() + "$")));
wireMock.verify(WireMock.getRequestedFor(WireMock.urlPathMatching(".*diff$")));
wireMock.verify(WireMock.postRequestedFor(WireMock.urlPathMatching(".*comments$")));
// Making sure we find the proper agent info in a string like: User-Agent: SonarQube/4.5.7 Stash/1.2.0 AHC/1.0
wireMock.verify(WireMock.getRequestedFor(WireMock.anyUrl())
.withHeader("User-Agent", WireMock.matching("^(.*)Stash/[0-9.]+(.*)$")));
wireMock.verify(WireMock.getRequestedFor(WireMock.anyUrl())
.withHeader("User-Agent", WireMock.matching("^(.*)SonarQube/[0-9.]+(.*)$")));
}
@Test
public void testMultiModule() throws Exception {
installFile("sonar-project.properties");
targetLocation("module1/src/main/java").toFile().mkdirs();
targetLocation("module2/src/main/java").toFile().mkdirs();
scan(false, false, null);
installFile("module1/src/main/java/Foo.java");
installFile("module2/src/main/java/Bar.java");
stash.mockPrDiff(
pr,
"{\"fromHash\":\"bf19fb766666d80486aa81bc728a4e394ffa7ea8\",\"toHash\":\"79748088d6810b6e29eaa0319228fb8fecce14bb\",\"contextLines\":10,\"whitespace\":\"SHOW\",\"diffs\":[{\"source\":null,\"destination\":{\"components\":[\"module1\",\"src\",\"main\",\"java\",\"Foo.java\"],\"parent\":\"module1/src/main/java\",\"name\":\"Foo.java\",\"extension\":\"java\",\"toString\":\"module1/src/main/java/Foo.java\"},\"hunks\":[{\"sourceLine\":0,\"sourceSpan\":0,\"destinationLine\":1,\"destinationSpan\":5,\"segments\":[{\"type\":\"ADDED\",\"lines\":[{\"source\":0,\"destination\":1,\"line\":\"public class Foo {\",\"truncated\":false},{\"source\":0,\"destination\":2,\"line\":\" public static void main(String[] args) {\",\"truncated\":false},{\"source\":0,\"destination\":3,\"line\":\" System.out.println(\\\"Foo\\\");\",\"truncated\":false},{\"source\":0,\"destination\":4,\"line\":\" }\",\"truncated\":false},{\"source\":0,\"destination\":5,\"line\":\"}\",\"truncated\":false}],\"truncated\":false}],\"truncated\":false}],\"truncated\":false},{\"source\":null,\"destination\":{\"components\":[\"module2\",\"src\",\"main\",\"java\",\"Bar.java\"],\"parent\":\"module2/src/main/java\",\"name\":\"Bar.java\",\"extension\":\"java\",\"toString\":\"module2/src/main/java/Bar.java\"},\"hunks\":[{\"sourceLine\":0,\"sourceSpan\":0,\"destinationLine\":1,\"destinationSpan\":5,\"segments\":[{\"type\":\"ADDED\",\"lines\":[{\"source\":0,\"destination\":1,\"line\":\"public class Bar {\",\"truncated\":false},{\"source\":0,\"destination\":2,\"line\":\" public static void main(String[] args) {\",\"truncated\":false},{\"source\":0,\"destination\":3,\"line\":\" System.out.println(\\\"Bar\\\");\",\"truncated\":false},{\"source\":0,\"destination\":4,\"line\":\" }\",\"truncated\":false},{\"source\":0,\"destination\":5,\"line\":\"}\",\"truncated\":false}],\"truncated\":false}],\"truncated\":false}],\"truncated\":false}],\"truncated\":false}"
);
stash.noCommentsFor(pr);
stash.expectCommentsUpdateFor(pr);
scan(true, true, null);
List<JsonObject> comments = stash.getCreatedComments();
assertThat(comments).hasSize(5);
List<JsonObject> overviewComment = comments.stream().filter(c -> !c.has("anchor")).collect(Collectors.toList());
assertThat(overviewComment).hasSize(1);
assertThat(overviewComment.get(0).get("text").getAsString()).contains("SonarQube analysis Overview");
List<JsonObject> fileComments = comments.stream().filter(c -> c.has("anchor")).collect(Collectors.toList());
assertThat(fileComments).hasSize(4);
assertThat(fileComments).extracting(o -> o.get("anchor").getAsJsonObject().get("path").getAsString()).containsOnly(
"module1/src/main/java/Foo.java", "module2/src/main/java/Bar.java"
);
}
private Path targetLocation(String name) {
return sourcesDir.toPath().resolve(name);
}
private void installFile(String name) throws IOException {
Path target = targetLocation(name);
target.getParent().toFile().mkdirs();
Files.copy(
getClass().getClassLoader().getResourceAsStream("foo/" + name),
target
);
}
private String repoPath(String project, String repo, String... parts) {
return urlPath("rest", "api", "1.0", "projects", project, "repos", repo, urlPath(false, parts));
}
private String urlPath(String... parts) {
return urlPath(true, parts);
}
private String urlPath(boolean leading, String... parts) {
String prefix = "";
if (leading) {
prefix = "/";
}
return prefix + StringUtils.join(parts, '/');
}
protected void scan(boolean activateSonarStash, boolean issuesMode, Properties properties) throws Exception {
List<File> sources = new ArrayList<>();
sources.add(sourcesDir);
Properties extraProps = new Properties();
//extraProps.setProperty("sonar.analysis.mode", "incremental");
if (activateSonarStash) {
extraProps.setProperty("sonar.stash.url", "http://127.0.0.1:" + wireMock.port());
extraProps.setProperty("sonar.stash.login", user.getSlug());
extraProps.setProperty("sonar.stash.password", stashPassword);
extraProps.setProperty("sonar.stash.notification", "true");
extraProps.setProperty("sonar.stash.project", stashProject);
extraProps.setProperty("sonar.stash.repository", stashRepo);
extraProps.setProperty("sonar.stash.pullrequest.id", String.valueOf(stashPullRequest));
}
if (issuesMode) {
extraProps.put("sonar.analysis.mode", "issues");
}
extraProps.setProperty("sonar.log.level", "DEBUG");
if (properties != null) {
extraProps.putAll(properties);
}
sonarScanner.scan(sonarqube.get(), sourcesDir, projectKey, projectName, "0.0.0Final39", extraProps);
}
}
================================================
FILE: src/test/java/org/sonar/plugins/stash/DefaultIssue.java
================================================
package org.sonar.plugins.stash;
import org.sonar.api.batch.fs.InputComponent;
import org.sonar.api.batch.postjob.issue.PostJobIssue;
import org.sonar.api.batch.rule.Severity;
import org.sonar.api.rule.RuleKey;
public class DefaultIssue implements PostJobIssue {
private String key, componentKey, message;
private InputComponent inputComponent;
private boolean isNew;
private Severity severity;
private Integer line;
private RuleKey ruleKey;
public String key() {
return key;
}
public DefaultIssue setKey(String key) {
this.key = key;
return this;
}
public String componentKey() {
return componentKey;
}
public DefaultIssue setComponentKey(String componentKey) {
this.componentKey = componentKey;
return this;
}
public String message() {
return message;
}
public DefaultIssue setMessage(String message) {
this.message = message;
return this;
}
public InputComponent inputComponent() {
return inputComponent;
}
public DefaultIssue setInputComponent(InputComponent inputComponent) {
this.inputComponent = inputComponent;
return this;
}
@Override
public boolean isNew() {
return isNew;
}
public DefaultIssue setNew(boolean aNew) {
isNew = aNew;
return this;
}
public Severity severity() {
return severity;
}
public DefaultIssue setSeverity(Severity severity) {
this.severity = severity;
return this;
}
public RuleKey ruleKey() {
return ruleKey;
}
public DefaultIssue setRuleKey(RuleKey ruleKey) {
this.ruleKey = ruleKey;
return this;
}
public DefaultIssue setLine(Integer line) {
this.line = line;
return this;
}
@Override
public Integer line() {
return line;
}
}
================================================
FILE: src/test/java/org/sonar/plugins/stash/DummyStashProjectBuilder.java
================================================
package org.sonar.plugins.stash;
import java.io.File;
public class DummyStashProjectBuilder extends StashProjectBuilder {
private File projectBaseDir;
public DummyStashProjectBuilder(File projectBaseDir) {
this.projectBaseDir = projectBaseDir;
}
@Override
public File getProjectBaseDir() {
return projectBaseDir;
}
}
================================================
FILE: src/test/java/org/sonar/plugins/stash/JavaUtilLoggingCapture.java
================================================
package org.sonar.plugins.stash;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.logging.MemoryHandler;
public class JavaUtilLoggingCapture extends TestWatcherExtension {
private boolean gobbleOnSuccess;
private List<Handler> originalHandlers = new ArrayList<>();
private Level originalLevel;
private ConsoleHandler console = new ConsoleHandler();
private MemoryHandler spool = new MemoryHandler(console, 10000, Level.OFF);
private Logger logger;
public JavaUtilLoggingCapture(boolean gobbleOnSuccess) {
this.gobbleOnSuccess = gobbleOnSuccess;
logger = Logger.getGlobal();
Logger parent;
while ((parent = logger.getParent()) != null) {
logger = parent;
}
}
public JavaUtilLoggingCapture() {
this(true);
}
@Override
protected void succeeded() {
if (!gobbleOnSuccess) {
emit();
}
}
@Override
protected void failed() {
emit();
}
@Override
protected void finished() {
logger.removeHandler(spool);
for (Handler h : originalHandlers) {
logger.addHandler(h);
}
logger.setLevel(originalLevel);
}
@Override
protected void starting() {
for (Handler h : logger.getHandlers()) {
originalHandlers.add(h);
logger.removeHandler(h);
}
logger.addHandler(spool);
originalLevel = logger.getLevel();
logger.setLevel(Level.ALL);
}
private void emit() {
console.publish(new LogRecord(Level.SEVERE, "Replaying captured log entries"));
spool.push();
spool.flush();
}
}
================================================
FILE: src/test/java/org/sonar/plugins/stash/PeekableInputStreamTest.java
================================================
package org.sonar.plugins.stash;
import java.io.IOException;
import java.io.StringBufferInputStream;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
public class PeekableInputStreamTest {
@Test
public void testEmptyStream() throws IOException {
PeekableInputStream s = fromString("");
assertFalse(s.peek().isPresent());
assertFalse(s.peek().isPresent());
assertFalse(s.peek().isPresent());
}
@Test
public void testStream() throws IOException {
PeekableInputStream s = fromString("abcde");
assertEquals((Character)'a', s.peek().get());
assertEquals((Character)'a', s.peek().get());
assertEquals((Character)'a', s.peek().get());
}
private PeekableInputStream fromString(String s) {
return new PeekableInputStream(new StringBufferInputStream(s));
}
}
================================================
FILE: src/test/java/org/sonar/plugins/stash/PluginInfoTest.java
================================================
package org.sonar.plugins.stash;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
public class PluginInfoTest {
private static final String plugin_name = "sonar-stash";
private static final String plugin_vers = "1337.0";
@Test
public void testPluginInfoRef_ConstructorAndAccessors() {
PluginInfo PI = new PluginInfo(plugin_name, plugin_vers);
assertEquals(plugin_name, PI.getName());
assertEquals(plugin_vers, PI.getVersion());
}
}
================================================
FILE: src/test/java/org/sonar/plugins/stash/StashIssueReportingPostJobTest.java
================================================
package org.sonar.plugins.stash;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.sonar.api.batch.postjob.PostJobContext;
import org.sonar.api.batch.postjob.issue.PostJobIssue;
import org.sonar.api.batch.rule.Severity;
import org.sonar.api.platform.Server;
import org.sonar.plugins.stash.client.StashClient;
import org.sonar.plugins.stash.client.StashCredentials;
import org.sonar.plugins.stash.issue.StashDiffReport;
import org.sonar.plugins.stash.issue.StashUser;
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
public class StashIssueReportingPostJobTest extends StashTest {
private StashIssueReportingPostJob myJob;
@Mock
StashUser stashUser;
@Mock
StashRequestFacade stashRequestFacade;
@Mock
StashPluginConfiguration config;
@Mock
StashDiffReport diffReport;
@Mock
List<PostJobIssue> report;
@Mock
PostJobContext context;
@Mock
Server server;
private static final String STASH_PROJECT = "Project";
private static final String STASH_REPOSITORY = "Repository";
private static final int STASH_PULLREQUEST_ID = 1;
private static final String STASH_LOGIN = "login@email.com";
private static final String STASH_USER_SLUG = "login";
private static final String STASH_PASSWORD = "password";
private static final String STASH_URL = "http://url/to/stash";
private static final int STASH_TIMEOUT = 10000;
private static final int STASH_ISSUE_THRESHOLD = 100;
private static final PullRequestRef pr = PullRequestRef.builder()
.setProject(STASH_PROJECT)
.setRepository(STASH_REPOSITORY)
.setPullRequestId(STASH_PULLREQUEST_ID)
.build();
private static final String SONARQUBE_URL = "http://url/to/sonarqube";
@BeforeEach
public void setUp() throws Exception {
when(config.hasToNotifyStash()).thenReturn(true);
when(config.canApprovePullRequest()).thenReturn(false);
when(config.getStashURL()).thenReturn(STASH_URL);
when(config.getSonarQubeURL()).thenReturn(SONARQUBE_URL);
when(config.getStashTimeout()).thenReturn(STASH_TIMEOUT);
when(config.resetComments()).thenReturn(false);
when(config.hasToNotifyStash()).thenReturn(true);
when(config.includeAnalysisOverview()).thenReturn(Boolean.TRUE);
when(report.size()).thenReturn(10);
when(stashRequestFacade.extractIssueReport(eq(report)))
.thenReturn(report);
when(context.issues()).thenReturn(report);
when(stashRequestFacade.getIssueThreshold()).thenReturn(STASH_ISSUE_THRESHOLD);
when(stashRequestFacade.getStashProject()).thenReturn(STASH_PROJECT);
when(stashRequestFacade.getStashRepository()).thenReturn(STASH_REPOSITORY);
when(stashRequestFacade.getStashPullRequestId()).thenReturn(STASH_PULLREQUEST_ID);
when(stashRequestFacade.getCredentials()).thenReturn(new StashCredentials(STASH_LOGIN,
STASH_PASSWORD,
STASH_USER_SLUG));
when(stashRequestFacade
.getSonarQubeReviewer(anyString(), any(StashClient.class))).thenReturn(
stashUser);
when(stashRequestFacade.getPullRequestDiffReport(eq(pr), any()))
.thenReturn(diffReport);
when(stashRequestFacade.getIssueThreshold()).thenReturn(STASH_ISSUE_THRESHOLD);
when(stashRequestFacade.getStashURL()).thenReturn(STASH_URL);
when(stashRequestFacade.getPullRequest()).thenReturn(pr);
}
@Test
public void testExecuteOn() throws Exception {
myJob = new StashIssueReportingPostJob(config, stashRequestFacade, server);
myJob.execute(context);
verify(stashRequestFacade, times(0))
.resetComments(eq(pr), eq(diffReport), eq(stashUser), any());
verify(stashRequestFacade, times(1))
.postSonarQubeReport(eq(pr), eq(report), eq(diffReport), any());
verify(stashRequestFacade, times(1))
.postAnalysisOverview(eq(pr), eq(report),
any());
verify(stashRequestFacade, times(0))
.approvePullRequest(eq(pr), any());
verify(stashRequestFacade, times(0))
.resetPullRequestApproval(eq(pr), any());
}
@Test
public void testExecuteOnWithReachedThreshold() throws Exception {
when(stashRequestFacade.getIssueThreshold()).thenReturn(10);
List<PostJobIssue> report = mock(ArrayList.class);
when(report.size()).thenReturn(55);
when(stashRequestFacade.extractIssueReport(eq(report)))
.thenReturn(report);
myJob = new StashIssueReportingPostJob(config, stashRequestFacade, server);
myJob.execute(context);
verify(stashRequestFacade, times(0))
.resetComments(eq(pr), eq(diffReport), eq(stashUser), any());
verify(stashRequestFacade, times(0))
.postSonarQubeReport(eq(pr), eq(report), eq(diffReport), any());
verify(stashRequestFacade, times(1))
.postAnalysisOverview(eq(pr), any(Collection.class), any()
);
}
@Test
public void testExecuteOnWithNoPluginActivation() throws Exception {
when(config.hasToNotifyStash()).thenReturn(false);
myJob = new StashIssueReportingPostJob(config, stashRequestFacade, server);
myJob.execute(context);
verify(stashRequestFacade, times(0))
.resetComments(eq(pr), eq(diffReport), eq(stashUser), any());
verify(stashRequestFacade, times(0))
.postSonarQubeReport(eq(pr), eq(report), eq(diffReport), any());
verify(stashRequestFacade, times(0))
.postAnalysisOverview(eq(pr), eq(report), any());
verify(stashRequestFacade, times(0))
.approvePullRequest(eq(pr), any());
verify(stashRequestFacade, times(0))
.resetPullRequestApproval(eq(pr), any());
}
@Test
public void testExecuteOnWithNoStashUserDefined() throws Exception {
when(stashRequestFacade.getSonarQubeReviewer(anyString(), any())).thenReturn(null);
myJob = new StashIssueReportingPostJob(config, stashRequestFacade, server);
myJob.execute(context);
verify(stashRequestFacade, times(0))
.resetComments(eq(pr), eq(diffReport), eq(stashUser), any());
verify(stashRequestFacade, times(0))
.postSonarQubeReport(eq(pr), eq(report), eq(diffReport), any());
verify(stashRequestFacade, times(0))
.postAnalysisOverview(eq(pr), eq(report), any());
verify(stashRequestFacade, times(0))
.approvePullRequest(eq(pr), any());
verify(stashRequestFacade, times(0))
.resetPullRequestApproval(eq(pr), any());
}
@Test
public void testExecuteOnWithResetCommentActivated() throws Exception {
when(config.resetComments()).thenReturn(true);
myJob = new StashIssueReportingPostJob(config, stashRequestFacade, server);
myJob.execute(context);
verify(stashRequestFacade, times(1))
.resetComments(eq(pr), eq(diffReport), eq(stashUser), any());
verify(stashRequestFacade, times(1))
.postSonarQubeReport(eq(pr), eq(report), eq(diffReport), any());
verify(stashRequestFacade, times(1))
.postAnalysisOverview(eq(pr), eq(report), any());
}
@Test
public void testExecuteOnWithNoDiffReport() throws Exception {
diffReport = null;
when(stashRequestFacade.getPullRequestDiffReport(eq(pr), any()))
.thenReturn(diffReport);
myJob = new StashIssueReportingPostJob(config, stashRequestFacade, server);
myJob.execute(context);
verify(stashRequestFacade, times(0))
.resetComments(eq(pr), eq(diffReport), eq(stashUser), any());
verify(stashRequestFacade, times(0))
.postSonarQubeReport(eq(pr), eq(report), eq(diffReport), any());
verify(stashRequestFacade, times(0))
.postAnalysisOverview(eq(pr), eq(report), any());
verify(stashRequestFacade, times(0))
.approvePullRequest(eq(pr), any());
verify(stashRequestFacade, times(0))
.resetPullRequestApproval(eq(pr), any());
}
@Test
public void testShouldApprovePullRequest() {
PostJobIssue minorIssue = new DefaultIssue().setSeverity(Severity.MINOR);
PostJobIssue majorIssue = new DefaultIssue().setSeverity(Severity.MAJOR);
report = new ArrayList<>();
report.add(minorIssue);
report.add(majorIssue);
assertFalse(
StashIssueReportingPostJob.shouldApprovePullRequest(Optional.empty(), report)
);
report.clear();
assertTrue(
StashIssueReportingPostJob.shouldApprovePullRequest(Optional.empty(), report)
);
report.add(minorIssue);
assertTrue(
StashIssueReportingPostJob.shouldApprovePullRequest(Optional.of(Severity.MINOR), report)
);
report.add(majorIssue);
assertFalse(
StashIssueReportingPostJob.shouldApprovePullRequest(Optional.of(Severity.MINOR), report)
);
}
}
================================================
FILE: src/test/java/org/sonar/plugins/stash/StashPluginConfigurationTest.java
================================================
package org.sonar.plugins.stash;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import org.sonar.api.CoreProperties;
import org.sonar.api.batch.rule.Severity;
import org.sonar.api.config.Settings;
import org.sonar.api.config.internal.MapSettings;
public class StashPluginConfigurationTest {
@Test
public void testStashPluginConfiguration_ConstructorAndAccessors() {
Integer SPRI = 1337;
Settings settings = new MapSettings();
settings.setProperty(StashPlugin.STASH_NOTIFICATION, true);
settings.setProperty(StashPlugin.STASH_PROJECT, "take-over-the-world");
settings.setProperty(StashPlugin.STASH_REPOSITORY, "death-ray");
settings.setProperty(StashPlugin.STASH_PULL_REQUEST_ID, SPRI);
settings.setProperty(StashPlugin.STASH_URL, "https://stash");
settings.setProperty(StashPlugin.STASH_LOGIN, "me");
settings.setProperty(StashPlugin.STASH_USER_SLUG, "mini.me");
settings.setProperty(StashPlugin.STASH_PASSWORD, "unsecure");
settings.setProperty(StashPlugin.STASH_PASSWORD_ENVIRONMENT_VARIABLE, "you-should-not");
settings.setProperty(CoreProperties.LOGIN, "him");
settings.setProperty(CoreProperties.PASSWORD, "notsafe");
settings.setProperty(StashPlugin.STASH_ISSUE_THRESHOLD, 42000);
settings.setProperty(StashPlugin.STASH_ISSUE_SEVERITY_THRESHOLD, "MINOR");
settings.setProperty(StashPlugin.STASH_TIMEOUT, 42);
settings.setProperty(StashPlugin.STASH_REVIEWER_APPROVAL, true);
settings.setProperty(StashPlugin.STASH_RESET_COMMENTS, false);
settings.setProperty(StashPlugin.STASH_TASK_SEVERITY_THRESHOLD, "MINOR");
settings.setProperty(StashPlugin.STASH_INCLUDE_ANALYSIS_OVERVIEW, true);
//Optional getRepositoryRoot() ???
StashPluginConfiguration SPC = new StashPluginConfiguration(settings, null);
assertTrue(SPC.hasToNotifyStash());
assertEquals("take-over-the-world", SPC.getStashProject());
assertEquals("death-ray", SPC.getStashRepository());
assertEquals(SPRI, SPC.getPullRequestId());
assertEquals("https://stash", SPC.getStashURL());
assertEquals("me", SPC.getStashLogin());
assertEquals("mini.me", SPC.getStashUserSlug());
assertEquals("unsecure", SPC.getStashPassword());
assertEquals("you-should-not", SPC.getStashPasswordEnvironmentVariable());
assertEquals("him", SPC.getSonarQubeLogin());
assertEquals("notsafe", SPC.getSonarQubePassword());
assertEquals(42000, SPC.getIssueThreshold());
assertEquals(Severity.MINOR, SPC.getIssueSeverityThreshold());
assertEquals(42, SPC.getStashTimeout());
assertTrue(SPC.canApprovePullRequest());
assertFalse(SPC.resetComments());
assertEquals(Optional.of(Severity.MINOR), SPC.getTaskIssueSeverityThreshold());
assertTrue(SPC.includeAnalysisOverview());
//assertEquals(, SPC.getRepositoryRoot());
settings.setProperty(StashPlugin.STASH_TASK_SEVERITY_THRESHOLD, "NONE");
assertEquals(Optional.empty(), SPC.getTaskIssueSeverityThreshold());
}
}
================================================
FILE: src/test/java/org/sonar/plugins/stash/StashPluginUtilsTest.java
================================================
package org.sonar.plugins.stash;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.sonar.plugins.stash.StashPluginUtils.formatPercentage;
import static org.sonar.plugins.stash.StashPluginUtils.roundedPercentageGreaterThan;
public class StashPluginUtilsTest {
@Test
public void testFormatPercentage() {
assertEquals("10.9", formatPercentage(10.90));
assertEquals("10.9", formatPercentage(10.94));
assertEquals("11.0", formatPercentage(10.96));
assertEquals("11.0", formatPercentage(11.0));
assertEquals("31.3", formatPercentage(31.25));
assertEquals("50.3", formatPercentage(50.29));
// This test can fail with 50.2 instead of 50.3 if run with an older version of the JDK 8
// (failed with v25 & worked with v131)
}
@Test
public void testRoundedPercentageGreaterThan() {
assertTrue(roundedPercentageGreaterThan(0.1, 0.0));
assertTrue(roundedPercentageGreaterThan(0.2, 0.1));
assertTrue(roundedPercentageGreaterThan(1, 0.1));
assertTrue(roundedPercentageGreaterThan(1.1, 0.1));
assertFalse(roundedPercentageGreaterThan(0.0, 0.1));
assertFalse(roundedPercentageGreaterThan(0.1, 0.2));
assertFalse(roundedPercentageGreaterThan(1.1, 1.2));
assertFalse(roundedPercentageGreaterThan(1.01, 1.00));
assertFalse(roundedPercentageGreaterThan(1.04, 1.00));
assertTrue(roundedPercentageGreaterThan(1.05, 1.00));
}
}
================================================
FILE: src/test/java/org/sonar/plugins/stash/StashRequestFacadeTest.java
================================================
package org.sonar.plugins.stash;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.postjob.issue.PostJobIssue;
import org.sonar.api.batch.rule.Severity;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.utils.System2;
import org.sonar.plugins.stash.StashPlugin.IssueType;
import org.sonar.plugins.stash.client.StashClient;
import org.sonar.plugins.stash.client.StashCredentials;
import org.sonar.plugins.stash.exceptions.StashClientException;
import org.sonar.plugins.stash.exceptions.StashConfigurationException;
import org.sonar.plugins.stash.fixtures.DummyIssuePathResolver;
import org.sonar.plugins.stash.issue.MarkdownPrinter;
import org.sonar.plugins.stash.issue.StashComment;
import org.sonar.plugins.stash.issue.StashCommentReport;
import org.sonar.plugins.stash.issue.StashDiffReport;
import org.sonar.plugins.stash.issue.StashPullRequest;
import org.sonar.plugins.stash.issue.StashTask;
import org.sonar.plugins.stash.issue.StashUser;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyCollection;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.sonar.plugins.stash.TestUtils.inputFile;
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
public class StashRequestFacadeTest extends StashTest {
StashRequestFacade myFacade;
@Mock
StashPluginConfiguration config;
@Mock
StashClient stashClient;
@Mock
StashDiffReport diffReport;
@Mock
StashCommentReport stashCommentsReport1;
@Mock
StashCommentReport stashCommentsReport2;
@Mock
StashComment comment1;
@Mock
StashComment comment2;
@Mock
StashComment comment3;
@Mock
StashUser stashUser;
@Mock
System2 system;
String stashCommentMessage1;
String stashCommentMessage2;
String stashCommentMessage3;
List<PostJobIssue> report;
StashProjectBuilder spr;
private static final String STASH_PROJECT = "Project";
private static final String STASH_REPOSITORY = "Repository";
private static final int STASH_PULLREQUEST_ID = 1;
private static final PullRequestRef pr = PullRequestRef.builder()
.setProject(STASH_PROJECT)
.setRepository(STASH_REPOSITORY)
.setPullRequestId(STASH_PULLREQUEST_ID)
.build();
private static final IssueType STASH_DIFF_TYPE = IssueType.CONTEXT;
private static final String STASH_USER = "SonarQube";
private static final String STASH_URL = "http://stash/url";
private static final String SONARQUBE_URL = "http://sonar/url";
private static final String FILE_PATH_1 = "path/to/file1";
private static final String FILE_PATH_2 = "path/to/file2";
@BeforeEach
public void setUp() throws Exception {
config = mock(StashPluginConfiguration.class);
when(config.getTaskIssueSeverityThreshold()).thenReturn(Optional.empty());
when(config.getIssueSeverityThreshold()).thenReturn(Severity.INFO);
when(config.getSonarQubeURL()).thenReturn(SONARQUBE_URL);
when(config.getStashURL()).thenReturn(STASH_URL);
when(config.getStashProject()).thenReturn(STASH_PROJECT);
when(config.getStashRepository()).thenReturn(STASH_REPOSITORY);
when(config.getRepositoryRoot()).thenReturn(Optional.empty());
when(system.envVariable(any())).thenReturn(null);
spr = new DummyStashProjectBuilder(new File("/basedir"));
StashRequestFacade facade = new StashRequestFacade(config, spr, system);
myFacade = spy(facade);
stashClient = mock(StashClient.class);
diffReport = mock(StashDiffReport.class);
when(diffReport.getType(anyString(), anyLong(), anyInt())).thenReturn(STASH_DIFF_TYPE);
when(diffReport.getLine(FILE_PATH_1, 1)).thenReturn((long)1);
when(diffReport.getLine(FILE_PATH_1, 2)).thenReturn((long)2);
when(diffReport.getLine(FILE_PATH_2, 1)).thenReturn((long)1);
stashUser = mock(StashUser.class);
when(stashUser.getId()).thenReturn((long) 1234);
MarkdownPrinter printer = new MarkdownPrinter(100, SONARQUBE_URL, 0, new DummyIssuePathResolver());
report = new ArrayList<PostJobIssue>();
Path moduleBaseDir = FileSystems.getDefault().getPath("some", "dir");
PostJobIssue issue1 = new DefaultIssue().setKey("key1")
.setSeverity(Severity.CRITICAL)
.setMessage("message1")
.setRuleKey(RuleKey.of("foo", "rule1"))
.setInputComponent(inputFile("module1", moduleBaseDir, "/basedir/" + FILE_PATH_1))
.setLine(1);
stashCommentMessage1 = printer.printIssueMarkdown(issue1);
report.add(issue1);
PostJobIssue issue2 = new DefaultIssue().setKey("key2")
.setSeverity(Severity.MAJOR)
.setMessage("message2")
.setRuleKey(RuleKey.of("foo", "rule2"))
.setInputComponent(inputFile("module2", moduleBaseDir, "/basedir/" + FILE_PATH_1))
.setLine(2);
stashCommentMessage2 = printer.printIssueMarkdown(issue2);
report.add(issue2);
PostJobIssue issue3 = new DefaultIssue().setKey("key3")
.setSeverity(Severity.INFO)
.setMessage("message3")
.setRuleKey(RuleKey.of("foo", "rule3"))
.setInputComponent(inputFile("module3", moduleBaseDir, "/basedir/" + FILE_PATH_2))
.setLine(1);
stashCommentMessage3 = printer.printIssueMarkdown(issue3);
report.add(issue3);
StashTask task1 = mock(StashTask.class);
when(task1.getId()).thenReturn((long)1111);
List<StashTask> taskList1 = new ArrayList<>();
taskList1.add(task1);
comment1 = mock(StashComment.class);
when(comment1.getId()).thenReturn((long)1111);
when(comment1.getAuthor()).thenReturn(stashUser);
when(stashClient.postCommentLineOnPullRequest(pr,
stashCommentMessage1,
FILE_PATH_1,
1,
STASH_DIFF_TYPE)).thenReturn(comment1);
when(comment1.getTasks()).thenReturn(taskList1);
when(comment1.containsPermanentTasks()).thenReturn(false);
StashTask task2 = mock(StashTask.class);
when(task1.getId()).thenReturn((long)2222);
List<StashTask> taskList2 = new ArrayList<>();
taskList2.add(task2);
comment2 = mock(StashComment.class);
when(comment2.getId()).thenReturn((long)2222);
when(comment2.getAuthor()).thenReturn(stashUser);
when(stashClient.postCommentLineOnPullRequest(pr,
stashCommentMessage2,
FILE_PATH_1,
2,
STASH_DIFF_TYPE)).thenReturn(comment2);
when(comment2.getTasks()).thenReturn(taskList2);
when(comment2.containsPermanentTasks()).thenReturn(false);
StashTask task3 = mock(StashTask.class);
when(task3.getId()).thenReturn((long)3333);
List<StashTask> taskList3 = new ArrayList<>();
taskList3.add(task3);
comment3 = mock(StashComment.class);
when(comment3.getId()).thenReturn((long)3333);
when(comment3.getAuthor()).thenReturn(stashUser);
when(stashClient.postCommentLineOnPullRequest(pr,
stashCommentMessage3,
FILE_PATH_2,
1,
STASH_DIFF_TYPE)).thenReturn(comment3);
when(comment3.getTasks()).thenReturn(taskList3);
when(comment3.containsPermanentTasks()).thenReturn(false);
ArrayList<StashComment> comments = new ArrayList<>();
comments.add(comment1);
comments.add(comment2);
comments.add(comment3);
when(diffReport.getComments()).thenReturn(comments);
stashCommentsReport1 = mock(StashCommentReport.class);
when(stashCommentsReport1.getComments()).thenReturn(comments);
when(stashCommentsReport1.applyDiffReport(diffReport)).thenReturn(stashCommentsReport1);
when(stashClient.getPullRequestComments(pr, "path/to/file1")).thenReturn(stashCommentsReport1);
stashCommentsReport2 = mock(StashCommentReport.class);
when(stashCommentsReport1.getComments()).thenReturn(comments);
when(stashCommentsReport2.applyDiffReport(diffReport)).thenReturn(stashCommentsReport2);
when(stashClient.getPullRequestComments(pr, "path/to/file2")).thenReturn(stashCommentsReport2);
doNothing().when(stashClient).deletePullRequestComment(eq(pr), any(StashComment.class));
doNothing().when(stashClient).deleteTaskOnComment(any(StashTask.class));
}
@Test
public void testGetCredentials() throws StashConfigurationException {
when(config.getStashLogin()).thenReturn("login");
when(config.getStashPassword()).thenReturn("password");
StashCredentials credentials = myFacade.getCredentials();
assertEquals("login", credentials.getLogin());
assertEquals("password", credentials.getPassword());
}
@Test
public void testGetNoCredentials() throws StashConfigurationException {
when(config.getStashLogin()).thenReturn(null);
when(config.getStashPassword()).thenReturn(null);
StashCredentials credentials = myFacade.getCredentials();
assertNull(credentials.getLogin());
assertNull(credentials.getPassword());
}
@Test
public void testGetPasswordFromEnvironment() throws StashConfigurationException {
when(config.getStashLogin()).thenReturn("login");
when(config.getStashPasswordEnvironmentVariable()).thenReturn("SONAR_STASH_PASSWORD");
when(system.envVariable("SONAR_STASH_PASSWORD")).thenReturn("envPassword");
StashCredentials credentials = myFacade.getCredentials();
assertEquals("envPassword", credentials.getPassword());
when(config.getStashPassword()).thenReturn("password");
credentials = myFacade.getCredentials();
assertEquals("envPassword", credentials.getPassword());
}
@Test
public void testGetPasswordFromUnconfiguredEnvironment() throws StashConfigurationException {
when(config.getStashLogin()).thenReturn("login");
when(config.getStashPasswordEnvironmentVariable()).thenReturn("SONAR_STASH_PASSWORD");
assertThrows(StashConfigurationException.class, () ->
myFacade.getCredentials()
);
}
@Test
public void testGetIssueThreshold() throws StashConfigurationException {
when(config.getIssueThreshold()).thenReturn(1);
assertEquals(1, myFacade.getIssueThreshold());
}
@Test
public void testGetIssueThresholdThrowsException() throws StashConfigurationException {
when(config.getIssueThreshold()).thenThrow(new NumberFormatException());
assertThrows(StashConfigurationException.class, () ->
myFacade.getIssueThreshold()
);
}
@Test
public void testGetStashURL() throws StashConfigurationException {
when(config.getStashURL()).thenReturn("http://url");
assertEquals("http://url", myFacade.getStashURL());
when(config.getStashURL()).thenReturn("http://url/");
assertEquals("http://url", myFacade.getStashURL());
}
@Test
public void testGetStashURLThrowsException() throws StashConfigurationException {
when(config.getStashURL()).thenReturn(null);
assertThrows(StashConfigurationException.class, () ->
myFacade.getStashURL()
);
}
@Test
public void testGetStashProject() throws StashConfigurationException {
when(config.getStashProject()).thenReturn("project");
assertEquals("project", myFacade.getStashProject());
}
@Test
public void testGetStashProjectThrowsException() throws StashConfigurationException {
when(config.getStashProject()).thenReturn(null);
assertThrows(StashConfigurationException.class, () ->
myFacade.getStashProject()
);
}
@Test
public void testGetStashRepository() throws StashConfigurationException {
when(config.getStashRepository()).thenReturn("repository");
assertEquals("repository", myFacade.getStashRepository());
}
@Test
public void testGetStashRepositoryThrowsException() throws StashConfigurationException {
when(config.getStashRepository()).thenReturn(null);
assertThrows(StashConfigurationException.class, () ->
myFacade.getStashRepository()
);
}
@Test
public void testGetStashPullRequestId() throws StashConfigurationException {
when(config.getPullRequestId()).thenReturn(12345);
assertEquals(12345, myFacade.getStashPullRequestId());
}
@Test
public void testGetStashPullRequestIdThrowsException() throws StashConfigurationException {
when(config.getPullRequestId()).thenReturn(null);
assertThrows(StashConfigurationException.class, () ->
myFacade.getStashPullRequestId()
);
}
@Test
public void testPostCommentPerIssue() throws Exception {
when(stashCommentsReport1.contains(stashCommentMessage1, FILE_PATH_1, 1)).thenReturn(true);
when(stashCommentsReport1.contains(stashCommentMessage2, FILE_PATH_1, 2)).thenReturn(false);
when(stashCommentsReport2.contains(stashCommentMessage3, FILE_PATH_2, 1)).thenReturn(false);
myFacade.postCommentPerIssue(pr, report, diffReport, stashClient);
verify(stashClient, times(0)).postCommentLineOnPullRequest(pr,
stashCommentMessage1,
FILE_PATH_1,
1,
STASH_DIFF_TYPE);
verify(stashClient, times(1)).postCommentLineOnPullRequest(pr,
stashCommentMessage2,
"path/to/file1",
2,
STASH_DIFF_TYPE);
verify(stashClient, times(1)).postCommentLineOnPullRequest(pr,
stashCommentMessage3,
"path/to/file2",
1,
STASH_DIFF_TYPE);
}
@Test
public void testPostCommentPerIssueWithNoStashCommentAlreadyPushed() throws Exception {
when(stashCommentsReport1.contains(stashCommentMessage1, FILE_PATH_1, 1)).thenReturn(true);
when(stashCommentsReport1.contains(stashCommentMessage2, FILE_PATH_1, 2)).thenReturn(true);
when(stashCommentsReport2.contains(stashCommentMessage3, FILE_PATH_2, 1)).thenReturn(true);
myFacade.postCommentPerIssue(pr, report, diffReport, stashClient);
verify(stashClient, times(0)).postCommentLineOnPullRequest(pr,
stashCommentMessage1,
FILE_PATH_1,
1,
STASH_DIFF_TYPE);
verify(stashClient, times(0)).postCommentLineOnPullRequest(pr,
stashCommentMessage2,
FILE_PATH_1,
2,
STASH_DIFF_TYPE);
verify(stashClient, times(0)).postCommentLineOnPullRequest(pr,
stashCommentMessage3,
FILE_PATH_2,
1,
STASH_DIFF_TYPE);
}
@Test
public void testPostCommentPerIssueIgnoresLowerSeverityIssues() throws Exception {
when(config.getIssueSeverityThreshold()).thenReturn(Severity.MAJOR);
when(stashCommentsReport1.contains(stashCommentMessage1, FILE_PATH_1, 1)).thenReturn(true);
when(stashCommentsReport1.contains(stashCommentMessage2, FILE_PATH_1, 2)).thenReturn(false);
when(stashCommentsReport2.contains(stashCommentMessage3, FILE_PATH_2, 1)).thenReturn(false);
myFacade.postCommentPerIssue(pr, report, diffReport, stashClient);
verify(stashClient, times(0)).postCommentLineOnPullRequest(pr,
stashCommentMessage1,
FILE_PATH_1,
1,
STASH_DIFF_TYPE);
verify(stashClient, times(1)).postCommentLineOnPullRequest(pr,
stashCommentMessage2,
"path/to/file1",
2,
STASH_DIFF_TYPE);
verify(stashClient, times(0)).postCommentLineOnPullRequest(pr,
stashCommentMessage3,
"path/to/file2",
1,
STASH_DIFF_TYPE);
}
@Test
public void testPostSonarQubeReport() throws StashClientException, StashConfigurationException {
myFacade.postSonarQubeReport(pr, report, diffReport, stashClient);
verify(myFacade, times(1)).postCommentPerIssue(eq(pr),
anyCollection(),
eq(diffReport),
eq(stashClient));
}
@Test
public void testPostTaskOnComment() throws Exception {
when(config.getTaskIssueSeverityThreshold()).thenReturn(Optional.of(Severity.INFO));
myFacade.postSonarQubeReport(pr, report, diffReport, stashClient);
verify(stashClient, times(1)).postCommentLineOnPullRequest(pr,
stashCommentMessage1,
FILE_PATH_1,
1,
STASH_DIFF_TYPE);
verify(stashClient, times(1)).postCommentLineOnPullRequest(pr,
stashCommentMessage2,
FILE_PATH_1,
2,
STASH_DIFF_TYPE);
verify(stashClient, times(1)).postCommentLineOnPullRequest(pr,
stashCommentMessage3,
FILE_PATH_2,
1,
STASH_DIFF_TYPE);
verify(stashClient, times(1)).postTaskOnComment("message3", (long)3333);
verify(stashClient, times(1)).postTaskOnComment("message2", (long)2222);
verify(stashClient, times(1)).postTaskOnComment("message1", (long)1111);
}
@Test
public void testPostTaskOnCommentWithRestrictedLevel() throws Exception {
when(config.getTaskIssueSeverityThreshold()).thenReturn(Optional.of(Severity.MAJOR));
myFacade.postSonarQubeReport(pr, report, diffReport, stashClient);
verify(stashClient, times(1)).postCommentLineOnPullRequest(pr,
stashCommentMessage1,
FILE_PATH_1,
1,
STASH_DIFF_TYPE);
verify(stashClient, times(1)).postCommentLineOnPullRequest(pr,
stashCommentMessage2,
FILE_PATH_1,
2,
STASH_DIFF_TYPE);
verify(stashClient, times(1)).postCommentLineOnPullRequest(pr,
stashCommentMessage3,
FILE_PATH_2,
1,
STASH_DIFF_TYPE);
verify(stashClient, times(0)).postTaskOnComment("message3", (long)3333);
verify(stashClient, times(1)).postTaskOnComment("message2", (long)2222);
verify(stashClient, times(1)).postTaskOnComment("message1", (long)1111);
}
@Test
public void testPostTaskOnCommentWithNotPresentLevel() throws Exception {
when(config.getTaskIssueSeverityThreshold()).thenReturn(Optional.of(Severity.BLOCKER));
myFacade.postSonarQubeReport(pr, report, diffReport, stashClient);
verify(stashClient, times(1)).postCommentLineOnPullRequest(pr,
stashCommentMessage1,
FILE_PATH_1,
1,
STASH_DIFF_TYPE);
verify(stashClient, times(1)).postCommentLineOnPullRequest(pr,
stashCommentMessage2,
FILE_PATH_1,
2,
STASH_DIFF_TYPE);
verify(stashClient, times(1)).postCommentLineOnPullRequest(pr,
stashCommentMessage3,
FILE_PATH_2,
1,
STASH_DIFF_TYPE);
verify(stashClient, times(0)).postTaskOnComment("message3", (long)3333);
verify(stashClient, times(0)).postTaskOnComment("message2", (long)2222);
verify(stashClient, times(0)).postTaskOnComment("message1", (long)1111);
}
@Test
public void testPostTaskOnCommentWithSeverityNone() throws Exception {
when(config.getTaskIssueSeverityThreshold()).thenReturn(Optional.empty());
myFacade.postSonarQubeReport(pr, report, diffReport, stashClient);
verify(stashClient, times(1)).postCommentLineOnPullRequest(pr,
stashCommentMessage1,
FILE_PATH_1,
1,
STASH_DIFF_TYPE);
verify(stashClient, times(1)).postCommentLineOnPullRequest(pr,
stashCommentMessage2,
FILE_PATH_1,
2,
STASH_DIFF_TYPE);
verify(stashClient, times(1)).postCommentLineOnPullRequest(pr,
stashCommentMessage3,
FILE_PATH_2,
1,
STASH_DIFF_TYPE);
verify(stashClient, times(0)).postTaskOnComment("message3", (long)3333);
verify(stashClient, times(0)).postTaskOnComment("message2", (long)2222);
verify(stashClient, times(0)).postTaskOnComment("message1", (long)1111);
}
@Test
public void testPostSonarQubeReportWithNoType() throws Exception {
when(stashCommentsReport1.contains(stashCommentMessage1, FILE_PATH_1, 1)).thenReturn(false);
when(stashCommentsReport1.contains(stashCommentMessage2, FILE_PATH_1, 2)).thenReturn(false);
when(stashCommentsReport2.contains(stashCommentMessage3, FILE_PATH_2, 1)).thenReturn(false);
when(diffReport.getType(FILE_PATH_1, 1, config.issueVicinityRange())).thenReturn(null);
when(diffReport.getType(FILE_PATH_1, 2, config.issueVicinityRange())).thenReturn(STASH_DIFF_TYPE);
when(diffReport.getType(FILE_PATH_2, 1, config.issueVicinityRange())).thenReturn(null);
myFacade.postSonarQubeReport(pr, report, diffReport, stashClient);
verify(stashClient, times(0)).postCommentLineOnPullRequest(pr,
stashCommentMessage1,
FILE_PATH_1,
1,
STASH_DIFF_TYPE);
verify(stashClient, times(1)).postCommentLineOnPullRequest(pr,
stashCommentMessage2,
FILE_PATH_1,
2,
STASH_DIFF_TYPE);
verify(stashClient, times(0)).postCommentLineOnPullRequest(pr,
stashCommentMessage3,
FILE_PATH_2,
1,
STASH_DIFF_TYPE);
}
@Test
public void testPostSonarQubeReportWithNoSonarQubeIssues() throws Exception {
myFacade.postSonarQubeReport(pr, new ArrayList<>(), diffReport, stashClient);
verify(stashClient, times(0)).postCommentLineOnPullRequest(pr,
stashCommentMessage1,
FILE_PATH_1,
1,
STASH_DIFF_TYPE);
verify(stashClient, times(0)).postCommentLineOnPullRequest(pr,
stashCommentMessage2,
FILE_PATH_1,
2,
STASH_DIFF_TYPE);
verify(stashClient, times(0)).postCommentLineOnPullRequest(pr,
stashCommentMessage3,
FILE_PATH_2,
1,
STASH_DIFF_TYPE);
}
@Test
public void testGetSonarQubeReviewer() throws Exception {
when(stashClient.getUser(STASH_USER)).thenReturn(stashUser);
StashUser reviewer = myFacade.getSonarQubeReviewer(STASH_USER, stashClient);
assertEquals(1234, reviewer.getId());
}
@Test
public void testGetPullRequestDiffReport() throws Exception {
when(stashClient.getPullRequestDiffs(pr)).thenReturn(diffReport);
StashDiffReport result = myFacade.getPullRequestDiffReport(pr, stashClient);
assertEquals(1, result.getLine(FILE_PATH_1, 1));
assertEquals(2, result.getLine(FILE_PATH_1, 2));
assertEquals(1, result.getLine(FILE_PATH_2, 1));
}
@Test
public void testResetComments() throws Exception {
myFacade.resetComments(pr, diffReport, stashUser, stashClient);
verify(stashClient, times(3)).deleteTaskOnComment(any(StashTask.class));
verify(stashClient, times(3)).deletePullRequestComment(eq(pr), any(StashComment.class));
}
@Test
public void testResetCommentsWithNotDeletableTasks() throws Exception {
when(comment1.containsPermanentTasks()).thenReturn(true);
myFacade.resetComments(pr, diffReport, stashUser, stashClient);
verify(stashClient, times(2)).deleteTaskOnComment(any(StashTask.class));
verify(stashClient, times(2)).deletePullRequestComment(eq(pr), any(StashComment.class));
}
@Test
public void testResetCommentsWithNoTasks() throws Exception {
when(comment1.getTasks()).thenReturn(new ArrayList<StashTask>());
myFacade.resetComments(pr, diffReport, stashUser, stashClient);
verify(stashClient, times(2)).deleteTaskOnComment(any(StashTask.class));
verify(stashClient, times(3)).deletePullRequestComment(eq(pr), any(StashComment.class));
}
@Test
public void testResetCommentsWithDifferentStashUsers() throws Exception {
StashUser stashUser2 = mock(StashUser.class);
when(stashUser2.getId()).thenReturn((long)4321);
StashComment comment = mock(StashComment.class);
when(comment.getAuthor()).thenReturn(stashUser2);
ArrayList<StashComment> comments = new ArrayList<>();
comments.add(comment);
when(diffReport.getComments()).thenReturn(comments);
myFacade.resetComments(pr, diffReport, stashUser, stashClient);
verify(stashClient, times(0)).deletePullRequestComment(eq(pr), any(StashComment.class));
}
@Test
public void testResetCommentsWithoutAnyComments() throws Exception {
when(diffReport.getComments()).thenReturn(new ArrayList<StashComment>());
myFacade.resetComments(pr, diffReport, stashUser, stashClient);
verify(stashClient, times(0)).deletePullRequestComment(eq(pr), any(StashComment.class));
}
@Test
public void testApprovePullRequest() throws Exception {
myFacade.approvePullRequest(pr, stashClient);
verify(stashClient, times(1)).approvePullRequest(pr);
}
@Test
public void addResetPullRequestApproval() throws Exception {
myFacade.resetPullRequestApproval(pr, stashClient);
verify(stashClient, times(1)).resetPullRequestApproval(pr);
}
@Test
public void testAddPullRequestReviewer() throws Exception {
ArrayList<StashUser> reviewers = new ArrayList<>();
StashUser stashUser = mock(StashUser.class);
reviewers.add(stashUser);
StashPullRequest pullRequest = mock(StashPullRequest.class);
when(pullRequest.getReviewer(STASH_USER)).thenReturn(null);
when(pullRequest.getVersion()).thenReturn((long)1);
when(stashClient.getPullRequest(pr)).thenReturn(pullRequest);
when(stashClient.getUser(STASH_USER)).thenReturn(stashUser);
myFacade.addPullRequestReviewer(pr, STASH_USER, stashClient);
verify(stashClient, times(1)).addPullRequestReviewer(pr, 1, reviewers);
}
@Test
public void testAddPullRequestReviewerWithReviewerAlreadyAdded() throws Exception {
ArrayList<StashUser> reviewers = new ArrayList<>();
StashUser stashUser = mock(StashUser.class);
reviewers.add(stashUser);
StashPullRequest pullRequest = mock(StashPullRequest.class);
when(pullRequest.getReviewer(STASH_USER)).thenReturn(stashUser);
when(pullRequest.getVersion()).thenReturn((long)1);
when(stashClient.getPullRequest(pr)).thenReturn(pullRequest);
when(stashClient.getUser(STASH_USER)).thenReturn(stashUser);
myFacade.addPullRequestReviewer(pr, STASH_USER, stashClient);
verify(stashClient, times(0)).addPullRequestReviewer(pr, 1, reviewers);
}
@Test
public void testGetPullRequest() throws Exception {
doReturn("SonarQube").when(myFacade).getStashProject();
doReturn("sonar-stash-plugin").when(myFacade).getStashRepository();
doReturn(1337).when(myFacade).getStashPullRequestId();
assertTrue(myFacade.getPullRequest() instanceof PullRequestRef);
}
/* FIXME
@Test
public void testGetIssuePathWithoutExplicitSourceRootDir() {
when(config.getRepositoryRoot()).thenReturn(Optional.empty());
inputFileCache.putInputFile(
"key",
new DefaultInputFile("some/relative/path").setAbsolutePath("/root/some/absolute/path")
);
assertEquals("some/absolute/path",
myFacade.getIssuePath(new DefaultIssue().setComponentKey("key")));
}
@Test
public void testGetIssuePathWithExplicitSourceRootDir() {
when(config.getRepositoryRoot()).thenReturn(Optional.of(new File("/root/some/")));
inputFileCache.putInputFile(
"key",
new DefaultInputFile("some/relative/path").setAbsolutePath("/root/some/absolute/path")
);
assertEquals("absolute/path",
myFacade.getIssuePath(new DefaultIssue().setComponentKey("key")));
}
*/
@Test
public void testPostCommentPerIssueWithIncludeVicinityIssues() throws Exception {
when(config.issueVicinityRange()).thenReturn(10);
when(stashCommentsReport1.contains(stashCommentMessage1, FILE_PATH_1, 1)).thenReturn(false);
when(stashCommentsReport1.contains(stashCommentMessage2, FILE_PATH_1, 2)).thenReturn(false);
when(stashCommentsReport2.contains(stashCommentMessage3, FILE_PATH_2, 1)).thenReturn(false);
when(diffReport.getType(FILE_PATH_1, 1, config.issueVicinityRange())).thenReturn(STASH_DIFF_TYPE);
when(diffReport.getType(FILE_PATH_1, 2, config.issueVicinityRange())).thenReturn(STASH_DIFF_TYPE);
when(diffReport.getType(FILE_PATH_2, 1, config.issueVicinityRange())).thenReturn(STASH_DIFF_TYPE);
myFacade.postCommentPerIssue(pr, report, diffReport, stashClient);
verify(stashClient, times(1)).postCommentLineOnPullRequest(pr, stashCommentMessage1, FILE_PATH_1, 1, STASH_DIFF_TYPE);
verify(stashClient, times(1)).postCommentLineOnPullRequest(pr, stashCommentMessage2, FILE_PATH_1, 2, STASH_DIFF_TYPE);
verify(stashClient, times(1)).postCommentLineOnPullRequest(pr, stashCommentMessage3, FILE_PATH_2, 1, STASH_DIFF_TYPE);
}
}
================================================
FILE: src/test/java/org/sonar/plugins/stash/StashTest.java
================================================
package org.sonar.plugins.stash;
import org.junit.jupiter.api.extension.RegisterExtension;
public abstract class StashTest {
@RegisterExtension
public JavaUtilLoggingCapture logRule = new JavaUtilLoggingCapture();
}
================================================
FILE: src/test/java/org/sonar/plugins/stash/TestUtils.java
================================================
package org.sonar.plugins.stash;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.stubbing.StubMapping;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
public class TestUtils {
private TestUtils() {}
public static <T> T notNull(T t) {
assertNotNull(t);
return t;
}
public static void assertContains(String s, String expected) {
assertNotNull(s);
assertNotNull(expected);
assertTrue(s.contains(expected));
}
// The first request to wiremock may be slow.
// We could increase the timeout on our StashClient but then all the timeout test take longer.
// So instead we perform a dummy request on each test invocation with a high timeout.
// We now have many more request than before, but are faster anyways.
public static void primeWireMock(WireMockServer wireMock) throws Exception {
StubMapping primingMapping = WireMock.get(WireMock.urlPathEqualTo("/")).build();
wireMock.addStubMapping(primingMapping);
HttpURLConnection conn = (HttpURLConnection)new URL("http://127.0.0.1:" + wireMock.port()).openConnection();
conn.setConnectTimeout(1000);
conn.setConnectTimeout(1000);
conn.connect();
conn.getResponseCode();
wireMock.removeStub(primingMapping);
wireMock.resetRequests();
}
public static WrappedProcessBuilder createProcess(String prefix, String... command) {
ProcessBuilder pb = new ProcessBuilder(command);
return new WrappedProcessBuilder(prefix, pb);
}
public static class WrappedProcessBuilder {
private final String prefix;
private final ProcessBuilder wrapped;
private WrappedProcessBuilder(String prefix, ProcessBuilder wrapped) {
this.prefix = prefix;
this.wrapped = wrapped;
}
public WrappedProcessBuilder directory(File directory) {
wrapped.directory(directory);
return this;
}
public Process start() throws IOException {
return new WrappedProcess(prefix, wrapped.start());
}
public List<String> command() {
return wrapped.command();
}
public Map<String, String> environment() {
return wrapped.environment();
}
}
public static class WrappedProcess extends Process {
private final Process wrapped;
private final ForwarderThread outputLogger;
private final ForwarderThread errorLogger;
private WrappedProcess(String prefix, Process wrapped) {
this.wrapped = wrapped;
errorLogger = new ForwarderThread(prefix, wrapped.getErrorStream());
errorLogger.start();
outputLogger = new ForwarderThread(prefix, wrapped.getInputStream());
outputLogger.start();
}
@Override
public OutputStream getOutputStream() {
return wrapped.getOutputStream();
}
@Override
public InputStream getInputStream() {
return wrapped.getInputStream();
}
@Override
public InputStream getErrorStream() {
return wrapped.getErrorStream();
}
@Override
public int waitFor() throws InterruptedException {
int exitCode = wrapped.waitFor();
stopLoggers();
return exitCode;
}
@Override
public int exitValue() {
return wrapped.exitValue();
}
@Override
public void destroy() {
wrapped.destroy();
}
private void stopLoggers() throws InterruptedException {
outputLogger.interrupt();
errorLogger.interrupt();
outputLogger.join();
errorLogger.join();
}
}
private static class ForwarderThread extends Thread implements AutoCloseable {
private final String prefix;
private final InputStream input;
private ForwarderThread(String prefix, InputStream input) {
this.prefix = "[" + prefix + "] ";
this.input = input;
setDaemon(true);
}
@Override
public void run() {
try (BufferedReader lineReader = new BufferedReader(new InputStreamReader(input))) {
while (!Thread.interrupted()) {
String line = lineReader.readLine();
if (line != null) {
System.out.println(prefix + line);
//logger.info(line);
}
}
} catch (IOException e) {
/* ignored */
}
}
@Override
public void close() throws InterruptedException {
interrupt();
join();
}
}
public static InputFile inputFile(String moduleKey, String path) {
return TestInputFileBuilder.create(moduleKey, path).build();
}
public static InputFile inputFile(String moduleKey, Path moduleBaseDir, String path) {
return TestInputFileBuilder.create(moduleKey, path).setModuleBaseDir(moduleBaseDir).build();
}
}
================================================
FILE: src/test/java/org/sonar/plugins/stash/TestWatcherExtension.java
================================================
package org.sonar.plugins.stash;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
public abstract class TestWatcherExtension implements BeforeEachCallback, AfterEachCallback {
@Override
public void beforeEach(ExtensionContext context) throws Exception {
starting();
}
@Override
public void afterEach(ExtensionContext context) throws Exception {
finished();
if (context.getExecutionException().isPresent()) {
failed();
} else {
succeeded();
}
}
abstract void starting();
abstract void finished();
abstract void succeeded();
abstract void failed();
}
================================================
FILE: src/test/java/org/sonar/plugins/stash/client/ContentTypeTest.java
================================================
package org.sonar.plugins.stash.client;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
public class ContentTypeTest {
@Test
public void testContentType() {
ContentType json = new ContentType("application", "json", null);
assertTrue(json.match("application/json"));
assertTrue(json.match("application/json;charset=utf-8"));
assertTrue(json.match("APPLICATION/JSON"));
assertTrue(json.match("ApPlIcAtIoN/jSoN"));
assertFalse(json.match(""));
assertFalse(json.match("/"));
assertFalse(json.match(";"));
assertFalse(json.match("/;"));
assertFalse(json.match("foo"));
assertFalse(json.match("12!4"));
assertFalse(json.match("foo/json"));
assertFalse(json.match("application/foo"));
assertFalse(json.match("application/foo;charset=utf-8"));
}
@Test
public void testConstructorFailure() {
assertThrows(IllegalArgumentException.class, () ->
new ContentType("application", "json", "what-is-the-point-if-I-must-be-null?")
);
}
}
================================================
FILE: src/test/java/org/sonar/plugins/stash/client/StashClientTest.java
================================================
package org.sonar.plugins.stash.client;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.any;
import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl;
import static com.github.tomakehurst.wiremock.client.WireMock.deleteRequestedFor;
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehu
gitextract_2kj9ve84/
├── .gitignore
├── .travis/
│ ├── runSonarQubeAnalysis.sh
│ └── script.sh
├── .travis.yml
├── LICENSE.md
├── README.md
├── dependency-check-suppression.xml
├── pom.xml
└── src/
├── main/
│ ├── java/
│ │ └── org/
│ │ └── sonar/
│ │ └── plugins/
│ │ └── stash/
│ │ ├── IssuePathResolver.java
│ │ ├── PeekableInputStream.java
│ │ ├── PluginInfo.java
│ │ ├── PullRequestRef.java
│ │ ├── StashIssueReportingPostJob.java
│ │ ├── StashPlugin.java
│ │ ├── StashPluginConfiguration.java
│ │ ├── StashPluginUtils.java
│ │ ├── StashProjectBuilder.java
│ │ ├── StashRequestFacade.java
│ │ ├── client/
│ │ │ ├── ContentType.java
│ │ │ ├── StashClient.java
│ │ │ └── StashCredentials.java
│ │ ├── exceptions/
│ │ │ ├── StashClientException.java
│ │ │ ├── StashConfigurationException.java
│ │ │ ├── StashException.java
│ │ │ └── StashReportExtractionException.java
│ │ └── issue/
│ │ ├── MarkdownPrinter.java
│ │ ├── StashComment.java
│ │ ├── StashCommentReport.java
│ │ ├── StashDiff.java
│ │ ├── StashDiffReport.java
│ │ ├── StashPullRequest.java
│ │ ├── StashTask.java
│ │ ├── StashUser.java
│ │ └── collector/
│ │ ├── SonarQubeCollector.java
│ │ └── StashCollector.java
│ └── resources/
│ └── org/
│ └── sonar/
│ └── plugins/
│ └── stash/
│ └── sonar-stash.properties
└── test/
├── java/
│ └── org/
│ └── sonar/
│ └── plugins/
│ └── stash/
│ ├── CompleteITCase.java
│ ├── DefaultIssue.java
│ ├── DummyStashProjectBuilder.java
│ ├── JavaUtilLoggingCapture.java
│ ├── PeekableInputStreamTest.java
│ ├── PluginInfoTest.java
│ ├── StashIssueReportingPostJobTest.java
│ ├── StashPluginConfigurationTest.java
│ ├── StashPluginUtilsTest.java
│ ├── StashRequestFacadeTest.java
│ ├── StashTest.java
│ ├── TestUtils.java
│ ├── TestWatcherExtension.java
│ ├── client/
│ │ ├── ContentTypeTest.java
│ │ └── StashClientTest.java
│ ├── end2end/
│ │ ├── EndToEndTest.java
│ │ └── Issue194.java
│ ├── fixtures/
│ │ ├── DummyIssuePathResolver.java
│ │ ├── DummyPostJobContext.java
│ │ ├── DummyPostJobIssue.java
│ │ ├── DummyServer.java
│ │ ├── DummyStashServer.java
│ │ ├── MavenSonarFixtures.java
│ │ ├── SonarQube.java
│ │ ├── SonarQubeRule.java
│ │ ├── SonarScanner.java
│ │ ├── WireMockExtension.java
│ │ └── WireMockResponseCallback.java
│ └── issue/
│ ├── MarkdownPrinterTest.java
│ ├── StashCommentReportTest.java
│ ├── StashCommentTest.java
│ ├── StashDiffReportTest.java
│ ├── StashDiffTest.java
│ ├── StashPullRequestTest.java
│ ├── StashTaskTest.java
│ ├── StashUserTest.java
│ └── collector/
│ ├── DiffReportSample.java
│ ├── SonarQubeCollectorTest.java
│ └── StashCollectorTest.java
└── resources/
├── fixtures/
│ └── issue194_stash_diff.json
└── foo/
├── module1/
│ └── src/
│ └── main/
│ └── java/
│ └── Foo.java
├── module2/
│ └── src/
│ └── main/
│ └── java/
│ └── Bar.java
└── sonar-project.properties
SYMBOL INDEX (634 symbols across 68 files)
FILE: src/main/java/org/sonar/plugins/stash/IssuePathResolver.java
type IssuePathResolver (line 5) | @FunctionalInterface
method getIssuePath (line 7) | String getIssuePath(PostJobIssue issue);
FILE: src/main/java/org/sonar/plugins/stash/PeekableInputStream.java
class PeekableInputStream (line 8) | public class PeekableInputStream extends PushbackInputStream {
method PeekableInputStream (line 9) | public PeekableInputStream(InputStream in) {
method peek (line 13) | public Optional<Character> peek() throws IOException {
FILE: src/main/java/org/sonar/plugins/stash/PluginInfo.java
class PluginInfo (line 3) | public class PluginInfo {
method PluginInfo (line 7) | public PluginInfo(String name, String version) {
method getVersion (line 12) | public String getVersion() {
method getName (line 16) | public String getName() {
FILE: src/main/java/org/sonar/plugins/stash/PullRequestRef.java
class PullRequestRef (line 3) | public final class PullRequestRef {
method PullRequestRef (line 8) | private PullRequestRef(String project, String repository, int pullRequ...
method project (line 14) | public String project() {
method repository (line 18) | public String repository() {
method pullRequestId (line 22) | public int pullRequestId() {
method builder (line 26) | public static Builder builder() {
class Builder (line 30) | public static class Builder {
method setProject (line 35) | public Builder setProject(String value) {
method setRepository (line 40) | public Builder setRepository(String value) {
method setPullRequestId (line 45) | public Builder setPullRequestId(int value) {
method build (line 50) | public PullRequestRef build() {
FILE: src/main/java/org/sonar/plugins/stash/StashIssueReportingPostJob.java
class StashIssueReportingPostJob (line 23) | @ScannerSide
method StashIssueReportingPostJob (line 33) | public StashIssueReportingPostJob(StashPluginConfiguration stashPlugin...
method execute (line 41) | @Override
method executeThrowing (line 54) | @VisibleForTesting
method updateStashWithSonarInfo (line 75) | private void updateStashWithSonarInfo(StashClient stashClient,
method postInfoAndPRsActions (line 118) | private void postInfoAndPRsActions(
method shouldApprovePullRequest (line 146) | static boolean shouldApprovePullRequest(Optional<Severity> approvalSev...
method describe (line 156) | @Override
FILE: src/main/java/org/sonar/plugins/stash/StashPlugin.java
class StashPlugin (line 17) | @Properties({
type IssueType (line 55) | public enum IssueType {
method define (line 84) | @Override
FILE: src/main/java/org/sonar/plugins/stash/StashPluginConfiguration.java
class StashPluginConfiguration (line 18) | @ScannerSide
method StashPluginConfiguration (line 25) | public StashPluginConfiguration(Settings settings, Server server) {
method hasToNotifyStash (line 30) | public boolean hasToNotifyStash() {
method getStashProject (line 34) | public String getStashProject() {
method getStashRepository (line 38) | public String getStashRepository() {
method getPullRequestId (line 42) | public Integer getPullRequestId() {
method getStashURL (line 46) | public String getStashURL() {
method getStashLogin (line 50) | public String getStashLogin() {
method getStashUserSlug (line 54) | public String getStashUserSlug() {
method getStashPassword (line 58) | public String getStashPassword() {
method getStashPasswordEnvironmentVariable (line 62) | public String getStashPasswordEnvironmentVariable() {
method getSonarQubeURL (line 66) | public String getSonarQubeURL() {
method getSonarQubeLogin (line 70) | public String getSonarQubeLogin() {
method getSonarQubePassword (line 74) | public String getSonarQubePassword() {
method getIssueThreshold (line 78) | public int getIssueThreshold() {
method getIssueSeverityThreshold (line 82) | public Severity getIssueSeverityThreshold() {
method getStashTimeout (line 87) | public int getStashTimeout() {
method canApprovePullRequest (line 91) | public boolean canApprovePullRequest() {
method resetComments (line 95) | public boolean resetComments() {
method getTaskIssueSeverityThreshold (line 99) | public Optional<Severity> getTaskIssueSeverityThreshold() {
method getApprovalSeverityThreshold (line 103) | public Optional<Severity> getApprovalSeverityThreshold() {
method includeAnalysisOverview (line 107) | public boolean includeAnalysisOverview() {
method getRepositoryRoot (line 111) | public Optional<File> getRepositoryRoot() {
method includeExistingIssues (line 115) | public boolean includeExistingIssues() {
method getFilesLimitInOverview (line 119) | public int getFilesLimitInOverview() {
method issueVicinityRange (line 123) | public int issueVicinityRange() {
method excludedRules (line 127) | public Set<RuleKey> excludedRules() {
method getOptionalSeveritySetting (line 136) | private Optional<Severity> getOptionalSeveritySetting(String key) {
FILE: src/main/java/org/sonar/plugins/stash/StashPluginUtils.java
class StashPluginUtils (line 16) | public final class StashPluginUtils {
method StashPluginUtils (line 18) | private StashPluginUtils() {}
method formatPercentage (line 20) | public static String formatPercentage(double d) {
method roundedPercentageGreaterThan (line 35) | public static boolean roundedPercentageGreaterThan(double left, double...
method countIssuesBySeverity (line 39) | public static long countIssuesBySeverity(Collection<PostJobIssue> issu...
method isProjectWide (line 43) | public static boolean isProjectWide(PostJobIssue issue) {
method getPluginInfo (line 55) | public static PluginInfo getPluginInfo() {
method removeEnd (line 69) | public static String removeEnd(String s, String suffix) {
FILE: src/main/java/org/sonar/plugins/stash/StashProjectBuilder.java
class StashProjectBuilder (line 8) | public class StashProjectBuilder extends ProjectBuilder {
method build (line 11) | @Override
method getProjectBaseDir (line 16) | public File getProjectBaseDir() {
FILE: src/main/java/org/sonar/plugins/stash/StashRequestFacade.java
class StashRequestFacade (line 34) | @ScannerSide
method StashRequestFacade (line 46) | public StashRequestFacade(
method extractIssueReport (line 56) | public List<PostJobIssue> extractIssueReport(Iterable<PostJobIssue> is...
method getMarkdownPrinter (line 62) | private MarkdownPrinter getMarkdownPrinter() {
method postAnalysisOverview (line 69) | public void postAnalysisOverview(PullRequestRef pr,
method approvePullRequest (line 82) | public void approvePullRequest(PullRequestRef pr, StashClient stashCli...
method resetPullRequestApproval (line 94) | public void resetPullRequestApproval(PullRequestRef pr, StashClient st...
method addPullRequestReviewer (line 106) | public void addPullRequestReviewer(PullRequestRef pr, String userSlug,...
method postSonarQubeReport (line 127) | public void postSonarQubeReport(PullRequestRef pr,
method postCommentPerIssue (line 138) | void postCommentPerIssue(PullRequestRef pr, Iterable<PostJobIssue> iss...
method postIssueComment (line 163) | private void postIssueComment(PullRequestRef pr,
method getCredentials (line 220) | public StashCredentials getCredentials() {
method getIssueThreshold (line 246) | public int getIssueThreshold() {
method getStashURL (line 262) | public String getStashURL() {
method getPullRequest (line 278) | public PullRequestRef getPullRequest() {
method getStashProject (line 291) | public String getStashProject() {
method getStashRepository (line 306) | public String getStashRepository() {
method getStashPullRequestId (line 321) | public int getStashPullRequestId() {
method getSonarQubeReviewer (line 334) | public StashUser getSonarQubeReviewer(String userSlug, StashClient sta...
method getPullRequestDiffReport (line 346) | public StashDiffReport getPullRequestDiffReport(PullRequestRef pr, Sta...
method resetComments (line 360) | public void resetComments(PullRequestRef pr,
method getIssuePath (line 393) | @Override
FILE: src/main/java/org/sonar/plugins/stash/client/ContentType.java
class ContentType (line 9) | public class ContentType {
method ContentType (line 16) | public ContentType(String primaryType, String subType, Object list) {
method match (line 24) | public boolean match(String s) {
method toString (line 36) | @Override
FILE: src/main/java/org/sonar/plugins/stash/client/StashClient.java
class StashClient (line 44) | public class StashClient implements AutoCloseable {
method StashClient (line 81) | public StashClient(String url, StashCredentials credentials, int stash...
method getBaseUrl (line 88) | public String getBaseUrl() {
method getLogin (line 92) | public String getLogin() {
method postCommentOnPullRequest (line 96) | public void postCommentOnPullRequest(PullRequestRef pr, String report) {
method getPullRequestComments (line 109) | public StashCommentReport getPullRequestComments(PullRequestRef pr, St...
method deletePullRequestComment (line 137) | public void deletePullRequestComment(PullRequestRef pr, StashComment c...
method getPullRequestDiffs (line 151) | public StashDiffReport getPullRequestDiffs(PullRequestRef pr) {
method postCommentLineOnPullRequest (line 162) | public StashComment postCommentLineOnPullRequest(PullRequestRef pr,
method getUser (line 199) | public StashUser getUser(String userSlug) {
method getPullRequest (line 207) | public StashPullRequest getPullRequest(PullRequestRef pr) {
method addPullRequestReviewer (line 217) | public void addPullRequestReviewer(PullRequestRef pr, long pullRequest...
method approvePullRequest (line 240) | public void approvePullRequest(PullRequestRef pr) {
method resetPullRequestApproval (line 251) | public void resetPullRequestApproval(PullRequestRef pr) {
method postTaskOnComment (line 262) | public void postTaskOnComment(String message, Long commentId) {
method deleteTaskOnComment (line 276) | public void deleteTaskOnComment(StashTask task) {
method close (line 281) | @Override
method get (line 290) | private JsonObject get(String url, String errorMessage) {
method post (line 294) | private JsonObject post(String url, JsonObject body, String errorMessa...
method postCreate (line 298) | private JsonObject postCreate(String url, JsonObject body, String erro...
method delete (line 302) | private JsonObject delete(String url, int expectedStatusCode, String e...
method delete (line 306) | private JsonObject delete(String url, String errorMessage) {
method put (line 310) | private JsonObject put(String url, JsonObject body, String errorMessag...
method addAuth (line 314) | private void addAuth(BoundRequestBuilder requestBuilder) {
method performRequest (line 323) | private JsonObject performRequest(BoundRequestBuilder requestBuilder,
method validateResponse (line 352) | private static void validateResponse(Response response, int expectedSt...
method extractResponse (line 360) | private static JsonObject extractResponse(Response response) {
method formatStashApiError (line 379) | private static String formatStashApiError(Response response) {
method getUserAgent (line 411) | private static String getUserAgent(String sonarQubeVersion) {
method createHttpClient (line 427) | AsyncHttpClient createHttpClient(String sonarQubeVersion) {
FILE: src/main/java/org/sonar/plugins/stash/client/StashCredentials.java
class StashCredentials (line 3) | public class StashCredentials {
method StashCredentials (line 9) | public StashCredentials(String login, String password, String userSlug) {
method getLogin (line 15) | public String getLogin() {
method getPassword (line 19) | public String getPassword() {
method getUserSlug (line 23) | public String getUserSlug() {
FILE: src/main/java/org/sonar/plugins/stash/exceptions/StashClientException.java
class StashClientException (line 3) | public class StashClientException extends StashException {
method StashClientException (line 7) | public StashClientException(String message) {
method StashClientException (line 11) | public StashClientException(Throwable cause) {
method StashClientException (line 15) | public StashClientException(String message, Throwable cause) {
FILE: src/main/java/org/sonar/plugins/stash/exceptions/StashConfigurationException.java
class StashConfigurationException (line 3) | public class StashConfigurationException extends StashException {
method StashConfigurationException (line 7) | public StashConfigurationException(String message) {
method StashConfigurationException (line 11) | public StashConfigurationException(Throwable cause) {
method StashConfigurationException (line 15) | public StashConfigurationException(String message, Throwable cause) {
FILE: src/main/java/org/sonar/plugins/stash/exceptions/StashException.java
class StashException (line 3) | public class StashException extends RuntimeException {
method StashException (line 7) | public StashException(String message) {
method StashException (line 11) | public StashException(Throwable cause) {
method StashException (line 15) | public StashException(String message, Throwable cause) {
FILE: src/main/java/org/sonar/plugins/stash/exceptions/StashReportExtractionException.java
class StashReportExtractionException (line 3) | public class StashReportExtractionException extends StashException {
method StashReportExtractionException (line 7) | public StashReportExtractionException(String message) {
method StashReportExtractionException (line 11) | public StashReportExtractionException(Throwable cause) {
method StashReportExtractionException (line 15) | public StashReportExtractionException(String message, Throwable cause) {
FILE: src/main/java/org/sonar/plugins/stash/issue/MarkdownPrinter.java
class MarkdownPrinter (line 24) | public final class MarkdownPrinter {
method MarkdownPrinter (line 36) | public MarkdownPrinter(
method printSeverityMarkdown (line 48) | public static String printSeverityMarkdown(org.sonar.api.batch.rule.Se...
method printIssueNumberBySeverityMarkdown (line 55) | public static String printIssueNumberBySeverityMarkdown(Collection<Pos...
method printIssueMarkdown (line 64) | public String printIssueMarkdown(PostJobIssue issue) {
method printReportMarkdown (line 82) | public String printReportMarkdown(Collection<PostJobIssue> allIssues) {
method formatTableList (line 120) | private String formatTableList(List<Map.Entry<RuleKey, List<PostJobIss...
method fileNameList (line 144) | private String fileNameList(List<PostJobIssue> issues) {
method repeatString (line 164) | private String repeatString(int n, String s) {
method link (line 168) | private static String link(String title, String target) {
method firstNonNull (line 192) | @SafeVarargs
FILE: src/main/java/org/sonar/plugins/stash/issue/StashComment.java
class StashComment (line 9) | public class StashComment {
method StashComment (line 19) | public StashComment(long id, String message, String path, Long line, S...
method getId (line 36) | public long getId() {
method setLine (line 40) | public void setLine(long line) {
method setPath (line 44) | public void setPath(String path) {
method getMessage (line 48) | public String getMessage() {
method getPath (line 52) | public String getPath() {
method getLine (line 56) | public long getLine() {
method getAuthor (line 60) | public StashUser getAuthor() {
method getVersion (line 64) | public long getVersion() {
method getTasks (line 68) | public List<StashTask> getTasks() {
method addTask (line 72) | public void addTask(StashTask task) {
method containsPermanentTasks (line 76) | public boolean containsPermanentTasks() {
method equals (line 89) | @Override
method hashCode (line 100) | @Override
method toString (line 105) | @Override
FILE: src/main/java/org/sonar/plugins/stash/issue/StashCommentReport.java
class StashCommentReport (line 11) | public class StashCommentReport {
method StashCommentReport (line 17) | public StashCommentReport() {
method getComments (line 21) | public List<StashComment> getComments() {
method add (line 25) | public void add(StashComment comment) {
method add (line 29) | public void add(StashCommentReport report) {
method contains (line 35) | public boolean contains(String message, String path, long line) {
method applyDiffReport (line 51) | public StashCommentReport applyDiffReport(StashDiffReport diffReport) {
method size (line 70) | public int size() {
FILE: src/main/java/org/sonar/plugins/stash/issue/StashDiff.java
class StashDiff (line 8) | public class StashDiff {
method StashDiff (line 16) | public StashDiff(IssueType type, String path, long source, long destin...
method addComment (line 24) | public void addComment(StashComment comment) {
method getPath (line 28) | public String getPath() {
method getSource (line 32) | public long getSource() {
method getDestination (line 36) | public long getDestination() {
method getType (line 40) | public IssueType getType() {
method getComments (line 44) | public List<StashComment> getComments() {
method containsComment (line 48) | public boolean containsComment(long commentId) {
FILE: src/main/java/org/sonar/plugins/stash/issue/StashDiffReport.java
class StashDiffReport (line 17) | public class StashDiffReport {
method StashDiffReport (line 23) | public StashDiffReport() {
method getDiffs (line 27) | public List<StashDiff> getDiffs() {
method add (line 31) | public void add(StashDiff diff) {
method add (line 35) | public void add(StashDiffReport report) {
method inVicinityOfChangedDiff (line 39) | private static boolean inVicinityOfChangedDiff(StashDiff diff, long de...
method isChangedDiff (line 48) | private static boolean isChangedDiff(StashDiff diff, long destination) {
method lineIsChangedDiff (line 52) | private static boolean lineIsChangedDiff(StashDiff diff) {
method getType (line 56) | public IssueType getType(String path, long destination, int vicinityRa...
method getLine (line 82) | public long getLine(String path, long destination) {
method getDiffByComment (line 96) | public StashDiff getDiffByComment(long commentId) {
method getComments (line 108) | public List<StashComment> getComments() {
FILE: src/main/java/org/sonar/plugins/stash/issue/StashPullRequest.java
class StashPullRequest (line 8) | public class StashPullRequest {
method getId (line 16) | public int getId() {
method getProject (line 20) | public String getProject() {
method getRepository (line 24) | public String getRepository() {
method StashPullRequest (line 28) | public StashPullRequest(PullRequestRef pr) {
method getRef (line 34) | public PullRequestRef getRef() {
method getVersion (line 38) | public long getVersion() {
method setVersion (line 42) | public void setVersion(long version) {
method addReviewer (line 46) | public void addReviewer(StashUser reviewer) {
method getReviewers (line 50) | public List<StashUser> getReviewers() {
method getReviewer (line 54) | public StashUser getReviewer(String user) {
method containsReviewer (line 66) | public boolean containsReviewer(StashUser reviewer) {
FILE: src/main/java/org/sonar/plugins/stash/issue/StashTask.java
class StashTask (line 3) | public class StashTask {
method StashTask (line 10) | public StashTask(Long id, String text, String state, boolean deletable) {
method getId (line 17) | public Long getId() {
method getText (line 21) | public String getText() {
method getState (line 25) | public String getState() {
method isDeletable (line 29) | public boolean isDeletable() {
FILE: src/main/java/org/sonar/plugins/stash/issue/StashUser.java
class StashUser (line 3) | public class StashUser {
method StashUser (line 10) | public StashUser(long id, String name, String slug, String email) {
method getId (line 17) | public long getId() {
method getName (line 21) | public String getName() {
method getSlug (line 25) | public String getSlug() {
method getEmail (line 29) | public String getEmail() {
FILE: src/main/java/org/sonar/plugins/stash/issue/collector/SonarQubeCollector.java
class SonarQubeCollector (line 16) | public final class SonarQubeCollector {
method SonarQubeCollector (line 20) | private SonarQubeCollector() {
method extractIssueReport (line 28) | public static List<PostJobIssue> extractIssueReport(
method shouldIncludeIssue (line 40) | static boolean shouldIncludeIssue(
FILE: src/main/java/org/sonar/plugins/stash/issue/collector/StashCollector.java
class StashCollector (line 17) | public final class StashCollector {
method StashCollector (line 22) | private StashCollector() {
method extractComments (line 26) | public static StashCommentReport extractComments(JsonObject jsonCommen...
method extractComment (line 43) | public static StashComment extractComment(JsonObject jsonComment, Stri...
method extractComment (line 56) | public static StashComment extractComment(JsonObject jsonComment) {
method extractPullRequest (line 72) | public static StashPullRequest extractPullRequest(PullRequestRef pr,
method extractUser (line 95) | public static StashUser extractUser(JsonObject jsonUser) {
method extractDiffs (line 104) | public static StashDiffReport extractDiffs(JsonObject jsonObject) {
method parseHunksIntoDiffs (line 171) | private static StashDiffReport parseHunksIntoDiffs(String path, JsonAr...
method extractCommentsForDiff (line 220) | private static StashDiff extractCommentsForDiff(StashDiff diff, JsonOb...
method buildCommentFromJSON (line 266) | private static StashComment buildCommentFromJSON(JsonObject jsonLineCo...
method updateCommentTasks (line 285) | private static void updateCommentTasks(StashComment comment, JsonArray...
method extractTask (line 299) | public static StashTask extractTask(JsonObject jsonTask) {
method isLastPage (line 314) | public static boolean isLastPage(JsonObject jsonObject) {
method getNextPageStart (line 321) | public static long getNextPageStart(JsonObject jsonObject) {
method getLong (line 330) | private static final Long getLong(JsonObject o, String name) {
method getLong (line 334) | private static final Long getLong(Object o) {
FILE: src/test/java/org/sonar/plugins/stash/CompleteITCase.java
class CompleteITCase (line 33) | public class CompleteITCase {
method setUpClass (line 61) | @BeforeAll
method setUp (line 70) | @BeforeEach
method testBasic (line 87) | @Test
method testMultiModule (line 107) | @Test
method targetLocation (line 142) | private Path targetLocation(String name) {
method installFile (line 146) | private void installFile(String name) throws IOException {
method repoPath (line 155) | private String repoPath(String project, String repo, String... parts) {
method urlPath (line 159) | private String urlPath(String... parts) {
method urlPath (line 163) | private String urlPath(boolean leading, String... parts) {
method scan (line 171) | protected void scan(boolean activateSonarStash, boolean issuesMode, Pr...
FILE: src/test/java/org/sonar/plugins/stash/DefaultIssue.java
class DefaultIssue (line 8) | public class DefaultIssue implements PostJobIssue {
method key (line 16) | public String key() {
method setKey (line 20) | public DefaultIssue setKey(String key) {
method componentKey (line 25) | public String componentKey() {
method setComponentKey (line 29) | public DefaultIssue setComponentKey(String componentKey) {
method message (line 34) | public String message() {
method setMessage (line 38) | public DefaultIssue setMessage(String message) {
method inputComponent (line 43) | public InputComponent inputComponent() {
method setInputComponent (line 47) | public DefaultIssue setInputComponent(InputComponent inputComponent) {
method isNew (line 52) | @Override
method setNew (line 57) | public DefaultIssue setNew(boolean aNew) {
method severity (line 62) | public Severity severity() {
method setSeverity (line 66) | public DefaultIssue setSeverity(Severity severity) {
method ruleKey (line 71) | public RuleKey ruleKey() {
method setRuleKey (line 75) | public DefaultIssue setRuleKey(RuleKey ruleKey) {
method setLine (line 80) | public DefaultIssue setLine(Integer line) {
method line (line 85) | @Override
FILE: src/test/java/org/sonar/plugins/stash/DummyStashProjectBuilder.java
class DummyStashProjectBuilder (line 5) | public class DummyStashProjectBuilder extends StashProjectBuilder {
method DummyStashProjectBuilder (line 8) | public DummyStashProjectBuilder(File projectBaseDir) {
method getProjectBaseDir (line 12) | @Override
FILE: src/test/java/org/sonar/plugins/stash/JavaUtilLoggingCapture.java
class JavaUtilLoggingCapture (line 12) | public class JavaUtilLoggingCapture extends TestWatcherExtension {
method JavaUtilLoggingCapture (line 20) | public JavaUtilLoggingCapture(boolean gobbleOnSuccess) {
method JavaUtilLoggingCapture (line 30) | public JavaUtilLoggingCapture() {
method succeeded (line 34) | @Override
method failed (line 41) | @Override
method finished (line 46) | @Override
method starting (line 56) | @Override
method emit (line 68) | private void emit() {
FILE: src/test/java/org/sonar/plugins/stash/PeekableInputStreamTest.java
class PeekableInputStreamTest (line 10) | public class PeekableInputStreamTest {
method testEmptyStream (line 11) | @Test
method testStream (line 20) | @Test
method fromString (line 29) | private PeekableInputStream fromString(String s) {
FILE: src/test/java/org/sonar/plugins/stash/PluginInfoTest.java
class PluginInfoTest (line 7) | public class PluginInfoTest {
method testPluginInfoRef_ConstructorAndAccessors (line 13) | @Test
FILE: src/test/java/org/sonar/plugins/stash/StashIssueReportingPostJobTest.java
class StashIssueReportingPostJobTest (line 33) | @ExtendWith(MockitoExtension.class)
method setUp (line 78) | @BeforeEach
method testExecuteOn (line 112) | @Test
method testExecuteOnWithReachedThreshold (line 130) | @Test
method testExecuteOnWithNoPluginActivation (line 151) | @Test
method testExecuteOnWithNoStashUserDefined (line 170) | @Test
method testExecuteOnWithResetCommentActivated (line 189) | @Test
method testExecuteOnWithNoDiffReport (line 204) | @Test
method testShouldApprovePullRequest (line 225) | @Test
FILE: src/test/java/org/sonar/plugins/stash/StashPluginConfigurationTest.java
class StashPluginConfigurationTest (line 14) | public class StashPluginConfigurationTest {
method testStashPluginConfiguration_ConstructorAndAccessors (line 17) | @Test
FILE: src/test/java/org/sonar/plugins/stash/StashPluginUtilsTest.java
class StashPluginUtilsTest (line 12) | public class StashPluginUtilsTest {
method testFormatPercentage (line 14) | @Test
method testRoundedPercentageGreaterThan (line 26) | @Test
FILE: src/test/java/org/sonar/plugins/stash/StashRequestFacadeTest.java
class StashRequestFacadeTest (line 57) | @ExtendWith(MockitoExtension.class)
method setUp (line 118) | @BeforeEach
method testGetCredentials (line 255) | @Test
method testGetNoCredentials (line 265) | @Test
method testGetPasswordFromEnvironment (line 275) | @Test
method testGetPasswordFromUnconfiguredEnvironment (line 289) | @Test
method testGetIssueThreshold (line 299) | @Test
method testGetIssueThresholdThrowsException (line 305) | @Test
method testGetStashURL (line 314) | @Test
method testGetStashURLThrowsException (line 323) | @Test
method testGetStashProject (line 332) | @Test
method testGetStashProjectThrowsException (line 338) | @Test
method testGetStashRepository (line 346) | @Test
method testGetStashRepositoryThrowsException (line 352) | @Test
method testGetStashPullRequestId (line 360) | @Test
method testGetStashPullRequestIdThrowsException (line 366) | @Test
method testPostCommentPerIssue (line 374) | @Test
method testPostCommentPerIssueWithNoStashCommentAlreadyPushed (line 399) | @Test
method testPostCommentPerIssueIgnoresLowerSeverityIssues (line 424) | @Test
method testPostSonarQubeReport (line 450) | @Test
method testPostTaskOnComment (line 459) | @Test
method testPostTaskOnCommentWithRestrictedLevel (line 486) | @Test
method testPostTaskOnCommentWithNotPresentLevel (line 513) | @Test
method testPostTaskOnCommentWithSeverityNone (line 540) | @Test
method testPostSonarQubeReportWithNoType (line 567) | @Test
method testPostSonarQubeReportWithNoSonarQubeIssues (line 596) | @Test
method testGetSonarQubeReviewer (line 617) | @Test
method testGetPullRequestDiffReport (line 625) | @Test
method testResetComments (line 636) | @Test
method testResetCommentsWithNotDeletableTasks (line 644) | @Test
method testResetCommentsWithNoTasks (line 654) | @Test
method testResetCommentsWithDifferentStashUsers (line 664) | @Test
method testResetCommentsWithoutAnyComments (line 682) | @Test
method testApprovePullRequest (line 692) | @Test
method addResetPullRequestApproval (line 699) | @Test
method testAddPullRequestReviewer (line 707) | @Test
method testAddPullRequestReviewerWithReviewerAlreadyAdded (line 726) | @Test
method testGetPullRequest (line 745) | @Test
method testPostCommentPerIssueWithIncludeVicinityIssues (line 786) | @Test
FILE: src/test/java/org/sonar/plugins/stash/StashTest.java
class StashTest (line 5) | public abstract class StashTest {
FILE: src/test/java/org/sonar/plugins/stash/TestUtils.java
class TestUtils (line 23) | public class TestUtils {
method TestUtils (line 24) | private TestUtils() {}
method notNull (line 26) | public static <T> T notNull(T t) {
method assertContains (line 31) | public static void assertContains(String s, String expected) {
method primeWireMock (line 41) | public static void primeWireMock(WireMockServer wireMock) throws Excep...
method createProcess (line 53) | public static WrappedProcessBuilder createProcess(String prefix, Strin...
class WrappedProcessBuilder (line 58) | public static class WrappedProcessBuilder {
method WrappedProcessBuilder (line 62) | private WrappedProcessBuilder(String prefix, ProcessBuilder wrapped) {
method directory (line 67) | public WrappedProcessBuilder directory(File directory) {
method start (line 72) | public Process start() throws IOException {
method command (line 76) | public List<String> command() {
method environment (line 80) | public Map<String, String> environment() {
class WrappedProcess (line 85) | public static class WrappedProcess extends Process {
method WrappedProcess (line 90) | private WrappedProcess(String prefix, Process wrapped) {
method getOutputStream (line 98) | @Override
method getInputStream (line 103) | @Override
method getErrorStream (line 108) | @Override
method waitFor (line 113) | @Override
method exitValue (line 120) | @Override
method destroy (line 125) | @Override
method stopLoggers (line 130) | private void stopLoggers() throws InterruptedException {
class ForwarderThread (line 138) | private static class ForwarderThread extends Thread implements AutoClo...
method ForwarderThread (line 143) | private ForwarderThread(String prefix, InputStream input) {
method run (line 149) | @Override
method close (line 164) | @Override
method inputFile (line 171) | public static InputFile inputFile(String moduleKey, String path) {
method inputFile (line 175) | public static InputFile inputFile(String moduleKey, Path moduleBaseDir...
FILE: src/test/java/org/sonar/plugins/stash/TestWatcherExtension.java
class TestWatcherExtension (line 7) | public abstract class TestWatcherExtension implements BeforeEachCallback...
method beforeEach (line 9) | @Override
method afterEach (line 14) | @Override
method starting (line 24) | abstract void starting();
method finished (line 25) | abstract void finished();
method succeeded (line 26) | abstract void succeeded();
method failed (line 27) | abstract void failed();
FILE: src/test/java/org/sonar/plugins/stash/client/ContentTypeTest.java
class ContentTypeTest (line 9) | public class ContentTypeTest {
method testContentType (line 11) | @Test
method testConstructorFailure (line 30) | @Test
FILE: src/test/java/org/sonar/plugins/stash/client/StashClientTest.java
class StashClientTest (line 53) | public class StashClientTest extends StashTest {
method setUp (line 69) | @BeforeEach
method testPostCommentOnPullRequest (line 78) | @Test
method testGetBaseUrl (line 87) | @Test
method testGetLogin (line 92) | @Test
method testPostCommentOnPullRequestWithWrongHTTPResult (line 97) | @Test
method testPostCommentOnPullRequestWithException (line 113) | @Test
method testGetPullRequestComments (line 121) | @Test
method testGetPullRequestCommentsWithoutAuthor (line 135) | @Test
method testGetPullRequestCommentsWithNextPage (line 147) | @Test
method testGetPullRequestCommentsWithNoNextPage (line 175) | @Test
method testGetPullRequestCommentsWithWrongHTTPResult (line 197) | @Test
method testGetPullRequestCommentsWithWrongContentType (line 205) | @Test
method testGetPullRequestCommentsWithException (line 213) | @Test
method testGetPullRequestDiffs (line 221) | @Test
method testGetPullRequestDiffsWithMalformedTasks (line 230) | @Test
method testGetPullRequestDiffsWithWrongHTTPResult (line 240) | @Test
method testGetPullRequestDiffsWithException (line 249) | @Test
method testPostCommentLineOnPullRequest (line 257) | @Test
method testPostCommentLineOnPullRequestWithWrongHTTPResult (line 267) | @Test
method testPostCommentLineOnPullRequestWithException (line 280) | @Test
method testGetUser (line 288) | @Test
method testGetUserWithWrongHTTPResult (line 302) | @Test
method testDeletePullRequestComment (line 310) | @Test
method testGetPullRequest (line 318) | @Test
method testApprovePullRequest (line 332) | @Test
method testResetPullRequestApproval (line 340) | @Test
method testAddPullRequestReviewer (line 347) | @Test
method testAddPullRequestReviewerWithNoReviewer (line 358) | @Test
method testPostTaskOnComment (line 365) | @Test
method testDeleteTaskOnComment (line 372) | @Test
method testFollowInternalRedirection (line 380) | @Test
method testPullRequestHugePullRequestId (line 390) | @Test
method testClientWithoutCredentials (line 407) | @Test
method testClientWithoutPassword (line 419) | @Test
method addErrorResponse (line 431) | private void addErrorResponse(MappingBuilder mapping, int statusCode) {
method aJsonResponse (line 447) | public static ResponseDefinitionBuilder aJsonResponse() {
method aXMLResponse (line 451) | public static ResponseDefinitionBuilder aXMLResponse() {
FILE: src/test/java/org/sonar/plugins/stash/end2end/EndToEndTest.java
class EndToEndTest (line 38) | public abstract class EndToEndTest {
method setUp (line 43) | @BeforeEach
method run (line 50) | protected RunResults run(String fixtureName, Consumer<Settings> settin...
class RunResults (line 99) | protected static class RunResults {
method readFixture (line 103) | private static String readFixture(String name) throws IOException {
method issue (line 111) | protected PostJobIssue issue(String key, String ruleKey, String module...
FILE: src/test/java/org/sonar/plugins/stash/end2end/Issue194.java
class Issue194 (line 13) | public class Issue194 extends EndToEndTest {
method testWithVicinitiyRange (line 14) | @Test
method testAfterVicinityRange (line 19) | @Test
method run (line 24) | private void run(int vicinityRange) throws Exception {
FILE: src/test/java/org/sonar/plugins/stash/fixtures/DummyIssuePathResolver.java
class DummyIssuePathResolver (line 8) | public class DummyIssuePathResolver implements IssuePathResolver {
method getIssuePath (line 9) | @Override
FILE: src/test/java/org/sonar/plugins/stash/fixtures/DummyPostJobContext.java
class DummyPostJobContext (line 12) | public class DummyPostJobContext implements PostJobContext {
method DummyPostJobContext (line 17) | public DummyPostJobContext(Settings settings, AnalysisMode mode,
method settings (line 24) | @Override
method config (line 29) | @Override
method analysisMode (line 34) | @Override
method issues (line 39) | @Override
method resolvedIssues (line 44) | @Override
FILE: src/test/java/org/sonar/plugins/stash/fixtures/DummyPostJobIssue.java
class DummyPostJobIssue (line 13) | public class DummyPostJobIssue implements PostJobIssue {
method DummyPostJobIssue (line 22) | public DummyPostJobIssue(Path moduleBaseDir, String key, RuleKey ruleK...
method key (line 33) | @Override
method ruleKey (line 38) | @Override
method componentKey (line 43) | @Override
method inputComponent (line 48) | @CheckForNull
method line (line 54) | @CheckForNull
method message (line 60) | @CheckForNull
method severity (line 66) | @Override
method isNew (line 71) | @Override
FILE: src/test/java/org/sonar/plugins/stash/fixtures/DummyServer.java
class DummyServer (line 8) | public class DummyServer extends Server {
method DummyServer (line 9) | public DummyServer() {}
method getId (line 11) | @Override
method getPermanentServerId (line 16) | @CheckForNull
method getVersion (line 22) | @Override
method getStartedAt (line 27) | @Override
method getRootDir (line 32) | @Override
method getContextPath (line 37) | @Override
method getPublicRootUrl (line 42) | @Override
method isDev (line 47) | @Override
method isSecured (line 52) | @Override
method getURL (line 57) | @Override
FILE: src/test/java/org/sonar/plugins/stash/fixtures/DummyStashServer.java
class DummyStashServer (line 26) | public class DummyStashServer {
method DummyStashServer (line 34) | public DummyStashServer(WireMockServer wireMock) {
method mockUser (line 38) | public void mockUser(StashUser user) {
method mockPrDiff (line 43) | public void mockPrDiff(PullRequestRef pr, String x) {
method noCommentsFor (line 47) | public void noCommentsFor(PullRequestRef pr) {
method expectCommentsUpdateFor (line 51) | public void expectCommentsUpdateFor(PullRequestRef pr) {
method getUserFromRequest (line 72) | private StashUser getUserFromRequest(Request request) {
method mockResponse (line 77) | private void mockResponse(String response, String... urlParts) {
method mockEntity (line 85) | private void mockEntity(Object entity, String... urlParts) {
method getCreatedComments (line 93) | public List<JsonObject> getCreatedComments() {
method urlPath (line 97) | private static String urlPath(String... parts) {
method coreApi (line 101) | private static String coreApi(String... parts) {
method urlPath (line 108) | private static String urlPath(boolean leading, String... parts) {
method aJsonResponse (line 116) | public static ResponseDefinitionBuilder aJsonResponse() {
method aJsonResponse (line 120) | public static ResponseDefinitionBuilder aJsonResponse(Object entity) {
method extend (line 126) | public static WireMockConfiguration extend(WireMockConfiguration optio...
FILE: src/test/java/org/sonar/plugins/stash/fixtures/MavenSonarFixtures.java
class MavenSonarFixtures (line 6) | public class MavenSonarFixtures {
method getSonarqube (line 10) | public synchronized static SonarQube getSonarqube(int port) {
method getSonarScanner (line 23) | public synchronized static SonarScanner getSonarScanner() {
FILE: src/test/java/org/sonar/plugins/stash/fixtures/SonarQube.java
class SonarQube (line 23) | public class SonarQube {
method SonarQube (line 33) | public SonarQube(Path installDir, String version, int port) {
method getPort (line 40) | public int getPort() {
method getHost (line 44) | public String getHost() {
method getExecutable (line 48) | protected File getExecutable() {
method setUp (line 73) | public void setUp() {
method getConfig (line 77) | public Properties getConfig() {
method writeConfig (line 81) | protected void writeConfig() throws Exception {
method startAsync (line 89) | public void startAsync() throws Exception {
method stop (line 99) | public void stop() throws Exception {
method installPlugin (line 105) | public void installPlugin(File sourceArchive) throws IOException {
method waitForReady (line 115) | public void waitForReady() throws MalformedURLException {
method isReady (line 126) | private boolean isReady() throws IOException {
method getUrl (line 133) | private URL getUrl(String file) throws MalformedURLException {
method getUrl (line 137) | public URL getUrl() throws MalformedURLException {
method createProject (line 141) | public boolean createProject(String key, String name) throws IOExcepti...
FILE: src/test/java/org/sonar/plugins/stash/fixtures/SonarQubeRule.java
class SonarQubeRule (line 9) | public class SonarQubeRule implements BeforeAllCallback, AfterAllCallback {
method SonarQubeRule (line 14) | public SonarQubeRule(SonarQube sonarqube) {
method start (line 18) | public void start() throws Exception {
method get (line 23) | public SonarQube get() {
method beforeAll (line 27) | @Override
method afterAll (line 32) | @Override
FILE: src/test/java/org/sonar/plugins/stash/fixtures/SonarScanner.java
class SonarScanner (line 14) | public class SonarScanner {
method SonarScanner (line 17) | public SonarScanner(Path installationDir) {
method getBinary (line 21) | protected Path getBinary(String name) {
method addCliProperty (line 29) | private void addCliProperty(List<String> cli, String name, Object valu...
method scan (line 33) | public void scan(SonarQube sonarqube,
FILE: src/test/java/org/sonar/plugins/stash/fixtures/WireMockExtension.java
class WireMockExtension (line 14) | public class WireMockExtension extends WireMockServer implements BeforeE...
method WireMockExtension (line 19) | public WireMockExtension(Options options) {
method WireMockExtension (line 23) | public WireMockExtension(Options options, boolean failOnUnmatchedReque...
method beforeEach (line 28) | @Override
method afterEach (line 33) | @Override
method checkForUnmatchedRequests (line 38) | private void checkForUnmatchedRequests() {
FILE: src/test/java/org/sonar/plugins/stash/fixtures/WireMockResponseCallback.java
class WireMockResponseCallback (line 12) | public class WireMockResponseCallback {
type ResponseTransformerCallback (line 13) | public interface ResponseTransformerCallback {
method transform (line 14) | Response transform(Request request, Response response, FileSource fi...
method getExtension (line 22) | public static Extension getExtension() {
method callback (line 26) | public ResponseDefinitionBuilder callback(ResponseDefinitionBuilder rd...
class DummySQResponseTransformer (line 32) | private static class DummySQResponseTransformer extends ResponseTransf...
method getName (line 34) | @Override
method transform (line 39) | @Override
class ToStringWrapper (line 56) | private static class ToStringWrapper implements ResponseTransformerCal...
method ToStringWrapper (line 59) | private ToStringWrapper(
method getLamba (line 65) | public String getLamba() {
method transform (line 69) | @Override
FILE: src/test/java/org/sonar/plugins/stash/issue/MarkdownPrinterTest.java
class MarkdownPrinterTest (line 18) | public class MarkdownPrinterTest {
method setUp (line 28) | @BeforeEach
method testPrintIssueMarkdown (line 74) | @Test
method testPrintIssueMarkdownWithEmptyMessage (line 82) | @Test
method testPrintIssueNumberBySeverityMarkdown (line 93) | @Test
method testPrintIssueNumberBySeverityMarkdownWithNoIssues (line 111) | @Test
method testPrintReportMarkdown (line 127) | @Test
method testPrintReportMarkdownWithIssueLimitation (line 150) | @Test
method testPrintReportMarkdownWithFileWideIssues (line 172) | @Test
method testPrintEmptyReportMarkdown (line 204) | @Test
FILE: src/test/java/org/sonar/plugins/stash/issue/StashCommentReportTest.java
class StashCommentReportTest (line 14) | public class StashCommentReportTest {
method setUp (line 22) | @BeforeEach
method testContains (line 37) | @Test
method testNotContains (line 51) | @Test
method testSize (line 61) | @Test
method testSizeOfEmptyReport (line 72) | @Test
method testAddReport (line 78) | @Test
method testAddEmptyReportToNotEmptyReport (line 101) | @Test
method testAddNotEmptyReportToEmptyReport (line 112) | @Test
method testAddEmptyReportToEmptyReport (line 123) | @Test
method applyDiffReportWithCONTEXT (line 132) | @Test
method applyDiffReportWithADDED (line 153) | @Test
FILE: src/test/java/org/sonar/plugins/stash/issue/StashCommentTest.java
class StashCommentTest (line 13) | public class StashCommentTest {
method testGetLine (line 18) | @Test
method testGetNoLine (line 24) | @Test
method testEquals (line 30) | @Test
method testAddTask (line 41) | @Test
method testContainsNotDeletableTasks (line 62) | @Test
method testContainsNotDeletableTasksWithoutTasks (line 83) | @Test
FILE: src/test/java/org/sonar/plugins/stash/issue/StashDiffReportTest.java
class StashDiffReportTest (line 14) | public class StashDiffReportTest {
method setUp (line 28) | @BeforeEach
method testAdd (line 56) | @Test
method testAddReport (line 80) | @Test
method testGetType (line 91) | @Test
method testGetTypeWithNoDestination (line 101) | @Test
method testGetTypeVicinity (line 107) | @Test
method testGetLine (line 116) | @Test
method testGetDiffByComment (line 125) | @Test
method testGetComments (line 137) | @Test
method testGetCommentsWithoutAnyIssues (line 146) | @Test
method testGetCommentsWithDuplicatedComments (line 153) | @Test
method testVicinitySourceBeforeDestination (line 171) | @Test
FILE: src/test/java/org/sonar/plugins/stash/issue/StashDiffTest.java
class StashDiffTest (line 14) | public class StashDiffTest {
method setUp (line 20) | @BeforeEach
method testIsTypeOfContext (line 37) | @Test
method testContainsComment (line 43) | @Test
FILE: src/test/java/org/sonar/plugins/stash/issue/StashPullRequestTest.java
class StashPullRequestTest (line 12) | public class StashPullRequestTest {
method setUp (line 19) | @BeforeEach
method testGetProject (line 32) | @Test
method testGetRepository (line 37) | @Test
method testGetId (line 42) | @Test
method testGetVersion (line 47) | @Test
method testAddReviewer (line 54) | @Test
method testAddReviewerTwice (line 64) | @Test
method testAddReviewerSameTwice (line 76) | @Test
method testGetReviewer (line 88) | @Test
method testContainsReviewer (line 107) | @Test
FILE: src/test/java/org/sonar/plugins/stash/issue/StashTaskTest.java
class StashTaskTest (line 10) | public class StashTaskTest {
method setUp (line 14) | @BeforeEach
method testGetId (line 19) | @Test
method testGetState (line 24) | @Test
method testGetText (line 29) | @Test
method testIsDeletable (line 34) | @Test
FILE: src/test/java/org/sonar/plugins/stash/issue/StashUserTest.java
class StashUserTest (line 9) | public class StashUserTest {
method setUp (line 13) | @BeforeEach
method testGetId (line 18) | @Test
method testGetName (line 23) | @Test
method testGetSlug (line 28) | @Test
method testGetEmail (line 33) | @Test
FILE: src/test/java/org/sonar/plugins/stash/issue/collector/DiffReportSample.java
class DiffReportSample (line 3) | public class DiffReportSample {
FILE: src/test/java/org/sonar/plugins/stash/issue/collector/SonarQubeCollectorTest.java
class SonarQubeCollectorTest (line 34) | @ExtendWith(MockitoExtension.class)
method setUp (line 54) | @BeforeEach
method testExtractEmptyIssueReport (line 91) | @Test
method testExtractIssueReport (line 99) | @Test
method testExtractIssueReportWithNoLine (line 122) | @Test
method testExtractIssueReportWithOldOption (line 140) | @Test
method testExtractIssueReportWithOneIssueWithoutInputFile (line 155) | @Test
method testExtractIssueReportWithIncludeExistingIssuesOption (line 174) | @Test
method testExtractIssueReportWithExcludedRules (line 187) | @Test
method testShouldIncludeIssue (line 203) | @Test
FILE: src/test/java/org/sonar/plugins/stash/issue/collector/StashCollectorTest.java
class StashCollectorTest (line 22) | public class StashCollectorTest {
method testExtractCommentReport (line 31) | @Test
method testExtractCommentReportWithSeveralComment (line 49) | @Test
method testExtractEmptyCommentReport (line 78) | @Test
method testExtractComment (line 86) | @Test
method testExtractEmptyCommentWithNoAnchor (line 101) | @Test
method testExtractCommentWithPathAndLineAsParameters (line 111) | @Test
method testIsLastPage (line 126) | @Test
method testNextPageStart (line 138) | @Test
method testExtractDiffsWithBaseReport (line 147) | @Test
method testExtractDiffsWithNoComments (line 221) | @Test
method testExtractDiffsWithFileComments (line 255) | @Test
method testExtractDiffsWithEmptyFileComments (line 336) | @Test
method testExtractDiffsWithEmptyReport (line 382) | @Test
method testExtractDiffsWithMultipleFile (line 393) | @Test
method testExtractDiffsWithDeletedFile (line 415) | @Test
method testExtractPullRequest (line 433) | @Test
method testExtractPullRequestWithSeveralReviewer (line 474) | @Test
method testExtractPullRequestWithNoReviewer (line 532) | @Test
method testExtractUser (line 552) | @Test
method testExtractTask (line 569) | @Test
method testExtractTaskWithoutPermittedOperation (line 587) | @Test
method parse (line 603) | private static JsonObject parse(String s) throws Exception {
FILE: src/test/resources/foo/module1/src/main/java/Foo.java
class Foo (line 1) | public class Foo {
method main (line 2) | public static void main(String[] args) {
FILE: src/test/resources/foo/module2/src/main/java/Bar.java
class Bar (line 1) | public class Bar {
method main (line 2) | public static void main(String[] args) {
Condensed preview — 79 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (715K chars).
[
{
"path": ".gitignore",
"chars": 126,
"preview": "target\n.idea\n.project\n.classpath\n.settings\n*.iml\n/bin/\n/release.properties\n/pom.xml.releaseBackup\n/dependency-reduced-po"
},
{
"path": ".travis/runSonarQubeAnalysis.sh",
"chars": 360,
"preview": "#!/bin/sh\n# Exit on failure\nset -e\n\n# We don't want to run X times the same analysis because of the matrix configuration"
},
{
"path": ".travis/script.sh",
"chars": 733,
"preview": "#!/bin/bash\nset -ev\n\nif [ -z \"${TEST_SUITE}\" ]; then\n\techo \"No \\$TEST_SUITE specified, aborting!\"\n\texit 1\n\nelif [ \"unit\""
},
{
"path": ".travis.yml",
"chars": 780,
"preview": "language: java\n\nmatrix:\n fast_finish: true\n include:\n # The basic unit-tests environments\n - jdk: oraclejdk8\n "
},
{
"path": "LICENSE.md",
"chars": 1180,
"preview": "\n\nThe MIT License (MIT)\n\nCopyright (c) 2015 Amadeus S.A.S., Antoine Copet, Mathieu-Antoine Simon\nCopyright (c) 2016 Amad"
},
{
"path": "README.md",
"chars": 7352,
"preview": "# SonarQube Stash (BitBucket) plugin\n\n## Important note\n\nVersion 7.7 of SonarQube [dropped support for the extension poi"
},
{
"path": "dependency-check-suppression.xml",
"chars": 435,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<suppressions xmlns=\"https://jeremylong.github.io/DependencyCheck/dependency-supp"
},
{
"path": "pom.xml",
"chars": 11681,
"preview": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocat"
},
{
"path": "src/main/java/org/sonar/plugins/stash/IssuePathResolver.java",
"chars": 193,
"preview": "package org.sonar.plugins.stash;\n\nimport org.sonar.api.batch.postjob.issue.PostJobIssue;\n\n@FunctionalInterface\npublic in"
},
{
"path": "src/main/java/org/sonar/plugins/stash/PeekableInputStream.java",
"chars": 519,
"preview": "package org.sonar.plugins.stash;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.PushbackInputSt"
},
{
"path": "src/main/java/org/sonar/plugins/stash/PluginInfo.java",
"chars": 323,
"preview": "package org.sonar.plugins.stash;\n\npublic class PluginInfo {\n private String name;\n private String version;\n\n public P"
},
{
"path": "src/main/java/org/sonar/plugins/stash/PullRequestRef.java",
"chars": 1124,
"preview": "package org.sonar.plugins.stash;\n\npublic final class PullRequestRef {\n private String project;\n private String reposit"
},
{
"path": "src/main/java/org/sonar/plugins/stash/StashIssueReportingPostJob.java",
"chars": 5975,
"preview": "package org.sonar.plugins.stash;\r\n\r\nimport com.google.common.annotations.VisibleForTesting;\r\nimport java.util.Optional;\r"
},
{
"path": "src/main/java/org/sonar/plugins/stash/StashPlugin.java",
"chars": 12438,
"preview": "package org.sonar.plugins.stash;\r\n\r\nimport com.google.common.collect.Lists;\r\nimport java.util.Arrays;\r\nimport java.util."
},
{
"path": "src/main/java/org/sonar/plugins/stash/StashPluginConfiguration.java",
"chars": 4256,
"preview": "package org.sonar.plugins.stash;\r\n\r\nimport com.google.common.collect.Sets;\r\nimport java.util.Objects;\r\nimport java.util."
},
{
"path": "src/main/java/org/sonar/plugins/stash/StashPluginUtils.java",
"chars": 2506,
"preview": "package org.sonar.plugins.stash;\r\n\r\nimport com.google.common.base.CharMatcher;\r\nimport java.io.IOException;\r\nimport java"
},
{
"path": "src/main/java/org/sonar/plugins/stash/StashProjectBuilder.java",
"chars": 471,
"preview": "package org.sonar.plugins.stash;\n\nimport org.sonar.api.batch.bootstrap.ProjectBuilder;\n\nimport java.io.File;\n\n// FIXME w"
},
{
"path": "src/main/java/org/sonar/plugins/stash/StashRequestFacade.java",
"chars": 13637,
"preview": "package org.sonar.plugins.stash;\n\nimport java.io.File;\nimport java.nio.file.Path;\nimport java.util.Collection;\nimport ja"
},
{
"path": "src/main/java/org/sonar/plugins/stash/client/ContentType.java",
"chars": 1094,
"preview": "package org.sonar.plugins.stash.client;\n\nimport static java.util.Objects.requireNonNull;\n\n/*\n * basic implementation of "
},
{
"path": "src/main/java/org/sonar/plugins/stash/client/StashClient.java",
"chars": 18117,
"preview": "package org.sonar.plugins.stash.client;\r\n\r\nimport com.github.cliftonlabs.json_simple.JsonArray;\r\nimport com.github.clift"
},
{
"path": "src/main/java/org/sonar/plugins/stash/client/StashCredentials.java",
"chars": 533,
"preview": "package org.sonar.plugins.stash.client;\r\n\r\npublic class StashCredentials {\r\n\r\n private final String login;\r\n private f"
},
{
"path": "src/main/java/org/sonar/plugins/stash/exceptions/StashClientException.java",
"chars": 439,
"preview": "package org.sonar.plugins.stash.exceptions;\r\n\r\npublic class StashClientException extends StashException {\r\n\r\n private s"
},
{
"path": "src/main/java/org/sonar/plugins/stash/exceptions/StashConfigurationException.java",
"chars": 466,
"preview": "package org.sonar.plugins.stash.exceptions;\r\n\r\npublic class StashConfigurationException extends StashException {\r\n\r\n pr"
},
{
"path": "src/main/java/org/sonar/plugins/stash/exceptions/StashException.java",
"chars": 417,
"preview": "package org.sonar.plugins.stash.exceptions;\r\n\r\npublic class StashException extends RuntimeException {\r\n\r\n private stati"
},
{
"path": "src/main/java/org/sonar/plugins/stash/exceptions/StashReportExtractionException.java",
"chars": 479,
"preview": "package org.sonar.plugins.stash.exceptions;\r\n\r\npublic class StashReportExtractionException extends StashException {\r\n\r\n "
},
{
"path": "src/main/java/org/sonar/plugins/stash/issue/MarkdownPrinter.java",
"chars": 7047,
"preview": "package org.sonar.plugins.stash.issue;\r\n\r\nimport com.google.common.collect.ListMultimap;\r\nimport com.google.common.colle"
},
{
"path": "src/main/java/org/sonar/plugins/stash/issue/StashComment.java",
"chars": 2407,
"preview": "package org.sonar.plugins.stash.issue;\r\n\r\nimport com.google.common.base.MoreObjects;\r\nimport java.util.ArrayList;\r\nimpor"
},
{
"path": "src/main/java/org/sonar/plugins/stash/issue/StashCommentReport.java",
"chars": 1981,
"preview": "package org.sonar.plugins.stash.issue;\r\n\r\nimport java.util.Objects;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.Lis"
},
{
"path": "src/main/java/org/sonar/plugins/stash/issue/StashDiff.java",
"chars": 1182,
"preview": "package org.sonar.plugins.stash.issue;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.Collections;\r\nimport java.util.L"
},
{
"path": "src/main/java/org/sonar/plugins/stash/issue/StashDiffReport.java",
"chars": 3594,
"preview": "package org.sonar.plugins.stash.issue;\r\n\r\nimport com.google.common.collect.Range;\r\nimport java.util.Collections;\r\nimport"
},
{
"path": "src/main/java/org/sonar/plugins/stash/issue/StashPullRequest.java",
"chars": 1527,
"preview": "package org.sonar.plugins.stash.issue;\r\n\r\nimport org.sonar.plugins.stash.PullRequestRef;\r\n\r\nimport java.util.ArrayList;\r"
},
{
"path": "src/main/java/org/sonar/plugins/stash/issue/StashTask.java",
"chars": 602,
"preview": "package org.sonar.plugins.stash.issue;\r\n\r\npublic class StashTask {\r\n\r\n private final Long id;\r\n private final String t"
},
{
"path": "src/main/java/org/sonar/plugins/stash/issue/StashUser.java",
"chars": 570,
"preview": "package org.sonar.plugins.stash.issue;\r\n\r\npublic class StashUser {\r\n\r\n private final long id;\r\n private final String n"
},
{
"path": "src/main/java/org/sonar/plugins/stash/issue/collector/SonarQubeCollector.java",
"chars": 2302,
"preview": "package org.sonar.plugins.stash.issue.collector;\r\n\r\nimport java.util.Set;\r\n\r\nimport static org.sonar.plugins.stash.Stash"
},
{
"path": "src/main/java/org/sonar/plugins/stash/issue/collector/StashCollector.java",
"chars": 11710,
"preview": "package org.sonar.plugins.stash.issue.collector;\r\n\r\nimport com.github.cliftonlabs.json_simple.JsonArray;\r\nimport com.git"
},
{
"path": "src/main/resources/org/sonar/plugins/stash/sonar-stash.properties",
"chars": 64,
"preview": "project.version=${project.version}\nproject.name=${project.name}\n"
},
{
"path": "src/test/java/org/sonar/plugins/stash/CompleteITCase.java",
"chars": 9276,
"preview": "package org.sonar.plugins.stash;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupi"
},
{
"path": "src/test/java/org/sonar/plugins/stash/DefaultIssue.java",
"chars": 1754,
"preview": "package org.sonar.plugins.stash;\n\nimport org.sonar.api.batch.fs.InputComponent;\nimport org.sonar.api.batch.postjob.issue"
},
{
"path": "src/test/java/org/sonar/plugins/stash/DummyStashProjectBuilder.java",
"chars": 341,
"preview": "package org.sonar.plugins.stash;\n\nimport java.io.File;\n\npublic class DummyStashProjectBuilder extends StashProjectBuilde"
},
{
"path": "src/test/java/org/sonar/plugins/stash/JavaUtilLoggingCapture.java",
"chars": 1715,
"preview": "package org.sonar.plugins.stash;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.logging.ConsoleHan"
},
{
"path": "src/test/java/org/sonar/plugins/stash/PeekableInputStreamTest.java",
"chars": 915,
"preview": "package org.sonar.plugins.stash;\n\nimport java.io.IOException;\nimport java.io.StringBufferInputStream;\nimport org.junit.j"
},
{
"path": "src/test/java/org/sonar/plugins/stash/PluginInfoTest.java",
"chars": 530,
"preview": "package org.sonar.plugins.stash;\r\n\r\nimport static org.junit.jupiter.api.Assertions.assertEquals;\r\n\r\nimport org.junit.jup"
},
{
"path": "src/test/java/org/sonar/plugins/stash/StashIssueReportingPostJobTest.java",
"chars": 9623,
"preview": "package org.sonar.plugins.stash;\r\n\r\nimport static org.junit.jupiter.api.Assertions.assertFalse;\r\nimport static org.junit"
},
{
"path": "src/test/java/org/sonar/plugins/stash/StashPluginConfigurationTest.java",
"chars": 3251,
"preview": "package org.sonar.plugins.stash;\r\n\r\nimport static org.junit.jupiter.api.Assertions.assertEquals;\r\nimport static org.juni"
},
{
"path": "src/test/java/org/sonar/plugins/stash/StashPluginUtilsTest.java",
"chars": 1624,
"preview": "package org.sonar.plugins.stash;\r\n\r\nimport org.junit.jupiter.api.Test;\r\n\r\nimport static org.junit.jupiter.api.Assertions"
},
{
"path": "src/test/java/org/sonar/plugins/stash/StashRequestFacadeTest.java",
"chars": 36200,
"preview": "package org.sonar.plugins.stash;\n\nimport java.nio.file.FileSystems;\nimport java.nio.file.Path;\nimport org.junit.jupiter."
},
{
"path": "src/test/java/org/sonar/plugins/stash/StashTest.java",
"chars": 222,
"preview": "package org.sonar.plugins.stash;\n\nimport org.junit.jupiter.api.extension.RegisterExtension;\n\npublic abstract class Stash"
},
{
"path": "src/test/java/org/sonar/plugins/stash/TestUtils.java",
"chars": 5183,
"preview": "package org.sonar.plugins.stash;\n\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit."
},
{
"path": "src/test/java/org/sonar/plugins/stash/TestWatcherExtension.java",
"chars": 731,
"preview": "package org.sonar.plugins.stash;\n\nimport org.junit.jupiter.api.extension.AfterEachCallback;\nimport org.junit.jupiter.api"
},
{
"path": "src/test/java/org/sonar/plugins/stash/client/ContentTypeTest.java",
"chars": 1176,
"preview": "package org.sonar.plugins.stash.client;\n\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.j"
},
{
"path": "src/test/java/org/sonar/plugins/stash/client/StashClientTest.java",
"chars": 20489,
"preview": "package org.sonar.plugins.stash.client;\r\n\r\nimport static com.github.tomakehurst.wiremock.client.WireMock.aResponse;\r\nimp"
},
{
"path": "src/test/java/org/sonar/plugins/stash/end2end/EndToEndTest.java",
"chars": 4987,
"preview": "package org.sonar.plugins.stash.end2end;\n\nimport static org.sonar.plugins.stash.TestUtils.notNull;\nimport static org.son"
},
{
"path": "src/test/java/org/sonar/plugins/stash/end2end/Issue194.java",
"chars": 2820,
"preview": "package org.sonar.plugins.stash.end2end;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org"
},
{
"path": "src/test/java/org/sonar/plugins/stash/fixtures/DummyIssuePathResolver.java",
"chars": 511,
"preview": "package org.sonar.plugins.stash.fixtures;\n\nimport org.sonar.api.batch.fs.InputComponent;\nimport org.sonar.api.batch.fs.I"
},
{
"path": "src/test/java/org/sonar/plugins/stash/fixtures/DummyPostJobContext.java",
"chars": 1184,
"preview": "package org.sonar.plugins.stash.fixtures;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport org.sonar.a"
},
{
"path": "src/test/java/org/sonar/plugins/stash/fixtures/DummyPostJobIssue.java",
"chars": 1708,
"preview": "package org.sonar.plugins.stash.fixtures;\n\nimport static org.sonar.plugins.stash.TestUtils.inputFile;\n\nimport java.nio.f"
},
{
"path": "src/test/java/org/sonar/plugins/stash/fixtures/DummyServer.java",
"chars": 907,
"preview": "package org.sonar.plugins.stash.fixtures;\n\nimport java.io.File;\nimport java.util.Date;\nimport javax.annotation.CheckForN"
},
{
"path": "src/test/java/org/sonar/plugins/stash/fixtures/DummyStashServer.java",
"chars": 4798,
"preview": "package org.sonar.plugins.stash.fixtures;\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.aResponse;\nimpo"
},
{
"path": "src/test/java/org/sonar/plugins/stash/fixtures/MavenSonarFixtures.java",
"chars": 1400,
"preview": "package org.sonar.plugins.stash.fixtures;\n\nimport java.nio.file.FileSystems;\nimport java.nio.file.Path;\n\npublic class Ma"
},
{
"path": "src/test/java/org/sonar/plugins/stash/fixtures/SonarQube.java",
"chars": 4606,
"preview": "package org.sonar.plugins.stash.fixtures;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOExcept"
},
{
"path": "src/test/java/org/sonar/plugins/stash/fixtures/SonarQubeRule.java",
"chars": 925,
"preview": "package org.sonar.plugins.stash.fixtures;\n\nimport org.junit.jupiter.api.extension.AfterAllCallback;\nimport org.junit.jup"
},
{
"path": "src/test/java/org/sonar/plugins/stash/fixtures/SonarScanner.java",
"chars": 2213,
"preview": "package org.sonar.plugins.stash.fixtures;\n\nimport com.google.common.base.Joiner;\n\nimport java.io.File;\nimport java.io.IO"
},
{
"path": "src/test/java/org/sonar/plugins/stash/fixtures/WireMockExtension.java",
"chars": 1715,
"preview": "package org.sonar.plugins.stash.fixtures;\n\nimport com.github.tomakehurst.wiremock.WireMockServer;\nimport com.github.toma"
},
{
"path": "src/test/java/org/sonar/plugins/stash/fixtures/WireMockResponseCallback.java",
"chars": 2467,
"preview": "package org.sonar.plugins.stash.fixtures;\n\n\nimport com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder;\nimp"
},
{
"path": "src/test/java/org/sonar/plugins/stash/issue/MarkdownPrinterTest.java",
"chars": 8911,
"preview": "package org.sonar.plugins.stash.issue;\r\n\r\nimport static org.junit.jupiter.api.Assertions.assertEquals;\r\nimport static or"
},
{
"path": "src/test/java/org/sonar/plugins/stash/issue/StashCommentReportTest.java",
"chars": 5337,
"preview": "package org.sonar.plugins.stash.issue;\r\n\r\nimport org.junit.jupiter.api.BeforeEach;\r\nimport org.junit.jupiter.api.Test;\r\n"
},
{
"path": "src/test/java/org/sonar/plugins/stash/issue/StashCommentTest.java",
"chars": 2913,
"preview": "package org.sonar.plugins.stash.issue;\r\n\r\nimport org.junit.jupiter.api.Test;\r\nimport org.mockito.Mock;\r\n\r\nimport static "
},
{
"path": "src/test/java/org/sonar/plugins/stash/issue/StashDiffReportTest.java",
"chars": 6371,
"preview": "package org.sonar.plugins.stash.issue;\r\n\r\nimport com.google.common.collect.Range;\r\nimport java.util.List;\r\nimport org.ju"
},
{
"path": "src/test/java/org/sonar/plugins/stash/issue/StashDiffTest.java",
"chars": 1559,
"preview": "package org.sonar.plugins.stash.issue;\r\n\r\nimport org.junit.jupiter.api.BeforeEach;\r\nimport org.junit.jupiter.api.Test;\r\n"
},
{
"path": "src/test/java/org/sonar/plugins/stash/issue/StashPullRequestTest.java",
"chars": 4091,
"preview": "package org.sonar.plugins.stash.issue;\r\n\r\nimport static org.junit.jupiter.api.Assertions.assertEquals;\r\nimport static or"
},
{
"path": "src/test/java/org/sonar/plugins/stash/issue/StashTaskTest.java",
"chars": 784,
"preview": "package org.sonar.plugins.stash.issue;\r\n\r\n\r\nimport org.junit.jupiter.api.BeforeEach;\r\nimport org.junit.jupiter.api.Test;"
},
{
"path": "src/test/java/org/sonar/plugins/stash/issue/StashUserTest.java",
"chars": 748,
"preview": "package org.sonar.plugins.stash.issue;\r\n\r\n\r\nimport org.junit.jupiter.api.BeforeEach;\r\nimport org.junit.jupiter.api.Test;"
},
{
"path": "src/test/java/org/sonar/plugins/stash/issue/collector/DiffReportSample.java",
"chars": 69965,
"preview": "package org.sonar.plugins.stash.issue.collector;\r\n\r\npublic class DiffReportSample {\r\n\r\n public static String baseReport"
},
{
"path": "src/test/java/org/sonar/plugins/stash/issue/collector/SonarQubeCollectorTest.java",
"chars": 8114,
"preview": "package org.sonar.plugins.stash.issue.collector;\r\n\r\nimport java.util.HashSet;\r\nimport java.util.Set;\r\n\r\nimport static or"
},
{
"path": "src/test/java/org/sonar/plugins/stash/issue/collector/StashCollectorTest.java",
"chars": 25527,
"preview": "package org.sonar.plugins.stash.issue.collector;\r\n\r\nimport static org.junit.jupiter.api.Assertions.assertEquals;\r\nimport"
},
{
"path": "src/test/resources/fixtures/issue194_stash_diff.json",
"chars": 290298,
"preview": "{\n \"fromHash\": \"820a8e5219b4ebdf44060f8db5a19893abf5360c\",\n \"toHash\": \"7424a87f33d9665ed7a9b6800a2ad7926d10715b\",\n"
},
{
"path": "src/test/resources/foo/module1/src/main/java/Foo.java",
"chars": 106,
"preview": "public class Foo {\n public static void main(String[] args) {\n System.out.println(\"Foo\");\n }\n}"
},
{
"path": "src/test/resources/foo/module2/src/main/java/Bar.java",
"chars": 106,
"preview": "public class Bar {\n public static void main(String[] args) {\n System.out.println(\"Bar\");\n }\n}"
},
{
"path": "src/test/resources/foo/sonar-project.properties",
"chars": 101,
"preview": "sonar.modules=module1,module2\nmodule1.sonar.sources=src/main/java\nmodule2.sonar.sources=src/main/java"
}
]
About this extraction
This page contains the full source code of the AmadeusITGroup/sonar-stash GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 79 files (651.2 KB), approximately 126.5k tokens, and a symbol index with 634 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.