Skip to content

Commit f871f9d

Browse files
authored
Merge pull request #47 from swisscom/feature/41317-refactor-into-multi-module-gradle-project-with-service-and-gui-modules
#41317 Split project into a service and a UI module
2 parents 9e0d9ce + 627698e commit f871f9d

64 files changed

Lines changed: 1274 additions & 612 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

build.gradle.kts

Lines changed: 16 additions & 393 deletions
Large diffs are not rendered by default.
Lines changed: 392 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,392 @@
1+
import io.gitlab.arturbosch.detekt.Detekt
2+
import org.springframework.boot.gradle.tasks.bundling.BootJar
3+
import java.net.URI
4+
import java.time.Duration
5+
6+
group = "com.swisscom.health.des.cdr.client.service"
7+
8+
val outputDir: Provider<Directory> = layout.buildDirectory.dir(".")
9+
10+
plugins {
11+
alias(libs.plugins.dockerCompose)
12+
alias(libs.plugins.springBoot)
13+
alias(libs.plugins.springDependencyManagement)
14+
alias(libs.plugins.detekt)
15+
jacoco
16+
application
17+
kotlin("jvm").version(libs.versions.kotlin.lang)
18+
kotlin("plugin.spring").version(libs.versions.kotlin.lang)
19+
// https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.kotlin.configuration-properties
20+
// KAPT is end of life, but KSP is not supported yet: https://github.com/spring-projects/spring-boot/issues/28046
21+
kotlin("kapt").version(libs.versions.kotlin.lang)
22+
`maven-publish`
23+
idea
24+
}
25+
26+
idea {
27+
module {
28+
isDownloadJavadoc = true
29+
isDownloadSources = true
30+
}
31+
}
32+
33+
tasks.bootRun {
34+
environment["JDK_JAVA_OPTIONS"] =
35+
listOfNotNull(
36+
environment["JDK_JAVA_OPTIONS"],
37+
"-Djavax.net.ssl.trustStore=src/main/resources/caddy_truststore.p12",
38+
"-Djavax.net.ssl.trustStorePassword=changeit",
39+
"-Djdk.net.hosts.file=src/main/resources/msal4j_hosts"
40+
).joinToString(" ")
41+
}
42+
43+
application {
44+
mainClass = "com.swisscom.health.des.cdr.client.CdrClientApplicationKt"
45+
}
46+
47+
gradle.taskGraph.whenReady(
48+
closureOf<TaskExecutionGraph> {
49+
println("Tasks to be executed: ${this.allTasks}")
50+
application {
51+
applicationDefaultJvmArgs =
52+
if (gradle.taskGraph.hasTask(":bootRun")) {
53+
// when running the client locally via the `bootRun` target
54+
listOf("-Dspring.profiles.active=client,dev")
55+
} else {
56+
// gets picked up by application plugin when building the distribution as the
57+
// value of `DEFAULT_JVM_OPTS` in the generated start script
58+
listOf("-Dspring.profiles.active=client,customer")
59+
}
60+
}
61+
}
62+
)
63+
64+
project.afterEvaluate {
65+
// https://github.com/detekt/detekt/issues/6198#issuecomment-2265183695
66+
configurations.matching { it.name == "detekt" }.all {
67+
resolutionStrategy.eachDependency {
68+
if (requested.group == "org.jetbrains.kotlin") {
69+
useVersion(io.gitlab.arturbosch.detekt.getSupportedKotlinVersion())
70+
}
71+
}
72+
}
73+
}
74+
75+
dependencies {
76+
implementation(platform(libs.spring.boot.dependencies))
77+
implementation(platform(libs.spring.cloud.dependencies))
78+
implementation(libs.kache)
79+
implementation(libs.msal4j)
80+
implementation(libs.okhttp)
81+
implementation(libs.kfswatch)
82+
implementation(libs.kotlin.logging)
83+
implementation(libs.micrometer.tracing)
84+
implementation(libs.micrometer.tracing.bridge.otel)
85+
implementation(libs.logstash.encoder)
86+
implementation(libs.kotlin.reflect)
87+
implementation(libs.kotlin.stdlib)
88+
implementation(libs.kotlinx.coroutines.core)
89+
implementation(libs.kotlinx.coroutines.reactor) // to enable @Scheduled on Kotlin suspending functions
90+
implementation(libs.spring.boot.starter.actuator)
91+
implementation(libs.spring.boot.starter.web)
92+
implementation(libs.spring.retry)
93+
implementation(libs.spring.cloud.context)
94+
implementation(libs.jackson.dataformat.yaml)
95+
96+
// Note: At the time of writing the configuration processor seems to be broken; might be related to the upgrade to Kotlin 2.x
97+
// Enable annotation processing via menu File | Settings | Build, Execution, Deployment
98+
// | Compiler | Annotation Processors | Enable annotation processing
99+
kapt("org.springframework.boot:spring-boot-configuration-processor")
100+
101+
testImplementation(libs.jacocoCore)
102+
testImplementation(libs.spring.boot.starter.test)
103+
testImplementation(libs.kotlinx.coroutines.test)
104+
testImplementation(libs.mock.webserver) {
105+
// Unfortunately we cannot exclude JUnit 4 as MockWebServer implements interfaces from that version
106+
// exclude(group = "junit", module = "junit")
107+
}
108+
testImplementation(libs.mockk) {
109+
exclude(group = "junit", module = "junit")
110+
}
111+
testImplementation(libs.junit.jupiter)
112+
testImplementation(libs.micrometer.tracing.test)
113+
testImplementation(libs.spring.mockk)
114+
testImplementation(libs.awaitility)
115+
116+
}
117+
118+
kapt {
119+
correctErrorTypes = true
120+
mapDiagnosticLocations = true
121+
includeCompileClasspath = false
122+
}
123+
124+
springBoot {
125+
buildInfo()
126+
}
127+
128+
kotlin {
129+
jvmToolchain (libs.versions.jdk.get().toInt())
130+
compilerOptions {
131+
freeCompilerArgs = listOf("-Xjsr305=strict")
132+
}
133+
}
134+
135+
tasks.test {
136+
useJUnitPlatform {
137+
excludeTags(Constants.INTEGRATION_TEST_TAG)
138+
}
139+
}
140+
141+
val jacocoTestCoverageVerification = tasks.named<JacocoCoverageVerification>("jacocoTestCoverageVerification") {
142+
violationRules {
143+
/**
144+
* Ensure tests cover at least 75% of the LoC.
145+
*/
146+
rule {
147+
classDirectories.setFrom(files(classDirectories.files.map {
148+
fileTree(it) {
149+
setExcludes(
150+
// CdrApiClient is tested with integration tests; need to find out how to merge the jacoco reports of the two test types
151+
listOf(
152+
"**/com/swisscom/health/des/cdr/client/handler/CdrApiClient*"
153+
)
154+
)
155+
}
156+
}))
157+
limit {
158+
minimum = "0.70".toBigDecimal()
159+
}
160+
}
161+
}
162+
}
163+
164+
val jacocoTestReport = tasks.named<JacocoReport>("jacocoTestReport") {
165+
finalizedBy(jacocoTestCoverageVerification) // Verify after generating the report.
166+
group = "Reporting"
167+
168+
reports {
169+
xml.required.set(true)
170+
xml.outputLocation.set(File("${outputDir.get().asFile.absolutePath}/reports/jacoco.xml"))
171+
csv.required.set(false)
172+
html.required.set(true)
173+
html.outputLocation.set(File("${outputDir.get().asFile.absolutePath}/reports/coverage"))
174+
}
175+
}
176+
177+
tasks.withType<Test> {
178+
// show log output produced by tests in console
179+
testLogging.showStandardStreams = false
180+
181+
useJUnitPlatform {
182+
includeEngines("junit-jupiter")
183+
}
184+
finalizedBy(jacocoTestReport)
185+
186+
jvmArgs(
187+
// tests_hosts is used to redirect msal4j, which insists on talking to the Mothership, to our docker compose setup
188+
"-Djdk.net.hosts.file=${layout.projectDirectory.file("src/test/resources/test_hosts").asFile.absolutePath}"
189+
)
190+
}
191+
192+
jacoco {
193+
toolVersion = libs.versions.jacoco.get()
194+
}
195+
196+
tasks.named<BootJar>("bootJar") {
197+
manifest {
198+
// Required so application name and version get rendered in the banner.txt; see
199+
// https://stackoverflow.com/questions/34519759/application-version-does-not-show-up-in-spring-boot-banner-txt
200+
attributes("Implementation-Title" to rootProject.name)
201+
attributes("Implementation-Version" to archiveVersion)
202+
}
203+
}
204+
205+
// https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto.properties-and-configuration.expand-properties.gradle
206+
// and https://stackoverflow.com/questions/40096007/how-to-configure-the-processresources-task-in-a-gradle-kotlin-build
207+
tasks.processResources {
208+
filesMatching("**/application.yaml") {
209+
expand(project.properties)
210+
}
211+
}
212+
213+
tasks.withType<Detekt> {
214+
reports {
215+
xml {
216+
required.set(true)
217+
outputLocation.set(File("${outputDir.get().asFile.absolutePath}/reports/detekt.xml"))
218+
}
219+
html.required.set(false)
220+
sarif.required.set(false)
221+
txt.required.set(false)
222+
}
223+
}
224+
225+
/**
226+
* Detekt
227+
* See https://docs.sonarqube.org/latest/analysis/scan/sonarscanner-for-gradle/
228+
*/
229+
detekt {
230+
buildUponDefaultConfig = false // preconfigure defaults
231+
allRules = true
232+
parallel = true
233+
config.setFrom(files("$rootDir/config/detekt.yml")) // Global Detekt rule set.
234+
}
235+
236+
tasks.register("publishVersion") {
237+
group = "publishing"
238+
description = "Publishes boot jar"
239+
dependsOn(tasks.withType<PublishToMavenRepository>().matching {
240+
it.repository == publishing.repositories["GitHubPackages"] && it.publication == publishing.publications["bootJava"]
241+
})
242+
}
243+
244+
245+
publishing {
246+
publications {
247+
create<MavenPublication>("bootJava") {
248+
artifact(tasks.named("bootJar"))
249+
}
250+
}
251+
repositories {
252+
maven {
253+
name = "GitHubPackages"
254+
url = URI("https://maven.pkg.github.com/swisscom/cdr-client")
255+
credentials {
256+
username = System.getenv("GITHUB_ACTOR")
257+
password = System.getenv("GITHUB_TOKEN")
258+
}
259+
}
260+
}
261+
}
262+
263+
/***********************
264+
* Integration Testing *
265+
***********************/
266+
object Constants {
267+
const val TASK_GROUP_VERIFICATION = "verification"
268+
const val INTEGRATION_TEST_TAG = "integration-test"
269+
}
270+
271+
tasks.register<Test>("integrationTest") {
272+
group = Constants.TASK_GROUP_VERIFICATION
273+
useJUnitPlatform {
274+
includeTags(Constants.INTEGRATION_TEST_TAG)
275+
}
276+
environment["JDK_JAVA_OPTIONS"] =
277+
listOfNotNull(
278+
environment["JDK_JAVA_OPTIONS"],
279+
"-Djavax.net.ssl.trustStore=src/main/resources/caddy_truststore.p12",
280+
"-Djavax.net.ssl.trustStorePassword=changeit",
281+
"-Djdk.net.hosts.file=src/main/resources/msal4j_hosts"
282+
).joinToString(" ")
283+
shouldRunAfter(tasks.test)
284+
// Ensure latest images get pulled
285+
dependsOn(tasks.composePull)
286+
}
287+
288+
dockerCompose {
289+
dockerComposeWorkingDirectory.set(rootProject.file("docker-compose"))
290+
dockerComposeStopTimeout.set(Duration.ofSeconds(5)) // time before docker-compose sends SIGTERM to the running containers after the composeDown task has been started
291+
ignorePullFailure.set(true)
292+
isRequiredBy(tasks.getByName("integrationTest"))
293+
}
294+
/***************************
295+
* END Integration Testing *
296+
***************************/
297+
298+
tasks.named<Jar>("jar") {
299+
enabled = false
300+
}
301+
302+
val packagePrepare = "jpackage-prepare"
303+
304+
tasks.register<Delete>("clearPackagePrepare") {
305+
delete(file("${outputDir.get().asFile.absolutePath}/$packagePrepare"))
306+
}
307+
308+
tasks.register<Exec>("jpackageAppPrepareDebian") {
309+
dependsOn("clearPackagePrepare")
310+
executable = "jpackage"
311+
args(
312+
"--type", "app-image",
313+
"--name", project.name,
314+
"--input", "${outputDir.get().asFile.absolutePath}/libs",
315+
"--main-jar", "${project.name}-${project.version}.jar",
316+
"--app-version", project.version.toString(),
317+
"--vendor", "Swisscom (Schweiz) AG",
318+
"--copyright", "Copyright 2025, All rights reserved",
319+
"--icon", "resources/icon.png",
320+
"--dest", "${outputDir.get().asFile.absolutePath}/$packagePrepare",
321+
"--java-options", "-Dfile.encoding=UTF-8",
322+
"--java-options", "-Dspring.profiles.active=customer",
323+
)
324+
doLast{
325+
copy {
326+
from("."){
327+
include("LICENSE")
328+
}
329+
into("${outputDir.get().asFile.absolutePath}/$packagePrepare/${project.name}/lib/app")
330+
}
331+
}
332+
}
333+
334+
tasks.register<Exec>("jpackageAppFinishDebian") {
335+
dependsOn("jpackageAppPrepareDebian")
336+
executable = "jpackage"
337+
args(
338+
"--app-image", "${outputDir.get().asFile.absolutePath}/$packagePrepare/${project.name}",
339+
"--dest", "${outputDir.get().asFile.absolutePath}/jpackage",
340+
"--license-file", "${outputDir.get().asFile.absolutePath}/$packagePrepare/${project.name}/lib/app/LICENSE",
341+
"--copyright", "Copyright 2025, All rights reserved",
342+
"--app-version", project.version.toString(),
343+
"--verbose"
344+
)
345+
}
346+
347+
tasks.register<Exec>("jpackageAppPrepareWindows") {
348+
dependsOn("clearPackagePrepare")
349+
executable = "jpackage"
350+
args(
351+
"--type", "app-image",
352+
"--name", project.name,
353+
"--input", "${outputDir.get().asFile.absolutePath}/libs",
354+
"--main-jar", "${project.name}-${project.version}.jar",
355+
"--app-version", project.version.toString(),
356+
"--vendor", "Swisscom (Schweiz) AG",
357+
"--copyright", "Copyright 2025, All rights reserved",
358+
"--icon", "resources/windows/icon.ico",
359+
"--win-console",
360+
"--dest", "${outputDir.get().asFile.absolutePath}/$packagePrepare",
361+
"--java-options", "-Dfile.encoding=UTF-8",
362+
"--java-options", "-Dspring.profiles.active=customer",
363+
)
364+
doLast{
365+
copy {
366+
from("resources/windows") {
367+
include("cdrClient.exe")
368+
include("cdrClientw.exe")
369+
include("icon.ico")
370+
include("stop.bat")
371+
}
372+
from("."){
373+
include("LICENSE")
374+
}
375+
into("${outputDir.get().asFile.absolutePath}/$packagePrepare/${project.name}/lib/app")
376+
}
377+
}
378+
}
379+
380+
tasks.register<Exec>("jpackageAppFinishWindows") {
381+
dependsOn("jpackageAppPrepareWindows")
382+
executable = "jpackage"
383+
args(
384+
"--app-image", "${outputDir.get().asFile.absolutePath}/$packagePrepare/${project.name}",
385+
"--win-dir-chooser",
386+
"--license-file", "${outputDir.get().asFile.absolutePath}/$packagePrepare/${project.name}/lib/app/LICENSE",
387+
"--copyright", "Copyright 2025, All rights reserved",
388+
"--dest", "${outputDir.get().asFile.absolutePath}/jpackage",
389+
"--app-version", project.version.toString(),
390+
"--verbose"
391+
)
392+
}

