Repository: porscheinformatik/sonarqube-licensecheck
Pull request #370 (2023-03-24 14:43:05)

Add Maven, Javacript pom requirements to README
As per this issue, https://github.com/porscheinformatik/sonarqube-licensecheck/issues/309

Adds required `pom.xml` changes for Maven projects.


Patch:
diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml
index 2275228..5292079 100644
--- a/.github/workflows/verify.yml
+++ b/.github/workflows/verify.yml
@@ -9,14 +9,14 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        java: [ 8, 11, 17 ]
+        java: [ 11, 17, 20 ]
     steps:
       - uses: actions/checkout@v3
       - name: Setup java
         uses: actions/setup-java@v3
         with:
           java-version: ${{ matrix.java }}
-          distribution: adopt
+          distribution: temurin
       - name: Build with Maven
         run: |
           mvn -B clean
diff --git a/.gitignore b/.gitignore
index d3327c5..77956ff 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,4 @@ node/
 .idea/
 *.iml
 .vscode/
+docker/plugins/*.jar
diff --git a/README.md b/README.md
index ea40ce0..c9c98ff 100644
--- a/README.md
+++ b/README.md
@@ -52,7 +52,7 @@ The plugin contains a project dashboard showing a list of dependencies with vers
 
 This plugin is compatible:
 
- * 5.x version with 8.9 LTS and <= 9.7.x
+ * 5.x version with 8.9 LTS and < 10 (9.x is compatible)
  * 4.x version with SonarQube 8.x
  * 3.x version with SonarQube >= 7.9 LTS and < 8.
  * 2.x version with SonarQube >= 6.5 and < 7.
@@ -163,6 +163,18 @@ Groovy, Kotlin, Java, JavaScript, TypeScript
 
 ## Supported Project Types
 
+### Maven + NPM
+
+When using Maven and a Javascript Package Manager, define the `sonar.sources` property to point to the files which contain dependency information. 
+
+```xml
+...
+<properties>
+  <sonar.sources>pom.xml,package.json</sonar.sources>
+<properties>
+...
+```
+
 ### Maven
 
 Maven works if your project/module has a `pom.xml` on its root level (running with Maven, Gradle or SonarScanner).
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..eadea20
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,31 @@
+version: "3"
+
+services:
+  sonarqube:
+    image: sonarqube:10.1.0-community
+    depends_on:
+      - db
+    environment:
+      SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonar
+      SONAR_JDBC_USERNAME: sonar
+      SONAR_JDBC_PASSWORD: sonar
+    volumes:
+      - sonarqube_data:/opt/sonarqube/data
+      - ./target/sonarqube-licensecheck-plugin-6.0.0-SNAPSHOT.jar:/opt/sonarqube/extensions/plugins/sonarqube-licensecheck-plugin-6.0.0-SNAPSHOT.jar
+      - sonarqube_logs:/opt/sonarqube/logs
+    ports:
+      - "9000:9000"
+  db:
+    image: postgres:14-alpine
+    environment:
+      POSTGRES_USER: sonar
+      POSTGRES_PASSWORD: sonar
+    volumes:
+      - postgresql:/var/lib/postgresql
+      - postgresql_data:/var/lib/postgresql/data
+
+volumes:
+  sonarqube_data:
+  sonarqube_logs:
+  postgresql:
+  postgresql_data:
diff --git a/jsconfig.json b/jsconfig.json
new file mode 100644
index 0000000..14f634e
--- /dev/null
+++ b/jsconfig.json
@@ -0,0 +1,5 @@
+{
+  "include": [
+    "./src/main/web/**/*"
+  ]
+}
diff --git a/package.json b/package.json
index 22d4a36..70bf6af 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,7 @@
   "private": true,
   "scripts": {
     "build": "webpack --mode=production",
-    "watch": "concurrently --kill-others \"webpack -w\" \"node test-server.js\"",
+    "watch": "concurrently --kill-others \"webpack --watch --mode development --devtool inline-source-map\" \"node test-server.js\"",
     "generate-icons": "vsvg -s ./svg-icons -t ./src/compiled-icons"
   },
   "author": "Christian Köberl",
diff --git a/pom.xml b/pom.xml
index 1ec03b2..1ab2f07 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
 
   <groupId>at.porscheinformatik.sonarqube.licensecheck</groupId>
   <artifactId>sonarqube-licensecheck-plugin</artifactId>
-  <version>5.1.0-SNAPSHOT</version>
+  <version>6.0.0-SNAPSHOT</version>
   <name>SonarQube License Check Plugin</name>
   <packaging>sonar-plugin</packaging>
 
@@ -44,8 +44,9 @@
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <sonar.sources>src/main,pom.xml,package.json</sonar.sources>
     <sonar.pluginTermsConditionsUrl>https://github.com/porscheinformatik/sonarqube-licensecheck/blob/master/LICENSE</sonar.pluginTermsConditionsUrl>
-    <sonarqube.version>8.9.0.43852</sonarqube.version>
-    <java.version>1.8</java.version>
+    <sonar.apiVersion>9.5.0.71</sonar.apiVersion>
+    <sonar.apiImplVersion>9.5.0.56709</sonar.apiImplVersion>
+    <java.version>11</java.version>
   </properties>
 
   <dependencies>
@@ -73,23 +74,22 @@
       <scope>test</scope>
     </dependency>
     <dependency>
-      <groupId>commons-lang</groupId>
-      <artifactId>commons-lang</artifactId>
-      <version>2.6</version>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
+      <version>3.12.0</version>
       <scope>test</scope>
     </dependency>
     <dependency>
-      <groupId>org.sonarsource.sonarqube</groupId>
+      <groupId>org.sonarsource.api.plugin</groupId>
       <artifactId>sonar-plugin-api</artifactId>
-      <version>${sonarqube.version}</version>
+      <version>${sonar.apiVersion}</version>
       <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>org.sonarsource.sonarqube</groupId>
-      <artifactId>sonar-application</artifactId>
-      <version>${sonarqube.version}</version>
-      <scope>system</scope>
-      <systemPath>${project.build.directory}/sonarqube-${sonarqube.version}/lib/sonar-application-${sonarqube.version}.jar</systemPath>
+      <artifactId>sonar-plugin-api-impl</artifactId>
+      <version>${sonar.apiImplVersion}</version>
+      <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>xpp3</groupId>
@@ -117,33 +117,6 @@
 
   <build>
     <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-dependency-plugin</artifactId>
-        <version>3.3.0</version>
-        <executions>
-          <execution>
-            <id>unpack</id>
-            <phase>clean</phase>
-            <goals>
-              <goal>unpack</goal>
-            </goals>
-            <configuration>
-              <artifactItems>
-                <artifactItem>
-                  <groupId>org.sonarsource.sonarqube</groupId>
-                  <artifactId>sonar-application</artifactId>
-                  <version>${sonarqube.version}</version>
-                  <type>zip</type>
-                  <outputDirectory>${project.build.directory}</outputDirectory>
-                  <overWrite>false</overWrite>
-                </artifactItem>
-              </artifactItems>
-              <includes>sonarqube-*/lib/sonar-application*</includes>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
       <plugin>
         <groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
         <artifactId>sonar-packaging-maven-plugin</artifactId>
