Skip to content

Commit 875cc8d

Browse files
pgcartermduesterhoeft
authored andcommitted
Add optional plugin config for tags descriptions to OpenAPI specs (#68)
Open API plugin will accept optional tagDescriptionsPropertiesFile attribute. The attribute expects a yaml file containing tag name and description
1 parent a3a6230 commit 875cc8d

File tree

12 files changed

+73
-4
lines changed

12 files changed

+73
-4
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ openapi { //6
108108
basePath = '/api'
109109
title = 'My API'
110110
description = 'My API description'
111+
tagDescriptionsPropertiesFile = 'src/docs/tag-descriptions.yaml'
111112
version = '1.0.0'
112113
format = 'json'
113114
}
@@ -116,6 +117,7 @@ openapi3 {
116117
server = 'https://localhost:8080'
117118
title = 'My API'
118119
description = 'My API description'
120+
tagDescriptionsPropertiesFile = 'src/docs/tag-descriptions.yaml'
119121
version = '0.1.0'
120122
format = 'yaml'
121123
}
@@ -334,6 +336,7 @@ title | The title of the application. Used for the `title` attribute in the [Inf
334336
description | A description of the application. Used for the `description` attribute in the [Info object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#info-object) | empty
335337
version | The version of the api. Used for the `version` attribute in the [Info object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#info-object) | project version
336338
format | The format of the output OpenAPI file - supported values are `json` and `yaml` | `json`
339+
tagDescriptionsPropertiesFile | A yaml file mapping tag names to descriptions. These are populated into the top level ` [Tags attribute](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#tag-object) | no default - if not provided no tags created.
337340
separatePublicApi | Should the plugin generate an additional OpenAPI specification file that does not contain the resources marked as private | `false`
338341
outputDirectory | The output directory | `build/openapi`
339342
snippetsDirectory | The directory Spring REST Docs generated the snippets to | `build/generated-snippets`

restdocs-api-spec-gradle-plugin/src/main/kotlin/com/epages/restdocs/apispec/gradle/OpenApi3Task.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ open class OpenApi3Task : OpenApiBaseTask() {
2323
servers = servers,
2424
title = title,
2525
description = apiDescription,
26+
tagDescriptions = tagDescriptions,
2627
version = apiVersion,
2728
oauth2SecuritySchemeDefinition = oauth2SecuritySchemeDefinition,
2829
format = format

restdocs-api-spec-gradle-plugin/src/main/kotlin/com/epages/restdocs/apispec/gradle/OpenApiBaseTask.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ abstract class OpenApiBaseTask : ApiSpecTask() {
2020
@Optional
2121
lateinit var format: String
2222

23+
@Input
24+
@Optional
25+
lateinit var tagDescriptions: Map<String, String>
26+
2327
@Input @Optional
2428
var oauth2SecuritySchemeDefinition: PluginOauth2Configuration? = null
2529

@@ -31,6 +35,7 @@ abstract class OpenApiBaseTask : ApiSpecTask() {
3135
oauth2SecuritySchemeDefinition = extension.oauth2SecuritySchemeDefinition
3236
title = extension.title
3337
apiDescription = extension.description
38+
tagDescriptions = extension.tagDescriptions()
3439
apiVersion = extension.version
3540
}
3641
}

restdocs-api-spec-gradle-plugin/src/main/kotlin/com/epages/restdocs/apispec/gradle/OpenApiExtension.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ abstract class OpenApiBaseExtension(project: Project) : ApiSpecExtension(project
1717
var title = "API documentation"
1818
var version = project.version as? String ?: "1.0.0"
1919
var description: String? = null
20+
var tagDescriptionsPropertiesFile: String? = null
2021

2122
var format = "json"
2223

@@ -31,6 +32,10 @@ abstract class OpenApiBaseExtension(project: Project) : ApiSpecExtension(project
3132
}
3233
}
3334

35+
fun tagDescriptions(): Map<String, String> {
36+
return tagDescriptionsPropertiesFile?.let { objectMapper.readValue(project.file(it)) } ?: emptyMap()
37+
}
38+
3439
private fun scopeDescriptionSource(scopeDescriptionsPropertiesFile: File): Map<String, String> {
3540
return scopeDescriptionsPropertiesFile.let { objectMapper.readValue<Map<String, String>>(it) } ?: emptyMap()
3641
}

restdocs-api-spec-gradle-plugin/src/main/kotlin/com/epages/restdocs/apispec/gradle/OpenApiTask.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ open class OpenApiTask : OpenApiBaseTask() {
3232
schemes = schemes.toList(),
3333
title = title,
3434
description = apiDescription,
35+
tagDescriptions = tagDescriptions,
3536
version = apiVersion,
3637
oauth2SecuritySchemeDefinition = oauth2SecuritySchemeDefinition,
3738
format = format

restdocs-api-spec-gradle-plugin/src/test/kotlin/com/epages/restdocs/apispec/gradle/RestdocsOpenApi3TaskTest.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ class RestdocsOpenApi3TaskTest : RestdocsOpenApiTaskTestBase() {
109109
servers = [ { url = "http://some.api" } ]
110110
title = '$title'
111111
description = '$description'
112+
tagDescriptionsPropertiesFile = "tagDescriptions.yaml"
112113
version = '$version'
113114
format = '$format'
114115
separatePublicApi = $separatePublicApi

restdocs-api-spec-gradle-plugin/src/test/kotlin/com/epages/restdocs/apispec/gradle/RestdocsOpenApiTaskTest.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class RestdocsOpenApiTaskTest : RestdocsOpenApiTaskTestBase() {
1818
schemes = ${schemes.joinToString(",", "['", "']")}
1919
title = '$title'
2020
description = '$description'
21+
tagDescriptionsPropertiesFile = "tagDescriptions.yaml"
2122
version = '$version'
2223
format = '$format'
2324
separatePublicApi = $separatePublicApi

restdocs-api-spec-gradle-plugin/src/test/kotlin/com/epages/restdocs/apispec/gradle/RestdocsOpenApiTaskTestBase.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ abstract class RestdocsOpenApiTaskTestBase {
5555
@Test
5656
open fun `should run openapi task`() {
5757
givenBuildFileWithOpenApiClosure()
58+
givenTagsTextFile()
5859
givenResourceSnippet()
5960

6061
whenPluginExecuted()
@@ -80,6 +81,7 @@ abstract class RestdocsOpenApiTaskTestBase {
8081
fun `should run openapi task with yaml format`() {
8182
format = "yaml"
8283
givenBuildFileWithOpenApiClosure()
84+
givenTagsTextFile()
8385
givenResourceSnippet()
8486

8587
whenPluginExecuted()
@@ -92,6 +94,7 @@ abstract class RestdocsOpenApiTaskTestBase {
9294
fun `should generate separate public api specification`() {
9395
separatePublicApi = true
9496
givenBuildFileWithOpenApiClosure()
97+
givenTagsTextFile()
9598
givenResourceSnippet()
9699
givenPrivateResourceSnippet()
97100

@@ -105,6 +108,7 @@ abstract class RestdocsOpenApiTaskTestBase {
105108
@Test
106109
fun `should consider security definitions`() {
107110
givenBuildFileWithOpenApiClosureAndSecurityDefinitions()
111+
givenTagsTextFile()
108112
givenResourceSnippet()
109113
givenScopeTextFile()
110114

@@ -124,7 +128,14 @@ abstract class RestdocsOpenApiTaskTestBase {
124128
""".trimIndent()
125129
)
126130
}
127-
131+
private fun givenTagsTextFile() {
132+
testProjectDir.resolve("tagDescriptions.yaml").toFile().writeText(
133+
"""
134+
"tag1": "tag1 description"
135+
"tag2": "tag2 description"
136+
""".trimIndent()
137+
)
138+
}
128139
protected fun thenOpenApiTaskSuccessful() {
129140
then(result.task(":$taskName")!!.outcome).isEqualTo(TaskOutcome.SUCCESS)
130141
}

restdocs-api-spec-openapi-generator/src/main/kotlin/com/epages/restdocs/apispec/openapi2/OpenApi20Generator.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import io.swagger.models.RefModel
2020
import io.swagger.models.Response
2121
import io.swagger.models.Scheme
2222
import io.swagger.models.Swagger
23+
import io.swagger.models.Tag
2324
import io.swagger.models.auth.ApiKeyAuthDefinition
2425
import io.swagger.models.auth.BasicAuthDefinition
2526
import io.swagger.models.auth.OAuth2Definition
@@ -44,6 +45,7 @@ object OpenApi20Generator {
4445
schemes: List<String> = listOf("http"),
4546
title: String = "API",
4647
description: String? = null,
48+
tagDescriptions: Map<String, String> = emptyMap(),
4749
version: String = "1.0.0",
4850
oauth2SecuritySchemeDefinition: Oauth2Configuration? = null
4951
): Swagger {
@@ -57,6 +59,10 @@ object OpenApi20Generator {
5759
this.description = description
5860
this.version = version
5961
}
62+
this.tags(tagDescriptions.map { Tag().apply {
63+
this.name = it.key
64+
this.description = it.value
65+
} })
6066
paths = generatePaths(
6167
resources,
6268
oauth2SecuritySchemeDefinition
@@ -78,11 +84,12 @@ object OpenApi20Generator {
7884
schemes: List<String> = listOf("http"),
7985
title: String = "API",
8086
description: String? = null,
87+
tagDescriptions: Map<String, String> = emptyMap(),
8188
version: String = "1.0.0",
8289
oauth2SecuritySchemeDefinition: Oauth2Configuration? = null,
8390
format: String
8491
): String {
85-
val specification = generate(resources, basePath, host, schemes, title, description, version, oauth2SecuritySchemeDefinition)
92+
val specification = generate(resources, basePath, host, schemes, title, description, tagDescriptions, version, oauth2SecuritySchemeDefinition)
8693
return ApiSpecificationWriter.serialize(format, specification)
8794
}
8895

restdocs-api-spec-openapi-generator/src/test/kotlin/com/epages/restdocs/apispec/openapi2/OpenApi20GeneratorTest.kt

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,29 @@ import io.swagger.models.parameters.Parameter
2323
import io.swagger.models.parameters.PathParameter
2424
import io.swagger.parser.Swagger20Parser
2525
import io.swagger.util.Json
26+
import org.assertj.core.api.Assertions.tuple
2627
import org.assertj.core.api.BDDAssertions.then
2728
import org.junit.jupiter.api.Test
2829

2930
private const val SCHEMA_JSONPATH_PREFIX = "#/definitions/"
3031

3132
class OpenApi20GeneratorTest {
3233

34+
@Test
35+
fun `should have parent tags generated for openapi`() {
36+
val api = givenGetProductResourceModel()
37+
38+
val openapi = whenOpenApiObjectGenerated(api)
39+
40+
with(openapi) {
41+
then(this.tags).extracting("name", "description")
42+
.containsExactly(
43+
tuple("tag1", "tag1 description"),
44+
tuple("tag2", "tag2 description")
45+
)
46+
}
47+
}
48+
3349
@Test
3450
fun `should convert single resource model to openapi`() {
3551
val api = givenGetProductResourceModel()
@@ -143,7 +159,8 @@ class OpenApi20GeneratorTest {
143159
"http://example.com/authorize",
144160
arrayOf("application", "accessCode")
145161
),
146-
description = "API description"
162+
description = "API description",
163+
tagDescriptions = mapOf("tag1" to "tag1 description", "tag2" to "tag2 description")
147164
)
148165

149166
println(ApiSpecificationWriter.serialize("yaml", openapi))

0 commit comments

Comments
 (0)