Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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.0"
}

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