@@ -232,6 +205,30 @@
           <npmVersion>8.11.0</npmVersion>
         </configuration>
       </plugin>
+      <plugin>
+        <artifactId>maven-resources-plugin</artifactId>
+        <version>3.3.1</version>
+        <executions>
+            <execution>
+                <id>copy-resources</id>
+                <phase>install</phase>
+                <goals>
+                    <goal>copy-resources</goal>
+                </goals>
+                <configuration>
+                    <outputDirectory>${project.basedir}/docker/plugins</outputDirectory>
+                    <resources>
+                        <resource>
+                            <directory>${project.build.directory}</directory>
+                            <includes>
+                                <include>${project.artifactId}-${project.version}.jar</include>
+                            </includes>
+                        </resource>
+                    </resources>
+                </configuration>
+            </execution>
+        </executions>
+    </plugin>
     </plugins>
   </build>
 
diff --git a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckPlugin.java b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckPlugin.java
index 20c450d..f4fe2bf 100644
--- a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckPlugin.java
+++ b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckPlugin.java
@@ -2,17 +2,12 @@ package at.porscheinformatik.sonarqube.licensecheck;
 
 import at.porscheinformatik.sonarqube.licensecheck.dependencymapping.DependencyMapping;
 import at.porscheinformatik.sonarqube.licensecheck.dependencymapping.DependencyMappingService;
-import at.porscheinformatik.sonarqube.licensecheck.dependencymapping.DependencyMappingSettingsService;
 import at.porscheinformatik.sonarqube.licensecheck.license.License;
 import at.porscheinformatik.sonarqube.licensecheck.license.LicenseService;
-import at.porscheinformatik.sonarqube.licensecheck.license.LicenseSettingsService;
 import at.porscheinformatik.sonarqube.licensecheck.licensemapping.LicenseMapping;
 import at.porscheinformatik.sonarqube.licensecheck.licensemapping.LicenseMappingService;
-import at.porscheinformatik.sonarqube.licensecheck.licensemapping.LicenseMappingSettingsService;
 import at.porscheinformatik.sonarqube.licensecheck.projectlicense.ProjectLicense;
 import at.porscheinformatik.sonarqube.licensecheck.projectlicense.ProjectLicenseService;
-import at.porscheinformatik.sonarqube.licensecheck.projectlicense.ProjectLicenseSettingsService;
-import at.porscheinformatik.sonarqube.licensecheck.web.LicenseCheckWebService;
 import org.sonar.api.Plugin;
 import org.sonar.api.PropertyType;
 import org.sonar.api.config.PropertyDefinition;
@@ -41,14 +36,9 @@ public class LicenseCheckPlugin implements Plugin
             LicenseCheckPageDefinition.class,
             LicenseCheckRulesDefinition.class,
             LicenseService.class,
-            LicenseSettingsService.class,
             DependencyMappingService.class,
-            DependencyMappingSettingsService.class,
             LicenseMappingService.class,
-            LicenseMappingSettingsService.class,
             ProjectLicenseService.class,
-            ProjectLicenseSettingsService.class,
-            LicenseCheckWebService.class,
             PropertyDefinition.builder(LicenseCheckPropertyKeys.LICENSE_SET)
                 .category(LicenseCheckPropertyKeys.CATEGORY)
                 .type(PropertyType.PROPERTY_SET)
@@ -112,7 +102,7 @@ public class LicenseCheckPlugin implements Plugin
                 .category(LicenseCheckPropertyKeys.CATEGORY)
                 .type(PropertyType.PROPERTY_SET)
                 .name("Project Licenses")
-                .description("Allow/disallow licences for specific projects.")
+                .description("Allow/disallow licenses for specific projects.")
                 .fields(
                     PropertyFieldDefinition.build(ProjectLicense.FIELD_PROJECT_KEY)
                         .name("Project key")
@@ -129,22 +119,6 @@ public class LicenseCheckPlugin implements Plugin
                 )
                 .index(6)
                 .build(),
-            PropertyDefinition.builder(LicenseCheckPropertyKeys.LICENSE_KEY)
-                .hidden()
-                .type(PropertyType.TEXT)
-                .build(),
-            PropertyDefinition.builder(LicenseCheckPropertyKeys.ALLOWED_DEPENDENCIES_KEY)
-                .hidden()
-                .type(PropertyType.TEXT)
-                .build(),
-            PropertyDefinition.builder(LicenseCheckPropertyKeys.LICENSE_REGEX)
-                .hidden()
-                .type(PropertyType.TEXT)
-                .build(),
-            PropertyDefinition.builder(LicenseCheckPropertyKeys.PROJECT_LICENSE_KEY)
-                .hidden()
-                .type(PropertyType.TEXT)
-                .build(),
             PropertyDefinition.builder(LicenseCheckPropertyKeys.NPM_RESOLVE_TRANSITIVE_DEPS)
                 .category(LicenseCheckPropertyKeys.CATEGORY)
                 .name("NPM Transitive Dependencies")
diff --git a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckPropertyKeys.java b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckPropertyKeys.java
index d62206f..58dbd49 100644
--- a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckPropertyKeys.java
+++ b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckPropertyKeys.java
@@ -37,29 +37,6 @@ public class LicenseCheckPropertyKeys
      */
     public static final String NPM_RESOLVE_TRANSITIVE_DEPS = "licensecheck.npm.resolvetransitive";
 
-    /**
-     * @deprecated replaced with {@link #LICENSE_SET}
-     */
-    @Deprecated
-    public static final String LICENSE_KEY = "licensecheck.licenses";
-
-    /**
-     * @deprecated replaced with {@link #LICENSE_MAPPING}
-     */
-    @Deprecated
-    public static final String LICENSE_REGEX = "licensecheck.licensesregex";
-
-    /**
-     * @deprecated replaced with {@link #DEPENDENCY_MAPPING}
-     */
-    @Deprecated
-    public static final String ALLOWED_DEPENDENCIES_KEY = "licensecheck.alloweddependencies";
-
-    /**
-     * @deprecated replaced with {@link #PROJECT_LICENSE_SET}
-     */
-    @Deprecated
-    public static final String PROJECT_LICENSE_KEY = "licensecheck.projectlicense";
 
     private LicenseCheckPropertyKeys()
     {
diff --git a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/dependencymapping/DependencyMappingService.java b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/dependencymapping/DependencyMappingService.java
index 274d01e..130d708 100644
--- a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/dependencymapping/DependencyMappingService.java
+++ b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/dependencymapping/DependencyMappingService.java
@@ -1,22 +1,14 @@
 package at.porscheinformatik.sonarqube.licensecheck.dependencymapping;
 
-import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.ALLOWED_DEPENDENCIES_KEY;
 import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.DEPENDENCY_MAPPING;
 import static at.porscheinformatik.sonarqube.licensecheck.dependencymapping.DependencyMapping.FIELD_KEY;
 import static at.porscheinformatik.sonarqube.licensecheck.dependencymapping.DependencyMapping.FIELD_LICENSE;
 import static at.porscheinformatik.sonarqube.licensecheck.dependencymapping.DependencyMapping.FIELD_OVERWRITE;
 
-import java.io.StringReader;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.stream.Collectors;
 
-import javax.json.Json;
-import javax.json.JsonArray;
-import javax.json.JsonObject;
-import javax.json.JsonReader;
-
 import org.sonar.api.config.Configuration;
 import org.sonar.api.scanner.ScannerSide;
 import org.sonar.api.server.ServerSide;
@@ -47,26 +39,4 @@ public class DependencyMappingService
             .collect(Collectors.toList());
     }
 
