Merge pull request 'added sonarqube config' (#41) from enhancement/Configure-Sonarqube-server into main
SonarQube Analysis / Build and Analyze (push) Successful in 2m1s

This commit was merged in pull request #41.
This commit is contained in:
2026-06-27 03:23:57 +02:00
6 changed files with 119 additions and 14 deletions
+61
View File
@@ -0,0 +1,61 @@
name: SonarQube Analysis
on:
push:
branches:
- main
pull_request:
branches:
- main
workflow_dispatch:
jobs:
analyze:
name: Build and Analyze
runs-on: ubuntu-latest
services:
postgres:
image: postgres:18.4
env:
POSTGRES_USER: vaessl
POSTGRES_PASSWORD: vaessl
POSTGRES_DB: vaessl_test
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Checkout Code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Set up JDK 25
uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4.8.0
with:
java-version: "25"
distribution: "zulu" # Alternative distribution options are available.
- name: Make Gradle Executable
run: chmod +x ./gradlew
working-directory: backend
- name: Build and Analyze
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
DB_URL: jdbc:postgresql://postgres:5432/vaessl_test
DB_TEST_URL: jdbc:postgresql://postgres:5432/vaessl_test
DB_USERNAME: vaessl
DB_PASSWORD: vaessl
PG_DRIVER_CLASS_NAME: org.postgresql.Driver
OPENAI_BASE_URL: https://api.openai.com/v1
OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
ALLOWED_ORIGINS: http://localhost:3000
run: ./gradlew build sonar --info
working-directory: backend
+38 -2
View File
@@ -1,7 +1,11 @@
val wiremockVersion = "3.12.0"
plugins { plugins {
java java
jacoco
id("org.springframework.boot") version "4.1.0" id("org.springframework.boot") version "4.1.0"
id("io.spring.dependency-management") version "1.1.7" id("io.spring.dependency-management") version "1.1.7"
id("org.sonarqube") version "7.3.0.8198"
} }
group = "com.vaessl" group = "com.vaessl"
@@ -13,6 +17,15 @@ java {
} }
} }
sonar {
properties {
property("sonar.projectKey", "Vaessl")
property("sonar.projectName", "Vaessl")
property("sonar.coverage.jacoco.xmlReportPaths",
"${layout.buildDirectory.get()}/reports/jacoco/test/jacocoTestReport.xml")
}
}
configurations { configurations {
compileOnly { compileOnly {
extendsFrom(configurations.annotationProcessor.get()) extendsFrom(configurations.annotationProcessor.get())
@@ -25,6 +38,7 @@ repositories {
extra["springAiVersion"] = "2.0.0" extra["springAiVersion"] = "2.0.0"
dependencies { dependencies {
implementation("org.springframework.boot:spring-boot-starter-data-jpa") implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-session-jdbc") implementation("org.springframework.boot:spring-boot-starter-session-jdbc")
@@ -32,17 +46,24 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-validation") implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.boot:spring-boot-starter-webmvc") implementation("org.springframework.boot:spring-boot-starter-webmvc")
implementation("org.springframework.ai:spring-ai-starter-model-openai") implementation("org.springframework.ai:spring-ai-starter-model-openai")
compileOnly("org.projectlombok:lombok") compileOnly("org.projectlombok:lombok")
developmentOnly("org.springframework.boot:spring-boot-devtools") developmentOnly("org.springframework.boot:spring-boot-devtools")
runtimeOnly("org.postgresql:postgresql") runtimeOnly("org.postgresql:postgresql")
annotationProcessor("org.projectlombok:lombok") annotationProcessor("org.projectlombok:lombok")
testImplementation("org.springframework.boot:spring-boot-starter-data-jpa-test") testImplementation("org.springframework.boot:spring-boot-starter-data-jpa-test")
// testImplementation("org.springframework.boot:spring-boot-starter-security-test") // testImplementation("org.springframework.boot:spring-boot-starter-security-test")
testImplementation("org.springframework.boot:spring-boot-starter-validation-test") testImplementation("org.springframework.boot:spring-boot-starter-validation-test")
testImplementation("org.springframework.boot:spring-boot-starter-webmvc-test") testImplementation("org.springframework.boot:spring-boot-starter-webmvc-test")
testImplementation("org.wiremock:wiremock-standalone:$wiremockVersion")
testImplementation("org.springframework.boot:spring-boot-starter-session-jdbc-test")
testRuntimeOnly("org.junit.platform:junit-platform-launcher") testRuntimeOnly("org.junit.platform:junit-platform-launcher")
testImplementation("org.wiremock:wiremock-standalone:3.12.0") }
testImplementation("org.springframework.boot:spring-boot-starter-session-jdbc-test")}
dependencyManagement { dependencyManagement {
imports { imports {
@@ -56,4 +77,19 @@ tasks.withType<JavaCompile> {
tasks.withType<Test> { tasks.withType<Test> {
useJUnitPlatform() useJUnitPlatform()
finalizedBy(tasks.jacocoTestReport)
testLogging {
events("failed", "standardError")
exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
showExceptions = true
showCauses = true
showStackTraces = true
}
}
tasks.jacocoTestReport {
dependsOn(tasks.withType<Test>())
reports {
xml.required = true
}
} }
@@ -1,9 +1,9 @@
package com.vaessl.app.connection; package com.vaessl.app.connection;
import java.time.Instant; import java.time.Instant;
import java.util.EnumMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -18,8 +18,11 @@ public class ConnectionService {
private final ConnectionRepository cRepository; private final ConnectionRepository cRepository;
public ConnectionService(List<ConnectionProvider> providers, ConnectionRepository cRepository) { public ConnectionService(List<ConnectionProvider> providers, ConnectionRepository cRepository) {
this.providerRegistry = providers.stream() Map<ServiceType, ConnectionProvider> registry = new EnumMap<>(ServiceType.class);
.collect(Collectors.toMap(ConnectionProvider::getServiceType, p -> p)); for (ConnectionProvider provider : providers) {
registry.put(provider.getServiceType(), provider);
}
this.providerRegistry = registry;
this.cRepository = cRepository; this.cRepository = cRepository;
} }
@@ -49,7 +52,8 @@ public class ConnectionService {
return new LoginResult(saved.getId(), response.expiresAt()); return new LoginResult(saved.getId(), response.expiresAt());
} }
public ConnectionStatusResponse getConnectionStatus(ServiceType serviceType, Long connectionId) { public ConnectionStatusResponse getConnectionStatus(ServiceType serviceType,
Long connectionId) {
ConnectionEntity entity = cRepository.findById(connectionId).orElse(null); ConnectionEntity entity = cRepository.findById(connectionId).orElse(null);
if (entity == null) if (entity == null)
return null; return null;
@@ -58,7 +62,7 @@ public class ConnectionService {
Instant expiresAt = (provider != null) ? provider.getTokenExpiry(entity) : null; Instant expiresAt = (provider != null) ? provider.getTokenExpiry(entity) : null;
boolean connected = expiresAt == null || expiresAt.isAfter(Instant.now()); boolean connected = expiresAt == null || expiresAt.isAfter(Instant.now());
return new ConnectionStatusResponse(serviceType.name(), entity.getAppUrl(), entity.getUsername(), return new ConnectionStatusResponse(serviceType.name(), entity.getAppUrl(),
expiresAt, connected); entity.getUsername(), expiresAt, connected);
} }
} }
@@ -3,7 +3,6 @@ package com.vaessl.app.exception;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.ProblemDetail; import org.springframework.http.ProblemDetail;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.bind.annotation.RestControllerAdvice;
@@ -24,7 +23,7 @@ public class GlobalExceptionHandler {
public ProblemDetail handleEmptyCredentialInput(MethodArgumentNotValidException e) { public ProblemDetail handleEmptyCredentialInput(MethodArgumentNotValidException e) {
String defaultMessages = e.getBindingResult().getFieldErrors().stream() String defaultMessages = e.getBindingResult().getFieldErrors().stream()
.map(FieldError::getDefaultMessage).collect(Collectors.joining(", ")); .map(field -> field.getDefaultMessage()).collect(Collectors.joining(", "));
return ProblemDetail.forStatusAndDetail(BAD_REQUEST_EMPTY_FIELDS.getStatus(), return ProblemDetail.forStatusAndDetail(BAD_REQUEST_EMPTY_FIELDS.getStatus(),
BAD_REQUEST_EMPTY_FIELDS.getMessage() + " [" + defaultMessages + "]"); BAD_REQUEST_EMPTY_FIELDS.getMessage() + " [" + defaultMessages + "]");
@@ -14,6 +14,7 @@ public interface SearchProvider extends ServiceProvider {
* Executes a search query against the remote service and returns matching results. * Executes a search query against the remote service and returns matching results.
* *
* @param request the search request containing the query string, app URL, and user credentials * @param request the search request containing the query string, app URL, and user credentials
* @param pageable the Pageable interface
* @return a list of Page<SearchResponse> items matching the query * @return a list of Page<SearchResponse> items matching the query
*/ */
Page<SearchResponse> getSearchResults(SearchRequest request, Pageable pageable); Page<SearchResponse> getSearchResults(SearchRequest request, Pageable pageable);
@@ -1,8 +1,8 @@
package com.vaessl.app.search; package com.vaessl.app.search;
import java.util.EnumMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
@@ -16,8 +16,12 @@ public class SearchService {
private final Map<ServiceType, SearchProvider> providerRegistry; private final Map<ServiceType, SearchProvider> providerRegistry;
public SearchService(List<SearchProvider> providers) { public SearchService(List<SearchProvider> providers) {
this.providerRegistry = Map.copyOf(providers.stream() Map<ServiceType, SearchProvider> registry = new EnumMap<>(ServiceType.class);
.collect(Collectors.toMap(SearchProvider::getServiceType, p -> p))); for (SearchProvider provider : providers) {
registry.put(provider.getServiceType(), provider);
}
this.providerRegistry = registry;
} }
/** /**