2022-07-28 16:51:36 +02:00
plugins {
2022-08-04 09:41:27 +02:00
id 'java'
2025-01-12 13:40:32 +01:00
id 'org.springframework.boot' version '3.4.1'
2025-01-09 09:28:30 +01:00
id 'io.spring.dependency-management' version '1.1.7'
2024-01-03 09:24:14 +01:00
id 'io.openapiprocessor.openapi-processor' version '2023.2'
2024-10-10 09:31:43 +02:00
id 'com.github.jk1.dependency-license-report' version '2.9'
2025-01-09 09:28:30 +01:00
id "org.owasp.dependencycheck" version "11.1.1"
2025-01-12 13:40:32 +01:00
id "com.diffplug.spotless" version "7.0.1"
2022-08-19 10:11:19 +02:00
id 'jacoco'
2024-01-03 09:24:14 +01:00
id 'info.solidsoft.pitest' version '1.15.0'
2022-08-24 18:27:40 +02:00
id 'se.patrikerdes.use-latest-versions' version '0.2.18'
2024-04-02 13:24:25 +02:00
id 'com.github.ben-manes.versions' version '0.51.0'
2022-07-28 16:51:36 +02:00
}
group = 'net.hostsharing'
version = '0.0.1-SNAPSHOT'
2022-08-04 12:24:54 +02:00
wrapper {
distributionType = Wrapper . DistributionType . BIN
2024-01-03 09:24:14 +01:00
gradleVersion = '8.5'
2022-08-04 12:24:54 +02:00
}
2025-01-12 13:40:32 +01:00
// FIXME: Mockito is currently self-attaching to enable the inline-mock-maker. This will no longer work in future releases of the JDK. Please add Mockito as an agent to your build what is described in Mockito's documentation: https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#0.3
2022-07-28 16:51:36 +02:00
configurations {
compileOnly {
extendsFrom annotationProcessor
}
2022-08-23 14:10:50 +02:00
testCompile {
2022-09-10 14:49:01 +02:00
extendsFrom testAnnotationProcessor
2022-08-23 14:10:50 +02:00
// Only JUNit 5 (Jupiter) should be used at compile time.
// For runtime it's still needed by testcontainers, though.
exclude group: 'junit' , module: 'junit'
exclude group: 'org.junit.vintage' , module: 'junit-vintage-engine'
}
2022-07-28 16:51:36 +02:00
}
repositories {
mavenCentral ( )
2022-10-28 15:57:06 +02:00
maven { url 'https://repo.spring.io/milestone' }
maven { url 'https://repo.spring.io/snapshot' }
2022-07-28 16:51:36 +02:00
}
2022-08-05 13:10:11 +02:00
java {
toolchain {
2024-01-03 09:24:14 +01:00
languageVersion = JavaLanguageVersion . of ( 21 )
2024-01-05 15:16:12 +01:00
vendor = JvmVendorSpec . ADOPTIUM
implementation = JvmImplementation . VENDOR_SPECIFIC
2022-08-05 13:10:11 +02:00
}
}
2022-07-28 16:51:36 +02:00
ext {
set ( 'testcontainersVersion' , "1.17.3" )
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-rest'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-web'
2022-10-15 11:29:56 +02:00
implementation 'org.springframework.boot:spring-boot-starter-validation'
2024-12-03 12:39:19 +01:00
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-security'
2025-01-09 09:28:30 +01:00
implementation 'com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.10.0'
2025-01-12 13:40:32 +01:00
implementation 'org.springdoc:springdoc-openapi:2.8.1'
2024-10-10 09:31:43 +02:00
implementation 'org.postgresql:postgresql:42.7.4'
2025-01-09 09:28:30 +01:00
implementation 'org.liquibase:liquibase-core:4.30.0'
implementation 'io.hypersistence:hypersistence-utils-hibernate-63:3.9.0'
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2'
2024-01-03 09:24:14 +01:00
implementation 'org.openapitools:jackson-databind-nullable:0.2.6'
2025-01-09 09:28:30 +01:00
implementation 'org.apache.commons:commons-text:1.13.0'
implementation 'net.java.dev.jna:jna:5.16.0'
implementation 'org.modelmapper:modelmapper:3.2.2'
2024-10-10 09:31:43 +02:00
implementation 'org.iban4j:iban4j:3.2.10-RELEASE'
2025-01-12 13:40:32 +01:00
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.1'
2024-10-10 09:31:43 +02:00
implementation 'org.reflections:reflections:0.10.2'
2024-01-04 09:10:20 +01:00
2022-07-28 16:51:36 +02:00
compileOnly 'org.projectlombok:lombok'
2022-09-10 14:49:01 +02:00
testCompileOnly 'org.projectlombok:lombok'
2022-07-28 16:51:36 +02:00
2025-01-12 13:44:01 +01:00
developmentOnly 'org.springframework.boot:spring-boot-devtools'
2022-07-28 16:51:36 +02:00
annotationProcessor 'org.projectlombok:lombok'
2022-09-10 14:49:01 +02:00
testAnnotationProcessor 'org.projectlombok:lombok'
2022-07-28 16:51:36 +02:00
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.testcontainers:testcontainers'
testImplementation 'org.testcontainers:junit-jupiter'
2024-01-03 09:24:14 +01:00
testImplementation 'org.junit.jupiter:junit-jupiter'
2022-07-28 16:51:36 +02:00
testImplementation 'org.testcontainers:postgresql'
2024-10-10 09:31:43 +02:00
testImplementation 'com.tngtech.archunit:archunit-junit5:1.3.0'
2022-08-09 17:51:50 +02:00
testImplementation 'io.rest-assured:spring-mock-mvc'
2024-10-10 09:31:43 +02:00
testImplementation 'org.hamcrest:hamcrest-core:3.0'
2024-01-03 09:24:14 +01:00
testImplementation 'org.pitest:pitest-junit5-plugin:1.2.1'
2024-01-23 15:11:23 +01:00
testImplementation 'org.junit.jupiter:junit-jupiter-api'
2024-12-23 12:49:43 +01:00
testImplementation 'org.wiremock:wiremock-standalone:3.10.0'
2022-07-28 16:51:36 +02:00
}
dependencyManagement {
imports {
mavenBom "org.testcontainers:testcontainers-bom:${testcontainersVersion}"
}
}
2022-08-19 10:11:19 +02:00
// Java Compiler Options
2022-08-05 13:10:11 +02:00
tasks . withType ( JavaCompile ) {
2022-08-19 10:11:19 +02:00
options . compilerArgs + = [
"-parameters" // keep parameter names => no need for @Param for SpringData
]
2022-08-05 13:10:11 +02:00
}
2022-10-25 13:08:17 +02:00
// Configure tests
2022-07-28 16:51:36 +02:00
tasks . named ( 'test' ) {
useJUnitPlatform ( )
2022-10-25 13:08:17 +02:00
jvmArgs '-Duser.language=en'
jvmArgs '-Duser.country=US'
2022-07-28 16:51:36 +02:00
}
2022-08-04 09:41:27 +02:00
2022-08-19 10:11:19 +02:00
// OpenAPI Source Code Generation
2022-08-08 16:54:35 +02:00
openapiProcessor {
2022-09-02 13:11:15 +02:00
springRoot {
processorName 'spring'
2024-04-17 08:27:08 +02:00
processor 'io.openapiprocessor:openapi-processor-spring:2022.5'
2024-11-18 12:11:16 +01:00
apiPath "$projectDir/src/main/resources/api-definition/api-definition.yaml"
mapping "$projectDir/src/main/resources/api-definition/api-mappings.yaml"
2024-01-23 15:11:23 +01:00
targetDir "$buildDir/generated/sources/openapi-javax"
2022-09-02 13:11:15 +02:00
showWarnings true
openApiNullable true
}
springRbac {
processorName 'spring'
2024-04-17 08:27:08 +02:00
processor 'io.openapiprocessor:openapi-processor-spring:2022.5'
2022-09-02 13:11:15 +02:00
apiPath "$projectDir/src/main/resources/api-definition/rbac/rbac.yaml"
mapping "$projectDir/src/main/resources/api-definition/rbac/api-mappings.yaml"
2024-01-23 15:11:23 +01:00
targetDir "$buildDir/generated/sources/openapi-javax"
2022-09-02 13:11:15 +02:00
showWarnings true
openApiNullable true
}
springTest {
processorName 'spring'
2024-04-17 08:27:08 +02:00
processor 'io.openapiprocessor:openapi-processor-spring:2022.5'
2022-09-02 13:11:15 +02:00
apiPath "$projectDir/src/main/resources/api-definition/test/test.yaml"
mapping "$projectDir/src/main/resources/api-definition/test/api-mappings.yaml"
2024-01-23 15:11:23 +01:00
targetDir "$buildDir/generated/sources/openapi-javax"
2022-08-08 16:54:35 +02:00
showWarnings true
2022-08-09 17:51:50 +02:00
openApiNullable true
2022-08-08 16:54:35 +02:00
}
2024-04-16 11:21:34 +02:00
springHsOffice {
2022-09-06 11:07:08 +02:00
processorName 'spring'
2024-04-17 08:27:08 +02:00
processor 'io.openapiprocessor:openapi-processor-spring:2022.5'
2022-09-13 13:27:52 +02:00
apiPath "$projectDir/src/main/resources/api-definition/hs-office/hs-office.yaml"
mapping "$projectDir/src/main/resources/api-definition/hs-office/api-mappings.yaml"
2024-01-23 15:11:23 +01:00
targetDir "$buildDir/generated/sources/openapi-javax"
2022-09-06 11:07:08 +02:00
showWarnings true
openApiNullable true
}
2024-04-16 11:21:34 +02:00
springHsBooking {
processorName 'spring'
2024-04-17 08:27:08 +02:00
processor 'io.openapiprocessor:openapi-processor-spring:2022.5'
2024-04-16 11:21:34 +02:00
apiPath "$projectDir/src/main/resources/api-definition/hs-booking/hs-booking.yaml"
mapping "$projectDir/src/main/resources/api-definition/hs-booking/api-mappings.yaml"
targetDir "$buildDir/generated/sources/openapi-javax"
showWarnings true
openApiNullable true
}
2024-04-23 11:14:48 +02:00
springHsHosting {
processorName 'spring'
processor 'io.openapiprocessor:openapi-processor-spring:2022.5'
apiPath "$projectDir/src/main/resources/api-definition/hs-hosting/hs-hosting.yaml"
mapping "$projectDir/src/main/resources/api-definition/hs-hosting/api-mappings.yaml"
targetDir "$buildDir/generated/sources/openapi-javax"
showWarnings true
openApiNullable true
}
2022-08-08 16:54:35 +02:00
}
sourceSets . main . java . srcDir 'build/generated/sources/openapi'
2022-09-06 11:07:08 +02:00
abstract class ProcessSpring extends DefaultTask { }
tasks . register ( 'processSpring' , ProcessSpring )
2024-04-16 11:21:34 +02:00
[ 'processSpringRoot' ,
'processSpringRbac' ,
'processSpringTest' ,
'processSpringHsOffice' ,
2024-04-23 11:14:48 +02:00
'processSpringHsBooking' ,
'processSpringHsHosting'
2024-04-16 11:21:34 +02:00
] . each {
2022-09-06 11:07:08 +02:00
project . tasks . processSpring . dependsOn it
2022-09-02 13:11:15 +02:00
}
2022-09-06 11:07:08 +02:00
project . tasks . processResources . dependsOn processSpring
project . tasks . compileJava . dependsOn processSpring
2022-08-08 16:54:35 +02:00
2022-10-28 15:57:06 +02:00
// Rename javax to jakarta in OpenApi generated java files because
2024-04-23 10:42:24 +02:00
// io.openapiprocessor.openapi-processor 2022.5 does not yet support the openapiprocessor useSpringBoot3 config option.
2024-04-17 08:27:08 +02:00
// TODO.impl: Upgrade to io.openapiprocessor.openapi-processor >= 2024.2
// and use either `bean-validation: true` in api-mapping.yaml or `useSpringBoot3 true` (not sure where exactly).
2022-10-28 15:57:06 +02:00
task openApiGenerate ( type: Copy ) {
from "$buildDir/generated/sources/openapi-javax"
into "$buildDir/generated/sources/openapi"
filter { line - > line . replaceAll ( 'javax' , 'jakarta' ) }
}
compileJava . source "$buildDir/generated/sources/openapi"
compileJava . dependsOn openApiGenerate
openApiGenerate . dependsOn processSpring
2022-08-19 10:11:19 +02:00
// Spotless Code Formatting
2022-08-04 09:41:27 +02:00
spotless {
java {
2024-01-04 12:39:39 +01:00
removeUnusedImports ( )
2025-01-09 09:28:30 +01:00
leadingTabsToSpaces ( 4 )
2022-08-04 09:41:27 +02:00
endWithNewline ( )
toggleOffOn ( )
2022-08-08 16:54:35 +02:00
2022-08-09 17:51:50 +02:00
target fileTree ( rootDir ) {
2022-08-08 16:54:35 +02:00
include '**/*.java'
exclude '**/generated/**/*.java'
}
2022-08-04 09:41:27 +02:00
}
}
2022-08-04 12:26:41 +02:00
project . tasks . check . dependsOn ( spotlessCheck )
2024-01-04 12:39:39 +01:00
// HACK: no idea why spotless uses the output of these tasks, but we get warnings without those
project . tasks . spotlessJava . dependsOn (
tasks . generateLicenseReport ,
tasks . pitest ,
tasks . jacocoTestReport ,
tasks . processResources ,
tasks . processTestResources )
2022-08-04 12:26:41 +02:00
2022-08-19 10:11:19 +02:00
// OWASP Dependency Security Test
2022-08-04 12:26:41 +02:00
dependencyCheck {
2024-01-03 09:24:14 +01:00
nvd {
2024-01-06 18:11:24 +01:00
apiKey = project . properties [ 'OWASP_API_KEY' ] // set it in ~/.gradle/gradle.properties
2024-01-03 09:24:14 +01:00
delay = 16000
}
2022-08-04 12:26:41 +02:00
format = 'ALL'
suppressionFile = 'etc/owasp-dependency-check-suppression.xml'
failOnError = true
2024-01-03 09:24:14 +01:00
failBuildOnCVSS = 5
2022-08-04 12:26:41 +02:00
}
project . tasks . check . dependsOn ( dependencyCheckAnalyze )
2022-09-06 11:07:49 +02:00
project . tasks . dependencyCheckAnalyze . doFirst { // Why not doLast? See README.md!
2022-09-02 08:58:15 +02:00
println "OWASP Dependency Security Report: file:///${project.rootDir}/build/reports/dependency-check-report.html"
}
2022-08-08 10:03:26 +02:00
2022-08-19 10:11:19 +02:00
// License Check
2022-08-08 10:03:26 +02:00
licenseReport {
excludeBoms = true
allowedLicensesFile = new File ( "$projectDir/etc/allowed-licenses.json" )
}
project . tasks . check . dependsOn ( checkLicense )
2022-08-19 10:11:19 +02:00
// JaCoCo Test Code Coverage
jacoco {
2024-01-03 18:13:22 +01:00
toolVersion = "0.8.10"
2022-08-19 10:11:19 +02:00
}
test {
finalizedBy jacocoTestReport // generate report after tests
excludes = [
2022-09-02 13:11:15 +02:00
'net.hostsharing.hsadminng.**.generated.**' ,
2022-08-19 10:11:19 +02:00
]
2024-01-23 15:11:23 +01:00
useJUnitPlatform {
2024-11-05 13:58:31 +01:00
excludeTags 'importOfficeData' , 'importHostingData' , 'scenarioTest'
2024-01-23 15:11:23 +01:00
}
2022-08-19 10:11:19 +02:00
}
jacocoTestReport {
dependsOn test
afterEvaluate {
classDirectories . setFrom ( files ( classDirectories . files . collect {
fileTree ( dir: it , exclude: [
2022-09-02 13:11:15 +02:00
"net/hostsharing/hsadminng/**/generated/**/*.class" ,
2022-09-13 13:27:52 +02:00
"net/hostsharing/hsadminng/hs/HsadminNgApplication.class"
2022-08-19 10:11:19 +02:00
] )
} ) )
}
2022-09-06 11:07:49 +02:00
doFirst { // Why not doLast? See README.md!
2022-08-19 10:11:19 +02:00
println "HTML Jacoco Test Code Coverage Report: file://${reports.html.outputLocation.get()}/index.html"
}
}
project . tasks . check . dependsOn ( jacocoTestCoverageVerification )
jacocoTestCoverageVerification {
violationRules {
rule {
limit {
2024-10-11 17:06:44 +02:00
minimum = 0.80 // TODO.test: improve instruction coverage
2022-08-19 10:11:19 +02:00
}
}
// element: PACKAGE, BUNDLE, CLASS, SOURCEFILE or METHOD
// counter: INSTRUCTION, BRANCH, LINE, COMPLEXITY, METHOD, or CLASS
// value: TOTALCOUNT, COVEREDCOUNT, MISSEDCOUNT, COVEREDRATIO or MISSEDRATIO
rule {
element = 'CLASS'
excludes = [
2022-09-02 13:11:15 +02:00
'net.hostsharing.hsadminng.**.generated.**' ,
2024-10-11 17:06:44 +02:00
'net.hostsharing.hsadminng.rbac.test.dom.TestDomainEntity' ,
2022-08-19 10:11:19 +02:00
'net.hostsharing.hsadminng.HsadminNgApplication' ,
2022-10-21 17:05:28 +02:00
'net.hostsharing.hsadminng.ping.PingController' ,
2024-10-11 17:06:44 +02:00
'net.hostsharing.hsadminng.rbac.generator.*' ,
'net.hostsharing.hsadminng.rbac.grant.RbacGrantsDiagramService' ,
'net.hostsharing.hsadminng.rbac.grant.RbacGrantsDiagramService.Node' ,
'net.hostsharing.hsadminng.**.*Repository' ,
2022-10-21 17:05:28 +02:00
'net.hostsharing.hsadminng.mapper.Mapper'
2022-08-19 10:11:19 +02:00
]
limit {
counter = 'LINE'
value = 'COVEREDRATIO'
2024-10-11 17:06:44 +02:00
minimum = 0.75 // TODO.test: improve line coverage
2022-08-19 10:11:19 +02:00
}
}
rule {
element = 'METHOD'
excludes = [
2022-09-02 13:11:15 +02:00
'net.hostsharing.hsadminng.**.generated.**' ,
2022-08-26 14:13:02 +02:00
'net.hostsharing.hsadminng.HsadminNgApplication.main' ,
2022-10-21 17:05:28 +02:00
'net.hostsharing.hsadminng.ping.PingController.*'
2022-08-28 17:30:27 +02:00
]
2022-08-19 10:11:19 +02:00
limit {
counter = 'BRANCH'
value = 'COVEREDRATIO'
2024-10-11 17:06:44 +02:00
minimum = 0.00 // TODO.test: improve branch coverage
2022-08-19 10:11:19 +02:00
}
}
}
}
2024-01-23 15:11:23 +01:00
tasks . register ( 'importOfficeData' , Test ) {
useJUnitPlatform {
2024-07-22 11:30:33 +02:00
includeTags 'importOfficeData'
2024-01-23 15:11:23 +01:00
}
group 'verification'
description 'run the import jobs as tests'
2024-02-01 14:48:15 +01:00
mustRunAfter spotlessJava
2024-01-23 15:11:23 +01:00
}
2024-07-22 11:30:33 +02:00
tasks . register ( 'importHostingAssets' , Test ) {
useJUnitPlatform {
includeTags 'importHostingAssets'
}
group 'verification'
description 'run the import jobs as tests'
mustRunAfter spotlessJava
}
2024-01-23 15:11:23 +01:00
2024-11-05 13:58:31 +01:00
tasks . register ( 'scenarioTests' , Test ) {
useJUnitPlatform {
includeTags 'scenarioTest'
}
group 'verification'
description 'run the import jobs as tests'
mustRunAfter spotlessJava
}
2022-08-26 16:06:27 +02:00
// pitest mutation testing
pitest {
targetClasses = [ 'net.hostsharing.hsadminng.**' ]
excludedClasses = [
'net.hostsharing.hsadminng.config.**' ,
2024-10-11 17:06:44 +02:00
// 'net.hostsharing.hsadminng.**.*Controller',
2022-09-02 13:11:15 +02:00
'net.hostsharing.hsadminng.**.generated.**'
2022-08-26 16:06:27 +02:00
]
targetTests = [ 'net.hostsharing.hsadminng.**.*UnitTest' , 'net.hostsharing.hsadminng.**.*RestTest' ]
excludedTestClasses = [ '**AcceptanceTest*' , '**IntegrationTest*' ]
2024-10-11 17:06:44 +02:00
pitestVersion = '1.17.0'
2022-10-28 15:57:06 +02:00
junit5PluginVersion = '1.1.0'
2022-08-26 16:06:27 +02:00
threads = 4
// As Java unit tests are pretty pointless in our case, this maybe makes not much sense.
2022-09-06 11:07:08 +02:00
mutationThreshold = 71
coverageThreshold = 57
2022-09-30 16:27:18 +02:00
testStrengthThreshold = 87
2022-08-26 16:06:27 +02:00
outputFormats = [ 'XML' , 'HTML' ]
timestampedReports = false
}
project . tasks . check . dependsOn ( project . tasks . pitest )
2022-09-06 11:07:49 +02:00
project . tasks . pitest . doFirst { // Why not doLast? See README.md!
2022-08-26 16:06:27 +02:00
println "PiTest Mutation Report: file:///${project.rootDir}/build/reports/pitest/index.html"
}
2022-08-24 18:27:40 +02:00
// Dependency Versions Upgrade
useLatestVersions {
finalizedBy check
}
2022-10-21 06:39:57 +02:00
def isNonStable = { String version - >
def stableKeyword = [ 'RELEASE' , 'FINAL' , 'GA' ] . any { it - > version . toUpperCase ( ) . contains ( it ) }
def regex = /^[0-9,.v-]+(-r)?$/
return ! stableKeyword & & ! ( version = = ~ regex )
}
tasks . named ( "dependencyUpdates" ) . configure {
rejectVersionIf {
isNonStable ( it . candidate . version )
}
}
2024-11-05 13:58:31 +01:00
// Generate HTML from Markdown scenario-test-reports using Pandoc:
tasks . register ( 'convertMarkdownToHtml' ) {
description = 'Generates HTML from Markdown scenario-test-reports using Pandoc.'
group = 'Conversion'
// Define the template file and input directory
2024-11-15 11:54:18 +01:00
def templateFile = file ( 'doc/scenarios/.template.html' )
2024-11-05 13:58:31 +01:00
// Task configuration and execution
doFirst {
// Check if pandoc is installed
try {
exec {
commandLine 'pandoc' , '--version'
}
} catch ( Exception ) {
throw new GradleException ( "Pandoc is not installed or not found in the system path." )
}
// Check if the template file exists
if ( ! templateFile . exists ( ) ) {
2024-11-15 11:54:18 +01:00
throw new GradleException ( "Template file 'doc/scenarios/.template.html' not found." )
2024-11-05 13:58:31 +01:00
}
}
doLast {
// Gather all Markdown files in the current directory
2024-11-15 11:54:18 +01:00
fileTree ( dir: '.' , include: 'build/doc/scenarios/*.md' ) . each { file - >
2024-11-05 13:58:31 +01:00
// Corrected way to create the output file path
def outputFile = new File ( file . parent , file . name . replaceAll ( /\.md$/ , '.html' ) )
// Execute pandoc for each markdown file
exec {
commandLine 'pandoc' , file . absolutePath , '--template' , templateFile . absolutePath , '-o' , outputFile . absolutePath
}
println "Converted ${file.name} to ${outputFile.name}"
}
}
}
2024-11-15 11:54:18 +01:00
convertMarkdownToHtml . dependsOn scenarioTests
2024-11-25 16:06:24 +01:00
// shortcut for compiling all files
tasks . register ( 'compile' ) {
dependsOn 'compileJava' , 'compileTestJava'
}