-    /**
-     * @return maven dependencies via deprecated setting
-     * @deprecated use {@link #getDependencyMappings()} instead
-     */
-    @Deprecated
-    public List<DependencyMapping> getDependencyMappingsOld()
-    {
-        final List<DependencyMapping> dependencyMappings = new ArrayList<>();
-        String dependencyString = configuration.get(ALLOWED_DEPENDENCIES_KEY).orElse("[]");
-
-        JsonReader jsonReader = Json.createReader(new StringReader(dependencyString));
-        JsonArray jsonArray = jsonReader.readArray();
-        jsonReader.close();
-
-        for (int i = 0; i < jsonArray.size(); i++)
-        {
-            JsonObject jsonObject = jsonArray.getJsonObject(i);
-            dependencyMappings.add(
-                new DependencyMapping(jsonObject.getString("nameMatches"), jsonObject.getString("license"), true));
-        }
-        return dependencyMappings;
-    }
 }
diff --git a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/dependencymapping/DependencyMappingSettingsService.java b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/dependencymapping/DependencyMappingSettingsService.java
deleted file mode 100644
index 571bf59..0000000
--- a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/dependencymapping/DependencyMappingSettingsService.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package at.porscheinformatik.sonarqube.licensecheck.dependencymapping;
-
-import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.ALLOWED_DEPENDENCIES_KEY;
-import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.DEPENDENCY_MAPPING;
-import static at.porscheinformatik.sonarqube.licensecheck.dependencymapping.DependencyMapping.FIELD_KEY;
-import static at.porscheinformatik.sonarqube.licensecheck.dependencymapping.DependencyMapping.FIELD_LICENSE;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-
-import org.sonar.api.config.Configuration;
-import org.sonar.api.server.ServerSide;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.server.platform.PersistentSettings;
-
-/**
- * @deprecated for reading use {@link DependencyMappingService}
- */
-@ServerSide
-@Deprecated
-public class DependencyMappingSettingsService
-{
-    private static final Logger LOGGER = Loggers.get(DependencyMappingSettingsService.class);
-
-    /**
-     * This is not official API
-     */
-    private final PersistentSettings persistentSettings;
-
-    private final Configuration configuration;
-    private final DependencyMappingService dependencyMappingService;
-
-    public DependencyMappingSettingsService(PersistentSettings persistentSettings, Configuration configuration,
-        DependencyMappingService dependencyMappingService)
-    {
-        super();
-        this.persistentSettings = persistentSettings;
-        this.configuration = configuration;
-        this.dependencyMappingService = dependencyMappingService;
-    }
-
-    public void init()
-    {
-        if (configuration.get(DEPENDENCY_MAPPING).orElse(null) == null
-            && configuration.get(ALLOWED_DEPENDENCIES_KEY).orElse(null) != null)
-        {
-            LOGGER.info("Migrating old settings to new format for Maven dependency mappings");
-            migrateOldSettings();
-        }
-    }
-
-    private void saveSettings(List<DependencyMapping> dependencyMappings)
-    {
-        Collections.sort(dependencyMappings);
-
-        String indexes = IntStream.range(1, dependencyMappings.size() + 1)
-            .mapToObj(Integer::toString)
-            .collect(Collectors.joining(","));
-        persistentSettings.saveProperty(DEPENDENCY_MAPPING, indexes);
-        for (int i = 0; i < dependencyMappings.size(); i++)
-        {
-            DependencyMapping depMap = dependencyMappings.get(i);
-            String idxProp = "." + (i + 1) + ".";
-            persistentSettings.saveProperty(DEPENDENCY_MAPPING + idxProp + FIELD_KEY, depMap.getKey());
-            persistentSettings.saveProperty(DEPENDENCY_MAPPING + idxProp + FIELD_LICENSE, depMap.getLicense());
-        }
-    }
-
-    private void migrateOldSettings()
-    {
-        saveSettings(dependencyMappingService.getDependencyMappingsOld());
-        persistentSettings.saveProperty(ALLOWED_DEPENDENCIES_KEY, null);
-    }
-}
diff --git a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/license/LicenseService.java b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/license/LicenseService.java
index 00c0a43..c9b3df8 100644
--- a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/license/LicenseService.java
+++ b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/license/LicenseService.java
@@ -1,24 +1,21 @@
 package at.porscheinformatik.sonarqube.licensecheck.license;
 
 import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.LICENSE_SET;
-import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.LICENSE_KEY;
 import static at.porscheinformatik.sonarqube.licensecheck.license.License.FIELD_ALLOWED;
 import static at.porscheinformatik.sonarqube.licensecheck.license.License.FIELD_ID;
 import static at.porscheinformatik.sonarqube.licensecheck.license.License.FIELD_NAME;
 
+import at.porscheinformatik.sonarqube.licensecheck.projectlicense.ProjectLicense;
+import at.porscheinformatik.sonarqube.licensecheck.projectlicense.ProjectLicenseService;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.stream.Collectors;
-
 import org.sonar.api.config.Configuration;
 import org.sonar.api.scanner.ScannerSide;
 import org.sonar.api.scanner.fs.InputProject;
 import org.sonar.api.server.ServerSide;
 
-import at.porscheinformatik.sonarqube.licensecheck.projectlicense.ProjectLicense;
-import at.porscheinformatik.sonarqube.licensecheck.projectlicense.ProjectLicenseService;
-
 @ServerSide
 @ScannerSide
 public class LicenseService
@@ -50,7 +47,7 @@ public class LicenseService
             {
                 if (license.getIdentifier().equals(projectLicense.getLicense()))
                 {
-                    license.setAllowed(projectLicense.getAllowed()); //override the stati of the globalLicenses
+                    license.setAllowed(projectLicense.getAllowed()); // override the status of the globalLicenses
                 }
             }
         }
@@ -71,13 +68,4 @@ public class LicenseService
             }).collect(Collectors.toList());
     }
 