src/main/kotlin/com/swisscom/health/des/cdr/client/CdrClientApplication.kt renamed to cdr-client-service/src/main/kotlin/com/swisscom/health/des/cdr/client/CdrClientApplication.kt

File renamed without changes.

src/main/kotlin/com/swisscom/health/des/cdr/client/StartupHelper.kt renamed to cdr-client-service/src/main/kotlin/com/swisscom/health/des/cdr/client/StartupHelper.kt

File renamed without changes.

src/main/kotlin/com/swisscom/health/des/cdr/client/TraceSupport.kt renamed to cdr-client-service/src/main/kotlin/com/swisscom/health/des/cdr/client/TraceSupport.kt

File renamed without changes.

src/main/kotlin/com/swisscom/health/des/cdr/client/config/CdrClientConfig.kt renamed to cdr-client-service/src/main/kotlin/com/swisscom/health/des/cdr/client/config/CdrClientConfig.kt

File renamed without changes.

src/main/kotlin/com/swisscom/health/des/cdr/client/config/CdrClientContext.kt renamed to cdr-client-service/src/main/kotlin/com/swisscom/health/des/cdr/client/config/CdrClientContext.kt

File renamed without changes.

src/main/kotlin/com/swisscom/health/des/cdr/client/handler/CdrApiClient.kt renamed to cdr-client-service/src/main/kotlin/com/swisscom/health/des/cdr/client/handler/CdrApiClient.kt

File renamed without changes.

src/main/kotlin/com/swisscom/health/des/cdr/client/handler/ClientSecretRenewalService.kt renamed to cdr-client-service/src/main/kotlin/com/swisscom/health/des/cdr/client/handler/ClientSecretRenewalService.kt

File renamed without changes.

src/main/kotlin/com/swisscom/health/des/cdr/client/handler/FileHandlingBase.kt renamed to cdr-client-service/src/main/kotlin/com/swisscom/health/des/cdr/client/handler/FileHandlingBase.kt

File renamed without changes.

0 commit comments

Comments
 (0)