diff --git a/build.gradle.kts b/build.gradle.kts index e8cd7606..83570587 100755 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,7 +15,7 @@ plugins { id("pl.allegro.tech.build.axion-release") version "1.9.2" jacoco `maven-publish` - id("org.jmailen.kotlinter") version "1.25.2" apply false + id("org.jmailen.kotlinter") version "3.3.0" apply false id("com.github.kt3k.coveralls") version "2.8.2" id("com.jfrog.bintray") version "1.8.4" apply false } diff --git a/restdocs-api-spec-gradle-plugin/src/main/kotlin/com/epages/restdocs/apispec/gradle/PostmanExtension.kt b/restdocs-api-spec-gradle-plugin/src/main/kotlin/com/epages/restdocs/apispec/gradle/PostmanExtension.kt index 24650ce2..9659c4eb 100644 --- a/restdocs-api-spec-gradle-plugin/src/main/kotlin/com/epages/restdocs/apispec/gradle/PostmanExtension.kt +++ b/restdocs-api-spec-gradle-plugin/src/main/kotlin/com/epages/restdocs/apispec/gradle/PostmanExtension.kt @@ -14,4 +14,4 @@ open class PostmanExtension(project: Project) : ApiSpecExtension(project) { companion object { const val name = "postman" } -} \ No newline at end of file +} diff --git a/restdocs-api-spec-gradle-plugin/src/main/kotlin/com/epages/restdocs/apispec/gradle/PostmanTask.kt b/restdocs-api-spec-gradle-plugin/src/main/kotlin/com/epages/restdocs/apispec/gradle/PostmanTask.kt index 5db06599..b2f90bf4 100644 --- a/restdocs-api-spec-gradle-plugin/src/main/kotlin/com/epages/restdocs/apispec/gradle/PostmanTask.kt +++ b/restdocs-api-spec-gradle-plugin/src/main/kotlin/com/epages/restdocs/apispec/gradle/PostmanTask.kt @@ -25,12 +25,12 @@ open class PostmanTask : ApiSpecTask() { override fun generateSpecification(resourceModels: List): String = jacksonObjectMapper().enable(SerializationFeature.INDENT_OUTPUT).writeValueAsString( - PostmanCollectionGenerator.generate( - resources = resourceModels, - title = title, - version = apiVersion, - baseUrl = baseUrl - ) + PostmanCollectionGenerator.generate( + resources = resourceModels, + title = title, + version = apiVersion, + baseUrl = baseUrl + ) ) fun applyExtension(extension: PostmanExtension) { @@ -39,4 +39,4 @@ open class PostmanTask : ApiSpecTask() { apiVersion = extension.version baseUrl = extension.baseUrl } -} \ No newline at end of file +} diff --git a/restdocs-api-spec-gradle-plugin/src/test/kotlin/com/epages/restdocs/apispec/gradle/ApiSpecTaskTest.kt b/restdocs-api-spec-gradle-plugin/src/test/kotlin/com/epages/restdocs/apispec/gradle/ApiSpecTaskTest.kt index 6ac2c344..b26362d9 100644 --- a/restdocs-api-spec-gradle-plugin/src/test/kotlin/com/epages/restdocs/apispec/gradle/ApiSpecTaskTest.kt +++ b/restdocs-api-spec-gradle-plugin/src/test/kotlin/com/epages/restdocs/apispec/gradle/ApiSpecTaskTest.kt @@ -63,20 +63,20 @@ abstract class ApiSpecTaskTest { private fun Path.initializeGradleProperties() { // jacoco agent configuration resolve("gradle.properties").toFile() - .writeText(File("build/testkit/testkit-gradle.properties").readText()) + .writeText(File("build/testkit/testkit-gradle.properties").readText()) } protected fun whenPluginExecuted() { result = GradleRunner.create() - .withProjectDir(testProjectDir.toFile()) - .withArguments("--info", "--stacktrace", taskName) - .withPluginClasspath() - .withDebug(true) - .build() + .withProjectDir(testProjectDir.toFile()) + .withArguments("--info", "--stacktrace", taskName) + .withPluginClasspath() + .withDebug(true) + .build() } protected fun outputFileContext(): DocumentContext = - JsonPath.parse(outputFolder.resolve("$outputFileNamePrefix.$format").readText().also { println(it) }) + JsonPath.parse(outputFolder.resolve("$outputFileNamePrefix.$format").readText().also { println(it) }) fun baseBuildFile() = """ plugins { @@ -84,12 +84,12 @@ abstract class ApiSpecTaskTest { id 'com.epages.restdocs-api-spec' } - """.trimIndent() + """.trimIndent() protected fun givenResourceSnippet() { val operationDir = File(snippetsFolder, "some-operation").apply { mkdir() } File(operationDir, "resource.json").writeText( - """ + """ { "operationId" : "product-get", "summary" : null, @@ -125,7 +125,7 @@ abstract class ApiSpecTaskTest { protected fun givenPrivateResourceSnippet() { val operationDir = File(snippetsFolder, "some-private-operation").apply { mkdir() } File(operationDir, "resource.json").writeText( - """ + """ { "operationId" : "product-get-some", "summary" : null, @@ -176,13 +176,15 @@ abstract class ApiSpecTaskTest { protected fun thenExpectedFileFound(expectedFile: String) { BDDAssertions.then(outputFolder.resolve(expectedFile)) - .describedAs("Output file not found '$expectedFile' - output dir contains ${Files.list(outputFolder.toPath()).map { + .describedAs( + "Output file not found '$expectedFile' - output dir contains ${Files.list(outputFolder.toPath()).map { it.toFile().path - }.toList()}") - .exists() + }.toList()}" + ) + .exists() } protected fun givenBuildFileWithoutApiSpecClosure() { buildFile.writeText(baseBuildFile()) } -} \ No newline at end of file +} diff --git a/restdocs-api-spec-gradle-plugin/src/test/kotlin/com/epages/restdocs/apispec/gradle/PostmanTaskTest.kt b/restdocs-api-spec-gradle-plugin/src/test/kotlin/com/epages/restdocs/apispec/gradle/PostmanTaskTest.kt index d7fc2754..2a1cf85f 100644 --- a/restdocs-api-spec-gradle-plugin/src/test/kotlin/com/epages/restdocs/apispec/gradle/PostmanTaskTest.kt +++ b/restdocs-api-spec-gradle-plugin/src/test/kotlin/com/epages/restdocs/apispec/gradle/PostmanTaskTest.kt @@ -64,7 +64,8 @@ class PostmanTaskTest : ApiSpecTaskTest() { } private fun givenBuildFileWithPostmanClosure() { - buildFile.writeText(baseBuildFile() + """ + buildFile.writeText( + baseBuildFile() + """ postman { title = '$title' version = '$version' @@ -72,6 +73,7 @@ class PostmanTaskTest : ApiSpecTaskTest() { separatePublicApi = $separatePublicApi outputFileNamePrefix = '$outputFileNamePrefix' } - """.trimIndent()) + """.trimIndent() + ) } -} \ No newline at end of file +} diff --git a/restdocs-api-spec-gradle-plugin/src/test/kotlin/com/epages/restdocs/apispec/gradle/RestdocsOpenApi3TaskTest.kt b/restdocs-api-spec-gradle-plugin/src/test/kotlin/com/epages/restdocs/apispec/gradle/RestdocsOpenApi3TaskTest.kt index f22f43ca..7dead743 100644 --- a/restdocs-api-spec-gradle-plugin/src/test/kotlin/com/epages/restdocs/apispec/gradle/RestdocsOpenApi3TaskTest.kt +++ b/restdocs-api-spec-gradle-plugin/src/test/kotlin/com/epages/restdocs/apispec/gradle/RestdocsOpenApi3TaskTest.kt @@ -63,7 +63,8 @@ class RestdocsOpenApi3TaskTest : RestdocsOpenApiTaskTestBase() { } override fun givenBuildFileWithOpenApiClosure() { - givenBuildFileWithOpenApiClosure("servers", + givenBuildFileWithOpenApiClosure( + "servers", """[ { url = 'http://some.api/api/{id}' variables = [ @@ -87,11 +88,13 @@ class RestdocsOpenApi3TaskTest : RestdocsOpenApiTaskTestBase() { ] ] } - ]""".trimMargin()) + ]""".trimMargin() + ) } private fun givenBuildFileWithOpenApiClosure(serverConfigurationFieldName: String, serversSection: String) { - buildFile.writeText(baseBuildFile() + """ + buildFile.writeText( + baseBuildFile() + """ openapi3 { $serverConfigurationFieldName = $serversSection title = '$title' @@ -100,11 +103,13 @@ class RestdocsOpenApi3TaskTest : RestdocsOpenApiTaskTestBase() { separatePublicApi = $separatePublicApi outputFileNamePrefix = '$outputFileNamePrefix' } - """.trimIndent()) + """.trimIndent() + ) } override fun givenBuildFileWithOpenApiClosureAndSecurityDefinitions() { - buildFile.writeText(baseBuildFile() + """ + buildFile.writeText( + baseBuildFile() + """ openapi3 { servers = [ { url = "http://some.api" } ] title = '$title' @@ -121,7 +126,8 @@ class RestdocsOpenApi3TaskTest : RestdocsOpenApiTaskTestBase() { scopeDescriptionsPropertiesFile = "scopeDescriptions.yaml" } } - """.trimIndent()) + """.trimIndent() + ) } override fun thenSecurityDefinitionsFoundInOutputFile() { diff --git a/restdocs-api-spec-gradle-plugin/src/test/kotlin/com/epages/restdocs/apispec/gradle/RestdocsOpenApiTaskTest.kt b/restdocs-api-spec-gradle-plugin/src/test/kotlin/com/epages/restdocs/apispec/gradle/RestdocsOpenApiTaskTest.kt index 5b292b7b..578f1f38 100644 --- a/restdocs-api-spec-gradle-plugin/src/test/kotlin/com/epages/restdocs/apispec/gradle/RestdocsOpenApiTaskTest.kt +++ b/restdocs-api-spec-gradle-plugin/src/test/kotlin/com/epages/restdocs/apispec/gradle/RestdocsOpenApiTaskTest.kt @@ -11,7 +11,8 @@ class RestdocsOpenApiTaskTest : RestdocsOpenApiTaskTestBase() { override val taskName = "openapi" override fun givenBuildFileWithOpenApiClosure() { - buildFile.writeText(baseBuildFile() + """ + buildFile.writeText( + baseBuildFile() + """ openapi { host = '$host' basePath = '$basePath' @@ -24,11 +25,13 @@ class RestdocsOpenApiTaskTest : RestdocsOpenApiTaskTestBase() { separatePublicApi = $separatePublicApi outputFileNamePrefix = '$outputFileNamePrefix' } - """.trimIndent()) + """.trimIndent() + ) } override fun givenBuildFileWithOpenApiClosureAndSecurityDefinitions() { - buildFile.writeText(baseBuildFile() + """ + buildFile.writeText( + baseBuildFile() + """ openapi { host = '$host' basePath = '$basePath' @@ -45,7 +48,8 @@ class RestdocsOpenApiTaskTest : RestdocsOpenApiTaskTestBase() { scopeDescriptionsPropertiesFile = "scopeDescriptions.yaml" } } - """.trimIndent()) + """.trimIndent() + ) } override fun thenSecurityDefinitionsFoundInOutputFile() { diff --git a/restdocs-api-spec-gradle-plugin/src/test/kotlin/com/epages/restdocs/apispec/gradle/RestdocsOpenApiTaskTestBase.kt b/restdocs-api-spec-gradle-plugin/src/test/kotlin/com/epages/restdocs/apispec/gradle/RestdocsOpenApiTaskTestBase.kt index 73a54e34..363ad4fe 100644 --- a/restdocs-api-spec-gradle-plugin/src/test/kotlin/com/epages/restdocs/apispec/gradle/RestdocsOpenApiTaskTestBase.kt +++ b/restdocs-api-spec-gradle-plugin/src/test/kotlin/com/epages/restdocs/apispec/gradle/RestdocsOpenApiTaskTestBase.kt @@ -71,17 +71,17 @@ abstract class RestdocsOpenApiTaskTestBase : ApiSpecTaskTest() { private fun givenScopeTextFile() { testProjectDir.resolve("scopeDescriptions.yaml").toFile().writeText( - """ + """ "prod:r": "Some text" - """.trimIndent() - ) + """.trimIndent() + ) } private fun givenTagsTextFile() { testProjectDir.resolve("tagDescriptions.yaml").toFile().writeText( - """ + """ "tag1": "tag1 description" "tag2": "tag2 description" - """.trimIndent() + """.trimIndent() ) } diff --git a/restdocs-api-spec-jsonschema/src/main/kotlin/com/epages/restdocs/apispec/jsonschema/ConstraintResolver.kt b/restdocs-api-spec-jsonschema/src/main/kotlin/com/epages/restdocs/apispec/jsonschema/ConstraintResolver.kt index bf7df96f..e79f5845 100644 --- a/restdocs-api-spec-jsonschema/src/main/kotlin/com/epages/restdocs/apispec/jsonschema/ConstraintResolver.kt +++ b/restdocs-api-spec-jsonschema/src/main/kotlin/com/epages/restdocs/apispec/jsonschema/ConstraintResolver.kt @@ -25,9 +25,11 @@ internal object ConstraintResolver { internal fun minLengthString(fieldDescriptor: FieldDescriptor): Int? { return findConstraints(fieldDescriptor) .firstOrNull { constraint -> - (NOT_EMPTY_CONSTRAINTS.contains(constraint.name) || - NOT_BLANK_CONSTRAINTS.contains(constraint.name) || - LENGTH_CONSTRAINT == constraint.name) + ( + NOT_EMPTY_CONSTRAINTS.contains(constraint.name) || + NOT_BLANK_CONSTRAINTS.contains(constraint.name) || + LENGTH_CONSTRAINT == constraint.name + ) } ?.let { constraint -> if (LENGTH_CONSTRAINT == constraint.name) constraint.configuration["min"] as Int else 1 } } diff --git a/restdocs-api-spec-jsonschema/src/main/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGenerator.kt b/restdocs-api-spec-jsonschema/src/main/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGenerator.kt index 75331461..c2f0e4bc 100644 --- a/restdocs-api-spec-jsonschema/src/main/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGenerator.kt +++ b/restdocs-api-spec-jsonschema/src/main/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGenerator.kt @@ -11,10 +11,10 @@ import org.everit.json.schema.ArraySchema import org.everit.json.schema.BooleanSchema import org.everit.json.schema.CombinedSchema import org.everit.json.schema.EmptySchema +import org.everit.json.schema.EnumSchema import org.everit.json.schema.NullSchema import org.everit.json.schema.NumberSchema import org.everit.json.schema.ObjectSchema -import org.everit.json.schema.EnumSchema import org.everit.json.schema.Schema import org.everit.json.schema.StringSchema import org.everit.json.schema.internal.JSONPrinter @@ -46,11 +46,12 @@ class JsonSchemaFromFieldDescriptorsGenerator { it ) } - .foldRight(listOf()) { fieldDescriptor, groups -> groups - .firstOrNull { it.equalsOnPathAndType(fieldDescriptor) } - ?.let { groups } // omit the descriptor it is considered equal and can be omitted - ?: groups.firstOrNull { it.path == fieldDescriptor.path } - ?.let { groups - it + it.merge(fieldDescriptor) } // merge the type with the descriptor with the same name + .foldRight(listOf()) { fieldDescriptor, groups -> + groups + .firstOrNull { it.equalsOnPathAndType(fieldDescriptor) } + ?.let { groups } // omit the descriptor it is considered equal and can be omitted + ?: groups.firstOrNull { it.path == fieldDescriptor.path } + ?.let { groups - it + it.merge(fieldDescriptor) } // merge the type with the descriptor with the same name ?: groups + fieldDescriptor // it is new just add it } } @@ -140,7 +141,8 @@ class JsonSchemaFromFieldDescriptorsGenerator { ) { traversedSegments.add(remainingSegments[0]) builder.addPropertySchema( - propertyName, ArraySchema.builder() + propertyName, + ArraySchema.builder() .allItemSchema(traverse(traversedSegments, fields, ObjectSchema.builder())) .description(propertyField?.fieldDescriptor?.description) .build() @@ -150,8 +152,10 @@ class JsonSchemaFromFieldDescriptorsGenerator { builder.addRequiredProperty(propertyName) } builder.addPropertySchema( - propertyName, traverse( - traversedSegments, fields, ObjectSchema.builder() + propertyName, + traverse( + traversedSegments, fields, + ObjectSchema.builder() .description(propertyField?.fieldDescriptor?.description) as ObjectSchema.Builder ) ) @@ -167,8 +171,10 @@ class JsonSchemaFromFieldDescriptorsGenerator { builder.addRequiredProperty(propertyName) } if (propertyName == "[]") { - builder.addPropertySchema(propertyName, - createSchemaWithArrayContent(ObjectSchema.builder().build(), depthOfArrayPath(fieldDescriptor.path))) + builder.addPropertySchema( + propertyName, + createSchemaWithArrayContent(ObjectSchema.builder().build(), depthOfArrayPath(fieldDescriptor.path)) + ) } else { builder.addPropertySchema(propertyName, fieldDescriptor.jsonSchemaType()) } @@ -233,30 +239,35 @@ class JsonSchemaFromFieldDescriptorsGenerator { "null" -> NullSchema.builder() "empty" -> EmptySchema.builder() "object" -> ObjectSchema.builder() - "array" -> ArraySchema.builder().allItemSchema(CombinedSchema.oneOf( + "array" -> ArraySchema.builder().allItemSchema( + CombinedSchema.oneOf( listOf( - ObjectSchema.builder().build(), - BooleanSchema.builder().build(), - StringSchema.builder().build(), - NumberSchema.builder().build() + ObjectSchema.builder().build(), + BooleanSchema.builder().build(), + StringSchema.builder().build(), + NumberSchema.builder().build() ) - ).build()) + ).build() + ) "boolean" -> BooleanSchema.builder() "number" -> NumberSchema.builder() "string" -> StringSchema.builder() .minLength(minLengthString(this)) .maxLength(maxLengthString(this)) "enum" -> CombinedSchema.oneOf( - listOf( - StringSchema.builder().build(), - EnumSchema.builder().possibleValues(this.attributes.enumValues).build()) - ).isSynthetic(true) + listOf( + StringSchema.builder().build(), + EnumSchema.builder().possibleValues(this.attributes.enumValues).build() + ) + ).isSynthetic(true) else -> throw IllegalArgumentException("unknown field type $type") } fun equalsOnPathAndType(f: FieldDescriptorWithSchemaType): Boolean = - (this.path == f.path && - this.type == f.type) + ( + this.path == f.path && + this.type == f.type + ) companion object { fun fromFieldDescriptor(fieldDescriptor: FieldDescriptor) = diff --git a/restdocs-api-spec-jsonschema/src/test/kotlin/com/epages/restdocs/apispec/jsonschema/JsonFieldPathTest.kt b/restdocs-api-spec-jsonschema/src/test/kotlin/com/epages/restdocs/apispec/jsonschema/JsonFieldPathTest.kt index 87523bf6..8472db16 100644 --- a/restdocs-api-spec-jsonschema/src/test/kotlin/com/epages/restdocs/apispec/jsonschema/JsonFieldPathTest.kt +++ b/restdocs-api-spec-jsonschema/src/test/kotlin/com/epages/restdocs/apispec/jsonschema/JsonFieldPathTest.kt @@ -10,12 +10,14 @@ class JsonFieldPathTest { @Test fun should_get_remaining_segments() { - with(compile( - JsonSchemaFromFieldDescriptorsGenerator.FieldDescriptorWithSchemaType( - "a.b.c", "", "", false, false, - Attributes() + with( + compile( + JsonSchemaFromFieldDescriptorsGenerator.FieldDescriptorWithSchemaType( + "a.b.c", "", "", false, false, + Attributes() + ) ) - )) { + ) { then(remainingSegments(listOf("a"))).contains("b", "c") then(remainingSegments(listOf("a", "b"))).contains("c") then(remainingSegments(listOf("a", "b", "c"))).isEmpty() diff --git a/restdocs-api-spec-jsonschema/src/test/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGeneratorTest.kt b/restdocs-api-spec-jsonschema/src/test/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGeneratorTest.kt index d1dff0fb..2e4b1c00 100644 --- a/restdocs-api-spec-jsonschema/src/test/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGeneratorTest.kt +++ b/restdocs-api-spec-jsonschema/src/test/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGeneratorTest.kt @@ -9,11 +9,11 @@ import com.jayway.jsonpath.JsonPath import org.assertj.core.api.BDDAssertions.then import org.assertj.core.api.BDDAssertions.thenThrownBy import org.everit.json.schema.ArraySchema +import org.everit.json.schema.CombinedSchema +import org.everit.json.schema.EnumSchema import org.everit.json.schema.ObjectSchema import org.everit.json.schema.Schema import org.everit.json.schema.StringSchema -import org.everit.json.schema.CombinedSchema -import org.everit.json.schema.EnumSchema import org.everit.json.schema.ValidationException import org.everit.json.schema.loader.SchemaLoader import org.json.JSONArray @@ -106,7 +106,8 @@ class JsonSchemaFromFieldDescriptorsGeneratorTest { } ] } - }""".trimIndent() + } + """.trimIndent() ) } @@ -250,8 +251,8 @@ class JsonSchemaFromFieldDescriptorsGeneratorTest { private fun givenFieldDescriptorWithRequiredObject() { val notNullConstraint = Attributes(listOf(Constraint(NotNull::class.java.name, emptyMap()))) fieldDescriptors = listOf( - FieldDescriptor("obj", "some", "OBJECT", attributes = notNullConstraint), - FieldDescriptor("obj.field", "some", "STRING") + FieldDescriptor("obj", "some", "OBJECT", attributes = notNullConstraint), + FieldDescriptor("obj.field", "some", "STRING") ) } @@ -305,7 +306,8 @@ class JsonSchemaFromFieldDescriptorsGeneratorTest { Attributes( listOf( Constraint( - "org.hibernate.validator.constraints.Length", mapOf( + "org.hibernate.validator.constraints.Length", + mapOf( "min" to 2, "max" to 255 ) @@ -359,10 +361,11 @@ class JsonSchemaFromFieldDescriptorsGeneratorTest { private fun givenFieldDescriptorWithEnum() { fieldDescriptors = listOf( - FieldDescriptor( - "some", - "some", - "enum", attributes = Attributes(enumValues = listOf("ENUM_VALUE_1", "ENUM_VALUE_2"))) + FieldDescriptor( + "some", + "some", + "enum", attributes = Attributes(enumValues = listOf("ENUM_VALUE_1", "ENUM_VALUE_2")) + ) ) } diff --git a/restdocs-api-spec-mockmvc/src/main/kotlin/com/epages/restdocs/apispec/MockMvcRestDocumentationWrapper.kt b/restdocs-api-spec-mockmvc/src/main/kotlin/com/epages/restdocs/apispec/MockMvcRestDocumentationWrapper.kt index ccdd78e9..f8900e26 100644 --- a/restdocs-api-spec-mockmvc/src/main/kotlin/com/epages/restdocs/apispec/MockMvcRestDocumentationWrapper.kt +++ b/restdocs-api-spec-mockmvc/src/main/kotlin/com/epages/restdocs/apispec/MockMvcRestDocumentationWrapper.kt @@ -24,18 +24,18 @@ object MockMvcRestDocumentationWrapper : RestDocumentationWrapper() { ): RestDocumentationResultHandler { val enhancedSnippets = - enhanceSnippetsWithResourceSnippet( - resourceDetails = resourceDetails, - snippetFilter = snippetFilter, - snippets = *snippets - ) + enhanceSnippetsWithResourceSnippet( + resourceDetails = resourceDetails, + snippetFilter = snippetFilter, + snippets = *snippets + ) if (requestPreprocessor != null && responsePreprocessor != null) { return MockMvcRestDocumentation.document( - identifier, - requestPreprocessor, - responsePreprocessor, - *enhancedSnippets + identifier, + requestPreprocessor, + responsePreprocessor, + *enhancedSnippets ) } else if (requestPreprocessor != null) { return MockMvcRestDocumentation.document(identifier, requestPreprocessor, *enhancedSnippets) @@ -59,16 +59,16 @@ object MockMvcRestDocumentationWrapper : RestDocumentationWrapper() { vararg snippets: Snippet ): RestDocumentationResultHandler { return document( - identifier = identifier, - resourceDetails = ResourceSnippetParametersBuilder() - .description(description) - .summary(summary) - .privateResource(privateResource) - .deprecated(deprecated), - requestPreprocessor = requestPreprocessor, - responsePreprocessor = responsePreprocessor, - snippetFilter = snippetFilter, - snippets = *snippets + identifier = identifier, + resourceDetails = ResourceSnippetParametersBuilder() + .description(description) + .summary(summary) + .privateResource(privateResource) + .deprecated(deprecated), + requestPreprocessor = requestPreprocessor, + responsePreprocessor = responsePreprocessor, + snippetFilter = snippetFilter, + snippets = *snippets ) } diff --git a/restdocs-api-spec-mockmvc/src/test/kotlin/com/epages/restdocs/apispec/MockMvcRestDocumentationWrapperIntegrationTest.kt b/restdocs-api-spec-mockmvc/src/test/kotlin/com/epages/restdocs/apispec/MockMvcRestDocumentationWrapperIntegrationTest.kt index 9ecbe5cc..c3ee4f14 100644 --- a/restdocs-api-spec-mockmvc/src/test/kotlin/com/epages/restdocs/apispec/MockMvcRestDocumentationWrapperIntegrationTest.kt +++ b/restdocs-api-spec-mockmvc/src/test/kotlin/com/epages/restdocs/apispec/MockMvcRestDocumentationWrapperIntegrationTest.kt @@ -14,6 +14,8 @@ import org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders import org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders import org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel import org.springframework.restdocs.hypermedia.HypermediaDocumentation.links +import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post import org.springframework.restdocs.operation.preprocess.OperationRequestPreprocessor import org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath import org.springframework.restdocs.payload.PayloadDocumentation.requestFields @@ -24,8 +26,6 @@ import org.springframework.restdocs.request.RequestDocumentation.pathParameters import org.springframework.test.context.junit.jupiter.SpringExtension import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.result.MockMvcResultHandlers.print -import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document -import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status import java.io.File @@ -108,36 +108,38 @@ class MockMvcRestDocumentationWrapperIntegrationTest(@Autowired private val mock givenEndpointInvoked("null") assertThatCode { this.whenResourceSnippetDocumentedWithRequestAndResponseFields() } - .doesNotThrowAnyException() + .doesNotThrowAnyException() } private fun whenResourceSnippetDocumentedWithoutParameters() { resultActions - .andDo(document(operationName, resource())) + .andDo(document(operationName, resource())) } private fun whenResourceSnippetDocumentedWithDescription() { resultActions - .andDo(document(operationName, resource("A description"))) + .andDo(document(operationName, resource("A description"))) } private fun whenResourceSnippetDocumentedWithRequestAndResponseFields() { resultActions - .andDo(document(operationName, buildFullResourceSnippet())) + .andDo(document(operationName, buildFullResourceSnippet())) } private fun givenEndpointInvoked(flagValue: String = "true") { resultActions = mockMvc.perform( - post("/some/{someId}/other/{otherId}", "id", 1) - .contentType(APPLICATION_JSON) - .header("X-Custom-Header", "test") - .accept(HAL_JSON) - .content("""{ + post("/some/{someId}/other/{otherId}", "id", 1) + .contentType(APPLICATION_JSON) + .header("X-Custom-Header", "test") + .accept(HAL_JSON) + .content( + """{ "comment": "some", "flag": $flagValue, "count": 1 - }""".trimIndent() - ) + } + """.trimIndent() + ) ).andExpect(status().isOk) } diff --git a/restdocs-api-spec-mockmvc/src/test/kotlin/com/epages/restdocs/apispec/ResourceSnippetIntegrationTest.kt b/restdocs-api-spec-mockmvc/src/test/kotlin/com/epages/restdocs/apispec/ResourceSnippetIntegrationTest.kt index 2e73b22a..224f40e0 100644 --- a/restdocs-api-spec-mockmvc/src/test/kotlin/com/epages/restdocs/apispec/ResourceSnippetIntegrationTest.kt +++ b/restdocs-api-spec-mockmvc/src/test/kotlin/com/epages/restdocs/apispec/ResourceSnippetIntegrationTest.kt @@ -61,9 +61,9 @@ open class ResourceSnippetIntegrationTest { resource.add(Link(link, "multiple")) return ResponseEntity - .ok() - .header("X-Custom-Header", customHeader) - .body>(resource) + .ok() + .header("X-Custom-Header", customHeader) + .body>(resource) } } } @@ -81,37 +81,37 @@ open class ResourceSnippetIntegrationTest { fun fieldDescriptors(): FieldDescriptors { val fields = ConstrainedFields(ResourceSnippetIntegrationTest.TestDataHolder::class.java) return ResourceDocumentation.fields( - fields.withPath("comment").description("the comment").optional(), - fields.withPath("flag").description("the flag"), - fields.withMappedPath("count", "count").description("the count") + fields.withPath("comment").description("the comment").optional(), + fields.withPath("flag").description("the flag"), + fields.withMappedPath("count", "count").description("the count") ) } fun buildFullResourceSnippet(): ResourceSnippet { return resource( - ResourceSnippetParameters.builder() - .description("description") - .summary("summary") - .deprecated(true) - .privateResource(true) - .requestFields(fieldDescriptors()) - .responseFields(fieldDescriptors().and(fieldWithPath("id").description("id"))) - .requestHeaders( - headerWithName("X-Custom-Header").description("A custom header"), - headerWithName(ACCEPT).description("Accept") - ) - .responseHeaders( - headerWithName("X-Custom-Header").description("A custom header"), - headerWithName(CONTENT_TYPE).description("ContentType") - ) - .pathParameters( - parameterWithName("someId").description("some id"), - parameterWithName("otherId").description("otherId id").type(SimpleType.INTEGER) - ) - .links( - linkWithRel("self").description("some"), - linkWithRel("multiple").description("multiple") - ) - .build() + ResourceSnippetParameters.builder() + .description("description") + .summary("summary") + .deprecated(true) + .privateResource(true) + .requestFields(fieldDescriptors()) + .responseFields(fieldDescriptors().and(fieldWithPath("id").description("id"))) + .requestHeaders( + headerWithName("X-Custom-Header").description("A custom header"), + headerWithName(ACCEPT).description("Accept") + ) + .responseHeaders( + headerWithName("X-Custom-Header").description("A custom header"), + headerWithName(CONTENT_TYPE).description("ContentType") + ) + .pathParameters( + parameterWithName("someId").description("some id"), + parameterWithName("otherId").description("otherId id").type(SimpleType.INTEGER) + ) + .links( + linkWithRel("self").description("some"), + linkWithRel("multiple").description("multiple") + ) + .build() ) } diff --git a/restdocs-api-spec-model/src/main/kotlin/com/epages/restdocs/apispec/model/ResourceModel.kt b/restdocs-api-spec-model/src/main/kotlin/com/epages/restdocs/apispec/model/ResourceModel.kt index f4e53ac9..bcdcfe9c 100644 --- a/restdocs-api-spec-model/src/main/kotlin/com/epages/restdocs/apispec/model/ResourceModel.kt +++ b/restdocs-api-spec-model/src/main/kotlin/com/epages/restdocs/apispec/model/ResourceModel.kt @@ -20,7 +20,8 @@ fun List.groupByPath(): Map> { it.request.path.split("/").firstOrNull { s -> s.isNotEmpty() }.orEmpty() } .thenComparing(Comparator.comparingInt { it.request.path.count { c -> c == '/' } }) - .thenComparing(Comparator.comparing { it.request.path })) + .thenComparing(Comparator.comparing { it.request.path }) + ) .groupBy { it.request.path } } diff --git a/restdocs-api-spec-openapi-generator/src/main/kotlin/com/epages/restdocs/apispec/openapi2/OpenApi20Generator.kt b/restdocs-api-spec-openapi-generator/src/main/kotlin/com/epages/restdocs/apispec/openapi2/OpenApi20Generator.kt index 1b90ca6f..db7287d0 100644 --- a/restdocs-api-spec-openapi-generator/src/main/kotlin/com/epages/restdocs/apispec/openapi2/OpenApi20Generator.kt +++ b/restdocs-api-spec-openapi-generator/src/main/kotlin/com/epages/restdocs/apispec/openapi2/OpenApi20Generator.kt @@ -62,10 +62,14 @@ object OpenApi20Generator { this.description = description this.version = version } - this.tags(tagDescriptions.map { Tag().apply { - this.name = it.key - this.description = it.value - } }) + this.tags( + tagDescriptions.map { + Tag().apply { + this.name = it.key + this.description = it.value + } + } + ) paths = generatePaths( resources, oauth2SecuritySchemeDefinition @@ -164,12 +168,13 @@ object OpenApi20Generator { } internal fun generateSchemaName(path: String): (Model) -> String { - return { schema -> path - .replaceFirst("/", "") - .replace("/", "_") - .replace(Regex.fromLiteral("{"), "") - .replace(Regex.fromLiteral("}"), "") - .plus(schema.hashCode()) + return { schema -> + path + .replaceFirst("/", "") + .replace("/", "_") + .replace(Regex.fromLiteral("{"), "") + .replace(Regex.fromLiteral("}"), "") + .plus(schema.hashCode()) } } @@ -179,10 +184,11 @@ object OpenApi20Generator { ): Map { return groupByPath(resources) .entries - .map { it.key to resourceModels2Path( - it.value, - oauth2SecuritySchemeDefinition - ) + .map { + it.key to resourceModels2Path( + it.value, + oauth2SecuritySchemeDefinition + ) } .toMap() } @@ -192,7 +198,8 @@ object OpenApi20Generator { // by first path segment, then path length, then path comparing { it.request.path.split("/").firstOrNull { s -> s.isNotEmpty() }.orEmpty() } .thenComparing(comparingInt { it.request.path.count { c -> c == '/' } }) - .thenComparing(comparing { it.request.path })) + .thenComparing(comparing { it.request.path }) + ) .groupBy { it.request.path } } @@ -303,7 +310,8 @@ object OpenApi20Generator { .filter { it.request.contentType != null && it.request.example != null } .map { it.request.contentType!! to it.request.example!! } .toMap(), - firstModelForPathAndMethod.request.schema) + firstModelForPathAndMethod.request.schema + ) ) ).nullIfEmpty() responses = responsesByStatusCode( @@ -316,7 +324,8 @@ object OpenApi20Generator { if (securityRequirements != null) { when (securityRequirements.type) { SecurityType.OAUTH2 -> oauth2SecuritySchemeDefinition?.flows?.map { - addSecurity(oauth2SecuritySchemeDefinition.securitySchemeName(it), + addSecurity( + oauth2SecuritySchemeDefinition.securitySchemeName(it), securityRequirements2ScopesList( securityRequirements ) @@ -331,8 +340,8 @@ object OpenApi20Generator { private fun extractPathParameters(resourceModel: ResourceModel): List { val pathParameterNames = PATH_PARAMETER_PATTERN.findAll(resourceModel.request.path) - .map { matchResult -> matchResult.groupValues[1] } - .toList() + .map { matchResult -> matchResult.groupValues[1] } + .toList() return pathParameterNames.map { parameterName -> resourceModel.request.pathParameters diff --git a/restdocs-api-spec-openapi-generator/src/test/kotlin/com/epages/restdocs/apispec/openapi2/OpenApi20GeneratorTest.kt b/restdocs-api-spec-openapi-generator/src/test/kotlin/com/epages/restdocs/apispec/openapi2/OpenApi20GeneratorTest.kt index 69213c3f..c2fdd7aa 100644 --- a/restdocs-api-spec-openapi-generator/src/test/kotlin/com/epages/restdocs/apispec/openapi2/OpenApi20GeneratorTest.kt +++ b/restdocs-api-spec-openapi-generator/src/test/kotlin/com/epages/restdocs/apispec/openapi2/OpenApi20GeneratorTest.kt @@ -2,6 +2,7 @@ package com.epages.restdocs.apispec.openapi2 import com.epages.restdocs.apispec.jsonschema.JsonSchemaFromFieldDescriptorsGenerator import com.epages.restdocs.apispec.model.AbstractParameterDescriptor +import com.epages.restdocs.apispec.model.Attributes import com.epages.restdocs.apispec.model.FieldDescriptor import com.epages.restdocs.apispec.model.HTTPMethod import com.epages.restdocs.apispec.model.HeaderDescriptor @@ -10,11 +11,10 @@ import com.epages.restdocs.apispec.model.ParameterDescriptor import com.epages.restdocs.apispec.model.RequestModel import com.epages.restdocs.apispec.model.ResourceModel import com.epages.restdocs.apispec.model.ResponseModel -import com.epages.restdocs.apispec.model.SecurityRequirements import com.epages.restdocs.apispec.model.Schema +import com.epages.restdocs.apispec.model.SecurityRequirements import com.epages.restdocs.apispec.model.SecurityType.BASIC import com.epages.restdocs.apispec.model.SecurityType.OAUTH2 -import com.epages.restdocs.apispec.model.Attributes import com.fasterxml.jackson.module.kotlin.readValue import io.swagger.models.Model import io.swagger.models.Path @@ -44,10 +44,10 @@ class OpenApi20GeneratorTest { with(openapi) { then(this.tags).extracting("name", "description") - .containsExactly( - tuple("tag1", "tag1 description"), - tuple("tag2", "tag2 description") - ) + .containsExactly( + tuple("tag1", "tag1 description"), + tuple("tag2", "tag2 description") + ) } } @@ -143,8 +143,10 @@ class OpenApi20GeneratorTest { with(openapi.securityDefinitions) { then(this.containsKey("oauth2_accessCode")) then(this["oauth2_accessCode"]) - .isEqualToComparingFieldByField(OAuth2Definition().accessCode("http://example.com/authorize", "http://example.com/token") - .apply { addScope("prod:r", "No description") }) + .isEqualToComparingFieldByField( + OAuth2Definition().accessCode("http://example.com/authorize", "http://example.com/token") + .apply { addScope("prod:r", "No description") } + ) } thenValidateOpenApi(openapi) } @@ -158,7 +160,7 @@ class OpenApi20GeneratorTest { with(openapi.securityDefinitions) { then(this).containsKey("basic") then(this["basic"]) - .isEqualToComparingFieldByField(BasicAuthDefinition()) + .isEqualToComparingFieldByField(BasicAuthDefinition()) } then(openapi.paths.values.first().operations.first().security).hasSize(1) then(openapi.paths.values.first().operations.first().security.first()).containsKey("basic") @@ -276,8 +278,8 @@ class OpenApi20GeneratorTest { "http://example.com/authorize", arrayOf("application", "accessCode") ), - description = "API description", - tagDescriptions = mapOf("tag1" to "tag1 description", "tag2" to "tag2 description") + description = "API description", + tagDescriptions = mapOf("tag1" to "tag1 description", "tag2" to "tag2 description") ) println(ApiSpecificationWriter.serialize("yaml", openapi)) @@ -350,8 +352,10 @@ class OpenApi20GeneratorTest { then(successfulGetResponse.headers.get(header.name)!!.type).isEqualTo(header.type.toLowerCase()) } - then(successfulGetResponse - .examples.get(successfulGetProductModel.response.contentType)).isEqualTo(successfulGetProductModel.response.example) + then( + successfulGetResponse + .examples.get(successfulGetProductModel.response.contentType) + ).isEqualTo(successfulGetProductModel.response.example) thenParametersForGetMatch(productPath.get.parameters, successfulGetProductModel.request) } @@ -363,8 +367,10 @@ class OpenApi20GeneratorTest { then(productPath).isNotNull then(productPath.post.consumes).contains(successfulPostProductModel.request.contentType) then(successfulPostResponse).isNotNull - then(successfulPostResponse!! - .examples.get(successfulPostProductModel.response.contentType)).isEqualTo(successfulPostProductModel.response.example) + then( + successfulPostResponse!! + .examples.get(successfulPostProductModel.response.contentType) + ).isEqualTo(successfulPostProductModel.response.example) thenParametersForPostMatch(productPath.post.parameters, successfulPostProductModel.request) thenRequestAndResponseSchemataAreReferenced(productPath, successfulPostResponse, openapi.definitions) @@ -400,8 +406,10 @@ class OpenApi20GeneratorTest { val badGetProductModel = api[2] val productPath = openapi.paths.getValue(badGetProductModel.request.path) then(productPath.get.responses.get(badGetProductModel.response.status.toString())).isNotNull - then(productPath.get.responses.get(badGetProductModel.response.status.toString())!! - .examples.get(badGetProductModel.response.contentType)).isEqualTo(badGetProductModel.response.example) + then( + productPath.get.responses.get(badGetProductModel.response.status.toString())!! + .examples.get(badGetProductModel.response.contentType) + ).isEqualTo(badGetProductModel.response.example) thenParametersForGetMatch(productPath.get.parameters, badGetProductModel.request) } @@ -433,7 +441,7 @@ class OpenApi20GeneratorTest { then(productPath.delete.consumes).isNull() then(productPath.delete.responses[successfulDeleteProductModel.response.status.toString()]).isNotNull then(productPath.delete.security.reduce { map1, map2 -> map1 + map2 }.values) - .containsOnly(successfulDeleteProductModel.request.securityRequirements!!.requiredScopes) + .containsOnly(successfulDeleteProductModel.request.securityRequirements!!.requiredScopes) then( productPath.delete.responses[successfulDeleteProductModel.response.status.toString()]!! .examples[successfulDeleteProductModel.response.contentType] @@ -500,13 +508,13 @@ class OpenApi20GeneratorTest { private fun givenGetProductResourceModelWithMultiplePathParameters(): List { return listOf( - ResourceModel( - operationId = "test", - privateResource = false, - deprecated = false, - request = getProductRequestWithMultiplePathParameters(), - response = getProduct200Response(getProductPayloadExample()) - ) + ResourceModel( + operationId = "test", + privateResource = false, + deprecated = false, + request = getProductRequestWithMultiplePathParameters(), + response = getProduct200Response(getProductPayloadExample()) + ) ) } @@ -570,44 +578,44 @@ class OpenApi20GeneratorTest { private fun givenResourceModelsWithApplicationForm(method: HTTPMethod): List { return listOf( - ResourceModel( - operationId = "test", - privateResource = false, - deprecated = false, - request = productRequest(method = method), - response = postProduct200Response(getProductPayloadExample(), schema = Schema("ProductResponse")) - ) + ResourceModel( + operationId = "test", + privateResource = false, + deprecated = false, + request = productRequest(method = method), + response = postProduct200Response(getProductPayloadExample(), schema = Schema("ProductResponse")) + ) ) } private fun givenPostProductResourceModelWithCustomSchemaNames(): List { return listOf( - ResourceModel( - operationId = "test", - privateResource = false, - deprecated = false, - request = postProductRequest(schema = Schema("ProductRequest")), - response = postProduct200Response(getProductPayloadExample(), schema = Schema("ProductResponse")) - ) + ResourceModel( + operationId = "test", + privateResource = false, + deprecated = false, + request = postProductRequest(schema = Schema("ProductRequest")), + response = postProduct200Response(getProductPayloadExample(), schema = Schema("ProductResponse")) + ) ) } private fun givenMultiplePostProductResourceModelsWithCustomSchemaNames(): List { return listOf( - ResourceModel( - operationId = "test1", - privateResource = false, - deprecated = false, - request = postProductRequest(schema = Schema("ProductRequest1"), path = "/products1"), - response = postProduct200Response(getProductPayloadExample(), schema = Schema("ProductResponse1")) - ), - ResourceModel( - operationId = "test2", - privateResource = false, - deprecated = false, - request = postProductRequest(schema = Schema("ProductRequest2"), path = "/products2"), - response = postProduct200Response(getProductPayloadExample(), schema = Schema("ProductResponse2")) - ) + ResourceModel( + operationId = "test1", + privateResource = false, + deprecated = false, + request = postProductRequest(schema = Schema("ProductRequest1"), path = "/products1"), + response = postProduct200Response(getProductPayloadExample(), schema = Schema("ProductResponse1")) + ), + ResourceModel( + operationId = "test2", + privateResource = false, + deprecated = false, + request = postProductRequest(schema = Schema("ProductRequest2"), path = "/products2"), + response = postProduct200Response(getProductPayloadExample(), schema = Schema("ProductResponse2")) + ) ) } @@ -637,46 +645,46 @@ class OpenApi20GeneratorTest { private fun givenResourcesWithSamePathAndContentTypeAndDifferentParameters(): List { return listOf( - ResourceModel( - operationId = "test", - summary = "summary", - description = "description", - privateResource = false, - deprecated = false, - tags = setOf("tag1", "tag2"), - request = getProductRequest(), - response = getProduct200Response(getProductPayloadExample()) - ), - ResourceModel( - operationId = "test", - summary = "summary", - description = "description", - privateResource = false, - deprecated = false, - tags = setOf("tag1", "tag2"), - request = getProductRequest(), - response = getProduct200Response(getProductPayloadExample()) - ), - ResourceModel( - operationId = "test-1", - summary = "summary 1", - description = "description 1", - privateResource = false, - deprecated = false, - tags = setOf("tag1", "tag2"), - request = getProductRequestWithDifferentParameter("color", "Changes the color of the product"), - response = getProduct200Response(getProductPayloadExample()) - ), - ResourceModel( - operationId = "test-1", - summary = "summary 1", - description = "description 1", - privateResource = false, - deprecated = false, - tags = setOf("tag1", "tag2"), - request = getProductRequestWithDifferentParameter("color", "Modifies the color of the product"), - response = getProduct200Response(getProductPayloadExample()) - ) + ResourceModel( + operationId = "test", + summary = "summary", + description = "description", + privateResource = false, + deprecated = false, + tags = setOf("tag1", "tag2"), + request = getProductRequest(), + response = getProduct200Response(getProductPayloadExample()) + ), + ResourceModel( + operationId = "test", + summary = "summary", + description = "description", + privateResource = false, + deprecated = false, + tags = setOf("tag1", "tag2"), + request = getProductRequest(), + response = getProduct200Response(getProductPayloadExample()) + ), + ResourceModel( + operationId = "test-1", + summary = "summary 1", + description = "description 1", + privateResource = false, + deprecated = false, + tags = setOf("tag1", "tag2"), + request = getProductRequestWithDifferentParameter("color", "Changes the color of the product"), + response = getProduct200Response(getProductPayloadExample()) + ), + ResourceModel( + operationId = "test-1", + summary = "summary 1", + description = "description 1", + privateResource = false, + deprecated = false, + tags = setOf("tag1", "tag2"), + request = getProductRequestWithDifferentParameter("color", "Modifies the color of the product"), + response = getProduct200Response(getProductPayloadExample()) + ) ) } @@ -769,16 +777,16 @@ class OpenApi20GeneratorTest { private fun getProductPayloadExample(): String { return "{\n" + - " \"_id\": \"123\",\n" + - " \"description\": \"Good stuff!\"\n" + - "}" + " \"_id\": \"123\",\n" + + " \"description\": \"Good stuff!\"\n" + + "}" } private fun getProduct200ResponseAlternateExample(): String { return "{\n" + - " \"_id\": \"123\",\n" + - " \"description\": \"Bad stuff!\"\n" + - "}" + " \"_id\": \"123\",\n" + + " \"description\": \"Bad stuff!\"\n" + + "}" } private fun getProductRequest(): RequestModel { @@ -821,15 +829,17 @@ class OpenApi20GeneratorTest { } private fun getProductRequestWithDifferentParameter(name: String, description: String): RequestModel { - return getProductRequest().copy(requestParameters = listOf( + return getProductRequest().copy( + requestParameters = listOf( ParameterDescriptor( - name = name, - description = description, - type = "STRING", - optional = true, - ignored = false + name = name, + description = description, + type = "STRING", + optional = true, + ignored = false ) - )) + ) + ) } private fun getProductRequestWithBasicSecurity(): RequestModel { @@ -864,42 +874,42 @@ class OpenApi20GeneratorTest { private fun getProductRequestWithMultiplePathParameters(): RequestModel { return RequestModel( - path = "/products/{id}-{subId}", - method = HTTPMethod.GET, - contentType = "application/json", - securityRequirements = SecurityRequirements( - type = OAUTH2, - requiredScopes = listOf("prod:r") - ), - headers = listOf(), - pathParameters = listOf(), - requestParameters = listOf(), - requestFields = listOf() + path = "/products/{id}-{subId}", + method = HTTPMethod.GET, + contentType = "application/json", + securityRequirements = SecurityRequirements( + type = OAUTH2, + requiredScopes = listOf("prod:r") + ), + headers = listOf(), + pathParameters = listOf(), + requestParameters = listOf(), + requestFields = listOf() ) } private fun productRequest(schema: Schema? = null, path: String = "/products", method: HTTPMethod = HTTPMethod.POST): RequestModel { return RequestModel( - path = path, - method = method, - contentType = "application/x-www-form-urlencoded", - schema = schema, - securityRequirements = null, - headers = listOf(), - pathParameters = listOf(), - requestParameters = listOf( - ParameterDescriptor( - name = "locale", - description = "Localizes the product fields to the given locale code", - type = "STRING", - optional = true, - ignored = false - ) - ), - requestFields = listOf(), - example = """ + path = path, + method = method, + contentType = "application/x-www-form-urlencoded", + schema = schema, + securityRequirements = null, + headers = listOf(), + pathParameters = listOf(), + requestParameters = listOf( + ParameterDescriptor( + name = "locale", + description = "Localizes the product fields to the given locale code", + type = "STRING", + optional = true, + ignored = false + ) + ), + requestFields = listOf(), + example = """ locale=pl&irrelevant=true - """.trimIndent() + """.trimIndent() ) } diff --git a/restdocs-api-spec-openapi3-generator/src/main/kotlin/com/epages/restdocs/apispec/openapi3/OpenApi3Generator.kt b/restdocs-api-spec-openapi3-generator/src/main/kotlin/com/epages/restdocs/apispec/openapi3/OpenApi3Generator.kt index bb588b2f..b39b116b 100644 --- a/restdocs-api-spec-openapi3-generator/src/main/kotlin/com/epages/restdocs/apispec/openapi3/OpenApi3Generator.kt +++ b/restdocs-api-spec-openapi3-generator/src/main/kotlin/com/epages/restdocs/apispec/openapi3/OpenApi3Generator.kt @@ -59,10 +59,14 @@ object OpenApi3Generator { this.description = description this.version = version } - this.tags(tagDescriptions.map { Tag().apply { - this.name = it.key - this.description = it.value - } }) + this.tags( + tagDescriptions.map { + Tag().apply { + this.name = it.key + this.description = it.value + } + } + ) paths = generatePaths( resources, oauth2SecuritySchemeDefinition @@ -82,7 +86,8 @@ object OpenApi3Generator { oauth2SecuritySchemeDefinition: Oauth2Configuration? = null, format: String ) = - ApiSpecificationWriter.serialize(format, + ApiSpecificationWriter.serialize( + format, generate( resources = resources, servers = servers, @@ -91,7 +96,8 @@ object OpenApi3Generator { tagDescriptions = tagDescriptions, version = version, oauth2SecuritySchemeDefinition = oauth2SecuritySchemeDefinition - )) + ) + ) private fun OpenAPI.extractDefinitions() { val schemasToKeys = HashMap, String>() @@ -149,12 +155,13 @@ object OpenApi3Generator { } private fun generateSchemaName(path: String): (Schema) -> String { - return { schema -> path - .removePrefix("/") - .replace("/", "-") - .replace(Regex.fromLiteral("{"), "") - .replace(Regex.fromLiteral("}"), "") - .plus(schema.hashCode()) + return { schema -> + path + .removePrefix("/") + .replace("/", "-") + .replace(Regex.fromLiteral("{"), "") + .replace(Regex.fromLiteral("}"), "") + .plus(schema.hashCode()) } } @@ -163,13 +170,15 @@ object OpenApi3Generator { oauth2SecuritySchemeDefinition: Oauth2Configuration? ): Paths { return resources.groupByPath().entries - .map { it.key to resourceModels2PathItem( - it.value, - oauth2SecuritySchemeDefinition - ) + .map { + it.key to resourceModels2PathItem( + it.value, + oauth2SecuritySchemeDefinition + ) } .let { pathAndPathItem -> - Paths().apply { pathAndPathItem.forEach { addPathItem(it.first, it.second) } } } + Paths().apply { pathAndPathItem.forEach { addPathItem(it.first, it.second) } } + } } private fun groupByHttpMethod(resources: List): Map> { @@ -241,14 +250,16 @@ object OpenApi3Generator { it.operationId, it.request ) - }) + } + ) responses = resourceModelsToApiResponses( modelsWithSamePathAndMethod.map { ResponseModelWithOperationId( it.operationId, it.response ) - }) + } + ) }.apply { addSecurityItemFromSecurityRequirements(firstModelForPathAndMethod.request.securityRequirements, oauth2SecuritySchemeDefinition) } } @@ -310,9 +321,11 @@ object OpenApi3Generator { responses ) } - .let { ApiResponses().apply { - it.forEach { (status, apiResponse) -> addApiResponse(status.toString(), apiResponse) } - } } + .let { + ApiResponses().apply { + it.forEach { (status, apiResponse) -> addApiResponse(status.toString(), apiResponse) } + } + } } private fun responsesWithSameStatusToApiResponse(responseModelsSameStatus: List): ApiResponse { @@ -323,10 +336,11 @@ object OpenApi3Generator { val apiResponse = ApiResponse().apply { description = responseModelsSameStatus.first().response.status.toString() headers = responseModelsSameStatus.flatMap { it.response.headers } - .map { it.name to Header().apply { - description(it.description) - schema = simpleTypeToSchema(it.type) - } + .map { + it.name to Header().apply { + description(it.description) + schema = simpleTypeToSchema(it.type) + } }.toMap().nullIfEmpty() } return responsesByContentType @@ -366,8 +380,8 @@ object OpenApi3Generator { private fun extractPathParameters(resourceModel: ResourceModel): List { val pathParameterNames = PATH_PARAMETER_PATTERN.findAll(resourceModel.request.path) - .map { matchResult -> matchResult.groupValues[1] } - .toList() + .map { matchResult -> matchResult.groupValues[1] } + .toList() return pathParameterNames.map { parameterName -> resourceModel.request.pathParameters diff --git a/restdocs-api-spec-openapi3-generator/src/main/kotlin/com/epages/restdocs/apispec/openapi3/SecuritySchemeGenerator.kt b/restdocs-api-spec-openapi3-generator/src/main/kotlin/com/epages/restdocs/apispec/openapi3/SecuritySchemeGenerator.kt index eb72c184..d4d4b2ae 100644 --- a/restdocs-api-spec-openapi3-generator/src/main/kotlin/com/epages/restdocs/apispec/openapi3/SecuritySchemeGenerator.kt +++ b/restdocs-api-spec-openapi3-generator/src/main/kotlin/com/epages/restdocs/apispec/openapi3/SecuritySchemeGenerator.kt @@ -20,10 +20,13 @@ internal object SecuritySchemeGenerator { fun OpenAPI.addSecurityDefinitions(oauth2SecuritySchemeDefinition: Oauth2Configuration?) { if (oauth2SecuritySchemeDefinition?.flows?.isNotEmpty() == true) { val flows = OAuthFlows() - components.addSecuritySchemes("oauth2", SecurityScheme().apply { - type = SecurityScheme.Type.OAUTH2 - this.flows = flows - }) + components.addSecuritySchemes( + "oauth2", + SecurityScheme().apply { + type = SecurityScheme.Type.OAUTH2 + this.flows = flows + } + ) oauth2SecuritySchemeDefinition.flows.forEach { flow -> val scopeAndDescriptions = oauth2SecuritySchemeDefinition.scopes val allScopes = collectScopesFromOperations() @@ -33,44 +36,57 @@ internal object SecuritySchemeGenerator { OAuthFlow() .authorizationUrl(oauth2SecuritySchemeDefinition.authorizationUrl) .tokenUrl(oauth2SecuritySchemeDefinition.tokenUrl) - .scopes(allScopes, scopeAndDescriptions)) + .scopes(allScopes, scopeAndDescriptions) + ) "clientCredentials" -> flows.clientCredentials( OAuthFlow() .tokenUrl(oauth2SecuritySchemeDefinition.tokenUrl) - .scopes(allScopes, scopeAndDescriptions)) + .scopes(allScopes, scopeAndDescriptions) + ) "password" -> flows.password( OAuthFlow() .tokenUrl(oauth2SecuritySchemeDefinition.tokenUrl) - .scopes(allScopes, scopeAndDescriptions)) + .scopes(allScopes, scopeAndDescriptions) + ) "implicit" -> flows.implicit( OAuthFlow() .authorizationUrl(oauth2SecuritySchemeDefinition.authorizationUrl) - .scopes(allScopes, scopeAndDescriptions)) + .scopes(allScopes, scopeAndDescriptions) + ) else -> throw IllegalArgumentException("Unknown flow '$flow' in oauth2SecuritySchemeDefinition") } } } if (hasAnyOperationWithSecurityName(this, BASIC_SECURITY_NAME)) { - components.addSecuritySchemes(BASIC_SECURITY_NAME, SecurityScheme().apply { - type = SecurityScheme.Type.HTTP - scheme = "basic" - }) + components.addSecuritySchemes( + BASIC_SECURITY_NAME, + SecurityScheme().apply { + type = SecurityScheme.Type.HTTP + scheme = "basic" + } + ) } if (hasAnyOperationWithSecurityName(this, API_KEY_SECURITY_NAME)) { - components.addSecuritySchemes(API_KEY_SECURITY_NAME, SecurityScheme().apply { - type = SecurityScheme.Type.APIKEY - `in` = SecurityScheme.In.HEADER - name = "Authorization" - }) + components.addSecuritySchemes( + API_KEY_SECURITY_NAME, + SecurityScheme().apply { + type = SecurityScheme.Type.APIKEY + `in` = SecurityScheme.In.HEADER + name = "Authorization" + } + ) } if (hasAnyOperationWithSecurityName(this, JWT_BEARER_SECURITY_NAME)) { - components.addSecuritySchemes(JWT_BEARER_SECURITY_NAME, SecurityScheme().apply { - type = SecurityScheme.Type.HTTP - scheme = "bearer" - bearerFormat = "JWT" - }) + components.addSecuritySchemes( + JWT_BEARER_SECURITY_NAME, + SecurityScheme().apply { + type = SecurityScheme.Type.HTTP + scheme = "bearer" + bearerFormat = "JWT" + } + ) } } @@ -79,7 +95,8 @@ internal object SecuritySchemeGenerator { when (securityRequirements.type) { SecurityType.OAUTH2 -> oauth2SecuritySchemeDefinition?.flows?.map { addSecurityItem( - SecurityRequirement().addList(oauth2SecuritySchemeDefinition.securitySchemeName(it), + SecurityRequirement().addList( + oauth2SecuritySchemeDefinition.securitySchemeName(it), securityRequirements2ScopesList(securityRequirements) ) ) diff --git a/restdocs-api-spec-openapi3-generator/src/test/kotlin/com/epages/restdocs/apispec/openapi3/OpenApi3GeneratorTest.kt b/restdocs-api-spec-openapi3-generator/src/test/kotlin/com/epages/restdocs/apispec/openapi3/OpenApi3GeneratorTest.kt index 431c9518..749839ff 100644 --- a/restdocs-api-spec-openapi3-generator/src/test/kotlin/com/epages/restdocs/apispec/openapi3/OpenApi3GeneratorTest.kt +++ b/restdocs-api-spec-openapi3-generator/src/test/kotlin/com/epages/restdocs/apispec/openapi3/OpenApi3GeneratorTest.kt @@ -363,36 +363,44 @@ class OpenApi3GeneratorTest { ) println(openApiSpecJsonString) - openApiJsonPathContext = JsonPath.parse(openApiSpecJsonString, Configuration.defaultConfiguration().addOptions( - Option.SUPPRESS_EXCEPTIONS)) + openApiJsonPathContext = JsonPath.parse( + openApiSpecJsonString, + Configuration.defaultConfiguration().addOptions( + Option.SUPPRESS_EXCEPTIONS + ) + ) } private fun whenOpenApiObjectGeneratedWithoutOAuth2() { openApiSpecJsonString = OpenApi3Generator.generateAndSerialize( - resources = resources, - servers = listOf(Server().apply { url = "https://localhost/api" }), - format = "json", - description = "API Description", - tagDescriptions = mapOf("tag1" to "tag1 description", "tag2" to "tag2 description") + resources = resources, + servers = listOf(Server().apply { url = "https://localhost/api" }), + format = "json", + description = "API Description", + tagDescriptions = mapOf("tag1" to "tag1 description", "tag2" to "tag2 description") ) println(openApiSpecJsonString) - openApiJsonPathContext = JsonPath.parse(openApiSpecJsonString, Configuration.defaultConfiguration().addOptions( - Option.SUPPRESS_EXCEPTIONS)) + openApiJsonPathContext = JsonPath.parse( + openApiSpecJsonString, + Configuration.defaultConfiguration().addOptions( + Option.SUPPRESS_EXCEPTIONS + ) + ) } private fun givenResourceWithFormDataSentAs(method: HTTPMethod) { resources = listOf( - ResourceModel( - operationId = "test", - summary = "summary", - description = "description", - privateResource = false, - deprecated = false, - tags = setOf("tag1", "tag2"), - request = productRequestAsFormData(method, schema = Schema("ProductRequest")), - response = getProductResponse() - ) + ResourceModel( + operationId = "test", + summary = "summary", + description = "description", + privateResource = false, + deprecated = false, + tags = setOf("tag1", "tag2"), + request = productRequestAsFormData(method, schema = Schema("ProductRequest")), + response = getProductResponse() + ) ) } @@ -423,26 +431,26 @@ class OpenApi3GeneratorTest { private fun givenResourcesWithSamePathAndContentTypeButOperationIdsWithoutCommonPrefix() { resources = listOf( - ResourceModel( - operationId = "first", - summary = "summary", - description = "description", - privateResource = false, - deprecated = false, - tags = setOf("tag1", "tag2"), - request = getProductRequest(), - response = getProductResponse() - ), - ResourceModel( - operationId = "second", - summary = "summary 1", - description = "description 1", - privateResource = false, - deprecated = false, - tags = setOf("tag1", "tag2"), - request = getProductRequest(), - response = getProductResponse() - ) + ResourceModel( + operationId = "first", + summary = "summary", + description = "description", + privateResource = false, + deprecated = false, + tags = setOf("tag1", "tag2"), + request = getProductRequest(), + response = getProductResponse() + ), + ResourceModel( + operationId = "second", + summary = "summary 1", + description = "description 1", + privateResource = false, + deprecated = false, + tags = setOf("tag1", "tag2"), + request = getProductRequest(), + response = getProductResponse() + ) ) } @@ -473,46 +481,46 @@ class OpenApi3GeneratorTest { private fun givenResourcesWithSamePathAndContentTypeAndDifferentParameters() { resources = listOf( - ResourceModel( - operationId = "test", - summary = "summary", - description = "description", - privateResource = false, - deprecated = false, - tags = setOf("tag1", "tag2"), - request = getProductRequest(), - response = getProductResponse() - ), - ResourceModel( - operationId = "test", - summary = "summary", - description = "description", - privateResource = false, - deprecated = false, - tags = setOf("tag1", "tag2"), - request = getProductRequest(), - response = getProductResponse() - ), - ResourceModel( - operationId = "test-1", - summary = "summary 1", - description = "description 1", - privateResource = false, - deprecated = false, - tags = setOf("tag1", "tag2"), - request = getProductRequestWithDifferentParameter("color", "Changes the color of the product"), - response = getProductResponse() - ), - ResourceModel( - operationId = "test-1", - summary = "summary 1", - description = "description 1", - privateResource = false, - deprecated = false, - tags = setOf("tag1", "tag2"), - request = getProductRequestWithDifferentParameter("color", "Modifies the color of the product"), - response = getProductResponse() - ) + ResourceModel( + operationId = "test", + summary = "summary", + description = "description", + privateResource = false, + deprecated = false, + tags = setOf("tag1", "tag2"), + request = getProductRequest(), + response = getProductResponse() + ), + ResourceModel( + operationId = "test", + summary = "summary", + description = "description", + privateResource = false, + deprecated = false, + tags = setOf("tag1", "tag2"), + request = getProductRequest(), + response = getProductResponse() + ), + ResourceModel( + operationId = "test-1", + summary = "summary 1", + description = "description 1", + privateResource = false, + deprecated = false, + tags = setOf("tag1", "tag2"), + request = getProductRequestWithDifferentParameter("color", "Changes the color of the product"), + response = getProductResponse() + ), + ResourceModel( + operationId = "test-1", + summary = "summary 1", + description = "description 1", + privateResource = false, + deprecated = false, + tags = setOf("tag1", "tag2"), + request = getProductRequestWithDifferentParameter("color", "Modifies the color of the product"), + response = getProductResponse() + ) ) } @@ -568,16 +576,16 @@ class OpenApi3GeneratorTest { private fun givenResourceWithMultiplePathParameters() { resources = listOf( - ResourceModel( - operationId = "test", - summary = "summary", - description = "description", - privateResource = false, - deprecated = false, - tags = setOf("tag1", "tag2"), - request = getProductRequestWithMultiplePathParameters(), - response = getProductResponse() - ) + ResourceModel( + operationId = "test", + summary = "summary", + description = "description", + privateResource = false, + deprecated = false, + tags = setOf("tag1", "tag2"), + request = getProductRequestWithMultiplePathParameters(), + response = getProductResponse() + ) ) } @@ -625,56 +633,56 @@ class OpenApi3GeneratorTest { private fun givenGetProductResourceModelWithJWTSecurityRequirement() { resources = listOf( - ResourceModel( - operationId = "test", - summary = "summary", - description = "description", - privateResource = false, - deprecated = false, - tags = setOf("tag1", "tag2"), - request = getProductRequest(::getJWTSecurityRequirement), - response = getProductResponse() - ) + ResourceModel( + operationId = "test", + summary = "summary", + description = "description", + privateResource = false, + deprecated = false, + tags = setOf("tag1", "tag2"), + request = getProductRequest(::getJWTSecurityRequirement), + response = getProductResponse() + ) ) } private fun givenPatchProductResourceModelWithCustomSchemaNames() { resources = listOf( - ResourceModel( - operationId = "test", - summary = "summary", - description = "description", - privateResource = false, - deprecated = false, - tags = setOf("tag1", "tag2"), - request = getProductPatchRequest(schema = Schema("ProductRequest")), - response = getProductResponse(schema = Schema("ProductResponse")) - ) + ResourceModel( + operationId = "test", + summary = "summary", + description = "description", + privateResource = false, + deprecated = false, + tags = setOf("tag1", "tag2"), + request = getProductPatchRequest(schema = Schema("ProductRequest")), + response = getProductResponse(schema = Schema("ProductResponse")) + ) ) } private fun givenMultiplePatchProductResourceModelsWithCustomSchemaNames() { resources = listOf( - ResourceModel( - operationId = "test1", - summary = "summary1", - description = "description1", - privateResource = false, - deprecated = false, - tags = setOf("tag1", "tag2"), - request = getProductPatchRequest(schema = Schema("ProductRequest1"), path = "/products1/{id}"), - response = getProductResponse(schema = Schema("ProductResponse1")) - ), - ResourceModel( - operationId = "test2", - summary = "summary2", - description = "description2", - privateResource = false, - deprecated = false, - tags = setOf("tag1", "tag2"), - request = getProductPatchRequest(schema = Schema("ProductRequest2"), path = "/products2/{id}"), - response = getProductResponse(schema = Schema("ProductResponse2")) - ) + ResourceModel( + operationId = "test1", + summary = "summary1", + description = "description1", + privateResource = false, + deprecated = false, + tags = setOf("tag1", "tag2"), + request = getProductPatchRequest(schema = Schema("ProductRequest1"), path = "/products1/{id}"), + response = getProductResponse(schema = Schema("ProductResponse1")) + ), + ResourceModel( + operationId = "test2", + summary = "summary2", + description = "description2", + privateResource = false, + deprecated = false, + tags = setOf("tag1", "tag2"), + request = getProductPatchRequest(schema = Schema("ProductRequest2"), path = "/products2/{id}"), + response = getProductResponse(schema = Schema("ProductResponse2")) + ) ) } @@ -830,96 +838,98 @@ class OpenApi3GeneratorTest { private fun getProductRequestWithMultiplePathParameters(getSecurityRequirement: () -> SecurityRequirements = ::getOAuth2SecurityRequirement): RequestModel { return RequestModel( - path = "/products/{id}-{subId}", - method = HTTPMethod.GET, - securityRequirements = getSecurityRequirement(), - headers = emptyList(), - pathParameters = emptyList(), - requestParameters = emptyList(), - requestFields = listOf() + path = "/products/{id}-{subId}", + method = HTTPMethod.GET, + securityRequirements = getSecurityRequirement(), + headers = emptyList(), + pathParameters = emptyList(), + requestParameters = emptyList(), + requestFields = listOf() ) } private fun productRequestAsFormData(method: HTTPMethod, schema: Schema? = null, getSecurityRequirement: () -> SecurityRequirements = ::getOAuth2SecurityRequirement): RequestModel { return RequestModel( - path = "/products/{id}", - method = method, - contentType = "application/x-www-form-urlencoded", - securityRequirements = getSecurityRequirement(), - headers = emptyList(), - pathParameters = emptyList(), - requestParameters = listOf( - ParameterDescriptor( - name = "locale", - description = "Localizes the product fields to the given locale code", - type = "STRING", - optional = true, - ignored = false - ) - ), - schema = schema, - requestFields = listOf(), - example = """ + path = "/products/{id}", + method = method, + contentType = "application/x-www-form-urlencoded", + securityRequirements = getSecurityRequirement(), + headers = emptyList(), + pathParameters = emptyList(), + requestParameters = listOf( + ParameterDescriptor( + name = "locale", + description = "Localizes the product fields to the given locale code", + type = "STRING", + optional = true, + ignored = false + ) + ), + schema = schema, + requestFields = listOf(), + example = """ locale=pl&irrelevant=true - """.trimIndent() + """.trimIndent() ) } private fun getProductRequest(getSecurityRequirement: () -> SecurityRequirements = ::getOAuth2SecurityRequirement): RequestModel { return RequestModel( - path = "/products/{id}", - method = HTTPMethod.GET, - securityRequirements = getSecurityRequirement(), - headers = listOf( - HeaderDescriptor( - name = "Authorization", - description = "Access token", - type = "string", - optional = false, - example = "some example" - ) - ), - pathParameters = listOf( - ParameterDescriptor( - name = "id", - description = "Product ID", - type = "STRING", - optional = false, - ignored = false - ) - ), - requestParameters = listOf( - ParameterDescriptor( - name = "locale", - description = "Localizes the product fields to the given locale code", - type = "STRING", - optional = true, - ignored = false - ) - ), - requestFields = listOf() + path = "/products/{id}", + method = HTTPMethod.GET, + securityRequirements = getSecurityRequirement(), + headers = listOf( + HeaderDescriptor( + name = "Authorization", + description = "Access token", + type = "string", + optional = false, + example = "some example" + ) + ), + pathParameters = listOf( + ParameterDescriptor( + name = "id", + description = "Product ID", + type = "STRING", + optional = false, + ignored = false + ) + ), + requestParameters = listOf( + ParameterDescriptor( + name = "locale", + description = "Localizes the product fields to the given locale code", + type = "STRING", + optional = true, + ignored = false + ) + ), + requestFields = listOf() ) } private fun getOAuth2SecurityRequirement() = SecurityRequirements( - type = SecurityType.OAUTH2, - requiredScopes = listOf("prod:r") + type = SecurityType.OAUTH2, + requiredScopes = listOf("prod:r") ) private fun getJWTSecurityRequirement() = SecurityRequirements( - type = SecurityType.JWT_BEARER + type = SecurityType.JWT_BEARER ) private fun getProductRequestWithDifferentParameter(name: String, description: String): RequestModel { - return getProductRequest().copy(requestParameters = listOf( + return getProductRequest().copy( + requestParameters = listOf( ParameterDescriptor( - name = name, - description = description, - type = "STRING", - optional = true, - ignored = false + name = name, + description = description, + type = "STRING", + optional = true, + ignored = false ) - )) + ) + ) } private fun thenOpenApiSpecIsValid() { diff --git a/restdocs-api-spec-postman-generator/src/test/kotlin/com/epages/restdocs/apispec/postman/PostmanCollectionGeneratorTest.kt b/restdocs-api-spec-postman-generator/src/test/kotlin/com/epages/restdocs/apispec/postman/PostmanCollectionGeneratorTest.kt index 7c150b9b..115882fc 100644 --- a/restdocs-api-spec-postman-generator/src/test/kotlin/com/epages/restdocs/apispec/postman/PostmanCollectionGeneratorTest.kt +++ b/restdocs-api-spec-postman-generator/src/test/kotlin/com/epages/restdocs/apispec/postman/PostmanCollectionGeneratorTest.kt @@ -28,8 +28,8 @@ internal class PostmanCollectionGeneratorTest { var baseUrl = "http://localhost:8080" private val objectMapper = jacksonObjectMapper().enable(INDENT_OUTPUT) private val collectionSchema: JsonSchema = JsonSchemaFactory - .byDefault() - .getJsonSchema(objectMapper.readTree(this.javaClass.classLoader.getResourceAsStream("collection-schema.json"))) + .byDefault() + .getJsonSchema(objectMapper.readTree(this.javaClass.classLoader.getResourceAsStream("collection-schema.json"))) @Test fun `should convert single resource model to postman`() { @@ -153,90 +153,96 @@ internal class PostmanCollectionGeneratorTest { then(postmanCollectionJsonPathContext.read>("item[0].response")).hasSize(2) then(postmanCollectionJsonPathContext.read>("item[0].response[*].header[*].value")) - .contains("application/hal+json", "application/json") + .contains("application/hal+json", "application/json") then(postmanCollectionJsonPathContext.read>("item[0].response[*].originalRequest.header[*].value")) - .contains("application/json-patch+json", "application/json") + .contains("application/json-patch+json", "application/json") thenPostmanSpecIsValid() } private fun whenPostmanCollectionGenerated() { - postmanCollectionJsonString = objectMapper.writeValueAsString(PostmanCollectionGenerator.generate( + postmanCollectionJsonString = objectMapper.writeValueAsString( + PostmanCollectionGenerator.generate( resources = resources, baseUrl = baseUrl, title = "my postman collection", version = "1.0.0" - )) + ) + ) println(postmanCollectionJsonString) - postmanCollectionJsonPathContext = JsonPath.parse(postmanCollectionJsonString, Configuration.defaultConfiguration().addOptions( - Option.SUPPRESS_EXCEPTIONS)) + postmanCollectionJsonPathContext = JsonPath.parse( + postmanCollectionJsonString, + Configuration.defaultConfiguration().addOptions( + Option.SUPPRESS_EXCEPTIONS + ) + ) } private fun givenResourcesWithSamePathAndDifferentContentType() { resources = listOf( - ResourceModel( - operationId = "test", - summary = "summary", - description = "description", - privateResource = false, - deprecated = false, - tags = setOf("tag1", "tag2"), - request = getProductPatchRequest(), - response = getProductResponse() - ), - ResourceModel( - operationId = "test-1", - summary = "summary 1", - description = "description 1", - privateResource = false, - deprecated = false, - tags = setOf("tag1", "tag2"), - request = getProductPatchJsonPatchRequest(), - response = getProductHalResponse() - ) + ResourceModel( + operationId = "test", + summary = "summary", + description = "description", + privateResource = false, + deprecated = false, + tags = setOf("tag1", "tag2"), + request = getProductPatchRequest(), + response = getProductResponse() + ), + ResourceModel( + operationId = "test-1", + summary = "summary 1", + description = "description 1", + privateResource = false, + deprecated = false, + tags = setOf("tag1", "tag2"), + request = getProductPatchJsonPatchRequest(), + response = getProductHalResponse() + ) ) } private fun givenDeleteProductResourceModel() { resources = listOf( - ResourceModel( - operationId = "test", - summary = "summary", - description = "description", - privateResource = false, - deprecated = false, - request = RequestModel( - path = "/products/{id}", - method = HTTPMethod.DELETE, - headers = listOf(), - pathParameters = listOf(), - requestParameters = listOf(), - securityRequirements = null, - requestFields = listOf() - ), - response = ResponseModel( - status = 204, - contentType = null, - headers = emptyList(), - responseFields = listOf() - ) + ResourceModel( + operationId = "test", + summary = "summary", + description = "description", + privateResource = false, + deprecated = false, + request = RequestModel( + path = "/products/{id}", + method = HTTPMethod.DELETE, + headers = listOf(), + pathParameters = listOf(), + requestParameters = listOf(), + securityRequirements = null, + requestFields = listOf() + ), + response = ResponseModel( + status = 204, + contentType = null, + headers = emptyList(), + responseFields = listOf() ) + ) ) } private fun givenGetProductResourceModel() { resources = listOf( - ResourceModel( - operationId = "test", - summary = "summary", - description = "description", - privateResource = false, - deprecated = false, - tags = setOf("tag1", "tag2"), - request = getProductRequest(), - response = getProductResponse() - ) + ResourceModel( + operationId = "test", + summary = "summary", + description = "description", + privateResource = false, + deprecated = false, + tags = setOf("tag1", "tag2"), + request = getProductRequest(), + response = getProductResponse() + ) ) } @@ -257,30 +263,30 @@ internal class PostmanCollectionGeneratorTest { private fun getProductResponse(): ResponseModel { return ResponseModel( - status = 200, - contentType = "application/json", - headers = listOf( - HeaderDescriptor( - name = "SIGNATURE", - description = "This is some signature", - type = "STRING", - optional = false, - example = "some" - ) - ), - responseFields = listOf( - FieldDescriptor( - path = "_id", - description = "ID of the product", - type = "STRING" - ), - FieldDescriptor( - path = "description", - description = "Product description, localized.", - type = "STRING" - ) + status = 200, + contentType = "application/json", + headers = listOf( + HeaderDescriptor( + name = "SIGNATURE", + description = "This is some signature", + type = "STRING", + optional = false, + example = "some" + ) + ), + responseFields = listOf( + FieldDescriptor( + path = "_id", + description = "ID of the product", + type = "STRING" ), - example = """{ + FieldDescriptor( + path = "description", + description = "Product description, localized.", + type = "STRING" + ) + ), + example = """{ "_id": "123", "description": "Good stuff!" }""" @@ -289,22 +295,22 @@ internal class PostmanCollectionGeneratorTest { private fun getProductHalResponse(): ResponseModel { return ResponseModel( - status = 200, - contentType = "application/hal+json", - responseFields = listOf( - FieldDescriptor( - path = "_id", - description = "ID of the product", - type = "STRING" - ), - FieldDescriptor( - path = "description1", - description = "Product description, localized.", - type = "STRING" - ) + status = 200, + contentType = "application/hal+json", + responseFields = listOf( + FieldDescriptor( + path = "_id", + description = "ID of the product", + type = "STRING" ), - headers = emptyList(), - example = """{ + FieldDescriptor( + path = "description1", + description = "Product description, localized.", + type = "STRING" + ) + ), + headers = emptyList(), + example = """{ "_id": "123", "description": "Good stuff!", "_links": { @@ -316,21 +322,21 @@ internal class PostmanCollectionGeneratorTest { private fun getProductPatchRequest(): RequestModel { return RequestModel( - path = "/products/{id}", - method = HTTPMethod.PATCH, - headers = listOf(), - pathParameters = listOf(), - requestParameters = listOf(), - securityRequirements = null, - requestFields = listOf( - FieldDescriptor( - path = "description1", - description = "Product description, localized.", - type = "STRING" - ) - ), - contentType = "application/json", - example = """{ + path = "/products/{id}", + method = HTTPMethod.PATCH, + headers = listOf(), + pathParameters = listOf(), + requestParameters = listOf(), + securityRequirements = null, + requestFields = listOf( + FieldDescriptor( + path = "description1", + description = "Product description, localized.", + type = "STRING" + ) + ), + contentType = "application/json", + example = """{ "description": "Good stuff!" }""" ) @@ -338,31 +344,31 @@ internal class PostmanCollectionGeneratorTest { private fun getProductPatchJsonPatchRequest(): RequestModel { return RequestModel( - path = "/products/{id}", - method = HTTPMethod.PATCH, - headers = listOf(), - pathParameters = listOf(), - requestParameters = listOf(), - securityRequirements = null, - requestFields = listOf( - FieldDescriptor( - path = "[].op", - description = "operation", - type = "STRING" - ), - FieldDescriptor( - path = "[].path", - description = "path", - type = "STRING" - ), - FieldDescriptor( - path = "[].value", - description = "the new value", - type = "STRING" - ) + path = "/products/{id}", + method = HTTPMethod.PATCH, + headers = listOf(), + pathParameters = listOf(), + requestParameters = listOf(), + securityRequirements = null, + requestFields = listOf( + FieldDescriptor( + path = "[].op", + description = "operation", + type = "STRING" ), - contentType = "application/json-patch+json", - example = """ + FieldDescriptor( + path = "[].path", + description = "path", + type = "STRING" + ), + FieldDescriptor( + path = "[].value", + description = "the new value", + type = "STRING" + ) + ), + contentType = "application/json-patch+json", + example = """ [ { "op": "add", @@ -376,40 +382,40 @@ internal class PostmanCollectionGeneratorTest { private fun getProductRequest(): RequestModel { return RequestModel( - path = "/products/{id}", - method = HTTPMethod.GET, - securityRequirements = SecurityRequirements( - type = SecurityType.OAUTH2, - requiredScopes = listOf("prod:r") - ), - headers = listOf( - HeaderDescriptor( - name = "Authorization", - description = "Access token", - type = "string", - optional = false, - example = "some" - ) - ), - pathParameters = listOf( - ParameterDescriptor( - name = "id", - description = "Product ID", - type = "STRING", - optional = false, - ignored = false - ) - ), - requestParameters = listOf( - ParameterDescriptor( - name = "locale", - description = "Localizes the product fields to the given locale code", - type = "STRING", - optional = true, - ignored = false - ) - ), - requestFields = listOf() + path = "/products/{id}", + method = HTTPMethod.GET, + securityRequirements = SecurityRequirements( + type = SecurityType.OAUTH2, + requiredScopes = listOf("prod:r") + ), + headers = listOf( + HeaderDescriptor( + name = "Authorization", + description = "Access token", + type = "string", + optional = false, + example = "some" + ) + ), + pathParameters = listOf( + ParameterDescriptor( + name = "id", + description = "Product ID", + type = "STRING", + optional = false, + ignored = false + ) + ), + requestParameters = listOf( + ParameterDescriptor( + name = "locale", + description = "Localizes the product fields to the given locale code", + type = "STRING", + optional = true, + ignored = false + ) + ), + requestFields = listOf() ) } @@ -419,4 +425,4 @@ internal class PostmanCollectionGeneratorTest { val validationReport = collectionSchema.validate(objectMapper.readTree(postmanCollectionJsonString)) then(validationReport.isSuccess).describedAs("Postman validation messages should be empty - report is $validationReport.").isTrue() } -} \ No newline at end of file +} diff --git a/restdocs-api-spec-restassured/src/main/kotlin/com/epages/restdocs/apispec/RestAssuredRestDocumentationWrapper.kt b/restdocs-api-spec-restassured/src/main/kotlin/com/epages/restdocs/apispec/RestAssuredRestDocumentationWrapper.kt index 0d00f99a..2c4588b1 100644 --- a/restdocs-api-spec-restassured/src/main/kotlin/com/epages/restdocs/apispec/RestAssuredRestDocumentationWrapper.kt +++ b/restdocs-api-spec-restassured/src/main/kotlin/com/epages/restdocs/apispec/RestAssuredRestDocumentationWrapper.kt @@ -24,18 +24,18 @@ object RestAssuredRestDocumentationWrapper : RestDocumentationWrapper() { ): RestDocumentationFilter { val enhancedSnippets = - enhanceSnippetsWithResourceSnippet( - resourceDetails = resourceDetails, - snippetFilter = snippetFilter, - snippets = *snippets - ) + enhanceSnippetsWithResourceSnippet( + resourceDetails = resourceDetails, + snippetFilter = snippetFilter, + snippets = *snippets + ) if (requestPreprocessor != null && responsePreprocessor != null) { return RestAssuredRestDocumentation.document( - identifier, - requestPreprocessor, - responsePreprocessor, - *enhancedSnippets + identifier, + requestPreprocessor, + responsePreprocessor, + *enhancedSnippets ) } else if (requestPreprocessor != null) { return RestAssuredRestDocumentation.document(identifier, requestPreprocessor, *enhancedSnippets) @@ -59,16 +59,16 @@ object RestAssuredRestDocumentationWrapper : RestDocumentationWrapper() { vararg snippets: Snippet ): RestDocumentationFilter { return document( - identifier = identifier, - resourceDetails = ResourceSnippetParametersBuilder() - .description(description) - .summary(summary) - .privateResource(privateResource) - .deprecated(deprecated), - requestPreprocessor = requestPreprocessor, - responsePreprocessor = responsePreprocessor, - snippetFilter = snippetFilter, - snippets = *snippets + identifier = identifier, + resourceDetails = ResourceSnippetParametersBuilder() + .description(description) + .summary(summary) + .privateResource(privateResource) + .deprecated(deprecated), + requestPreprocessor = requestPreprocessor, + responsePreprocessor = responsePreprocessor, + snippetFilter = snippetFilter, + snippets = *snippets ) } diff --git a/restdocs-api-spec-restassured/src/test/kotlin/com/epages/restdocs/apispec/ResourceSnippetIntegrationTest.kt b/restdocs-api-spec-restassured/src/test/kotlin/com/epages/restdocs/apispec/ResourceSnippetIntegrationTest.kt index d582d5df..faa69c28 100644 --- a/restdocs-api-spec-restassured/src/test/kotlin/com/epages/restdocs/apispec/ResourceSnippetIntegrationTest.kt +++ b/restdocs-api-spec-restassured/src/test/kotlin/com/epages/restdocs/apispec/ResourceSnippetIntegrationTest.kt @@ -79,9 +79,9 @@ open class ResourceSnippetIntegrationTest { resource.add(Link(link, "multiple")) return ResponseEntity - .ok() - .header("X-Custom-Header", customHeader) - .body>(resource) + .ok() + .header("X-Custom-Header", customHeader) + .body>(resource) } } } @@ -99,37 +99,37 @@ open class ResourceSnippetIntegrationTest { fun fieldDescriptors(): FieldDescriptors { val fields = ConstrainedFields(ResourceSnippetIntegrationTest.TestDataHolder::class.java) return ResourceDocumentation.fields( - fields.withPath("comment").description("the comment").optional(), - fields.withPath("flag").description("the flag"), - fields.withMappedPath("count", "count").description("the count") + fields.withPath("comment").description("the comment").optional(), + fields.withPath("flag").description("the flag"), + fields.withMappedPath("count", "count").description("the count") ) } fun buildFullResourceSnippet(): ResourceSnippet { return resource( - ResourceSnippetParameters.builder() - .description("description") - .summary("summary") - .deprecated(true) - .privateResource(true) - .requestFields(fieldDescriptors()) - .responseFields(fieldDescriptors().and(fieldWithPath("id").description("id"))) - .requestHeaders( - headerWithName("X-Custom-Header").description("A custom header"), - headerWithName(ACCEPT).description("Accept") - ) - .responseHeaders( - headerWithName("X-Custom-Header").description("A custom header"), - headerWithName(CONTENT_TYPE).description("ContentType") - ) - .pathParameters( - parameterWithName("someId").description("some id"), - parameterWithName("otherId").description("otherId id").type(SimpleType.INTEGER) - ) - .links( - linkWithRel("self").description("some"), - linkWithRel("multiple").description("multiple") - ) - .build() + ResourceSnippetParameters.builder() + .description("description") + .summary("summary") + .deprecated(true) + .privateResource(true) + .requestFields(fieldDescriptors()) + .responseFields(fieldDescriptors().and(fieldWithPath("id").description("id"))) + .requestHeaders( + headerWithName("X-Custom-Header").description("A custom header"), + headerWithName(ACCEPT).description("Accept") + ) + .responseHeaders( + headerWithName("X-Custom-Header").description("A custom header"), + headerWithName(CONTENT_TYPE).description("ContentType") + ) + .pathParameters( + parameterWithName("someId").description("some id"), + parameterWithName("otherId").description("otherId id").type(SimpleType.INTEGER) + ) + .links( + linkWithRel("self").description("some"), + linkWithRel("multiple").description("multiple") + ) + .build() ) } diff --git a/restdocs-api-spec-restassured/src/test/kotlin/com/epages/restdocs/apispec/RestAssuredRestDocumentationWrapperIntegrationTest.kt b/restdocs-api-spec-restassured/src/test/kotlin/com/epages/restdocs/apispec/RestAssuredRestDocumentationWrapperIntegrationTest.kt index 472ea5d7..bd41948b 100644 --- a/restdocs-api-spec-restassured/src/test/kotlin/com/epages/restdocs/apispec/RestAssuredRestDocumentationWrapperIntegrationTest.kt +++ b/restdocs-api-spec-restassured/src/test/kotlin/com/epages/restdocs/apispec/RestAssuredRestDocumentationWrapperIntegrationTest.kt @@ -37,29 +37,32 @@ class RestAssuredRestDocumentationWrapperIntegrationTest : ResourceSnippetIntegr @BeforeEach fun setUpSpec(restDocumentation: RestDocumentationContextProvider) { spec = RequestSpecBuilder() - .addFilter(RestAssuredRestDocumentation.documentationConfiguration(restDocumentation)) - .build() + .addFilter(RestAssuredRestDocumentation.documentationConfiguration(restDocumentation)) + .build() } private fun givenEndpointInvoked(documentationFilter: Filter, flagValue: String = "true") { RestAssured.given(spec) - .filter(documentationFilter) - .baseUri("http://localhost") - .port(requireNotNull(serverPort) { IllegalStateException("Server port is not available!") }) - .pathParam("someId", "id") - .pathParam("otherId", 1) - .contentType(ContentType.JSON) - .header("X-Custom-Header", "test") - .accept(MediaTypes.HAL_JSON_VALUE) - .body("""{ + .filter(documentationFilter) + .baseUri("http://localhost") + .port(requireNotNull(serverPort) { IllegalStateException("Server port is not available!") }) + .pathParam("someId", "id") + .pathParam("otherId", 1) + .contentType(ContentType.JSON) + .header("X-Custom-Header", "test") + .accept(MediaTypes.HAL_JSON_VALUE) + .body( + """{ "comment": "some", "flag": $flagValue, "count": 1 - }""".trimIndent()) - .`when`() - .post("/some/{someId}/other/{otherId}") - .then() - .statusCode(200) + } + """.trimIndent() + ) + .`when`() + .post("/some/{someId}/other/{otherId}") + .then() + .statusCode(200) } @Test @@ -114,7 +117,7 @@ class RestAssuredRestDocumentationWrapperIntegrationTest : ResourceSnippetIntegr assertThatCode { givenEndpointInvoked(this.whenResourceSnippetDocumentedWithRequestAndResponseFields(), "null") } - .doesNotThrowAnyException() + .doesNotThrowAnyException() } private fun whenResourceSnippetDocumentedWithoutParameters(): RestDocumentationFilter { @@ -127,68 +130,68 @@ class RestAssuredRestDocumentationWrapperIntegrationTest : ResourceSnippetIntegr private fun whenResourceSnippetDocumentedWithRequestAndResponseFields(): RestDocumentationFilter { return RestAssuredRestDocumentationWrapper.document( - identifier = operationName, - snippets = *arrayOf(buildFullResourceSnippet()) + identifier = operationName, + snippets = *arrayOf(buildFullResourceSnippet()) ) } @Throws(Exception::class) private fun whenDocumentedWithRestdocsAndResource(): RestDocumentationFilter { return RestAssuredRestDocumentationWrapper.document( - identifier = operationName, - snippets = *arrayOf( - pathParameters( - parameterWithName("someId").description("someId"), - parameterWithName("otherId").description("otherId") - ), - requestFields(fieldDescriptors().fieldDescriptors), - requestHeaders( - headerWithName("X-Custom-Header").description("some custom header") - ), - responseFields( - fieldWithPath("comment").description("the comment"), - fieldWithPath("flag").description("the flag"), - fieldWithPath("count").description("the count"), - fieldWithPath("id").description("id"), - subsectionWithPath("_links").ignored() - ), - responseHeaders( - headerWithName("X-Custom-Header").description("some custom header") - ), - links( - linkWithRel("self").description("some"), - linkWithRel("multiple").description("multiple") - ) + identifier = operationName, + snippets = *arrayOf( + pathParameters( + parameterWithName("someId").description("someId"), + parameterWithName("otherId").description("otherId") + ), + requestFields(fieldDescriptors().fieldDescriptors), + requestHeaders( + headerWithName("X-Custom-Header").description("some custom header") + ), + responseFields( + fieldWithPath("comment").description("the comment"), + fieldWithPath("flag").description("the flag"), + fieldWithPath("count").description("the count"), + fieldWithPath("id").description("id"), + subsectionWithPath("_links").ignored() + ), + responseHeaders( + headerWithName("X-Custom-Header").description("some custom header") + ), + links( + linkWithRel("self").description("some"), + linkWithRel("multiple").description("multiple") ) + ) ) } @Throws(Exception::class) private fun whenDocumentedWithRamlSnippet(): RestDocumentationFilter { return RestAssuredRestDocumentationWrapper.document( - identifier = operationName, - snippets = *arrayOf(buildFullResourceSnippet()) + identifier = operationName, + snippets = *arrayOf(buildFullResourceSnippet()) ) } @Throws(Exception::class) private fun whenDocumentedWithAllFieldsLinksIgnored(): RestDocumentationFilter { return RestAssuredRestDocumentationWrapper.document( - identifier = operationName, - snippets = *arrayOf( - requestFields(fieldDescriptors().fieldDescriptors), - responseFields( - fieldWithPath("comment").ignored(), - fieldWithPath("flag").ignored(), - fieldWithPath("count").ignored(), - fieldWithPath("id").ignored(), - subsectionWithPath("_links").ignored() - ), - links( - linkWithRel("self").optional().ignored(), - linkWithRel("multiple").optional().ignored() - ) + identifier = operationName, + snippets = *arrayOf( + requestFields(fieldDescriptors().fieldDescriptors), + responseFields( + fieldWithPath("comment").ignored(), + fieldWithPath("flag").ignored(), + fieldWithPath("count").ignored(), + fieldWithPath("id").ignored(), + subsectionWithPath("_links").ignored() + ), + links( + linkWithRel("self").optional().ignored(), + linkWithRel("multiple").optional().ignored() ) + ) ) } @@ -196,23 +199,23 @@ class RestAssuredRestDocumentationWrapperIntegrationTest : ResourceSnippetIntegr private fun whenDocumentedAsPrivateResource(): RestDocumentationFilter { val operationRequestPreprocessor = OperationRequestPreprocessor { r -> r } return RestAssuredRestDocumentationWrapper.document( - identifier = operationName, - privateResource = true, - requestPreprocessor = operationRequestPreprocessor, - snippets = *arrayOf( - requestFields(fieldDescriptors().fieldDescriptors), - responseFields( - fieldWithPath("comment").description("the comment"), - fieldWithPath("flag").description("the flag"), - fieldWithPath("count").description("the count"), - fieldWithPath("id").description("id"), - subsectionWithPath("_links").ignored() - ), - links( - linkWithRel("self").description("some"), - linkWithRel("multiple").description("multiple") - ) + identifier = operationName, + privateResource = true, + requestPreprocessor = operationRequestPreprocessor, + snippets = *arrayOf( + requestFields(fieldDescriptors().fieldDescriptors), + responseFields( + fieldWithPath("comment").description("the comment"), + fieldWithPath("flag").description("the flag"), + fieldWithPath("count").description("the count"), + fieldWithPath("id").description("id"), + subsectionWithPath("_links").ignored() + ), + links( + linkWithRel("self").description("some"), + linkWithRel("multiple").description("multiple") ) + ) ) } @@ -220,26 +223,26 @@ class RestAssuredRestDocumentationWrapperIntegrationTest : ResourceSnippetIntegr private fun whenDocumentedWithResourceSnippetDetails(): RestDocumentationFilter { val operationRequestPreprocessor = OperationRequestPreprocessor { r -> r } return RestAssuredRestDocumentationWrapper.document( - identifier = operationName, - resourceDetails = RestAssuredRestDocumentationWrapper.resourceDetails() - .description("The Resource") - .privateResource(true) - .tag("some-tag"), - requestPreprocessor = operationRequestPreprocessor, - snippets = *arrayOf( - requestFields(fieldDescriptors().fieldDescriptors), - responseFields( - fieldWithPath("comment").description("the comment"), - fieldWithPath("flag").description("the flag"), - fieldWithPath("count").description("the count"), - fieldWithPath("id").description("id"), - subsectionWithPath("_links").ignored() - ), - links( - linkWithRel("self").description("some"), - linkWithRel("multiple").description("multiple") - ) + identifier = operationName, + resourceDetails = RestAssuredRestDocumentationWrapper.resourceDetails() + .description("The Resource") + .privateResource(true) + .tag("some-tag"), + requestPreprocessor = operationRequestPreprocessor, + snippets = *arrayOf( + requestFields(fieldDescriptors().fieldDescriptors), + responseFields( + fieldWithPath("comment").description("the comment"), + fieldWithPath("flag").description("the flag"), + fieldWithPath("count").description("the count"), + fieldWithPath("id").description("id"), + subsectionWithPath("_links").ignored() + ), + links( + linkWithRel("self").description("some"), + linkWithRel("multiple").description("multiple") ) + ) ) } diff --git a/restdocs-api-spec-webtestclient/src/main/kotlin/com/epages/restdocs/apispec/WebTestClientRestDocumentationWrapper.kt b/restdocs-api-spec-webtestclient/src/main/kotlin/com/epages/restdocs/apispec/WebTestClientRestDocumentationWrapper.kt index ede74a35..48e32989 100644 --- a/restdocs-api-spec-webtestclient/src/main/kotlin/com/epages/restdocs/apispec/WebTestClientRestDocumentationWrapper.kt +++ b/restdocs-api-spec-webtestclient/src/main/kotlin/com/epages/restdocs/apispec/WebTestClientRestDocumentationWrapper.kt @@ -22,18 +22,18 @@ object WebTestClientRestDocumentationWrapper : RestDocumentationWrapper() { ): Consumer> { val enhancedSnippets = - enhanceSnippetsWithResourceSnippet( - resourceDetails = resourceDetails, - snippetFilter = snippetFilter, - snippets = *snippets - ) + enhanceSnippetsWithResourceSnippet( + resourceDetails = resourceDetails, + snippetFilter = snippetFilter, + snippets = *snippets + ) if (requestPreprocessor != null && responsePreprocessor != null) { return WebTestClientRestDocumentation.document( - identifier, - requestPreprocessor, - responsePreprocessor, - *enhancedSnippets + identifier, + requestPreprocessor, + responsePreprocessor, + *enhancedSnippets ) } else if (requestPreprocessor != null) { return WebTestClientRestDocumentation.document(identifier, requestPreprocessor, *enhancedSnippets) @@ -58,16 +58,16 @@ object WebTestClientRestDocumentationWrapper : RestDocumentationWrapper() { vararg snippets: Snippet ): Consumer> { return document( - identifier = identifier, - resourceDetails = resourceDetails() - .description(description) - .summary(summary) - .privateResource(privateResource) - .deprecated(deprecated), - requestPreprocessor = requestPreprocessor, - responsePreprocessor = responsePreprocessor, - snippetFilter = snippetFilter, - snippets = *snippets + identifier = identifier, + resourceDetails = resourceDetails() + .description(description) + .summary(summary) + .privateResource(privateResource) + .deprecated(deprecated), + requestPreprocessor = requestPreprocessor, + responsePreprocessor = responsePreprocessor, + snippetFilter = snippetFilter, + snippets = *snippets ) } diff --git a/restdocs-api-spec-webtestclient/src/test/kotlin/com/epages/restdocs/apispec/ResourceSnippetIntegrationTest.kt b/restdocs-api-spec-webtestclient/src/test/kotlin/com/epages/restdocs/apispec/ResourceSnippetIntegrationTest.kt index f6af9ae3..d61d18b5 100644 --- a/restdocs-api-spec-webtestclient/src/test/kotlin/com/epages/restdocs/apispec/ResourceSnippetIntegrationTest.kt +++ b/restdocs-api-spec-webtestclient/src/test/kotlin/com/epages/restdocs/apispec/ResourceSnippetIntegrationTest.kt @@ -20,8 +20,8 @@ import org.springframework.test.context.junit.jupiter.SpringExtension import org.springframework.test.web.reactive.server.WebTestClient import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping -import org.springframework.web.bind.annotation.RequestHeader import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestHeader import org.springframework.web.bind.annotation.RestController import java.util.UUID import javax.validation.constraints.NotEmpty @@ -62,10 +62,10 @@ open class ResourceSnippetIntegrationTest { responseData._links = linksHolder return ResponseEntity - .ok() - .header("X-Custom-Header", customHeader) - .header("Content-Type", HAL_JSON_VALUE) - .body(responseData) + .ok() + .header("X-Custom-Header", customHeader) + .header("Content-Type", HAL_JSON_VALUE) + .body(responseData) } } } @@ -95,37 +95,37 @@ open class ResourceSnippetIntegrationTest { fun fieldDescriptors(): FieldDescriptors { val fields = ConstrainedFields(ResourceSnippetIntegrationTest.TestDataHolder::class.java) return ResourceDocumentation.fields( - fields.withPath("comment").description("the comment").optional(), - fields.withPath("flag").description("the flag"), - fields.withMappedPath("count", "count").description("the count") + fields.withPath("comment").description("the comment").optional(), + fields.withPath("flag").description("the flag"), + fields.withMappedPath("count", "count").description("the count") ) } fun buildFullResourceSnippet(): ResourceSnippet { return resource( - ResourceSnippetParameters.builder() - .description("description") - .summary("summary") - .deprecated(true) - .privateResource(true) - .requestFields(fieldDescriptors()) - .responseFields(fieldDescriptors().and(fieldWithPath("id").description("id"))) - .requestHeaders( - headerWithName("X-Custom-Header").description("A custom header"), - headerWithName(ACCEPT).description("Accept") - ) - .responseHeaders( - headerWithName("X-Custom-Header").description("A custom header"), - headerWithName(CONTENT_TYPE).description("ContentType") - ) - .pathParameters( - parameterWithName("someId").description("some id"), - parameterWithName("otherId").description("otherId id").type(SimpleType.INTEGER) - ) - .links( - linkWithRel("self").description("some"), - linkWithRel("multiple").description("multiple") - ) - .build() + ResourceSnippetParameters.builder() + .description("description") + .summary("summary") + .deprecated(true) + .privateResource(true) + .requestFields(fieldDescriptors()) + .responseFields(fieldDescriptors().and(fieldWithPath("id").description("id"))) + .requestHeaders( + headerWithName("X-Custom-Header").description("A custom header"), + headerWithName(ACCEPT).description("Accept") + ) + .responseHeaders( + headerWithName("X-Custom-Header").description("A custom header"), + headerWithName(CONTENT_TYPE).description("ContentType") + ) + .pathParameters( + parameterWithName("someId").description("some id"), + parameterWithName("otherId").description("otherId id").type(SimpleType.INTEGER) + ) + .links( + linkWithRel("self").description("some"), + linkWithRel("multiple").description("multiple") + ) + .build() ) } diff --git a/restdocs-api-spec-webtestclient/src/test/kotlin/com/epages/restdocs/apispec/WebTestClientRestDocumentationWrapperIntegrationTest.kt b/restdocs-api-spec-webtestclient/src/test/kotlin/com/epages/restdocs/apispec/WebTestClientRestDocumentationWrapperIntegrationTest.kt index a5dabdea..f6c9542c 100644 --- a/restdocs-api-spec-webtestclient/src/test/kotlin/com/epages/restdocs/apispec/WebTestClientRestDocumentationWrapperIntegrationTest.kt +++ b/restdocs-api-spec-webtestclient/src/test/kotlin/com/epages/restdocs/apispec/WebTestClientRestDocumentationWrapperIntegrationTest.kt @@ -104,40 +104,42 @@ class WebTestClientRestDocumentationWrapperIntegrationTest(@Autowired val webTes givenEndpointInvoked("null") assertThatCode { this.whenResourceSnippetDocumentedWithRequestAndResponseFields() } - .doesNotThrowAnyException() + .doesNotThrowAnyException() } private fun whenResourceSnippetDocumentedWithoutParameters() { bodyContentSpec - .consumeWith(document(operationName, resource())) + .consumeWith(document(operationName, resource())) } private fun whenResourceSnippetDocumentedWithDescription() { bodyContentSpec - .consumeWith(document(operationName, resource("A description"))) + .consumeWith(document(operationName, resource("A description"))) } private fun whenResourceSnippetDocumentedWithRequestAndResponseFields() { bodyContentSpec - .consumeWith(document(operationName, buildFullResourceSnippet())) + .consumeWith(document(operationName, buildFullResourceSnippet())) } private fun givenEndpointInvoked(flagValue: String = "true") { bodyContentSpec = webTestClient.post() - .uri("/some/{someId}/other/{otherId}", "id", 1) - .contentType(APPLICATION_JSON) - .header("X-Custom-Header", "test") - .accept(APPLICATION_JSON) - .syncBody("""{ + .uri("/some/{someId}/other/{otherId}", "id", 1) + .contentType(APPLICATION_JSON) + .header("X-Custom-Header", "test") + .accept(APPLICATION_JSON) + .syncBody( + """{ "comment": "some", "flag": $flagValue, "count": 1 - }""".trimIndent() - ) - .exchange() - .expectStatus().isOk - .expectBody() + } + """.trimIndent() + ) + .exchange() + .expectStatus().isOk + .expectBody() } private fun thenSnippetFileExists() { @@ -152,126 +154,126 @@ class WebTestClientRestDocumentationWrapperIntegrationTest(@Autowired val webTes private fun whenDocumentedWithRestdocsAndResource() { bodyContentSpec - .consumeWith { print(it) } - .consumeWith( - WebTestClientRestDocumentationWrapper.document( - identifier = operationName, - snippets = *arrayOf( - pathParameters( - parameterWithName("someId").description("someId"), - parameterWithName("otherId").description("otherId") - ), - requestFields(fieldDescriptors().fieldDescriptors), - requestHeaders( - headerWithName("X-Custom-Header").description("some custom header") - ), - responseFields( - fieldWithPath("comment").description("the comment"), - fieldWithPath("flag").description("the flag"), - fieldWithPath("count").description("the count"), - fieldWithPath("id").description("id"), - subsectionWithPath("_links").ignored() - ), - responseHeaders( - headerWithName("X-Custom-Header").description("some custom header") - ), - links( - linkWithRel("self").description("some"), - linkWithRel("multiple").description("multiple") - ) - ) + .consumeWith { print(it) } + .consumeWith( + WebTestClientRestDocumentationWrapper.document( + identifier = operationName, + snippets = *arrayOf( + pathParameters( + parameterWithName("someId").description("someId"), + parameterWithName("otherId").description("otherId") + ), + requestFields(fieldDescriptors().fieldDescriptors), + requestHeaders( + headerWithName("X-Custom-Header").description("some custom header") + ), + responseFields( + fieldWithPath("comment").description("the comment"), + fieldWithPath("flag").description("the flag"), + fieldWithPath("count").description("the count"), + fieldWithPath("id").description("id"), + subsectionWithPath("_links").ignored() + ), + responseHeaders( + headerWithName("X-Custom-Header").description("some custom header") + ), + links( + linkWithRel("self").description("some"), + linkWithRel("multiple").description("multiple") ) + ) ) + ) } private fun whenDocumentedWithRamlSnippet() { bodyContentSpec - .consumeWith( - WebTestClientRestDocumentationWrapper.document( - identifier = operationName, - snippets = *arrayOf(buildFullResourceSnippet()) - ) + .consumeWith( + WebTestClientRestDocumentationWrapper.document( + identifier = operationName, + snippets = *arrayOf(buildFullResourceSnippet()) ) + ) } @Throws(Exception::class) private fun whenDocumentedWithAllFieldsLinksIgnored() { bodyContentSpec - .consumeWith( - WebTestClientRestDocumentationWrapper.document( - identifier = operationName, - snippets = *arrayOf( - requestFields(fieldDescriptors().fieldDescriptors), - responseFields( - fieldWithPath("comment").ignored(), - fieldWithPath("flag").ignored(), - fieldWithPath("count").ignored(), - fieldWithPath("id").ignored(), - subsectionWithPath("_links").ignored() - ), - links( - linkWithRel("self").optional().ignored(), - linkWithRel("multiple").optional().ignored() - ) - ) + .consumeWith( + WebTestClientRestDocumentationWrapper.document( + identifier = operationName, + snippets = *arrayOf( + requestFields(fieldDescriptors().fieldDescriptors), + responseFields( + fieldWithPath("comment").ignored(), + fieldWithPath("flag").ignored(), + fieldWithPath("count").ignored(), + fieldWithPath("id").ignored(), + subsectionWithPath("_links").ignored() + ), + links( + linkWithRel("self").optional().ignored(), + linkWithRel("multiple").optional().ignored() ) + ) ) + ) } @Throws(Exception::class) private fun whenDocumentedAsPrivateResource() { val operationRequestPreprocessor = OperationRequestPreprocessor { r -> r } bodyContentSpec - .consumeWith( - WebTestClientRestDocumentationWrapper.document( - identifier = operationName, - privateResource = true, - requestPreprocessor = operationRequestPreprocessor, - snippets = *arrayOf( - requestFields(fieldDescriptors().fieldDescriptors), - responseFields( - fieldWithPath("comment").description("the comment"), - fieldWithPath("flag").description("the flag"), - fieldWithPath("count").description("the count"), - fieldWithPath("id").description("id"), - subsectionWithPath("_links").ignored() - ), - links( - linkWithRel("self").description("some"), - linkWithRel("multiple").description("multiple") - ) - ) + .consumeWith( + WebTestClientRestDocumentationWrapper.document( + identifier = operationName, + privateResource = true, + requestPreprocessor = operationRequestPreprocessor, + snippets = *arrayOf( + requestFields(fieldDescriptors().fieldDescriptors), + responseFields( + fieldWithPath("comment").description("the comment"), + fieldWithPath("flag").description("the flag"), + fieldWithPath("count").description("the count"), + fieldWithPath("id").description("id"), + subsectionWithPath("_links").ignored() + ), + links( + linkWithRel("self").description("some"), + linkWithRel("multiple").description("multiple") ) + ) ) + ) } @Throws(Exception::class) private fun whenDocumentedWithResourceSnippetDetails() { val operationRequestPreprocessor = OperationRequestPreprocessor { r -> r } bodyContentSpec - .consumeWith( - WebTestClientRestDocumentationWrapper.document( - identifier = operationName, - resourceDetails = WebTestClientRestDocumentationWrapper.resourceDetails() - .description("The Resource") - .privateResource(true) - .tag("some-tag"), - requestPreprocessor = operationRequestPreprocessor, - snippets = *arrayOf( - requestFields(fieldDescriptors().fieldDescriptors), - responseFields( - fieldWithPath("comment").description("the comment"), - fieldWithPath("flag").description("the flag"), - fieldWithPath("count").description("the count"), - fieldWithPath("id").description("id"), - subsectionWithPath("_links").ignored() - ), - links( - linkWithRel("self").description("some"), - linkWithRel("multiple").description("multiple") - ) - ) + .consumeWith( + WebTestClientRestDocumentationWrapper.document( + identifier = operationName, + resourceDetails = WebTestClientRestDocumentationWrapper.resourceDetails() + .description("The Resource") + .privateResource(true) + .tag("some-tag"), + requestPreprocessor = operationRequestPreprocessor, + snippets = *arrayOf( + requestFields(fieldDescriptors().fieldDescriptors), + responseFields( + fieldWithPath("comment").description("the comment"), + fieldWithPath("flag").description("the flag"), + fieldWithPath("count").description("the count"), + fieldWithPath("id").description("id"), + subsectionWithPath("_links").ignored() + ), + links( + linkWithRel("self").description("some"), + linkWithRel("multiple").description("multiple") ) + ) ) + ) } } diff --git a/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/ConstrainedFields.kt b/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/ConstrainedFields.kt index 224932c4..188d0465 100644 --- a/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/ConstrainedFields.kt +++ b/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/ConstrainedFields.kt @@ -2,7 +2,6 @@ package com.epages.restdocs.apispec import org.springframework.restdocs.constraints.ValidatorConstraintResolver import org.springframework.restdocs.payload.FieldDescriptor - import org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath import org.springframework.restdocs.snippet.Attributes.key diff --git a/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/DescriptorValidator.kt b/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/DescriptorValidator.kt index 5f6cb8b0..96924d91 100644 --- a/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/DescriptorValidator.kt +++ b/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/DescriptorValidator.kt @@ -81,9 +81,10 @@ internal object DescriptorValidator { } private fun toParameterDescriptors(parameters: List) = - parameters.map { p -> parameterWithName(p.name).description(p.description) - .apply { if (p.optional) optional() } - .apply { if (p.isIgnored) ignored() } + parameters.map { p -> + parameterWithName(p.name).description(p.description) + .apply { if (p.optional) optional() } + .apply { if (p.isIgnored) ignored() } } private fun toHeaderDescriptors(requestHeaders: List) = @@ -105,8 +106,10 @@ internal object DescriptorValidator { * * This is baked into [org.springframework.restdocs.payload.AbstractFieldsSnippet.createModel] and is not accessible separately. */ - private class RequestFieldsSnippetWrapper(val descriptors: List) : RequestFieldsSnippet(descriptors), - ValidateableSnippet, FieldTypeExtractor { + private class RequestFieldsSnippetWrapper(val descriptors: List) : + RequestFieldsSnippet(descriptors), + ValidateableSnippet, + FieldTypeExtractor { @Suppress("UNCHECKED_CAST") override fun validate(operation: Operation) { @@ -115,8 +118,10 @@ internal object DescriptorValidator { } } - private class ResponseFieldsSnippetWrapper(val descriptors: List) : ResponseFieldsSnippet(descriptors), - ValidateableSnippet, FieldTypeExtractor { + private class ResponseFieldsSnippetWrapper(val descriptors: List) : + ResponseFieldsSnippet(descriptors), + ValidateableSnippet, + FieldTypeExtractor { @Suppress("UNCHECKED_CAST") override fun validate(operation: Operation) { @@ -146,35 +151,40 @@ internal object DescriptorValidator { } } - private class PathParametersSnippetWrapper(descriptors: List) : PathParametersSnippet(descriptors), + private class PathParametersSnippetWrapper(descriptors: List) : + PathParametersSnippet(descriptors), ValidateableSnippet { override fun validate(operation: Operation) { super.createModel(operation) } } - private class RequestParameterSnippetWrapper(descriptors: List) : RequestParametersSnippet(descriptors), + private class RequestParameterSnippetWrapper(descriptors: List) : + RequestParametersSnippet(descriptors), ValidateableSnippet { override fun validate(operation: Operation) { super.createModel(operation) } } - private class RequestHeadersSnippetWrapper(descriptors: List) : RequestHeadersSnippet(descriptors), + private class RequestHeadersSnippetWrapper(descriptors: List) : + RequestHeadersSnippet(descriptors), ValidateableSnippet { override fun validate(operation: Operation) { this.createModel(operation) } } - private class ResponseHeadersSnippetWrapper(descriptors: List) : ResponseHeadersSnippet(descriptors), + private class ResponseHeadersSnippetWrapper(descriptors: List) : + ResponseHeadersSnippet(descriptors), ValidateableSnippet { override fun validate(operation: Operation) { this.createModel(operation) } } - private class LinksSnippetWrapper(descriptors: List) : LinksSnippet(HypermediaDocumentation.halLinks(), descriptors), + private class LinksSnippetWrapper(descriptors: List) : + LinksSnippet(HypermediaDocumentation.halLinks(), descriptors), ValidateableSnippet { override fun validate(operation: Operation) { this.createModel(operation) diff --git a/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/EnumFields.kt b/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/EnumFields.kt index 091c8605..fb0e723c 100644 --- a/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/EnumFields.kt +++ b/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/EnumFields.kt @@ -25,13 +25,13 @@ class EnumFields(enumType: Class<*>) { * @param path json path of the field */ fun withPath(path: String) = - addPossibleEnumValue(fieldWithPath(path)) + addPossibleEnumValue(fieldWithPath(path)) private fun addPossibleEnumValue(fieldDescriptor: FieldDescriptor): FieldDescriptor = - fieldDescriptor.type(ENUM_TYPE).attributes(key(ENUM_VALUES_KEY).value(possibleEnumValues)) + fieldDescriptor.type(ENUM_TYPE).attributes(key(ENUM_VALUES_KEY).value(possibleEnumValues)) companion object { private const val ENUM_TYPE = "enum" private const val ENUM_VALUES_KEY = "enumValues" } -} \ No newline at end of file +} diff --git a/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/JwtSecurityHandler.kt b/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/JwtSecurityHandler.kt index 9cdf665c..7363db1b 100644 --- a/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/JwtSecurityHandler.kt +++ b/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/JwtSecurityHandler.kt @@ -24,14 +24,14 @@ internal class JwtSecurityHandler : SecurityRequirementsExtractor { private fun hasJWTBearer(operation: Operation): Boolean { return getJWT(operation) - .any { isJWT(it) } + .any { isJWT(it) } } private fun getJWT(operation: Operation) = operation.request.headers - .filterKeys { it == HttpHeaders.AUTHORIZATION } - .flatMap { it.value } - .filter { it.startsWith("Bearer ") } - .map { it.replace("Bearer ", "") } + .filterKeys { it == HttpHeaders.AUTHORIZATION } + .flatMap { it.value } + .filter { it.startsWith("Bearer ") } + .map { it.replace("Bearer ", "") } private fun isJWT(jwt: String): Boolean { val jwtParts = jwt.split("\\.".toRegex()).dropLastWhile { it.isEmpty() } @@ -40,7 +40,7 @@ internal class JwtSecurityHandler : SecurityRequirementsExtractor { val decodedJwtHeader = String(Base64.getDecoder().decode(jwtHeader)) try { return ObjectMapper().readValue>(decodedJwtHeader) - .containsKey("alg") + .containsKey("alg") } catch (e: IOException) { // probably not JWT } @@ -50,7 +50,7 @@ internal class JwtSecurityHandler : SecurityRequirementsExtractor { private fun extractScopes(operation: Operation): List { return getJWT(operation) - .flatMap { jwt2scopes(it) } + .flatMap { jwt2scopes(it) } } @Suppress("UNCHECKED_CAST") diff --git a/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/ResourceSnippet.kt b/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/ResourceSnippet.kt index ca7c55a9..f2d9aa62 100755 --- a/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/ResourceSnippet.kt +++ b/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/ResourceSnippet.kt @@ -25,7 +25,7 @@ class ResourceSnippet(private val resourceSnippetParameters: ResourceSnippetPara override fun document(operation: Operation) { val context = operation - .attributes[RestDocumentationContext::class.java.name] as RestDocumentationContext + .attributes[RestDocumentationContext::class.java.name] as RestDocumentationContext DescriptorValidator.validatePresentParameters(resourceSnippetParameters, operation) @@ -33,10 +33,13 @@ class ResourceSnippet(private val resourceSnippetParameters: ResourceSnippetPara val model = createModel(operation, placeholderResolverFactory, context) - (StandardWriterResolver(placeholderResolverFactory, Charsets.UTF_8.name(), - JsonTemplateFormat - )) - .resolve(operation.name, "resource", context) + ( + StandardWriterResolver( + placeholderResolverFactory, Charsets.UTF_8.name(), + JsonTemplateFormat + ) + ) + .resolve(operation.name, "resource", context) .use { it.append(objectMapper.writeValueAsString(model)) } } diff --git a/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/ResourceSnippetParameters.kt b/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/ResourceSnippetParameters.kt index bf094fa0..038196da 100755 --- a/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/ResourceSnippetParameters.kt +++ b/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/ResourceSnippetParameters.kt @@ -41,9 +41,11 @@ data class ResourceSnippetParameters @JvmOverloads constructor( var descriptor = createLinkFieldDescriptor(linkDescriptor.rel) .description(linkDescriptor.description) .type(JsonFieldType.VARIES) - .attributes(*linkDescriptor.attributes.entries - .map { e -> Attributes.Attribute(e.key, e.value) } - .toTypedArray()) + .attributes( + *linkDescriptor.attributes.entries + .map { e -> Attributes.Attribute(e.key, e.value) } + .toTypedArray() + ) if (linkDescriptor.isOptional) { descriptor = descriptor.optional() @@ -69,7 +71,8 @@ data class ResourceSnippetParameters @JvmOverloads constructor( PayloadDocumentation::class.java, "subsectionWithPath", String::class.java - )) + ) + ) .map { m -> ReflectionUtils.invokeMethod(m, null, path) } .orElseGet { fieldWithPath(path) } as FieldDescriptor } @@ -211,27 +214,34 @@ class ResourceSnippetParametersBuilder : ResourceSnippetDetails() { fun pathParameters(vararg pathParameters: ParameterDescriptorWithType) = pathParameters(pathParameters.toList()) fun pathParameters(pathParameters: List) = apply { this.pathParameters = pathParameters } - fun pathParameters(vararg pathParameters: ParameterDescriptor) = pathParameters(pathParameters.map { - ParameterDescriptorWithType.fromParameterDescriptor(it) - }) + fun pathParameters(vararg pathParameters: ParameterDescriptor) = pathParameters( + pathParameters.map { + ParameterDescriptorWithType.fromParameterDescriptor(it) + } + ) fun requestParameters(vararg requestParameters: ParameterDescriptorWithType) = requestParameters(requestParameters.toList()) fun requestParameters(requestParameters: List) = apply { this.requestParameters = requestParameters } - fun requestParameters(vararg requestParameters: ParameterDescriptor) = requestParameters(requestParameters.map { - ParameterDescriptorWithType.fromParameterDescriptor(it) - }) + fun requestParameters(vararg requestParameters: ParameterDescriptor) = requestParameters( + requestParameters.map { + ParameterDescriptorWithType.fromParameterDescriptor(it) + } + ) fun requestHeaders(requestHeaders: List) = apply { this.requestHeaders = requestHeaders } fun requestHeaders(vararg requestHeaders: HeaderDescriptorWithType) = requestHeaders(requestHeaders.toList()) fun requestHeaders(vararg requestHeaders: HeaderDescriptor) = - requestHeaders(requestHeaders.map { - HeaderDescriptorWithType.fromHeaderDescriptor(it) - }) + requestHeaders( + requestHeaders.map { + HeaderDescriptorWithType.fromHeaderDescriptor(it) + } + ) fun responseHeaders(responseHeaders: List) = apply { this.responseHeaders = responseHeaders } fun responseHeaders(vararg responseHeaders: HeaderDescriptorWithType) = responseHeaders(responseHeaders.toList()) fun responseHeaders(vararg responseHeaders: HeaderDescriptor) = responseHeaders( - responseHeaders.map { HeaderDescriptorWithType.fromHeaderDescriptor(it) }) + responseHeaders.map { HeaderDescriptorWithType.fromHeaderDescriptor(it) } + ) override fun tag(tag: String) = tags(tag) override fun tags(vararg tags: String) = apply { this.tags += tags } diff --git a/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/RestDocumentationWrapper.kt b/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/RestDocumentationWrapper.kt index 8c05486b..dabae9e0 100644 --- a/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/RestDocumentationWrapper.kt +++ b/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/RestDocumentationWrapper.kt @@ -24,67 +24,67 @@ abstract class RestDocumentationWrapper { val enhancedSnippets = if (snippets.none { it is ResourceSnippet }) { // No ResourceSnippet, so we configure our own based on the info of the other snippets val resourceParameters = createBuilder(resourceDetails) - .requestFields( - snippets.filter { it is RequestFieldsSnippet } - .flatMap { - DescriptorExtractor.extractDescriptorsFor( - it - ) - } - ) - .responseFields( - snippets.filter { it is ResponseFieldsSnippet } - .flatMap { - DescriptorExtractor.extractDescriptorsFor( - it - ) - } - ) - .links( - snippets.filter { it is LinksSnippet } - .flatMap { - DescriptorExtractor.extractDescriptorsFor( - it - ) - } - ) - .requestParameters( - *snippets.filter { it is RequestParametersSnippet } - .flatMap { - DescriptorExtractor.extractDescriptorsFor( - it - ) - } - .toTypedArray() - ) - .pathParameters( - *snippets.filter { it is PathParametersSnippet } - .flatMap { - DescriptorExtractor.extractDescriptorsFor( - it - ) - } - .toTypedArray() - ) - .requestHeaders( - *snippets.filter { it is RequestHeadersSnippet } - .flatMap { - DescriptorExtractor.extractDescriptorsFor( - it - ) - } - .toTypedArray() - ) - .responseHeaders( - *snippets.filter { it is ResponseHeadersSnippet } - .flatMap { - DescriptorExtractor.extractDescriptorsFor( - it - ) - } - .toTypedArray() - ) - .build() + .requestFields( + snippets.filter { it is RequestFieldsSnippet } + .flatMap { + DescriptorExtractor.extractDescriptorsFor( + it + ) + } + ) + .responseFields( + snippets.filter { it is ResponseFieldsSnippet } + .flatMap { + DescriptorExtractor.extractDescriptorsFor( + it + ) + } + ) + .links( + snippets.filter { it is LinksSnippet } + .flatMap { + DescriptorExtractor.extractDescriptorsFor( + it + ) + } + ) + .requestParameters( + *snippets.filter { it is RequestParametersSnippet } + .flatMap { + DescriptorExtractor.extractDescriptorsFor( + it + ) + } + .toTypedArray() + ) + .pathParameters( + *snippets.filter { it is PathParametersSnippet } + .flatMap { + DescriptorExtractor.extractDescriptorsFor( + it + ) + } + .toTypedArray() + ) + .requestHeaders( + *snippets.filter { it is RequestHeadersSnippet } + .flatMap { + DescriptorExtractor.extractDescriptorsFor( + it + ) + } + .toTypedArray() + ) + .responseHeaders( + *snippets.filter { it is ResponseHeadersSnippet } + .flatMap { + DescriptorExtractor.extractDescriptorsFor( + it + ) + } + .toTypedArray() + ) + .build() snippets.toList() + ResourceDocumentation.resource(resourceParameters) } else snippets.toList() @@ -95,13 +95,13 @@ abstract class RestDocumentationWrapper { return when (resourceDetails) { is ResourceSnippetParametersBuilder -> resourceDetails else -> ResourceSnippetParametersBuilder() - .description(resourceDetails.description) - .requestSchema(resourceDetails.requestSchema) - .responseSchema(resourceDetails.responseSchema) - .summary(resourceDetails.summary) - .privateResource(resourceDetails.privateResource) - .deprecated(resourceDetails.deprecated) - .tags(*resourceDetails.tags.toTypedArray()) + .description(resourceDetails.description) + .requestSchema(resourceDetails.requestSchema) + .responseSchema(resourceDetails.responseSchema) + .summary(resourceDetails.summary) + .privateResource(resourceDetails.privateResource) + .deprecated(resourceDetails.deprecated) + .tags(*resourceDetails.tags.toTypedArray()) } } -} \ No newline at end of file +} diff --git a/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/SecurityRequirementsHandler.kt b/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/SecurityRequirementsHandler.kt index c5f51974..d38e1e6d 100644 --- a/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/SecurityRequirementsHandler.kt +++ b/restdocs-api-spec/src/main/kotlin/com/epages/restdocs/apispec/SecurityRequirementsHandler.kt @@ -6,13 +6,13 @@ import org.springframework.restdocs.operation.Operation internal class SecurityRequirementsHandler { private val handlers = listOf( - BasicSecurityHandler(), - JwtSecurityHandler() + BasicSecurityHandler(), + JwtSecurityHandler() ) fun extractSecurityRequirements(operation: Operation): SecurityRequirements? { return handlers.map { it.extractSecurityRequirements(operation) } - .firstOrNull { it != null } + .firstOrNull { it != null } } } @@ -29,10 +29,10 @@ internal class BasicSecurityHandler : SecurityRequirementsExtractor { private fun isBasicSecurity(operation: Operation): Boolean { return operation.request.headers - .filterKeys { it == HttpHeaders.AUTHORIZATION } - .flatMap { it.value } - .filter { it.startsWith("Basic ") } - .isNotEmpty() + .filterKeys { it == HttpHeaders.AUTHORIZATION } + .flatMap { it.value } + .filter { it.startsWith("Basic ") } + .isNotEmpty() } } @@ -41,7 +41,7 @@ internal interface SecurityRequirements { } internal data class Oauth2(val requiredScopes: List) : - SecurityRequirements { + SecurityRequirements { override val type = SecurityType.OAUTH2 } diff --git a/restdocs-api-spec/src/test/kotlin/com/epages/restdocs/apispec/ConstrainedFieldsTest.kt b/restdocs-api-spec/src/test/kotlin/com/epages/restdocs/apispec/ConstrainedFieldsTest.kt index 00f3ca09..9b0d64b6 100644 --- a/restdocs-api-spec/src/test/kotlin/com/epages/restdocs/apispec/ConstrainedFieldsTest.kt +++ b/restdocs-api-spec/src/test/kotlin/com/epages/restdocs/apispec/ConstrainedFieldsTest.kt @@ -37,7 +37,7 @@ internal class ConstrainedFieldsTest { then(descriptor.attributes).containsKey("validationConstraints") then((descriptor.attributes["validationConstraints"] as List).map { it.name }) - .containsExactly(NotEmpty::class.java.name) + .containsExactly(NotEmpty::class.java.name) } private data class SomeWithConstraints( diff --git a/restdocs-api-spec/src/test/kotlin/com/epages/restdocs/apispec/ResourceSnippetTest.kt b/restdocs-api-spec/src/test/kotlin/com/epages/restdocs/apispec/ResourceSnippetTest.kt index 65c75929..d768364d 100644 --- a/restdocs-api-spec/src/test/kotlin/com/epages/restdocs/apispec/ResourceSnippetTest.kt +++ b/restdocs-api-spec/src/test/kotlin/com/epages/restdocs/apispec/ResourceSnippetTest.kt @@ -264,14 +264,14 @@ class ResourceSnippetTest { private fun givenOperationWithNamePlaceholders() { operation = OperationBuilder("{class-name}/{method-name}", rootOutputDirectory) - .attribute(ATTRIBUTE_NAME_URL_TEMPLATE, "http://localhost:8080/some/{id}") - .testClass(ResourceSnippetTest::class.java) - .testMethodName("getSomeById") - .request("http://localhost:8080/some/123") - .method("POST") - .header(CONTENT_TYPE, APPLICATION_JSON_VALUE) - .content("{\"comment\": \"some\"}") - .build() + .attribute(ATTRIBUTE_NAME_URL_TEMPLATE, "http://localhost:8080/some/{id}") + .testClass(ResourceSnippetTest::class.java) + .testMethodName("getSomeById") + .request("http://localhost:8080/some/123") + .method("POST") + .header(CONTENT_TYPE, APPLICATION_JSON_VALUE) + .content("{\"comment\": \"some\"}") + .build() } private fun givenOperationWithRequestBody() { @@ -338,19 +338,22 @@ class ResourceSnippetTest { private fun givenIgnoredAndNotIgnoredRequestFieldDescriptors() { parametersBuilder.requestFields( fieldWithPath("comment").description("description"), - fieldWithPath("ignored").description("description").ignored()) + fieldWithPath("ignored").description("description").ignored() + ) } private fun givenIgnoredAndNotIgnoredResponseFieldDescriptors() { parametersBuilder.responseFields( fieldWithPath("comment").description("description"), - fieldWithPath("ignored").description("description").ignored()) + fieldWithPath("ignored").description("description").ignored() + ) } private fun givenIgnoredAndNotIgnoredRequestParameterDescriptors() { parametersBuilder.requestParameters( parameterWithName("describedParameter").description("description"), - parameterWithName("obviousParameter").description("needs no documentation, too obvious").ignored()) + parameterWithName("obviousParameter").description("needs no documentation, too obvious").ignored() + ) } private fun givenOperationWithRequestAndResponseBody(responseContentType: String = APPLICATION_JSON_VALUE) { diff --git a/restdocs-api-spec/src/test/kotlin/com/epages/restdocs/apispec/SecurityRequirementsHandlerTest.kt b/restdocs-api-spec/src/test/kotlin/com/epages/restdocs/apispec/SecurityRequirementsHandlerTest.kt index f36c788c..ad719f24 100644 --- a/restdocs-api-spec/src/test/kotlin/com/epages/restdocs/apispec/SecurityRequirementsHandlerTest.kt +++ b/restdocs-api-spec/src/test/kotlin/com/epages/restdocs/apispec/SecurityRequirementsHandlerTest.kt @@ -45,21 +45,21 @@ class SecurityRequirementsHandlerTest { private fun givenRequestWithBasicAuthHeader() { operation = OperationBuilder().request("/some") - .header(HttpHeaders.AUTHORIZATION, "Basic dGVzdDpwd2QK") - .build() + .header(HttpHeaders.AUTHORIZATION, "Basic dGVzdDpwd2QK") + .build() } private fun givenRequestWithJwtInAuthorizationHeader() { operation = OperationBuilder().request("/some") - .header( - HttpHeaders.AUTHORIZATION, - "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJzY29wZTEiLCJzY29wZTIiXSwiZXhwIjoxNTA3NzU4NDk4LCJpYXQiOjE1MDc3MTUyOTgsImp0aSI6IjQyYTBhOTFhLWQ2ZWQtNDBjYy1iMTA2LWU5MGNkYWU0M2Q2ZCJ9.eWGo7Y124_Hdrr-bKX08d_oCfdgtlGXo9csz-hvRhRORJi_ZK7PIwM0ChqoLa4AhR-dJ86npid75GB9IxCW2f5E24FyZW2p5swpOpfkEAA4oFuj7jxHiaiqL_HFKKCRsVNAN3hGiSp9Hn3fde0-LlABqMaihdzZzHL-xm8-CqbXT-qBfuscDImZrZQZqhizpSEV4idbEMzZykggLASGoOIL0t0ycfe3yeuQkMUhzZmXuu08VM7zXwWnqfXCa-RmA6wC7ZnWqiJoi0vBr4BrlLR067YoUrT6pgRfiy2HZ0vEE_XY5SBtA-qI2QnlJb7eTk7pgFtoGkYdeOZ86k6GDVw" - ) - .build() + .header( + HttpHeaders.AUTHORIZATION, + "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJzY29wZTEiLCJzY29wZTIiXSwiZXhwIjoxNTA3NzU4NDk4LCJpYXQiOjE1MDc3MTUyOTgsImp0aSI6IjQyYTBhOTFhLWQ2ZWQtNDBjYy1iMTA2LWU5MGNkYWU0M2Q2ZCJ9.eWGo7Y124_Hdrr-bKX08d_oCfdgtlGXo9csz-hvRhRORJi_ZK7PIwM0ChqoLa4AhR-dJ86npid75GB9IxCW2f5E24FyZW2p5swpOpfkEAA4oFuj7jxHiaiqL_HFKKCRsVNAN3hGiSp9Hn3fde0-LlABqMaihdzZzHL-xm8-CqbXT-qBfuscDImZrZQZqhizpSEV4idbEMzZykggLASGoOIL0t0ycfe3yeuQkMUhzZmXuu08VM7zXwWnqfXCa-RmA6wC7ZnWqiJoi0vBr4BrlLR067YoUrT6pgRfiy2HZ0vEE_XY5SBtA-qI2QnlJb7eTk7pgFtoGkYdeOZ86k6GDVw" + ) + .build() } private fun givenRequestWithoutAuthorizationHeader() { operation = OperationBuilder().request("/some") - .build() + .build() } }