-    /**
-     * @deprecated use {@link #getLicenses()} instead
-     */
-    @Deprecated
-    public List<License> getLicensesOld()
-    {
-        String licenseString = configuration.get(LICENSE_KEY).orElse(null);
-        return License.fromString(licenseString);
-    }
 }
diff --git a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/license/LicenseSettingsService.java b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/license/LicenseSettingsService.java
deleted file mode 100644
index ecacb96..0000000
--- a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/license/LicenseSettingsService.java
+++ /dev/null
@@ -1,107 +0,0 @@
-package at.porscheinformatik.sonarqube.licensecheck.license;
-
-import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.LICENSE_KEY;
-import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.LICENSE_SET;
-import static at.porscheinformatik.sonarqube.licensecheck.license.License.FIELD_ALLOWED;
-import static at.porscheinformatik.sonarqube.licensecheck.license.License.FIELD_ID;
-import static at.porscheinformatik.sonarqube.licensecheck.license.License.FIELD_NAME;
-
-import java.io.InputStream;
-import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-
-import org.sonar.api.config.Configuration;
-import org.sonar.api.server.ServerSide;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.server.platform.PersistentSettings;
-
-import at.porscheinformatik.sonarqube.licensecheck.utils.IOUtils;
-
-/**
- * @deprecated for reading licenses, use {@link LicenseService}
- */
-@ServerSide
-@Deprecated
-public class LicenseSettingsService
-{
-    private static final Logger LOGGER = Loggers.get(LicenseSettingsService.class);
-
-    /**
-     * This is not official API
-     */
-    private final PersistentSettings persistentSettings;
-
-    private final Configuration configuration;
-    private final LicenseService licenseService;
-
-    public LicenseSettingsService(PersistentSettings persistentSettings, Configuration configuration,
-        LicenseService licenseService)
-    {
-        super();
-        this.persistentSettings = persistentSettings;
-        this.configuration = configuration;
-        this.licenseService = licenseService;
-    }
-
-    public void init()
-    {
-        if (configuration.getStringArray(LICENSE_SET).length > 0)
-        {
-            LOGGER.debug("License configuration exists.");
-        }
-        else if (configuration.get(LICENSE_KEY).orElse(null) != null)
-        {
-            LOGGER.info("Old config found, migrating");
-
-            migrateOldSettings();
-        }
-        else
-        {
-            LOGGER.info("No old config found, import spdx_license_list");
-
-            try (InputStream inputStream = LicenseSettingsService.class.getResourceAsStream("spdx_license_list.json"))
-            {
-                String spdxLicenseListJson = IOUtils.readToString(inputStream);
-                List<License> spdxLicenses = License.fromString(spdxLicenseListJson);
-                saveSettings(spdxLicenses);
-            }
-            catch (Exception e)
-            {
-                LOGGER.error("Could not load spdx_license_list.json", e);
-            }
-        }
-    }
-
-    private void migrateOldSettings()
-    {
-        if (licenseService.getLicenses().isEmpty())
-        {
-            List<License> licensesOld = licenseService.getLicensesOld();
-            LOGGER.info("Migrating old config with {} entries", licensesOld.size());
-            if (!licensesOld.isEmpty())
-            {
-                saveSettings(licensesOld);
-            }
-        }
-        persistentSettings.saveProperty(LICENSE_KEY, null);
-    }
-
-    private void saveSettings(List<License> licenses)
-    {
-        String indexes = IntStream.range(1, licenses.size() + 1)
-            .mapToObj(Integer::toString)
-            .collect(Collectors.joining(","));
-        persistentSettings.saveProperty(LICENSE_SET, indexes);
-        for (int i = 0; i < licenses.size(); i++)
-        {
-            License license = licenses.get(i);
-            String idxProp = "." + (i + 1) + ".";
-            persistentSettings.saveProperty(LICENSE_SET + idxProp + FIELD_NAME, license.getName());
-            persistentSettings.saveProperty(LICENSE_SET + idxProp + FIELD_ID, license.getIdentifier());
-            persistentSettings.saveProperty(LICENSE_SET + idxProp + FIELD_ALLOWED, license.getAllowed().toString());
-        }
-        LOGGER.info("Saving the new config with {} entries", licenses.size());
-    }
-}
diff --git a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/licensemapping/LicenseMappingService.java b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/licensemapping/LicenseMappingService.java
index a6b108a..41bf1e7 100644
--- a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/licensemapping/LicenseMappingService.java
+++ b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/licensemapping/LicenseMappingService.java
@@ -1,6 +1,5 @@
 package at.porscheinformatik.sonarqube.licensecheck.licensemapping;
 
-import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.LICENSE_REGEX;
 import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.LICENSE_MAPPING;
 import static at.porscheinformatik.sonarqube.licensecheck.licensemapping.LicenseMapping.FIELD_LICENSE;
 import static at.porscheinformatik.sonarqube.licensecheck.licensemapping.LicenseMapping.FIELD_REGEX;
@@ -45,15 +44,6 @@ public class LicenseMappingService
             }).collect(Collectors.toList());
     }
 
