Skip to content

Commit 420b63b

Browse files
authored
Merge pull request #4 from kpramesh2212/master
Feat: Add readme file and an initial working plugin
2 parents fbf9cbf + 5d31639 commit 420b63b

File tree

8 files changed

+161
-24
lines changed

8 files changed

+161
-24
lines changed

README.md

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,91 @@
1+
#**Introduction to springdoc-openapi-gradle-plugin**
2+
Spring Docs Open API Gradle Plugin - Generates OpenAPI-3 specification docs for spring boot application
13

4+
This Gradle plugin provides the capability to generate OpenAPI-3 specification docs for spring boot application from a Gradle build.
5+
The plugin does this with help of springdoc-openapi-core
26

3-
## **Introduction to springdoc-openapi-gradle-plugin**
7+
Compatibility Notes
8+
-------------------
9+
10+
The plugin is build on gradle version 6.1.
11+
12+
Dependencies
13+
------------
14+
This plugin has a runtime dependency on the the following plugins
15+
16+
1. Spring boot gradle plugin - "org.springframework.boot"
17+
2. Gradle process plugin - "com.github.johnrengelman.processes"
18+
19+
Hence these plugins also needs to be added to your gradle builds.
20+
21+
Note: You will also need the springdocs-core jar file to be present in your spring boot application
22+
23+
How To Use
24+
----------
25+
26+
Gradle Groovy DSL
27+
28+
```groovy
29+
plugins {
30+
id("org.springframework.boot") version "2.2.4.RELEASE"
31+
id "com.github.johnrengelman.processes" version "0.5.0"
32+
id("org.springdoc.openapi-gradle-plugin") version "1.0-SNAPSHOT"
33+
}
34+
```
35+
36+
Gradle Kotlin DSL
37+
```groovy
38+
plugins {
39+
id("org.springframework.boot") version "2.2.4.RELEASE"
40+
id("com.github.johnrengelman.processes") version "0.5.0"
41+
id("org.springdoc.openapi-gradle-plugin") version "1.0-SNAPSHOT"
42+
}
43+
```
44+
45+
Note: For latest versions of the plugins please check the gradle plugin portal
46+
47+
How the plugin works
48+
------------
49+
50+
When the user add this plugin and its runtime dependency plugins to your build file
51+
52+
The plugin creates the following tasks
53+
54+
1. forkedSpringBootRun
55+
56+
2. generateOpenApiDocs
57+
58+
Running the task generateOpenApiDocs will generate the open api docs into a file openapi.json in your projects build dir
59+
60+
```bash
61+
gradle clean generateOpenApiDocs
62+
```
63+
64+
When you run the gradle task **generateOpenApiDocs**, it starts your spring boot application in the background using **forkedSpringBootRun** task.
65+
Once your application is up and running **generateOpenApiDocs** makes a rest call to your applications doc url to download and store the open api docs file as json.
66+
67+
68+
Customization
69+
-------------
70+
71+
The following customizations can be done on task generateOpenApiDocs using extension openApi as follows
72+
73+
```kotlin
74+
openApi {
75+
apiDocsUrl.set("https://localhost:9000/api/docs")
76+
outputDir.set(file("$buildDir/docs"))
77+
outputFileName.set("swagger.json")
78+
waitTimeInSeconds.set(10)
79+
}
80+
```
81+
82+
Parameter | Description | Required | Default
83+
--------- | ----------- | -------- | -------
84+
`apiDocsUrl` | The url from where the open api docs can be downloaded | No | http://localhost:8080/v3/api-docs
85+
`outputDir` | The output directory where the generated open api file would be placed | No | $buildDir - Your projects build dir
86+
`outputFileName` | The name of the output file with extension | No | openapi.json
87+
`waitTimeInSeconds` | Time to wait in seconds for your spring boot application to start, before we make calls to `apiDocsUrl` to download the openapi docs | No | 10 seconds
88+
89+
# Building the plugin
90+
91+
TODO

build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ repositories {
1313
name = "Spring Repositories"
1414
url = uri("https://repo.spring.io/libs-release/")
1515
}
16+
gradlePluginPortal()
1617
}
1718

