Skip to content

Commit f885851

Browse files
antoinelochetMarcelKonradAntoine Lochet
authored
Spring Boot 4 compatibility (#290)
* Update to gradle 8.14.3 * Update to kotlin 1.9.25 * Migrate to updated jacoco dsl * Update to plugin-publish 0.21.0 * Update to spring 3.2.0 * Migrate to gradle 8 dsl * Refactor deprecated api usage and suppress some warnings * Update swagger and json libraries * Update package path of ParseOptions * Run formatting * Revert URL to URI migration * Ktlint: Disable max-line-length rule * Target java 21 (cherry picked from commit be9184bf384bae70a32bedbfeeb6abfbc69fcd72) * Disable broken tests (cherry picked from commit 42043775dd9e6439ecf9aca09aaa5b788b550fbe) * Spring Rest Docs 3.0.5 * Kotlinter 5.2.0 * Axion-release 1.21.0 * Spring Boot 3.5.7 Jackson 2.19.2 JUnit 5.12.2 AssertJ 3.27.6 JUnit Pioneer 2.3.0 * Kotlin 2.0.21 * JSON path 2.10.0 JSON schema validator 2.2.14 * Fixed some Java 21 settings and compilation issues Imports cleanup * Spring Boot 4.0.0 RC2 * Spring Boot 4 GA * Spring Boot 4.0.1 * Comments --------- Co-authored-by: Marcel Konrad <Marcel.Konrad@tib.eu> Co-authored-by: Antoine Lochet <antoine.2.lochet@atos.net>
1 parent 0b8378c commit f885851

File tree

89 files changed

+4870
-4465
lines changed

Some content is hidden

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

89 files changed

+4870
-4465
lines changed

.github/workflows/publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
- name: Set up JDK
1717
uses: actions/setup-java@v4
1818
with:
19-
java-version: 17
19+
java-version: 21
2020
distribution: 'temurin'
2121
- name: Cache Gradle packages
2222
uses: actions/cache@v4

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
- name: Set up JDK
1919
uses: actions/setup-java@v3
2020
with:
21-
java-version: 17
21+
java-version: 21
2222
distribution: 'temurin'
2323
- name: Cache SonarCloud packages
2424
uses: actions/cache@v3

README.md

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ This is why we came up with this project.
4949
- [Documenting Bean Validation constraints](#documenting-bean-validation-constraints)
5050
- [Migrate existing Spring REST Docs tests](#migrate-existing-spring-rest-docs-tests)
5151
- [MockMvc based tests](#mockmvc-based-tests)
52-
- [REST Assured based tests](#rest-assured-based-tests)
5352
- [WebTestClient based tests](#webtestclient-based-tests)
5453
- [Security Definitions in OpenAPI](#security-definitions-in-openapi)
5554
- [Running the gradle plugin](#running-the-gradle-plugin)
@@ -70,10 +69,11 @@ This is why we came up with this project.
7069

7170
Spring Boot and Spring REST Docs 3.0.0 introduced [breaking chances to how request parameters are documented: `RequestParameterSnippet` was split into `QueryParameterSnippet` and `FormParameterSnippet`.](https://github.com/spring-projects/spring-restdocs/issues/832)
7271

73-
|Spring Boot version | restdocs-api-spec version|
74-
|---|---|
75-
|3.x|0.17.1 or later|
76-
|2.x|0.16.4|
72+
| Spring Boot version | restdocs-api-spec version |
73+
|---------------------|---------------------------|
74+
| 4.x | 0.20.X or later |
75+
| 3.x | 0.17.1 to 0.19.4 |
76+
| 2.x | 0.16.4 |
7777

7878
### Project structure
7979

@@ -83,7 +83,6 @@ The project consists of the following main components:
8383
This is most importantly the [ResourceDocumentation](restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/ResourceDocumentation.kt) which is the entry point to use the extension in your tests.
8484
The [ResourceSnippet](restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/ResourceSnippet.kt) is the snippet used to produce a json file `resource.json` containing all the details about the documented resource.
8585
- [restdocs-api-spec-mockmvc](restdocs-api-spec-mockmvc) - contains a wrapper for `MockMvcRestDocumentation` for easier migration to `restdocs-api-spec` from MockMvc tests that use plain `spring-rest-docs-mockmvc`.
86-
- [restdocs-api-spec-restassured](restdocs-api-spec-restassured) - contains a wrapper for `RestAssuredRestDocumentation` for easier migration to `restdocs-api-spec` from [Rest Assured](http://rest-assured.io) tests that use plain `spring-rest-docs-restassured`.
8786
- [restdocs-api-spec-gradle-plugin](restdocs-api-spec-gradle-plugin) - adds a gradle plugin that aggregates the `resource.json` files produced by `ResourceSnippet` into an API specification file for the whole project.
8887

8988
### Build configuration
@@ -94,7 +93,7 @@ The [ResourceSnippet](restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apis
9493
* Using the [plugins DSL](https://docs.gradle.org/current/userguide/plugins.html#sec:plugins_block):
9594
```groovy
9695
plugins {
97-
id 'com.epages.restdocs-api-spec' version '0.18.2'
96+
id 'com.epages.restdocs-api-spec' version '0.XX.X'
9897
}
9998
```
10099
Examples with Kotlin are also available [here](https://plugins.gradle.org/plugin/com.epages.restdocs-api-spec)
@@ -110,7 +109,7 @@ The [ResourceSnippet](restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apis
110109
}
111110
}
112111
dependencies {
113-
classpath "com.epages:restdocs-api-spec-gradle-plugin:0.18.2" //1.2
112+
classpath "com.epages:restdocs-api-spec-gradle-plugin:0.XX.X" //1.2
114113
}
115114
}
116115
@@ -119,7 +118,7 @@ The [ResourceSnippet](restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apis
119118
```
120119
2. Add required dependencies to your tests
121120
* *2.1* add the `mavenCentral` repository used to resolve the `com.epages:restdocs-api-spec` module of the project.
122-
* *2.2* add the actual `restdocs-api-spec-mockmvc` dependency to the test scope. Use `restdocs-api-spec-restassured` if you use `RestAssured` instead of `MockMvc`.
121+
* *2.2* add the actual `restdocs-api-spec-mockmvc` dependency to the test scope.
123122
* *2.3* add configuration options for `restdocs-api-spec-gradle-plugin`. See [Gradle plugin configuration](#gradle-plugin-configuration)
124123
```groovy
125124
@@ -129,7 +128,7 @@ The [ResourceSnippet](restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apis
129128
130129
dependencies {
131130
//..
132-
testImplementation('com.epages:restdocs-api-spec-mockmvc:0.18.2') //2.2
131+
testImplementation('com.epages:restdocs-api-spec-mockmvc:0.XX.X') //2.2
133132
}
134133
135134
openapi { //2.3
@@ -298,30 +297,6 @@ resultActions
298297
This will do exactly what `MockMvcRestDocumentation.document` does.
299298
Additionally it will add a `ResourceSnippet` with the descriptors you provided in the `RequestFieldsSnippet`, `ResponseFieldsSnippet`, and `LinksSnippet`.
300299

301-
#### REST Assured based tests
302-
303-
Also for REST Assured we offer a convenience wrapper similar to `MockMvcRestDocumentationWrapper`.
304-
The usage for REST Assured is also similar to MockMVC, except that [com.epages.restdocs.apispec.RestAssuredRestDocumentationWrapper](restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/RestAssuredRestDocumentationWrapper.kt) is used instead of [com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper](restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/MockMvcRestDocumentationWrapper.kt).
305-
306-
To use the `RestAssuredRestDocumentationWrapper`, you have to add a dependency to [restdocs-api-spec-restassured](restdocs-api-spec-restassured) to your build.
307-
```java
308-
RestAssured.given(this.spec)
309-
.filter(RestAssuredRestDocumentationWrapper.document("{method-name}",
310-
"The API description",
311-
requestParameters(
312-
parameterWithName("param").description("the param")
313-
),
314-
responseFields(
315-
fieldWithPath("doc.timestamp").description("Creation timestamp")
316-
)
317-
))
318-
.when()
319-
.queryParam("param", "foo")
320-
.get("/restAssuredExample")
321-
.then()
322-
.statusCode(200);
323-
```
324-
325300
#### WebTestClient based tests
326301

327302
We also offer a convenience wrapper for `WebTestClient` which works similar to `MockMvcRestDocumentationWrapper`.
@@ -586,7 +561,7 @@ Given that the `master` branch on the upstream repository is in the state from w
586561
587562
[Create release via the GitHub UI](https://github.com/ePages-de/restdocs-api-spec/releases/new).
588563
589-
Use the intended version number as "Tag version", e.g. "0.18.2".
564+
Use the intended version number as "Tag version", e.g. "0.XX.X".
590565
This will automatically trigger a GitHub Action build which publishes the JAR files for this release to Sonatype.
591566
592567
**(2) Login to Sonatype**

build.gradle.kts

Lines changed: 61 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,53 @@
11

2+
import org.gradle.kotlin.dsl.withType
3+
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
24
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
5+
import org.jmailen.gradle.kotlinter.tasks.LintTask
6+
import org.springframework.boot.gradle.tasks.bundling.BootJar
37
import pl.allegro.tech.build.axion.release.domain.TagNameSerializationConfig
48
import pl.allegro.tech.build.axion.release.domain.hooks.HooksConfig
59

610

711
plugins {
812
`maven-publish`
913
id("io.github.gradle-nexus.publish-plugin") version "1.0.0"
10-
id("org.jmailen.kotlinter") version "3.3.0" apply false
11-
id("org.sonarqube") version "4.0.0.2929"
12-
id("pl.allegro.tech.build.axion-release") version "1.9.2"
14+
id("org.jmailen.kotlinter") version "5.2.0" apply false
15+
id("org.sonarqube") version "7.0.1.6134"
16+
id("pl.allegro.tech.build.axion-release") version "1.21.0"
1317
jacoco
1418
java
15-
kotlin("jvm") version "1.7.22" apply false
19+
kotlin("jvm") version "2.2.21" apply false
20+
id("org.springframework.boot") version "4.0.1"
1621
}
1722

1823
repositories {
1924
mavenCentral()
2025
}
2126

2227
scmVersion {
23-
tag(closureOf<TagNameSerializationConfig> {
24-
prefix = ""
25-
})
2628

27-
hooks(closureOf<HooksConfig> {
28-
pre("fileUpdate", mapOf(
29+
tag {
30+
prefix.set("")
31+
}
32+
33+
hooks {
34+
pre(
35+
"fileUpdate",
36+
mapOf(
2937
"file" to "README.md",
3038
"pattern" to "{v,p -> /('$'v)/}",
31-
"replacement" to """{v, p -> "'$'v"}]))"""))
39+
"replacement" to """{v, p -> "'$'v"}]))""",
40+
),
41+
)
3242
pre("commit")
33-
})
43+
}
3444
}
3545

3646
val scmVer = scmVersion.version
3747

3848
fun Project.isSampleProject() = this.name.contains("sample")
3949

40-
val nonSampleProjects = subprojects.filterNot { it.isSampleProject() }
50+
val nonSampleProjects = subprojects.filterNot { it.isSampleProject() }
4151

4252
allprojects {
4353

@@ -50,19 +60,19 @@ allprojects {
5060
apply(plugin = "jacoco")
5161
apply(plugin = "maven-publish")
5262
apply(plugin = "org.jmailen.kotlinter")
63+
64+
java {
65+
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
66+
}
5367
}
5468
}
5569

56-
5770
subprojects {
5871

59-
val jacksonVersion by extra { "2.12.2" }
60-
val springBootVersion by extra { "3.0.2" }
61-
val springRestDocsVersion by extra { "3.0.0" }
62-
val junitVersion by extra { "5.4.2" }
72+
val jmustacheVersion by extra { "1.16" }
6373

6474
tasks.withType<KotlinCompile> {
65-
kotlinOptions.jvmTarget = "17"
75+
compilerOptions.jvmTarget.set(JvmTarget.JVM_21)
6676
}
6777

6878
tasks.withType<Test> {
@@ -77,35 +87,47 @@ subprojects {
7787
tasks.withType<JacocoReport> {
7888
dependsOn("test")
7989
reports {
80-
html.isEnabled = true
81-
xml.isEnabled = true
90+
html.required.set(false)
91+
xml.required.set(false)
8292
}
8393
}
84-
}
85-
}
8694

87-
tasks {
88-
val jacocoMerge by creating(JacocoMerge::class) {
89-
executionData = files(nonSampleProjects.map { File(it.buildDir, "/jacoco/test.exec") })
90-
doFirst {
91-
executionData = files(executionData.filter { it.exists() })
95+
tasks.withType<JavaCompile> {
96+
targetCompatibility = "21"
97+
sourceCompatibility = "21"
9298
}
9399
}
100+
}
94101

102+
tasks {
95103
val jacocoTestReport = this.getByName("jacocoTestReport")
96104
jacocoTestReport.dependsOn(nonSampleProjects.map { it.tasks["jacocoTestReport"] })
97-
jacocoMerge.dependsOn(jacocoTestReport)
98105

99-
val jacocoRootReport by creating(JacocoReport::class) {
106+
val jacocoRootReport by registering(JacocoReport::class) {
100107
description = "Generates an aggregate report from all subprojects"
101108
group = "Coverage reports"
102-
dependsOn(jacocoMerge)
103-
sourceDirectories.setFrom(files(nonSampleProjects.flatMap { it.sourceSets["main"].allSource.srcDirs.filter { it.exists() && !it.path.endsWith("restdocs-api-spec-postman-generator/src/main/java") } } ))
104-
classDirectories.setFrom(files(nonSampleProjects.flatMap { it.sourceSets["main"].output }.filter { !it.path.endsWith("restdocs-api-spec-postman-generator/build/classes/java/main") } ))
105-
executionData(jacocoMerge.destinationFile)
109+
sourceDirectories.setFrom(
110+
files(
111+
nonSampleProjects.flatMap {
112+
it.sourceSets["main"].allSource.srcDirs.filter {
113+
it.exists() &&
114+
!it.path.endsWith("restdocs-api-spec-postman-generator/src/main/java")
115+
}
116+
},
117+
),
118+
)
119+
classDirectories.setFrom(
120+
files(
121+
nonSampleProjects
122+
.flatMap {
123+
it.sourceSets["main"].output
124+
}.filter { !it.path.endsWith("restdocs-api-spec-postman-generator/build/classes/java/main") },
125+
),
126+
)
127+
executionData(files(nonSampleProjects.map { it.layout.buildDirectory.file("jacoco/test.exec") }))
106128
reports {
107-
html.isEnabled = true
108-
xml.isEnabled = true
129+
html.required.set(false)
130+
xml.required.set(false)
109131
}
110132
}
111133
getByName("sonar").dependsOn(jacocoRootReport)
@@ -128,3 +150,7 @@ sonar {
128150
property("sonar.exclusions", "**/samples/**")
129151
}
130152
}
153+
154+
tasks.withType<BootJar> {
155+
enabled = false
156+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#This file is generated by updateDaemonJvm
2+
toolchainVersion=21
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
44
networkTimeout=10000
55
zipStoreBase=GRADLE_USER_HOME
66
zipStorePath=wrapper/dists

restdocs-api-spec-gradle-plugin/build.gradle.kts

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import io.spring.gradle.dependencymanagement.dsl.DependencyManagementExtension
2+
import kotlin.apply
3+
14
repositories {
25
mavenCentral()
36
}
@@ -6,7 +9,14 @@ plugins {
69
kotlin("jvm")
710
`java-gradle-plugin`
811
`kotlin-dsl`
9-
id("com.gradle.plugin-publish") version "0.12.0"
12+
id("com.gradle.plugin-publish") version "0.21.0"
13+
}
14+
15+
apply(plugin = "io.spring.dependency-management")
16+
the<DependencyManagementExtension>().apply {
17+
imports {
18+
mavenBom(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)
19+
}
1020
}
1121

1222
gradlePlugin {
@@ -36,10 +46,6 @@ pluginBundle {
3646
}
3747
}
3848

39-
40-
val jacksonVersion: String by extra
41-
val junitVersion: String by extra
42-
4349
val jacocoRuntime by configurations.creating
4450

4551
dependencies {
@@ -51,14 +57,15 @@ dependencies {
5157
implementation(project(":restdocs-api-spec-openapi-generator"))
5258
implementation(project(":restdocs-api-spec-openapi3-generator"))
5359
implementation(project(":restdocs-api-spec-postman-generator"))
54-
implementation("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion")
55-
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonVersion")
60+
implementation("tools.jackson.core:jackson-databind")
61+
implementation("tools.jackson.module:jackson-module-kotlin")
62+
implementation("tools.jackson.dataformat:jackson-dataformat-yaml")
5663

57-
testImplementation("org.junit.jupiter:junit-jupiter-engine:$junitVersion")
58-
testImplementation("org.junit-pioneer:junit-pioneer:0.3.0")
59-
testImplementation("org.assertj:assertj-core:3.10.0")
64+
testImplementation("org.junit.jupiter:junit-jupiter-engine")
65+
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
66+
testImplementation("org.assertj:assertj-core")
6067

61-
testImplementation("com.jayway.jsonpath:json-path:2.4.0")
68+
testImplementation("com.jayway.jsonpath:json-path:2.10.0")
6269

6370
testImplementation(gradleTestKit())
6471

@@ -67,15 +74,21 @@ dependencies {
6774

6875
// generate gradle properties file with jacoco agent configured
6976
// see https://discuss.gradle.org/t/testkit-jacoco-coverage/18792
70-
val createTestKitFiles by tasks.creating {
71-
val outputDir = project.file("$buildDir/testkit")
77+
val createTestKitFiles by tasks.registering {
78+
val outputDir = project.layout.buildDirectory.dir("testkit")
7279

7380
inputs.files(jacocoRuntime)
7481
outputs.dir(outputDir)
7582

7683
doLast {
77-
outputDir.mkdirs()
78-
file("$outputDir/testkit-gradle.properties").writeText("org.gradle.jvmargs=-javaagent:${jacocoRuntime.asPath}=destfile=$buildDir/jacoco/test.exec")
84+
outputDir.get().asFile.mkdirs()
85+
val destFile =
86+
project.layout.buildDirectory
87+
.file("jacoco/test.exec")
88+
.get()
89+
.asFile.path
90+
val outFile = outputDir.get().file("testkit-gradle.properties").asFile
91+
outFile.writeText("org.gradle.jvmargs=-javaagent:${jacocoRuntime.asPath}=destfile=$destFile")
7992
}
8093
}
8194

@@ -84,7 +97,7 @@ tasks["test"].dependsOn(createTestKitFiles)
8497
// Set Gradle plugin publishing credentials from environment
8598
// see https://github.com/gradle/gradle/issues/1246
8699
// https://github.com/cortinico/kotlin-gradle-plugin-template/blob/1194fbbb2bc61857a76da5b5b2df919a558653de/plugin-build/plugin/build.gradle.kts#L43-L55
87-
val configureGradlePluginCredentials by tasks.creating {
100+
val configureGradlePluginCredentials by tasks.registering {
88101
doLast {
89102
val key = System.getenv("GRADLE_PUBLISH_KEY")
90103
val secret = System.getenv("GRADLE_PUBLISH_SECRET")

0 commit comments

Comments
 (0)