-    /**
-     * @deprecated use {@link #getLicenseMappingList()} instead
-     */
-    @Deprecated
-    public List<LicenseMapping> getLicenseMappingListOld()
-    {
-        return LicenseMapping.fromString(configuration.get(LICENSE_REGEX).orElse(null));
-    }
-
     public Map<Pattern, String> getLicenseMap()
     {
         if (LICENSE_MAP != null)
diff --git a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/licensemapping/LicenseMappingSettingsService.java b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/licensemapping/LicenseMappingSettingsService.java
deleted file mode 100644
index 59f7d10..0000000
--- a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/licensemapping/LicenseMappingSettingsService.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package at.porscheinformatik.sonarqube.licensecheck.licensemapping;
-
-import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.LICENSE_MAPPING;
-import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.LICENSE_REGEX;
-import static at.porscheinformatik.sonarqube.licensecheck.licensemapping.LicenseMapping.FIELD_LICENSE;
-import static at.porscheinformatik.sonarqube.licensecheck.licensemapping.LicenseMapping.FIELD_REGEX;
-
-import java.io.InputStream;
-import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-
-import org.sonar.api.config.Configuration;
-import org.sonar.api.server.ServerSide;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.server.platform.PersistentSettings;
-
-import at.porscheinformatik.sonarqube.licensecheck.utils.IOUtils;
-
-/**
- * @deprecated for reading use {@link LicenseMappingService}
- */
-@ServerSide
-@Deprecated
-public class LicenseMappingSettingsService
-{
-    private static final Logger LOGGER = Loggers.get(LicenseMappingSettingsService.class);
-
-    /**
-     * This is not official API
-     */
-    private final PersistentSettings persistentSettings;
-
-    private final Configuration configuration;
-    private final LicenseMappingService licenseMappingService;
-
-    public LicenseMappingSettingsService(PersistentSettings persistentSettings, Configuration configuration,
-        LicenseMappingService licenseMappingService)
-    {
-        super();
-        this.persistentSettings = persistentSettings;
-        this.configuration = configuration;
-        this.licenseMappingService = licenseMappingService;
-    }
-
-    public void init()
-    {
-        if (configuration.get(LICENSE_MAPPING).orElse(null) != null)
-        {
-            LOGGER.debug("Maven license mappings exists.");
-        }
-        else if (configuration.get(LICENSE_REGEX).orElse(null) != null)
-        {
-            LOGGER.info("Migrating old settings to new format for Maven license mappings");
-            migrateOldSettings();
-        }
-        else
-        {
-            LOGGER.info("No old config found, import default_license_mapping");
-
-            try (InputStream in = LicenseMappingSettingsService.class.getResourceAsStream(
-                "default_license_mapping.json"))
-            {
-                String licenseMappingListString = IOUtils.readToString(in);
-                saveSettings(LicenseMapping.fromString(licenseMappingListString));
-            }
-            catch (Exception e)
-            {
-                LOGGER.error("Could not load default_license_mapping.json", e);
-            }
-        }
-    }
-
-    private void saveSettings(List<LicenseMapping> licensMappings)
-    {
-        String indexes = IntStream.range(1, licensMappings.size() + 1)
-            .mapToObj(Integer::toString)
-            .collect(Collectors.joining(","));
-        persistentSettings.saveProperty(LICENSE_MAPPING, indexes);
-        for (int i = 0; i < licensMappings.size(); i++)
-        {
-            LicenseMapping licenseMapping = licensMappings.get(i);
-            String idxProp = "." + (i + 1) + ".";
-            persistentSettings.saveProperty(LICENSE_MAPPING + idxProp + FIELD_LICENSE, licenseMapping.getLicense());
-            persistentSettings.saveProperty(LICENSE_MAPPING + idxProp + FIELD_REGEX,
-                licenseMapping.getRegex().toString());
-        }
-    }
-
-    private void migrateOldSettings()
-    {
-        saveSettings(licenseMappingService.getLicenseMappingListOld());
-        persistentSettings.saveProperty(LICENSE_REGEX, null);
-    }
-}
diff --git a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/projectlicense/ProjectLicenseService.java b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/projectlicense/ProjectLicenseService.java
index c02d66e..71e1e22 100644
--- a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/projectlicense/ProjectLicenseService.java
+++ b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/projectlicense/ProjectLicenseService.java
@@ -1,6 +1,5 @@
 package at.porscheinformatik.sonarqube.licensecheck.projectlicense;
 
-import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.PROJECT_LICENSE_KEY;
 import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.PROJECT_LICENSE_SET;
 import static at.porscheinformatik.sonarqube.licensecheck.projectlicense.ProjectLicense.FIELD_ALLOWED;
 import static at.porscheinformatik.sonarqube.licensecheck.projectlicense.ProjectLicense.FIELD_LICENSE;
@@ -12,7 +11,6 @@ import java.util.Collection;
 import java.util.List;
 import java.util.stream.Collectors;
 
-import org.codehaus.plexus.util.StringUtils;
 import org.sonar.api.config.Configuration;
 import org.sonar.api.scanner.ScannerSide;
 import org.sonar.api.server.ServerSide;
@@ -42,22 +40,6 @@ public class ProjectLicenseService
             }).collect(Collectors.toList());
     }
 
