Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: 17
java-version: 21
distribution: 'temurin'
- name: Cache Gradle packages
uses: actions/cache@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- name: Set up JDK
uses: actions/setup-java@v3
with:
java-version: 17
java-version: 21
distribution: 'temurin'
- name: Cache SonarCloud packages
uses: actions/cache@v3
Expand Down
45 changes: 10 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ This is why we came up with this project.
- [Documenting Bean Validation constraints](#documenting-bean-validation-constraints)
- [Migrate existing Spring REST Docs tests](#migrate-existing-spring-rest-docs-tests)
- [MockMvc based tests](#mockmvc-based-tests)
- [REST Assured based tests](#rest-assured-based-tests)
- [WebTestClient based tests](#webtestclient-based-tests)
- [Security Definitions in OpenAPI](#security-definitions-in-openapi)
- [Running the gradle plugin](#running-the-gradle-plugin)
Expand All @@ -70,10 +69,11 @@ This is why we came up with this project.

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)

|Spring Boot version | restdocs-api-spec version|
|---|---|
|3.x|0.17.1 or later|
|2.x|0.16.4|
| Spring Boot version | restdocs-api-spec version |
|---------------------|---------------------------|
| 4.x | 0.XX.X or later |
| 3.x | 0.17.1 to 0.19.4 |
| 2.x | 0.16.4 |

### Project structure

Expand All @@ -83,7 +83,6 @@ The project consists of the following main components:
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.
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.
- [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`.
- [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`.
- [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.

### Build configuration
Expand All @@ -94,7 +93,7 @@ The [ResourceSnippet](restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apis
* Using the [plugins DSL](https://docs.gradle.org/current/userguide/plugins.html#sec:plugins_block):
```groovy
plugins {
id 'com.epages.restdocs-api-spec' version '0.18.2'
id 'com.epages.restdocs-api-spec' version '0.XX.X'
}
```
Examples with Kotlin are also available [here](https://plugins.gradle.org/plugin/com.epages.restdocs-api-spec)
Expand All @@ -110,7 +109,7 @@ The [ResourceSnippet](restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apis
}
}
dependencies {
classpath "com.epages:restdocs-api-spec-gradle-plugin:0.18.2" //1.2
classpath "com.epages:restdocs-api-spec-gradle-plugin:0.XX.X" //1.2
}
}

Expand All @@ -119,7 +118,7 @@ The [ResourceSnippet](restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apis
```
2. Add required dependencies to your tests
* *2.1* add the `mavenCentral` repository used to resolve the `com.epages:restdocs-api-spec` module of the project.
* *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`.
* *2.2* add the actual `restdocs-api-spec-mockmvc` dependency to the test scope.
* *2.3* add configuration options for `restdocs-api-spec-gradle-plugin`. See [Gradle plugin configuration](#gradle-plugin-configuration)
```groovy

Expand All @@ -129,7 +128,7 @@ The [ResourceSnippet](restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apis

dependencies {
//..
testImplementation('com.epages:restdocs-api-spec-mockmvc:0.18.2') //2.2
testImplementation('com.epages:restdocs-api-spec-mockmvc:0.XX.X') //2.2
}

openapi { //2.3
Expand Down Expand Up @@ -298,30 +297,6 @@ resultActions
This will do exactly what `MockMvcRestDocumentation.document` does.
Additionally it will add a `ResourceSnippet` with the descriptors you provided in the `RequestFieldsSnippet`, `ResponseFieldsSnippet`, and `LinksSnippet`.

#### REST Assured based tests

Also for REST Assured we offer a convenience wrapper similar to `MockMvcRestDocumentationWrapper`.
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).

To use the `RestAssuredRestDocumentationWrapper`, you have to add a dependency to [restdocs-api-spec-restassured](restdocs-api-spec-restassured) to your build.
```java
RestAssured.given(this.spec)
.filter(RestAssuredRestDocumentationWrapper.document("{method-name}",
"The API description",
requestParameters(
parameterWithName("param").description("the param")
),
responseFields(
fieldWithPath("doc.timestamp").description("Creation timestamp")
)
))
.when()
.queryParam("param", "foo")
.get("/restAssuredExample")
.then()
.statusCode(200);
```

#### WebTestClient based tests

We also offer a convenience wrapper for `WebTestClient` which works similar to `MockMvcRestDocumentationWrapper`.
Expand Down Expand Up @@ -586,7 +561,7 @@ Given that the `master` branch on the upstream repository is in the state from w

[Create release via the GitHub UI](https://github.com/ePages-de/restdocs-api-spec/releases/new).

Use the intended version number as "Tag version", e.g. "0.18.2".
Use the intended version number as "Tag version", e.g. "0.XX.X".
This will automatically trigger a GitHub Action build which publishes the JAR files for this release to Sonatype.

**(2) Login to Sonatype**
Expand Down
96 changes: 61 additions & 35 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,43 +1,53 @@

import org.gradle.kotlin.dsl.withType
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jmailen.gradle.kotlinter.tasks.LintTask
import org.springframework.boot.gradle.tasks.bundling.BootJar
import pl.allegro.tech.build.axion.release.domain.TagNameSerializationConfig
import pl.allegro.tech.build.axion.release.domain.hooks.HooksConfig


plugins {
`maven-publish`
id("io.github.gradle-nexus.publish-plugin") version "1.0.0"
id("org.jmailen.kotlinter") version "3.3.0" apply false
id("org.sonarqube") version "4.0.0.2929"
id("pl.allegro.tech.build.axion-release") version "1.9.2"
id("org.jmailen.kotlinter") version "5.2.0" apply false
id("org.sonarqube") version "7.0.1.6134"
id("pl.allegro.tech.build.axion-release") version "1.21.0"
jacoco
java
kotlin("jvm") version "1.7.22" apply false
kotlin("jvm") version "2.2.21" apply false
id("org.springframework.boot") version "4.0.1"
}

repositories {
mavenCentral()
}

scmVersion {
tag(closureOf<TagNameSerializationConfig> {
prefix = ""
})

hooks(closureOf<HooksConfig> {
pre("fileUpdate", mapOf(
tag {
prefix.set("")
}

hooks {
pre(
"fileUpdate",
mapOf(
"file" to "README.md",
"pattern" to "{v,p -> /('$'v)/}",
"replacement" to """{v, p -> "'$'v"}]))"""))
"replacement" to """{v, p -> "'$'v"}]))""",
),
)
pre("commit")
})
}
}

val scmVer = scmVersion.version

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

val nonSampleProjects = subprojects.filterNot { it.isSampleProject() }
val nonSampleProjects = subprojects.filterNot { it.isSampleProject() }

allprojects {

Expand All @@ -50,19 +60,19 @@ allprojects {
apply(plugin = "jacoco")
apply(plugin = "maven-publish")
apply(plugin = "org.jmailen.kotlinter")

java {
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
}
}
}


subprojects {

val jacksonVersion by extra { "2.12.2" }
val springBootVersion by extra { "3.0.2" }
val springRestDocsVersion by extra { "3.0.0" }
val junitVersion by extra { "5.4.2" }
val jmustacheVersion by extra { "1.16" }

tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = "17"
compilerOptions.jvmTarget.set(JvmTarget.JVM_21)
}

tasks.withType<Test> {
Expand All @@ -77,35 +87,47 @@ subprojects {
tasks.withType<JacocoReport> {
dependsOn("test")
reports {
html.isEnabled = true
xml.isEnabled = true
html.required.set(false)
xml.required.set(false)
}
}
}
}

tasks {
val jacocoMerge by creating(JacocoMerge::class) {
executionData = files(nonSampleProjects.map { File(it.buildDir, "/jacoco/test.exec") })
doFirst {
executionData = files(executionData.filter { it.exists() })
tasks.withType<JavaCompile> {
targetCompatibility = "21"
sourceCompatibility = "21"
}
}
}

tasks {
val jacocoTestReport = this.getByName("jacocoTestReport")
jacocoTestReport.dependsOn(nonSampleProjects.map { it.tasks["jacocoTestReport"] })
jacocoMerge.dependsOn(jacocoTestReport)

val jacocoRootReport by creating(JacocoReport::class) {
val jacocoRootReport by registering(JacocoReport::class) {
description = "Generates an aggregate report from all subprojects"
group = "Coverage reports"
dependsOn(jacocoMerge)
sourceDirectories.setFrom(files(nonSampleProjects.flatMap { it.sourceSets["main"].allSource.srcDirs.filter { it.exists() && !it.path.endsWith("restdocs-api-spec-postman-generator/src/main/java") } } ))
classDirectories.setFrom(files(nonSampleProjects.flatMap { it.sourceSets["main"].output }.filter { !it.path.endsWith("restdocs-api-spec-postman-generator/build/classes/java/main") } ))
executionData(jacocoMerge.destinationFile)
sourceDirectories.setFrom(
files(
nonSampleProjects.flatMap {
it.sourceSets["main"].allSource.srcDirs.filter {
it.exists() &&
!it.path.endsWith("restdocs-api-spec-postman-generator/src/main/java")
}
},
),
)
classDirectories.setFrom(
files(
nonSampleProjects
.flatMap {
it.sourceSets["main"].output
}.filter { !it.path.endsWith("restdocs-api-spec-postman-generator/build/classes/java/main") },
),
)
executionData(files(nonSampleProjects.map { it.layout.buildDirectory.file("jacoco/test.exec") }))
reports {
html.isEnabled = true
xml.isEnabled = true
html.required.set(false)
xml.required.set(false)
}
}
getByName("sonar").dependsOn(jacocoRootReport)
Expand All @@ -128,3 +150,7 @@ sonar {
property("sonar.exclusions", "**/samples/**")
}
}

tasks.withType<BootJar> {
enabled = false
}
2 changes: 2 additions & 0 deletions gradle/gradle-daemon-jvm.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#This file is generated by updateDaemonJvm
toolchainVersion=21
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
45 changes: 29 additions & 16 deletions restdocs-api-spec-gradle-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import io.spring.gradle.dependencymanagement.dsl.DependencyManagementExtension
import kotlin.apply

repositories {
mavenCentral()
}
Expand All @@ -6,7 +9,14 @@ plugins {
kotlin("jvm")
`java-gradle-plugin`
`kotlin-dsl`
id("com.gradle.plugin-publish") version "0.12.0"
id("com.gradle.plugin-publish") version "0.21.0"
}

apply(plugin = "io.spring.dependency-management")
the<DependencyManagementExtension>().apply {
imports {
mavenBom(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)
}
}

gradlePlugin {
Expand Down Expand Up @@ -36,10 +46,6 @@ pluginBundle {
}
}


val jacksonVersion: String by extra
val junitVersion: String by extra

val jacocoRuntime by configurations.creating

dependencies {
Expand All @@ -51,14 +57,15 @@ dependencies {
implementation(project(":restdocs-api-spec-openapi-generator"))
implementation(project(":restdocs-api-spec-openapi3-generator"))
implementation(project(":restdocs-api-spec-postman-generator"))
implementation("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonVersion")
implementation("tools.jackson.core:jackson-databind")
implementation("tools.jackson.module:jackson-module-kotlin")
implementation("tools.jackson.dataformat:jackson-dataformat-yaml")

testImplementation("org.junit.jupiter:junit-jupiter-engine:$junitVersion")
testImplementation("org.junit-pioneer:junit-pioneer:0.3.0")
testImplementation("org.assertj:assertj-core:3.10.0")
testImplementation("org.junit.jupiter:junit-jupiter-engine")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
testImplementation("org.assertj:assertj-core")

testImplementation("com.jayway.jsonpath:json-path:2.4.0")
testImplementation("com.jayway.jsonpath:json-path:2.10.0")

testImplementation(gradleTestKit())

Expand All @@ -67,15 +74,21 @@ dependencies {

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

inputs.files(jacocoRuntime)
outputs.dir(outputDir)

doLast {
outputDir.mkdirs()
file("$outputDir/testkit-gradle.properties").writeText("org.gradle.jvmargs=-javaagent:${jacocoRuntime.asPath}=destfile=$buildDir/jacoco/test.exec")
outputDir.get().asFile.mkdirs()
val destFile =
project.layout.buildDirectory
.file("jacoco/test.exec")
.get()
.asFile.path
val outFile = outputDir.get().file("testkit-gradle.properties").asFile
outFile.writeText("org.gradle.jvmargs=-javaagent:${jacocoRuntime.asPath}=destfile=$destFile")
}
}

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