1819
publishing {
@@ -31,6 +32,8 @@ dependencies {
3132
implementation(kotlin("reflect"))
3233
implementation(group = "khttp", name = "khttp", version = "1.0.0")
3334
implementation(group = "com.google.code.gson", name = "gson", version = "2.8.6")
35+
implementation(group = "com.jayway.awaitility", name = "awaitility", version = "1.7.0")
36+
implementation(files("/home/ramesh/gradle-processes/build/libs/gradle-processes-0.5.0.jar"))
3437
}
3538

3639
gradlePlugin {

gradle/wrapper/gradle-wrapper.jar

-57.3 KB
Binary file not shown.

gradle/wrapper/gradle-wrapper.properties

Lines changed: 0 additions & 5 deletions
This file was deleted.
Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
package org.springdoc.openapi.gradle.plugin
22

3-
const val TASK_NAME = "generateOpenApiFile"
43
const val EXTENSION_NAME = "openApi"
54
const val GROUP_NAME = "OpenApi"
6-
const val OPENAPI_TASK_DESCRIPTION = "Generates the spring doc openapi file"
5+
const val OPEN__API_TASK_NAME = "generateOpenApiDocs"
6+
const val OPEN_API_TASK_DESCRIPTION = "Generates the spring doc openapi file"
7+
const val SPRING_BOOT_JAR_TASK_NAME = "bootJar"
8+
const val FORKED_SPRING_BOOT_RUN_TASK_NAME = "forkedSpringBootRun"
9+
10+
const val DEFAULT_API_DOCS_URL = "http://localhost:8080/v3/api-docs"
11+
const val DEFAULT_OPEN_API_FILE_NAME = "openapi.json"
12+
const val DEFAULT_WAIT_TIME_IN_SECONDS = 10
13+
14+
const val SPRING_BOOT_PLUGIN = "org.springframework.boot"
15+
const val PROCESS_PLUGIN = "com.github.johnrengelman.processes"

src/main/kotlin/org/springdoc/openapi/gradle/plugin/OpenApiExtension.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ open class OpenApiExtension @Inject constructor(project: Project) {
99
val apiDocsUrl: Property<String> = project.objects.property(String::class.java)
1010
val outputFileName: Property<String> = project.objects.property(String::class.java)
1111
val outputDir: DirectoryProperty = project.objects.directoryProperty()
12+
val waitTimeInSeconds: Property<Int> = project.objects.property(Int::class.java)
1213
}

src/main/kotlin/org/springdoc/openapi/gradle/plugin/OpenApiGeneratorTask.kt

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package org.springdoc.openapi.gradle.plugin
22

33
import com.google.gson.GsonBuilder
44
import com.google.gson.JsonObject
5+
import com.jayway.awaitility.core.ConditionTimeoutException
6+
import khttp.responses.Response
57
import org.gradle.api.DefaultTask
68
import org.gradle.api.GradleException
79
import org.gradle.api.file.DirectoryProperty
@@ -19,34 +21,41 @@ open class OpenApiGeneratorTask: DefaultTask() {
1921
val outputFileName: Property<String> = project.objects.property(String::class.java)
2022
@get:OutputDirectory
2123
val outputDir: DirectoryProperty = project.objects.directoryProperty()
24+
private val waitTimeInSeconds: Property<Int> = project.objects.property(Int::class.java)
2225

2326
init {
24-
description = OPENAPI_TASK_DESCRIPTION
27+
description = OPEN_API_TASK_DESCRIPTION
2528
group = GROUP_NAME
26-
27-
val defaultOutputDir = project.objects.directoryProperty()
28-
defaultOutputDir.set(project.buildDir)
29+
// load my extensions
2930
val extension: OpenApiExtension = project.extensions.run {
3031
getByName(EXTENSION_NAME) as OpenApiExtension
3132
}
3233

33-
apiDocsUrl.set(extension.apiDocsUrl.getOrElse("http://localhost:8080/v3/api-docs"))
34-
outputFileName.set(extension.outputFileName.getOrElse("openapi.json"))
34+
// set a default value if not provided
35+
val defaultOutputDir = project.objects.directoryProperty()
36+
defaultOutputDir.set(project.buildDir)
37+
38+
apiDocsUrl.set(extension.apiDocsUrl.getOrElse(DEFAULT_API_DOCS_URL))
39+
outputFileName.set(extension.outputFileName.getOrElse(DEFAULT_OPEN_API_FILE_NAME))
3540
outputDir.set(extension.outputDir.getOrElse(defaultOutputDir.get()))
41+
waitTimeInSeconds.set(extension.waitTimeInSeconds.getOrElse(DEFAULT_WAIT_TIME_IN_SECONDS))
3642
}
3743

3844
@TaskAction
3945
fun execute() {
40-
val response = khttp.get(apiDocsUrl.get())
41-
if (response.statusCode > 299) {
42-
LOGGER.error("Invalid response code {} while connecting to {}", response.statusCode, apiDocsUrl.get())
43-
throw GradleException("Unable to connect to apiDocsUrl ${apiDocsUrl.get()}")
46+
try {
47+
// I need to change this later on to use a smart logic to wait only for required time
48+
Thread.sleep(waitTimeInSeconds.get().toLong() * 1000)
49+
val response: Response = khttp.get(apiDocsUrl.get())
50+
val gson = GsonBuilder().setPrettyPrinting().create();
51+
val googleJsonObject = gson.fromJson(response.jsonObject.toString(), JsonObject::class.java)
52+
53+
val outputFile = outputDir.file(outputFileName.get()).get().asFile
54+
outputFile.writeText(gson.toJson(googleJsonObject))
55+
} catch (e: ConditionTimeoutException) {
56+
LOGGER.error("Unable to connect to ${apiDocsUrl.get()} waited for ${waitTimeInSeconds.get()} seconds")
57+
throw GradleException("Timeout occurred while trying to connect to ${apiDocsUrl.get()}")
4458
}
45-
val gson = GsonBuilder().setPrettyPrinting().create();
46-
val googleJsonObject = gson.fromJson(response.jsonObject.toString(), JsonObject::class.java)
47-
48-
val outputFile = outputDir.file(outputFileName.get()).get().asFile
49-
outputFile.writeText(gson.toJson(googleJsonObject))
5059
}
5160

5261
}
Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,43 @@
11
package org.springdoc.openapi.gradle.plugin
22

3+
import com.github.jengelman.gradle.plugins.processes.tasks.Fork
34
import org.gradle.api.Plugin
45
import org.gradle.api.Project
6+
import org.slf4j.LoggerFactory
57

68
open class OpenApiGradlePlugin: Plugin<Project> {
9+
private val LOGGER = LoggerFactory.getLogger(OpenApiGradlePlugin::class.java)
10+
711
override fun apply(project: Project) {
12+
// Run time dependency on the following plugins
13+
project.plugins.apply(SPRING_BOOT_PLUGIN)
14+
project.plugins.apply(PROCESS_PLUGIN)
15+
816
project.extensions.create(EXTENSION_NAME, OpenApiExtension::class.java, project)
9-
project.tasks.register(TASK_NAME, OpenApiGeneratorTask::class.java)
17+
18+
project.afterEvaluate {
19+
// Spring boot jar task
20+
val bootJarTask = project.tasks.named(SPRING_BOOT_JAR_TASK_NAME)
21+
22+
// Create a forked version spring boot run task
23+
val forkedSpringBoot = project.tasks.register(FORKED_SPRING_BOOT_RUN_TASK_NAME, Fork::class.java) { fork ->
24+
fork.dependsOn(bootJarTask)
25+
26+
fork.onlyIf {
27+
val bootJar = bootJarTask.get().outputs.files.first()
28+
fork.commandLine = listOf("java", "-jar", "$bootJar")
29+
true
30+
}
31+
}
32+
33+
// This is my task. Before I can run it I have to run the dependent tasks
34+
val openApiGeneratorTask = project.tasks.register(OPEN__API_TASK_NAME, OpenApiGeneratorTask::class.java) {
35+
it.dependsOn(forkedSpringBoot)
36+
it.doLast {
37+
forkedSpringBoot.get().processHandle.abort()
38+
}
39+
}
40+
}
41+
1042
}
1143
}

0 commit comments

Comments
 (0)