-    /**
-     * @deprecated use {@link #getProjectLicenseList()} instead
-     */
-    @Deprecated
-    public List<ProjectLicense> getProjectLicenseListOld()
-    {
-        String projectLicenseString = configuration.get(PROJECT_LICENSE_KEY).orElse(null);
-
-        if (StringUtils.isNotEmpty(projectLicenseString))
-        {
-            return ProjectLicense.fromString(projectLicenseString);
-        }
-
-        return new ArrayList<>();
-    }
-
     public Collection<ProjectLicense> getProjectLicenseList(String projectKey)
     {
         Collection<ProjectLicense> allProjectLicenses = getProjectLicenseList();
diff --git a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/projectlicense/ProjectLicenseSettingsService.java b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/projectlicense/ProjectLicenseSettingsService.java
deleted file mode 100644
index e2cd726..0000000
--- a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/projectlicense/ProjectLicenseSettingsService.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package at.porscheinformatik.sonarqube.licensecheck.projectlicense;
-
-import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.PROJECT_LICENSE_KEY;
-import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.PROJECT_LICENSE_SET;
-import static at.porscheinformatik.sonarqube.licensecheck.projectlicense.ProjectLicense.FIELD_ALLOWED;
-import static at.porscheinformatik.sonarqube.licensecheck.projectlicense.ProjectLicense.FIELD_LICENSE;
-import static at.porscheinformatik.sonarqube.licensecheck.projectlicense.ProjectLicense.FIELD_PROJECT_KEY;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-
-import org.sonar.api.config.Configuration;
-import org.sonar.api.server.ServerSide;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.server.platform.PersistentSettings;
-
-/**
- * @deprecated for reading use {@link ProjectLicenseService}
- */
-@ServerSide
-@Deprecated
-public class ProjectLicenseSettingsService
-{
-    private static final Logger LOGGER = Loggers.get(ProjectLicenseSettingsService.class);
-
-    /**
-     * This is not official API
-     */
-    private final PersistentSettings persistentSettings;
-
-    private final Configuration configuration;
-    private final ProjectLicenseService projectLicenseService;
-
-    public ProjectLicenseSettingsService(PersistentSettings persistentSettings, Configuration configuration,
-        ProjectLicenseService projectLicenseService)
-    {
-        super();
-        this.persistentSettings = persistentSettings;
-        this.configuration = configuration;
-        this.projectLicenseService = projectLicenseService;
-    }
-
-    public void init()
-    {
-        if (configuration.get(PROJECT_LICENSE_SET).orElse(null) == null &&
-            configuration.get(PROJECT_LICENSE_KEY).orElse(null) != null)
-        {
-            LOGGER.info("Migrating old settings to new format for project licenses");
-
-            migrateOldSettings();
-        }
-    }
-
-    private void migrateOldSettings()
-    {
-        saveSettings(projectLicenseService.getProjectLicenseListOld());
-        persistentSettings.saveProperty(PROJECT_LICENSE_KEY, null);
-    }
-
-    private void saveSettings(List<ProjectLicense> projectLicenses)
-    {
-        Collections.sort(projectLicenses);
-
-        String indexes = IntStream.range(1, projectLicenses.size() + 1)
-            .mapToObj(Integer::toString)
-            .collect(Collectors.joining(","));
-        persistentSettings.saveProperty(PROJECT_LICENSE_SET, indexes);
-        for (int i = 0; i < projectLicenses.size(); i++)
-        {
-            ProjectLicense projectLicense = projectLicenses.get(i);
-            String idxProp = "." + (i + 1) + ".";
-            persistentSettings.saveProperty(PROJECT_LICENSE_SET + idxProp + FIELD_PROJECT_KEY,
-                projectLicense.getProjectKey());
-            persistentSettings.saveProperty(PROJECT_LICENSE_SET + idxProp + FIELD_LICENSE, projectLicense.getLicense());
-            persistentSettings.saveProperty(PROJECT_LICENSE_SET + idxProp + FIELD_ALLOWED,
-                projectLicense.getAllowed().toString());
-        }
-    }
-}
diff --git a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/web/LicenseCheckWebService.java b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/web/LicenseCheckWebService.java
deleted file mode 100644
index bc4067b..0000000
--- a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/web/LicenseCheckWebService.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package at.porscheinformatik.sonarqube.licensecheck.web;
-
-import org.sonar.api.server.ws.WebService;
-
-import at.porscheinformatik.sonarqube.licensecheck.license.LicenseSettingsService;
-import at.porscheinformatik.sonarqube.licensecheck.dependencymapping.DependencyMappingSettingsService;
-import at.porscheinformatik.sonarqube.licensecheck.licensemapping.LicenseMappingSettingsService;
-import at.porscheinformatik.sonarqube.licensecheck.projectlicense.ProjectLicenseSettingsService;
-
-/**
- * Web service for license-check rest API.
- */
-public class LicenseCheckWebService implements WebService
-{
-    private final LicenseSettingsService licenseSettingsService;
-    private final ProjectLicenseSettingsService projectLicenseSettingsService;
-    private final LicenseMappingSettingsService licenseMappingSettingsService;
-    private final DependencyMappingSettingsService dependencyMappingSettingsService;
-
-    public LicenseCheckWebService(LicenseSettingsService licenseSettingsService,
-        ProjectLicenseSettingsService projectLicenseSettingsService,
-        LicenseMappingSettingsService licenseMappingSettingsService,
-        DependencyMappingSettingsService dependencyMappingSettingsService)
-    {
-        super();
-        this.licenseSettingsService = licenseSettingsService;
-        this.projectLicenseSettingsService = projectLicenseSettingsService;
-        this.licenseMappingSettingsService = licenseMappingSettingsService;
-        this.dependencyMappingSettingsService = dependencyMappingSettingsService;
-    }
-
-    @Override
-    public void define(Context context)
-    {
-        // Init all services
-        licenseSettingsService.init();
-        projectLicenseSettingsService.init();
-        licenseMappingSettingsService.init();
-        dependencyMappingSettingsService.init();
-    }
-}
diff --git a/src/main/web/configuration.js b/src/main/web/configuration.js
index 8998e59..e22903b 100644
--- a/src/main/web/configuration.js
+++ b/src/main/web/configuration.js
@@ -1,9 +1,6 @@
 import Vue from 'vue';
-import LicensesPage from './configuration/licenses-page';
-import DependencyMappingsPage from './configuration/dependency-mappings-page';
-import LicenseMappingsPage from './configuration/license-mappings-page';
-import ProjectLicensesPage from './configuration/project-licenses-page';
 import ModalDialog from './modal-dialog';
+import Configuration from './configuration/configuration.vue';
 import VueSVGIcon from 'vue-svgicon'
 
 Vue.use(VueSVGIcon)
@@ -13,44 +10,14 @@ window.registerExtension('licensecheck/configuration', function (options) {
   Vue.component('modal-dialog', ModalDialog);
   const app = new Vue({
     el: options.el,
-    data: () => {
-      return {
-        currentRoute: 'licenses'
-      }
-    },
-    methods: {
-      activateCategory(event, route) {
-        event.preventDefault();
-        this.currentRoute = route;
-        window.history.pushState({}, document.title, `?category=${route}`)
-      },
-      routeListener() {
-        const result = window.location.search.match(/category=([^&=]*)/);
-        if (result && result.length > 1) {
-          this.currentRoute = result[1];
-        } else {
-          this.currentRoute = 'licenses';
+    // Cannot use `template`. Eval is blocked by CSP
+    render(createElement) {
+      return createElement(Configuration, {
+        props: {
+          options
         }
-      }
-    },
-    components: { LicensesPage, DependencyMappingsPage, LicenseMappingsPage, ProjectLicensesPage},
-    template: `<div class="page page-limited">
-  <ul class="tabs">
-    <li><a href="?category=licenses" :class="{selected: currentRoute==='licenses'}" @click="activateCategory($event, 'licenses')">Licenses</a></li>
-    <li><a href="?category=license-mappings" :class="{selected: currentRoute==='license-mappings'}" @click="activateCategory($event, 'license-mappings')">License Mappings</a></li>
-    <li><a href="?category=dependency-mappings" :class="{selected: currentRoute==='dependency-mappings'}" @click="activateCategory($event, 'dependency-mappings')">Dependency Mappings</a></li>
-    <li><a href="?category=project-licenses" :class="{selected: currentRoute==='project-licenses'}" @click="activateCategory($event, 'project-licenses')">Project Licenses</a></li>
-  </ul>
-  <br>
-  <licenses-page v-if="currentRoute === 'licenses'">
-  </licenses-page>
-  <dependency-mappings-page v-if="currentRoute === 'dependency-mappings'">
-  </dependency-mappings-page>
-  <license-mappings-page v-if="currentRoute === 'license-mappings'">
-  </license-mappings-page>
-  <project-licenses-page v-if="currentRoute === 'project-licenses'">
-  </project-licenses-page>
-</div>`
+      });
+    }
   });
 
   window.addEventListener('popstate', app.routeListener);
diff --git a/src/main/web/configuration/configuration.vue b/src/main/web/configuration/configuration.vue
new file mode 100644
index 0000000..f0fc753
--- /dev/null
+++ b/src/main/web/configuration/configuration.vue
@@ -0,0 +1,52 @@
+<template>
+  <div class="page page-limited">
+    <ul class="tabs">
+      <li><a href="?category=licenses" :class="{selected: currentRoute==='licenses'}" @click="activateCategory($event, 'licenses')">Licenses</a></li>
+      <li><a href="?category=license-mappings" :class="{selected: currentRoute==='license-mappings'}" @click="activateCategory($event, 'license-mappings')">License Mappings</a></li>
+      <li><a href="?category=dependency-mappings" :class="{selected: currentRoute==='dependency-mappings'}" @click="activateCategory($event, 'dependency-mappings')">Dependency Mappings</a></li>
+      <li><a href="?category=project-licenses" :class="{selected: currentRoute==='project-licenses'}" @click="activateCategory($event, 'project-licenses')">Project Licenses</a></li>
+    </ul>
+    <br>
+    <licenses-page v-if="currentRoute === 'licenses'">
+    </licenses-page>
+    <dependency-mappings-page v-if="currentRoute === 'dependency-mappings'">
+    </dependency-mappings-page>
+    <license-mappings-page v-if="currentRoute === 'license-mappings'">
+    </license-mappings-page>
+    <project-licenses-page v-if="currentRoute === 'project-licenses'">
+    </project-licenses-page>
+  </div>
+</template>
+
+<script>
+
+import LicensesPage from './licenses-page';
+import DependencyMappingsPage from './dependency-mappings-page';
+import LicenseMappingsPage from './license-mappings-page';
+import ProjectLicensesPage from './project-licenses-page';
+
+export default {
+  data: () => {
+      return {
+        currentRoute: 'licenses'
+      }
+    },
+    methods: {
+      activateCategory(event, route) {
+        event.preventDefault();
+        this.currentRoute = route;
+        window.history.pushState({}, document.title, `?category=${route}`)
+      },
+      routeListener() {
+        const result = window.location.search.match(/category=([^&=]*)/);
+        if (result && result.length > 1) {
+          this.currentRoute = result[1];
+        } else {
+          this.currentRoute = 'licenses';
+        }
+      }
+    },
+    components: { LicensesPage, DependencyMappingsPage, LicenseMappingsPage, ProjectLicensesPage},
+}
+</script>
+
diff --git a/src/main/web/dashboard.js b/src/main/web/dashboard.js
index fb5f976..802db64 100644
--- a/src/main/web/dashboard.js
+++ b/src/main/web/dashboard.js
@@ -1,66 +1,18 @@
 import Vue from 'vue';
-import Licenses from './dashboard/licenses.vue';
-import Dependencies from './dashboard/dependencies.vue';
-import saveAs from 'file-saverjs';
-import buildExcel from './dashboard/excel-builder';
+import Dashboard from './dashboard/dashboard.vue';
  
 window.registerExtension('licensecheck/dashboard', function (options) {
 
   const app = new Vue({
     el: options.el,
-    data: () => {
-      return {
-        licenses: [],
-        dependencies: [],
-        component: options.component
-      }
-    },
-    created() {
-      let params = new URLSearchParams(window.location.search);
-      let request = {
-        component : options.component.key,
-        metricKeys : "licensecheck.license,licensecheck.dependency"
-      };
-      if (params.has("branch")) {
-        request.branch = params.get("branch");
-      } else if (params.has("pullRequest")) {
-        request.pullRequest = params.get("pullRequest");
-      }
-      window.SonarRequest
-        .getJSON("/api/measures/component", request)
-        .then(response => {
-          response.component.measures.forEach(measure => {
-            if (measure.metric === 'licensecheck.license') {
-              this.licenses = JSON.parse(measure.value);
-            } else if (measure.metric === 'licensecheck.dependency') {
-              this.dependencies = JSON.parse(measure.value);
-            }
-          });
-          this.dependencies.forEach(dependency => {
-            dependency.status = 'Unknown';
-            this.licenses.forEach(license => {
-              if (dependency.license === license.identifier) {
-                dependency.status = license.status === 'true' ? 'Allowed' : 'Forbidden';
-              }
-            });
-          });
-        });
-    },
-    methods: {
-      exportExcel() {
-        const blob = new Blob([buildExcel(this.dependencies, this.licenses)], {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"});
-        saveAs(blob, `license-check-${this.component.key}.xls`);
-      }
-    },
-    template: `<div class="page page-limited">
-  <h1>License Check</h1>
-  <div><a href="#" v-on:click="exportExcel()">Export to Excel</a></div>
-  <p>&nbsp;</p>
-  <licenses :licenses="licenses"></licenses>
-  <p>&nbsp;</p>
-  <dependencies :dependencies="dependencies"></dependencies>
-</div>`,
-    components: { Licenses, Dependencies },
+    // Cannot use `template`. Eval is blocked by CSP
+    render(createElement) {
+      return createElement(Dashboard, {
+        props: {
+          options
+        }
+      })
+    }
   });
 
   return function () {
diff --git a/src/main/web/dashboard/dashboard.vue b/src/main/web/dashboard/dashboard.vue
new file mode 100644
index 0000000..dc11478
--- /dev/null
+++ b/src/main/web/dashboard/dashboard.vue
@@ -0,0 +1,67 @@
+<template>
+  <div class="page page-limited">
+    <h1>License Check</h1>
+    <div><a href="#" v-on:click="exportExcel()">Export to Excel</a></div>
+    <p>&nbsp;</p>
+    <licenses :licenses="licenses"></licenses>
+    <p>&nbsp;</p>
+    <dependencies :dependencies="dependencies"></dependencies>
+  </div>
+</template>
+
+<script>
+import Licenses from './licenses.vue';
+import Dependencies from './dependencies.vue';
+import saveAs from 'file-saverjs';
+import buildExcel from './excel-builder';
+
+export default {
+  props: ["options"],
+  data() {
+    return {
+      licenses: [],
+      dependencies: [],
+      component: this.options.component
+    }
+  },
+  created() {
+    let params = new URLSearchParams(window.location.search);
+    let request = {
+
+      component : this.component.key,
+      metricKeys : "licensecheck.license,licensecheck.dependency"
+    };
+    if (params.has("branch")) {
+      request.branch = params.get("branch");
+    } else if (params.has("pullRequest")) {
+      request.pullRequest = params.get("pullRequest");
+    }
+    window.SonarRequest
+      .getJSON("/api/measures/component", request)
+      .then(response => {
+        response.component.measures.forEach(measure => {
+          if (measure.metric === 'licensecheck.license') {
+            this.licenses = JSON.parse(measure.value);
+          } else if (measure.metric === 'licensecheck.dependency') {
+            this.dependencies = JSON.parse(measure.value);
+          }
+        });
+        this.dependencies.forEach(dependency => {
+          dependency.status = 'Unknown';
+          this.licenses.forEach(license => {
+            if (dependency.license === license.identifier) {
+              dependency.status = license.status === 'true' ? 'Allowed' : 'Forbidden';
+            }
+          });
+        });
+      });
+  },
+  methods: {
+    exportExcel() {
+      const blob = new Blob([buildExcel(this.dependencies, this.licenses)], {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"});
+      saveAs(blob, `license-check-${this.component.key}.xls`);
+    }
+  },
+  components: { Licenses, Dependencies },
+};
+</script>


Test patch:
diff --git a/src/test/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckSensorTest.java b/src/test/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckSensorTest.java
index c8fe5ef..9d5da49 100644
--- a/src/test/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckSensorTest.java
+++ b/src/test/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckSensorTest.java
@@ -13,7 +13,6 @@ import java.util.Optional;
 import java.util.Set;
 
 import org.apache.commons.lang3.reflect.FieldUtils;
-import org.jetbrains.annotations.NotNull;
 import org.junit.Test;
 import org.sonar.api.batch.fs.InputModule;
 import org.sonar.api.batch.sensor.SensorContext;
@@ -60,11 +59,11 @@ public class LicenseCheckSensorTest
         FieldUtils.writeField(sensor, "scanners", scanners, true);
         SensorContext context = mock(SensorContext.class);
         InputProject project = mock(InputProject.class);
-        when(project.key()).thenReturn("myproject");
         InputModule module = mock(InputModule.class);
-        when(module.key()).thenReturn("myproject");
-        when(context.project()).thenReturn(project);
+        when(module.key()).thenReturn("my-project");
         when(context.module()).thenReturn(module);
+        when(project.key()).thenReturn("my-project");
+        when(context.project()).thenReturn(project);
         NewMeasure measure = mock(NewMeasure.class);
         when(measure.forMetric(any())).thenReturn(measure);
         when(measure.withValue(any())).thenReturn(measure);
@@ -76,7 +75,6 @@ public class LicenseCheckSensorTest
         verify(measure, times(7)).save(); // 5 metrics + dependencies + licenses
     }
 
-    @NotNull
     private Configuration createConfiguration()
     {
         Configuration configuration = mock(Configuration.class);
diff --git a/src/test/java/at/porscheinformatik/sonarqube/licensecheck/dependencymapping/DependencyMappingSettingsServiceTest.java b/src/test/java/at/porscheinformatik/sonarqube/licensecheck/dependencymapping/DependencyMappingSettingsServiceTest.java
deleted file mode 100644
index 0c097ea..0000000
--- a/src/test/java/at/porscheinformatik/sonarqube/licensecheck/dependencymapping/DependencyMappingSettingsServiceTest.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package at.porscheinformatik.sonarqube.licensecheck.dependencymapping;
-
-import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.ALLOWED_DEPENDENCIES_KEY;
-import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.DEPENDENCY_MAPPING;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.util.Optional;
-
-import org.junit.Test;
-import org.sonar.api.config.Configuration;
-import org.sonar.server.platform.PersistentSettings;
-
-public class DependencyMappingSettingsServiceTest
-{
-    @Test
-    public void migrate()
-    {
-        PersistentSettings persistentSettings = mock(PersistentSettings.class);
-
-        Configuration configuration = mock(Configuration.class);
-        when(configuration.get(any())).thenReturn(Optional.empty());
-        when(configuration.get(ALLOWED_DEPENDENCIES_KEY)).thenReturn(Optional.of(
-            "[{ \"nameMatches\": \"test\", \"license\": \"TEST\" }, "
-                + "{\"nameMatches\":\"test2\", \"license\":\"TEST\"}]"));
-
-        DependencyMappingSettingsService service =
-            new DependencyMappingSettingsService(persistentSettings, configuration,
-                new DependencyMappingService(configuration));
-
-        service.init();
-
-        verify(persistentSettings).saveProperty(DEPENDENCY_MAPPING, "1,2");
-        verify(persistentSettings).saveProperty(DEPENDENCY_MAPPING + ".1." + DependencyMapping.FIELD_KEY, "test");
-        verify(persistentSettings).saveProperty(ALLOWED_DEPENDENCIES_KEY, null);
-
-    }
-}
diff --git a/src/test/java/at/porscheinformatik/sonarqube/licensecheck/license/LicenseSettingsServiceTest.java b/src/test/java/at/porscheinformatik/sonarqube/licensecheck/license/LicenseSettingsServiceTest.java
deleted file mode 100644
index 45b016f..0000000
--- a/src/test/java/at/porscheinformatik/sonarqube/licensecheck/license/LicenseSettingsServiceTest.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package at.porscheinformatik.sonarqube.licensecheck.license;
-
-import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.LICENSE_KEY;
-import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.LICENSE_SET;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.util.Optional;
-
-import org.junit.Test;
-import org.sonar.api.config.Configuration;
-import org.sonar.server.platform.PersistentSettings;
-
-import at.porscheinformatik.sonarqube.licensecheck.projectlicense.ProjectLicenseService;
-
-public class LicenseSettingsServiceTest
-{
-    @Test
-    public void migrate()
-    {
-        Configuration configuration = mock(Configuration.class);
-        PersistentSettings persistentSettings = mock(PersistentSettings.class);
-        LicenseSettingsService licenseSettingsService = new LicenseSettingsService(persistentSettings, configuration,
-            new LicenseService(configuration, mock(ProjectLicenseService.class)));
-        when(configuration.getStringArray(anyString())).thenReturn(new String[0]);
-        when(configuration.get(anyString())).thenReturn(Optional.empty());
-
-        when(configuration.get(LICENSE_KEY)).thenReturn(Optional.of(
-            "[{\"identifier\":\"MIT\",\"name\":\"MIT License\",\"status\":\"true\"},"
-                + "{\"identifier\":\"MY\",\"name\":\"MY License\",\"status\":\"false\"}]"));
-
-        licenseSettingsService.init();
-
-        verify(persistentSettings).saveProperty(LICENSE_SET, "1,2");
-        verify(persistentSettings).saveProperty(LICENSE_KEY, null);
-    }
-}
diff --git a/src/test/java/at/porscheinformatik/sonarqube/licensecheck/licensemapping/LicenseMappingSettingsServiceTest.java b/src/test/java/at/porscheinformatik/sonarqube/licensecheck/licensemapping/LicenseMappingSettingsServiceTest.java
deleted file mode 100644
index c86327e..0000000
--- a/src/test/java/at/porscheinformatik/sonarqube/licensecheck/licensemapping/LicenseMappingSettingsServiceTest.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package at.porscheinformatik.sonarqube.licensecheck.licensemapping;
-
-import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.LICENSE_MAPPING;
-import static at.porscheinformatik.sonarqube.licensecheck.LicenseCheckPropertyKeys.LICENSE_REGEX;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.util.Optional;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.sonar.api.config.Configuration;
-import org.sonar.server.platform.PersistentSettings;
-
-public class LicenseMappingSettingsServiceTest
-{
-    Configuration configuration;
-    PersistentSettings persistentSettings;
-    LicenseMappingSettingsService licenseMappingSettingsService;
-
-    @Before
-    public void setup()
-    {
-        configuration = mock(Configuration.class);
-        persistentSettings = mock(PersistentSettings.class);
-        licenseMappingSettingsService =
-            new LicenseMappingSettingsService(persistentSettings, configuration,
-                new LicenseMappingService(configuration));
-
-        when(configuration.get(anyString())).thenReturn(Optional.empty());
-    }
-
-    @Test
-    public void init()
-    {
-        licenseMappingSettingsService.init();
-
-        verify(persistentSettings, atLeastOnce()).saveProperty(anyString(), anyString());
-    }
-
-    @Test
-    public void migrate()
-    {
-        when(configuration.get(LICENSE_REGEX)).thenReturn(
-            Optional.of("[{ \"regex\": \"test\", \"license\": \"TEST\" }]"));
-
-        licenseMappingSettingsService.init();
-
-        verify(persistentSettings).saveProperty(LICENSE_MAPPING, "1");
-        verify(persistentSettings).saveProperty(LICENSE_REGEX, null);
-    }
-}