diff --git a/.github/workflows/deploy-to-mavencentral.yaml b/.github/workflows/deploy-to-mavencentral.yaml index 8f93096..10347b9 100644 --- a/.github/workflows/deploy-to-mavencentral.yaml +++ b/.github/workflows/deploy-to-mavencentral.yaml @@ -11,7 +11,7 @@ on: MAVEN_CENTRAL_PASSWORD: required: true jobs: - publish-to-maven-central: + publish-to-mavencentral: runs-on: ubuntu-latest steps: - name: GitHub 리포지토리 체크아웃 diff --git a/README.md b/README.md index a54a73f..8161b23 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # 📝 Documentify +[![Kotlin](https://img.shields.io/badge/kotlin-2.0.0-blue.svg?logo=kotlin)](http://kotlinlang.org) ![Latest Release](https://img.shields.io/github/v/release/BGMSound/documentify) [![Apache 2.0 license](https://img.shields.io/badge/License-APACHE%202.0-green.svg?logo=APACHE&style=flat)](https://opensource.org/licenses/Apache-2.0)
@@ -35,7 +36,7 @@ fun setUp(provider: RestDocumentationContextProvider) { ``` You can also set up the test environment with an application context or an auto-configured MockMvc (or WebTestClient).

-`Mvc Example` +`MVC Example` ```kotlin webApplicationContext(provider, context) mockMvc(provider, mockMvc) @@ -77,11 +78,29 @@ fun documentationGetApi() { } ``` +Additional validation of the mock response generated during the tests for document creation is also possible. +```kotlin +@Test +fun documentationGetApi() { + documentation("test-get-api") { + information { + summary("test get api") + description("this is test get api") + tag("test") + } + requestLine(Method.GET, "/api/test/{path}") + responseBody { + field("testField", "test", "test") + } + }.expect(jsonPath("$testField").value("test")) +} +``` + ### Generate OpenAPI Specification After setting up the test environment and writing the test code, run the test. The OpenAPI specification document will be generated in the `build/generated-snippets` directory. -First, apply documentify plugin to your `build.gradle.kts` file: +First, apply documentify plugin to your `build.gradle.kts` file *(need gradle plugin portal)*: ```kotlin plugins { id("io.github.bgmsound.documentify") version "${version}" @@ -112,3 +131,6 @@ you can also create Postman collection by running the following command: ## Documentify Development Story If you want to check out the development story of Documentify, please refer to the [blog post](https://bgmsound.medium.com/documentify-선언형-rest-docs-dsl-제작기-0a09f651be2c). + +## License +documentify is Open Source software released under the [Apache 2.0 license](https://www.apache.org/licenses/LICENSE-2.0.html). diff --git a/build.gradle.kts b/build.gradle.kts index 33e39e7..a3babed 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -19,7 +19,7 @@ version = extra["project.version.id"] as String val publicModulePathSet = setOf( rootProject.projects.documentifyProject.documentifyCore.identityPath.path, - rootProject.projects.documentifyProject.documentifyPlugin.identityPath.path, + rootProject.projects.documentifyProject.documentifyGradlePlugin.identityPath.path, rootProject.projects.documentifyProject.documentifyMvc.identityPath.path, rootProject.projects.documentifyProject.documentifyReactive.identityPath.path, rootProject.projects.documentifyStarters.documentifyStarterMvc.identityPath.path, @@ -67,7 +67,7 @@ subprojects { configure { publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL) - val artifactId = name.replace("-plugin", ".gradle.plugin") + val artifactId = name.replace("-gradle-plugin", ".gradle.plugin") val projectGroup = property("project.group").toString() val projectName = property("project.name").toString() val projectVersion = property("project.version.id").toString() diff --git a/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/emitter/AbstractDocumentEmitter.kt b/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/emitter/AbstractDocumentEmitter.kt index 54f5037..b394cfd 100644 --- a/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/emitter/AbstractDocumentEmitter.kt +++ b/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/emitter/AbstractDocumentEmitter.kt @@ -10,8 +10,9 @@ import org.springframework.restdocs.snippet.Snippet abstract class AbstractDocumentEmitter( protected val provider: RestDocumentationContextProvider, - protected val documentSpec: DocumentSpec -) { + protected val documentSpec: DocumentSpec, + protected val sampleAggregator: DocumentSpecSampleAggregator = DefaultDocumentSpecSampleAggregator +) : DocumentEmitter { protected fun ResponseSpec.buildResource(index: Int): Snippet { val resourceBuilder = ResourceSnippetParameters.builder() if (documentSpec.tags.isNotEmpty()) { diff --git a/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/emitter/AbstractDocumentResult.kt b/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/emitter/AbstractDocumentResult.kt new file mode 100644 index 0000000..1e54ae6 --- /dev/null +++ b/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/emitter/AbstractDocumentResult.kt @@ -0,0 +1,48 @@ +package io.github.bgmsound.documentify.core.emitter + +import io.github.bgmsound.documentify.core.specification.element.field.Field +import io.github.bgmsound.documentify.core.specification.schema.response.ResponseSpec + +abstract class AbstractDocumentResult : DocumentResult { + abstract fun validateJsonPath(jsonResultMatcher: JsonResultMatcher) + + override fun validateWith(responseSpec: ResponseSpec) { + val matchers = aggregateMatchers(responseSpec.fields) + if (matchers.isEmpty()) { + return + } + matchers.forEach { matcher -> + validateJsonPath(matcher) + } + } + + private fun aggregateMatchers(fields: List): List { + return fields.filter { + it.hasSample() || it.canHaveChild() || !it.isIgnored() + }.flatMap { + it.aggregateMatchers() + } + } + + private fun Field.aggregateMatchers(): List { + if (isIgnored()) { + return emptyList() + } + if (!hasSample() && !canHaveChild()) { + return emptyList() + } + val matchers = mutableListOf() + if (hasSample()) { + val jsonPath = StringBuilder("$.${path}").apply { + if (isArray()) { + append("[*]") + } + }.toString().replace("[]", "[*]") + matchers.add(JsonResultMatcher.of(jsonPath, sample)) + } else if (childFields().isNotEmpty()) { + val childMatchers = aggregateMatchers(childFields()) + matchers.addAll(childMatchers) + } + return matchers + } +} \ No newline at end of file diff --git a/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/emitter/SpecElementSampleAssociater.kt b/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/emitter/DefaultDocumentSpecSampleAggregator.kt similarity index 54% rename from documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/emitter/SpecElementSampleAssociater.kt rename to documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/emitter/DefaultDocumentSpecSampleAggregator.kt index 92a0244..3a7e7c7 100644 --- a/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/emitter/SpecElementSampleAssociater.kt +++ b/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/emitter/DefaultDocumentSpecSampleAggregator.kt @@ -3,16 +3,26 @@ package io.github.bgmsound.documentify.core.emitter import io.github.bgmsound.documentify.core.specification.element.SpecElement import io.github.bgmsound.documentify.core.specification.element.field.Field -object SpecElementSampleAssociater { - fun List.associatedFieldSample(): Map { - return filter { +object DefaultDocumentSpecSampleAggregator : DocumentSpecSampleAggregator { + @Suppress("UNCHECKED_CAST") + override fun aggregate(specElements: List): Map { + if (specElements.isEmpty()) { + return emptyMap() + } + if (!specElements.isField()) { + return specElements.associate { + it.key to it.sample + } + } + val fieldElements = specElements as List + return fieldElements.filter { it.hasSample() || it.canHaveChild() || !it.isIgnored() }.associate { - it.associatedSample() + it.aggregateSample() } } - fun Field.associatedSample(): Pair { + private fun Field.aggregateSample(): Pair { if (isIgnored()) { throw IllegalStateException("can't associate ignored field $key") } @@ -25,7 +35,7 @@ object SpecElementSampleAssociater { if (childFields().isEmpty()) { throw IllegalStateException("Field $key must have child fields") } - val sample = childFields().associate { it.associatedSample() } + val sample = childFields().associate { it.aggregateSample() } if (isArray()) { listOf(sample) } else { @@ -34,9 +44,9 @@ object SpecElementSampleAssociater { } } - fun List.associatedSample(): Map { - return associate { - it.key to it.sample + private fun List.isField(): Boolean { + return all { + it is Field } } } \ No newline at end of file diff --git a/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/emitter/DocumentEmitter.kt b/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/emitter/DocumentEmitter.kt new file mode 100644 index 0000000..a1d909e --- /dev/null +++ b/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/emitter/DocumentEmitter.kt @@ -0,0 +1,3 @@ +package io.github.bgmsound.documentify.core.emitter + +interface DocumentEmitter \ No newline at end of file diff --git a/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/emitter/DocumentResult.kt b/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/emitter/DocumentResult.kt new file mode 100644 index 0000000..a079c4f --- /dev/null +++ b/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/emitter/DocumentResult.kt @@ -0,0 +1,9 @@ +package io.github.bgmsound.documentify.core.emitter + +import io.github.bgmsound.documentify.core.specification.schema.response.ResponseSpec + +interface DocumentResult { + + fun validateWith(responseSpec: ResponseSpec) + +} \ No newline at end of file diff --git a/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/emitter/DocumentSpecSampleAggregator.kt b/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/emitter/DocumentSpecSampleAggregator.kt new file mode 100644 index 0000000..43e4970 --- /dev/null +++ b/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/emitter/DocumentSpecSampleAggregator.kt @@ -0,0 +1,9 @@ +package io.github.bgmsound.documentify.core.emitter + +import io.github.bgmsound.documentify.core.specification.element.SpecElement + +interface DocumentSpecSampleAggregator{ + + fun aggregate(specElements: List) : Map + +} \ No newline at end of file diff --git a/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/emitter/FieldJsonMatcherAssociater.kt b/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/emitter/FieldJsonMatcherAssociater.kt deleted file mode 100644 index 827b084..0000000 --- a/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/emitter/FieldJsonMatcherAssociater.kt +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.bgmsound.documentify.core.emitter - -import io.github.bgmsound.documentify.core.specification.element.field.Field - -object FieldJsonMatcherAssociater { - fun List.associatedMatchers(): List> { - return filter { - it.hasSample() || it.canHaveChild() || !it.isIgnored() - }.flatMap { - it.associatedMatchers() - } - } - - fun Field.associatedMatchers(): List> { - if (isIgnored()) { - return emptyList() - } - if (!hasSample() && !canHaveChild()) { - return emptyList() - } - val matchers = mutableListOf>() - if (hasSample()) { - val jsonPath = StringBuilder("$.${path}").apply { - if (isArray()) { - append("[*]") - } - }.toString().replace("[]", "[*]") - matchers.add(jsonPath to sample) - } else if (childFields().isNotEmpty()) { - val childMatchers = childFields().associatedMatchers() - matchers.addAll(childMatchers) - } - return matchers - } -} \ No newline at end of file diff --git a/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/emitter/JsonResultMatcher.kt b/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/emitter/JsonResultMatcher.kt new file mode 100644 index 0000000..ac59634 --- /dev/null +++ b/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/emitter/JsonResultMatcher.kt @@ -0,0 +1,10 @@ +package io.github.bgmsound.documentify.core.emitter + +data class JsonResultMatcher( + val jsonPath: String, + val expectedValue: Any +) { + companion object { + fun of(path: String, expectedValue: Any) = JsonResultMatcher(path, expectedValue) + } +} diff --git a/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/environment/AbstractStandaloneContextEnvironment.kt b/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/environment/AbstractStandaloneContextEnvironment.kt index 375958a..a971f43 100644 --- a/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/environment/AbstractStandaloneContextEnvironment.kt +++ b/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/environment/AbstractStandaloneContextEnvironment.kt @@ -6,10 +6,10 @@ import com.fasterxml.jackson.databind.ObjectMapper abstract class AbstractStandaloneContextEnvironment> : StandaloneContextEnvironment, AbstractDocumentContextEnvironment() { protected val controllers: MutableList = mutableListOf() protected val controllerAdvices: MutableList = mutableListOf() - protected var objectMapper: ObjectMapper? = null + protected var codec: ObjectMapper? = null - override fun objectMapper(objectMapper: ObjectMapper): T { - this.objectMapper = objectMapper + override fun codec(codec: ObjectMapper): T { + this.codec = codec return this as T } diff --git a/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/environment/StandaloneContextEnvironment.kt b/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/environment/StandaloneContextEnvironment.kt index 56594ee..38123de 100644 --- a/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/environment/StandaloneContextEnvironment.kt +++ b/documentify-project/documentify-core/src/main/kotlin/io/github/bgmsound/documentify/core/environment/StandaloneContextEnvironment.kt @@ -4,7 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper interface StandaloneContextEnvironment> : DocumentContextEnvironment { - fun objectMapper(objectMapper: ObjectMapper): T + fun codec(codec: ObjectMapper): T fun controller(controller: Any): T diff --git a/documentify-project/documentify-plugin/build.gradle.kts b/documentify-project/documentify-gradle-plugin/build.gradle.kts similarity index 93% rename from documentify-project/documentify-plugin/build.gradle.kts rename to documentify-project/documentify-gradle-plugin/build.gradle.kts index eda82ef..5abdf12 100644 --- a/documentify-project/documentify-plugin/build.gradle.kts +++ b/documentify-project/documentify-gradle-plugin/build.gradle.kts @@ -10,7 +10,7 @@ gradlePlugin { plugins { register("documentify") { id = "io.github.bgmsound.documentify" - implementationClass = "io.github.bgmsound.documentify.plugin.DocumentifyPlugin" + implementationClass = "io.github.bgmsound.documentify.gradle.plugin.DocumentifyPlugin" displayName = "Documentify" description = "easy and powerful API documentation tool for spring restdocs" } diff --git a/documentify-project/documentify-plugin/src/main/kotlin/io/github/bgmsound/documentify/plugin/DocumentifyPlugin.kt b/documentify-project/documentify-gradle-plugin/src/main/kotlin/io/github/bgmsound/documentify/gradle/plugin/DocumentifyPlugin.kt similarity index 85% rename from documentify-project/documentify-plugin/src/main/kotlin/io/github/bgmsound/documentify/plugin/DocumentifyPlugin.kt rename to documentify-project/documentify-gradle-plugin/src/main/kotlin/io/github/bgmsound/documentify/gradle/plugin/DocumentifyPlugin.kt index 3b4c62a..4f84367 100644 --- a/documentify-project/documentify-plugin/src/main/kotlin/io/github/bgmsound/documentify/plugin/DocumentifyPlugin.kt +++ b/documentify-project/documentify-gradle-plugin/src/main/kotlin/io/github/bgmsound/documentify/gradle/plugin/DocumentifyPlugin.kt @@ -1,4 +1,4 @@ -package io.github.bgmsound.documentify.plugin +package io.github.bgmsound.documentify.gradle.plugin import org.gradle.api.Plugin import org.gradle.api.Project diff --git a/documentify-project/documentify-gradle-plugin/src/main/kotlin/io/github/bgmsound/documentify/gradle/plugin/extensions/DocumentifyExtension.kt b/documentify-project/documentify-gradle-plugin/src/main/kotlin/io/github/bgmsound/documentify/gradle/plugin/extensions/DocumentifyExtension.kt new file mode 100644 index 0000000..fce1180 --- /dev/null +++ b/documentify-project/documentify-gradle-plugin/src/main/kotlin/io/github/bgmsound/documentify/gradle/plugin/extensions/DocumentifyExtension.kt @@ -0,0 +1,4 @@ +package io.github.bgmsound.documentify.gradle.plugin.extensions + +interface DocumentifyExtension { +} \ No newline at end of file diff --git a/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/Documentify.kt b/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/Documentify.kt index 871d311..a514274 100644 --- a/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/Documentify.kt +++ b/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/Documentify.kt @@ -2,6 +2,7 @@ package io.github.bgmsound.documentify.mvc import io.github.bgmsound.documentify.core.specification.schema.document.DocumentSpec import io.github.bgmsound.documentify.mvc.emitter.EmitterFactory +import io.github.bgmsound.documentify.mvc.emitter.MvcDocumentEmitter import io.github.bgmsound.documentify.mvc.environment.MockMvcContextEnvironment.Companion.mockMvcEnvironment import io.github.bgmsound.documentify.mvc.environment.StandaloneMvcContextEnvironment import io.github.bgmsound.documentify.mvc.environment.WebApplicationContextEnvironment.Companion.webApplicationContextEnvironment @@ -15,24 +16,31 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver @ExtendWith(RestDocumentationExtension::class) abstract class Documentify { private lateinit var provider: RestDocumentationContextProvider - private lateinit var documentContextEnvironment: MvcDocumentContextEnvironment + private lateinit var environment: MvcDocumentContextEnvironment + private var customEmitter: MvcDocumentEmitter? = null fun documentation( name: String, specCustomizer: DocumentSpec.() -> Unit ): ValidatableMockResponse { val documentSpec = DocumentSpec(name).also { specCustomizer(it) } - val emitter = EmitterFactory.createMvcEmitter(provider, documentSpec, documentContextEnvironment) + val emitter = customEmitter ?: EmitterFactory.of(provider, documentSpec, environment) return emitter.emit() } + fun emitter( + customEmitter: MvcDocumentEmitter + ) { + this.customEmitter = customEmitter + } + fun mockMvc( provider: RestDocumentationContextProvider, mockMvc: MockMvc ) { this.provider = provider - documentContextEnvironment = mockMvcEnvironment(mockMvc) + environment = mockMvcEnvironment(mockMvc) } fun standalone( @@ -40,7 +48,7 @@ abstract class Documentify { standaloneContext: StandaloneMvcContextEnvironment ) { this.provider = provider - documentContextEnvironment = standaloneContext + environment = standaloneContext } fun standalone( @@ -71,7 +79,7 @@ abstract class Documentify { provider: RestDocumentationContextProvider, context: WebApplicationContext ) { - documentContextEnvironment = webApplicationContextEnvironment(provider, context) + environment = webApplicationContextEnvironment(provider, context) this.provider = provider } } \ No newline at end of file diff --git a/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/ValidatableMockMvcResponseAdapter.kt b/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/ValidatableMockMvcResponseAdapter.kt index b836e6a..e996ec1 100644 --- a/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/ValidatableMockMvcResponseAdapter.kt +++ b/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/ValidatableMockMvcResponseAdapter.kt @@ -14,17 +14,17 @@ class ValidatableMockMvcResponseAdapter private constructor( return this } - fun status(status: HttpStatus): ValidatableMockResponse { + override fun status(status: HttpStatus): ValidatableMockResponse { restAssuredMockResponse.statusCode(status.value()) return this } - fun apply(handler: ResultHandler, vararg additionalHandlers: ResultHandler): ValidatableMockResponse { + override fun apply(handler: ResultHandler, vararg additionalHandlers: ResultHandler): ValidatableMockResponse { restAssuredMockResponse.apply(handler, *additionalHandlers) return this } - fun assertThat(matcher: ResultMatcher): ValidatableMockResponse { + override fun assertThat(matcher: ResultMatcher): ValidatableMockResponse { restAssuredMockResponse.assertThat(matcher) return this } diff --git a/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/ValidatableMockResponse.kt b/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/ValidatableMockResponse.kt index a0c5d84..5e436a5 100644 --- a/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/ValidatableMockResponse.kt +++ b/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/ValidatableMockResponse.kt @@ -1,9 +1,17 @@ package io.github.bgmsound.documentify.mvc +import org.springframework.http.HttpStatus +import org.springframework.test.web.servlet.ResultHandler import org.springframework.test.web.servlet.ResultMatcher interface ValidatableMockResponse { fun expect(matcher: ResultMatcher): ValidatableMockResponse + fun status(status: HttpStatus): ValidatableMockResponse + + fun apply(handler: ResultHandler, vararg additionalHandlers: ResultHandler): ValidatableMockResponse + + fun assertThat(matcher: ResultMatcher): ValidatableMockResponse + } \ No newline at end of file diff --git a/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/emitter/AbstractMvcDocumentEmitter.kt b/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/emitter/AbstractMvcDocumentEmitter.kt index f3ed1a8..903ebce 100644 --- a/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/emitter/AbstractMvcDocumentEmitter.kt +++ b/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/emitter/AbstractMvcDocumentEmitter.kt @@ -4,6 +4,7 @@ import io.github.bgmsound.documentify.core.emitter.AbstractDocumentEmitter import io.github.bgmsound.documentify.core.specification.schema.document.DocumentSpec import io.github.bgmsound.documentify.mvc.ValidatableMockMvcResponseAdapter import io.github.bgmsound.documentify.mvc.ValidatableMockResponse +import io.github.bgmsound.documentify.mvc.emitter.MvcDocumentResult.Companion.validateWith import io.restassured.module.mockmvc.response.ValidatableMockMvcResponse import org.springframework.restdocs.RestDocumentationContextProvider @@ -12,10 +13,11 @@ abstract class AbstractMvcDocumentEmitter( documentSpec: DocumentSpec ) : AbstractDocumentEmitter(provider, documentSpec), MvcDocumentEmitter { override fun emit(): ValidatableMockResponse { - val mockResponseSpec = emitDocument() + val documentResult = ValidatableMockMvcResponseAdapter.of(emitDocument()) + documentResult.validateWith(documentSpec.response) emitAlternativeResponseDocument() - return ValidatableMockMvcResponseAdapter.of(mockResponseSpec) + return documentResult } abstract fun emitDocument(): ValidatableMockMvcResponse diff --git a/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/emitter/AlternativeMvcResponseDocumentController.kt b/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/emitter/AlternativeMvcResponseDocumentController.kt index ff7871d..75d3476 100644 --- a/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/emitter/AlternativeMvcResponseDocumentController.kt +++ b/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/emitter/AlternativeMvcResponseDocumentController.kt @@ -6,39 +6,34 @@ import org.springframework.web.bind.annotation.* @RestController @RequestMapping("/**") class AlternativeMvcResponseDocumentController private constructor( - private val status: Int, - private val response: Any + status: Int, + response: Any ) { + private val response = response as? ResponseEntity<*> ?: ResponseEntity.status(status).body(response) + @GetMapping fun get(): Any { - return response() + return response } @PostMapping fun post(): Any { - return response() + return response } @PutMapping fun put(): Any { - return response() + return response } @DeleteMapping fun delete(): Any { - return response() + return response } @PatchMapping fun patch(): Any { - return response() - } - - private fun response(): Any { - if (response is ResponseEntity<*>) { - return response - } - return ResponseEntity.status(status).body(response) + return response } companion object { diff --git a/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/emitter/EmitterFactory.kt b/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/emitter/EmitterFactory.kt index 61a05f2..c1d0241 100644 --- a/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/emitter/EmitterFactory.kt +++ b/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/emitter/EmitterFactory.kt @@ -5,7 +5,7 @@ import io.github.bgmsound.documentify.mvc.MvcDocumentContextEnvironment import org.springframework.restdocs.RestDocumentationContextProvider object EmitterFactory { - fun createMvcEmitter( + fun of( provider: RestDocumentationContextProvider, documentSpec: DocumentSpec, environment: MvcDocumentContextEnvironment diff --git a/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/emitter/MvcDocumentResult.kt b/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/emitter/MvcDocumentResult.kt new file mode 100644 index 0000000..b6dbefe --- /dev/null +++ b/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/emitter/MvcDocumentResult.kt @@ -0,0 +1,34 @@ +package io.github.bgmsound.documentify.mvc.emitter + +import io.github.bgmsound.documentify.core.emitter.AbstractDocumentResult +import io.github.bgmsound.documentify.core.emitter.JsonResultMatcher +import io.github.bgmsound.documentify.core.specification.schema.response.ResponseSpec +import io.github.bgmsound.documentify.mvc.ValidatableMockResponse +import org.hamcrest.Matchers +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath + +class MvcDocumentResult( + private val actualResponse: ValidatableMockResponse +) : AbstractDocumentResult() { + override fun validateJsonPath(jsonResultMatcher: JsonResultMatcher) { + val jsonPath = jsonResultMatcher.jsonPath + val value = jsonResultMatcher.expectedValue + + if (jsonPath.endsWith("[*]")) { + if (value !is List<*>) { + throw IllegalArgumentException("sample value type must be List") + } + actualResponse.expect(jsonPath(jsonPath.substringBeforeLast("[*]")).value(Matchers.containsInAnyOrder(*value.toTypedArray()))) + } else if (jsonPath.contains("[*]") && !jsonPath.endsWith("[*]")) { + actualResponse.expect(jsonPath(jsonPath).value(Matchers.hasItem(value))) + } else { + actualResponse.expect(jsonPath(jsonPath).value(value)) + } + } + + companion object { + fun ValidatableMockResponse.validateWith(responseSpec: ResponseSpec) { + MvcDocumentResult(this).validateWith(responseSpec) + } + } +} \ No newline at end of file diff --git a/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/emitter/RestAssuredMvcDocumentEmitter.kt b/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/emitter/RestAssuredMvcDocumentEmitter.kt index fc0ba85..9a86e33 100644 --- a/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/emitter/RestAssuredMvcDocumentEmitter.kt +++ b/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/emitter/RestAssuredMvcDocumentEmitter.kt @@ -1,9 +1,6 @@ package io.github.bgmsound.documentify.mvc.emitter import com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document -import io.github.bgmsound.documentify.core.emitter.FieldJsonMatcherAssociater.associatedMatchers -import io.github.bgmsound.documentify.core.emitter.SpecElementSampleAssociater.associatedFieldSample -import io.github.bgmsound.documentify.core.emitter.SpecElementSampleAssociater.associatedSample import io.github.bgmsound.documentify.core.specification.schema.Method import io.github.bgmsound.documentify.core.specification.schema.document.DocumentSpec import io.github.bgmsound.documentify.mvc.MvcDocumentContextEnvironment @@ -12,11 +9,9 @@ import io.restassured.module.mockmvc.RestAssuredMockMvc.given import io.restassured.module.mockmvc.response.MockMvcResponse import io.restassured.module.mockmvc.response.ValidatableMockMvcResponse import io.restassured.module.mockmvc.specification.MockMvcRequestSpecification -import org.hamcrest.Matchers import org.springframework.restdocs.RestDocumentationContextProvider import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration import org.springframework.restdocs.operation.preprocess.Preprocessors.* -import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath import org.springframework.test.web.servlet.setup.MockMvcBuilders import org.springframework.test.web.servlet.setup.StandaloneMockMvcBuilder @@ -31,6 +26,11 @@ class RestAssuredMvcDocumentEmitter( override fun emitDocument(): ValidatableMockMvcResponse { val snippets = documentSpec.build() + val samplePathVariables = sampleAggregator.aggregate(documentSpec.request.pathVariables) + val sampleQueryParameters = sampleAggregator.aggregate(documentSpec.request.queryParameters) + val sampleHeaders = sampleAggregator.aggregate(documentSpec.request.headers) + val sampleFields = sampleAggregator.aggregate(documentSpec.request.fields) + val documentResultHandler = document( documentSpec.name, preprocessRequest(prettyPrint(), *requestPreprocessors), @@ -40,10 +40,10 @@ class RestAssuredMvcDocumentEmitter( val requestSpecification: MockMvcRequestSpecification = given().mockMvc(mockMvc) val response = requestSpecification .log().all() - .pathParams(documentSpec.request.pathVariables.associatedSample()) - .queryParams(documentSpec.request.queryParameters.associatedSample()) - .headers(documentSpec.request.headers.associatedSample()) - .bodyIfExists(documentSpec.request.fields.associatedFieldSample()) + .pathParams(samplePathVariables) + .queryParams(sampleQueryParameters) + .headers(sampleHeaders) + .bodyIfExists(sampleFields) .contentType(ContentType.JSON) .accept(ContentType.JSON) .request() @@ -53,23 +53,23 @@ class RestAssuredMvcDocumentEmitter( .assertThat() .apply(documentResultHandler) .statusCode(documentSpec.response.statusCode) - .validateExpectPayload() } override fun emitAlternativeResponseDocument() { documentSpec.otherResponses.forEachIndexed { index, response -> + val sampleResponseFields = sampleAggregator.aggregate(response.fields) val api = AlternativeMvcResponseDocumentController.new( response.statusCode, - response.fields.associatedFieldSample() + sampleResponseFields ) val mockMvc = MockMvcBuilders .standaloneSetup(api) .apply(documentationConfiguration(provider)) .build() val requestSpecification: MockMvcRequestSpecification = given().mockMvc(mockMvc) - + val samplePathVariables = sampleAggregator.aggregate(documentSpec.request.pathVariables) requestSpecification - .pathParams(documentSpec.request.pathVariables.associatedSample()) + .pathParams(samplePathVariables) .contentType(ContentType.JSON) .accept(ContentType.JSON) .request() @@ -103,21 +103,4 @@ class RestAssuredMvcDocumentEmitter( Method.DELETE -> delete(documentSpec.request.url) } } - - private fun ValidatableMockMvcResponse.validateExpectPayload(): ValidatableMockMvcResponse { - val matchers = documentSpec.response.fields.associatedMatchers() - for ((key, value) in matchers) { - if (key.endsWith("[*]")) { - if (value !is List<*>) { - throw IllegalArgumentException("sample value type must be List") - } - expect(jsonPath(key.substringBeforeLast("[*]")).value(Matchers.containsInAnyOrder(*value.toTypedArray()))) - } else if (key.contains("[*]") && !key.endsWith("[*]")) { - expect(jsonPath(key).value(Matchers.hasItem(value))) - } else { - expect(jsonPath(key).value(value)) - } - } - return this - } } \ No newline at end of file diff --git a/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/environment/StandaloneMvcContextEnvironment.kt b/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/environment/StandaloneMvcContextEnvironment.kt index 970e2bd..d36d5d6 100644 --- a/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/environment/StandaloneMvcContextEnvironment.kt +++ b/documentify-project/documentify-mvc/src/main/kotlin/io/github/bgmsound/documentify/mvc/environment/StandaloneMvcContextEnvironment.kt @@ -36,8 +36,8 @@ class StandaloneMvcContextEnvironment private constructor( .setControllerAdvice(*controllerAdvices.toTypedArray()) .setCustomArgumentResolvers(*argumentResolvers.toTypedArray()) .apply(documentationConfiguration(provider)) - .apply { if (objectMapper != null) { - val objectMapper = objectMapper!! + .apply { if (codec != null) { + val objectMapper = codec!! setMessageConverters(MappingJackson2HttpMessageConverter(objectMapper)) }} .build() diff --git a/documentify-project/documentify-plugin/src/main/kotlin/io/github/bgmsound/documentify/plugin/extensions/DocumentifyExtension.kt b/documentify-project/documentify-plugin/src/main/kotlin/io/github/bgmsound/documentify/plugin/extensions/DocumentifyExtension.kt deleted file mode 100644 index 3acc69e..0000000 --- a/documentify-project/documentify-plugin/src/main/kotlin/io/github/bgmsound/documentify/plugin/extensions/DocumentifyExtension.kt +++ /dev/null @@ -1,4 +0,0 @@ -package io.github.bgmsound.documentify.plugin.extensions - -interface DocumentifyExtension { -} \ No newline at end of file diff --git a/documentify-project/documentify-reactive/src/main/kotlin/io/github/bgmsound/documentify/reactive/Documentify.kt b/documentify-project/documentify-reactive/src/main/kotlin/io/github/bgmsound/documentify/reactive/Documentify.kt index 87d0a06..55582c1 100644 --- a/documentify-project/documentify-reactive/src/main/kotlin/io/github/bgmsound/documentify/reactive/Documentify.kt +++ b/documentify-project/documentify-reactive/src/main/kotlin/io/github/bgmsound/documentify/reactive/Documentify.kt @@ -2,6 +2,7 @@ package io.github.bgmsound.documentify.reactive import io.github.bgmsound.documentify.core.specification.schema.document.DocumentSpec import io.github.bgmsound.documentify.reactive.emitter.EmitterFactory +import io.github.bgmsound.documentify.reactive.emitter.ReactiveDocumentEmitter import io.github.bgmsound.documentify.reactive.environment.ApplicationContextEnvironment.Companion.applicationContextEnvironment import io.github.bgmsound.documentify.reactive.environment.StandaloneReactiveContextEnvironment import io.github.bgmsound.documentify.reactive.environment.StandaloneReactiveContextEnvironment.Companion.standaloneEnvironment @@ -16,24 +17,31 @@ import org.springframework.web.reactive.result.method.HandlerMethodArgumentResol @ExtendWith(RestDocumentationExtension::class) abstract class Documentify { private lateinit var provider: RestDocumentationContextProvider - private lateinit var documentContextEnvironment: ReactiveDocumentContextEnvironment + private lateinit var environment: ReactiveDocumentContextEnvironment + private var customEmitter: ReactiveDocumentEmitter? = null suspend fun documentation( name: String, specCustomizer: DocumentSpec.() -> Unit ): WebTestClient.BodyContentSpec { val documentSpec = DocumentSpec(name).also { specCustomizer(it) } - val emitter = EmitterFactory.createReactiveEmitter(provider, documentSpec, documentContextEnvironment) + val emitter = customEmitter ?: EmitterFactory.of(provider, documentSpec, environment) return emitter.emit() } + fun emitter( + customEmitter: ReactiveDocumentEmitter + ) { + this.customEmitter = customEmitter + } + fun webTestClient( provider: RestDocumentationContextProvider, webTestClient: WebTestClient ) { this.provider = provider - documentContextEnvironment = webTestClientEnvironment(provider, webTestClient) + environment = webTestClientEnvironment(provider, webTestClient) } fun standalone( @@ -41,7 +49,7 @@ abstract class Documentify { standaloneContext: StandaloneReactiveContextEnvironment ) { this.provider = provider - documentContextEnvironment = standaloneContext + environment = standaloneContext } fun standalone( @@ -70,6 +78,6 @@ abstract class Documentify { applicationContext: ApplicationContext ) { this.provider = provider - documentContextEnvironment = applicationContextEnvironment(provider, applicationContext) + environment = applicationContextEnvironment(provider, applicationContext) } } \ No newline at end of file diff --git a/documentify-project/documentify-reactive/src/main/kotlin/io/github/bgmsound/documentify/reactive/emitter/AbstractReactiveDocumentEmitter.kt b/documentify-project/documentify-reactive/src/main/kotlin/io/github/bgmsound/documentify/reactive/emitter/AbstractReactiveDocumentEmitter.kt index b149b3f..6bd7e72 100644 --- a/documentify-project/documentify-reactive/src/main/kotlin/io/github/bgmsound/documentify/reactive/emitter/AbstractReactiveDocumentEmitter.kt +++ b/documentify-project/documentify-reactive/src/main/kotlin/io/github/bgmsound/documentify/reactive/emitter/AbstractReactiveDocumentEmitter.kt @@ -2,6 +2,7 @@ package io.github.bgmsound.documentify.reactive.emitter import io.github.bgmsound.documentify.core.emitter.AbstractDocumentEmitter import io.github.bgmsound.documentify.core.specification.schema.document.DocumentSpec +import io.github.bgmsound.documentify.reactive.emitter.ReactiveDocumentResult.Companion.validateWith import org.springframework.restdocs.RestDocumentationContextProvider import org.springframework.test.web.reactive.server.WebTestClient.BodyContentSpec @@ -13,6 +14,7 @@ abstract class AbstractReactiveDocumentEmitter( override suspend fun emit(): BodyContentSpec { val validatableDocumentResponse = emitDocument() emitAlternativeResponseDocument() + validatableDocumentResponse.validateWith(documentSpec.response) return validatableDocumentResponse } diff --git a/documentify-project/documentify-reactive/src/main/kotlin/io/github/bgmsound/documentify/reactive/emitter/EmitterFactory.kt b/documentify-project/documentify-reactive/src/main/kotlin/io/github/bgmsound/documentify/reactive/emitter/EmitterFactory.kt index 01fb288..4587a82 100644 --- a/documentify-project/documentify-reactive/src/main/kotlin/io/github/bgmsound/documentify/reactive/emitter/EmitterFactory.kt +++ b/documentify-project/documentify-reactive/src/main/kotlin/io/github/bgmsound/documentify/reactive/emitter/EmitterFactory.kt @@ -5,7 +5,7 @@ import io.github.bgmsound.documentify.reactive.ReactiveDocumentContextEnvironmen import org.springframework.restdocs.RestDocumentationContextProvider object EmitterFactory { - fun createReactiveEmitter( + fun of( provider: RestDocumentationContextProvider, documentSpec: DocumentSpec, environment: ReactiveDocumentContextEnvironment diff --git a/documentify-project/documentify-reactive/src/main/kotlin/io/github/bgmsound/documentify/reactive/emitter/ReactiveDocumentResult.kt b/documentify-project/documentify-reactive/src/main/kotlin/io/github/bgmsound/documentify/reactive/emitter/ReactiveDocumentResult.kt new file mode 100644 index 0000000..76f9229 --- /dev/null +++ b/documentify-project/documentify-reactive/src/main/kotlin/io/github/bgmsound/documentify/reactive/emitter/ReactiveDocumentResult.kt @@ -0,0 +1,33 @@ +package io.github.bgmsound.documentify.reactive.emitter + +import io.github.bgmsound.documentify.core.emitter.AbstractDocumentResult +import io.github.bgmsound.documentify.core.emitter.JsonResultMatcher +import io.github.bgmsound.documentify.core.specification.schema.response.ResponseSpec +import org.hamcrest.Matchers +import org.springframework.test.web.reactive.server.WebTestClient + +class ReactiveDocumentResult( + private val actualResponse: WebTestClient.BodyContentSpec +) : AbstractDocumentResult() { + override fun validateJsonPath(jsonResultMatcher: JsonResultMatcher) { + val jsonPath = jsonResultMatcher.jsonPath + val value = jsonResultMatcher.expectedValue + + if (jsonPath.endsWith("[*]")) { + if (value !is List<*>) { + throw IllegalArgumentException("sample value type must be List") + } + actualResponse.jsonPath(jsonPath.substringBeforeLast("[*]")).value(Matchers.containsInAnyOrder(*value.toTypedArray())) + } else if (jsonPath.contains("[*]") && !jsonPath.endsWith("[*]")) { + actualResponse.jsonPath(jsonPath).value(Matchers.hasItem(value)) + } else { + actualResponse.jsonPath(jsonPath).value(Matchers.equalToObject(value)) + } + } + + companion object { + fun WebTestClient.BodyContentSpec.validateWith(responseSpec: ResponseSpec) { + ReactiveDocumentResult(this).validateWith(responseSpec) + } + } +} \ No newline at end of file diff --git a/documentify-project/documentify-reactive/src/main/kotlin/io/github/bgmsound/documentify/reactive/emitter/WebTestClientReactiveDocumentEmitter.kt b/documentify-project/documentify-reactive/src/main/kotlin/io/github/bgmsound/documentify/reactive/emitter/WebTestClientReactiveDocumentEmitter.kt index 36df2ac..e0171fa 100644 --- a/documentify-project/documentify-reactive/src/main/kotlin/io/github/bgmsound/documentify/reactive/emitter/WebTestClientReactiveDocumentEmitter.kt +++ b/documentify-project/documentify-reactive/src/main/kotlin/io/github/bgmsound/documentify/reactive/emitter/WebTestClientReactiveDocumentEmitter.kt @@ -1,13 +1,9 @@ package io.github.bgmsound.documentify.reactive.emitter -import io.github.bgmsound.documentify.core.emitter.FieldJsonMatcherAssociater.associatedMatchers -import io.github.bgmsound.documentify.core.emitter.SpecElementSampleAssociater.associatedFieldSample -import io.github.bgmsound.documentify.core.emitter.SpecElementSampleAssociater.associatedSample import io.github.bgmsound.documentify.core.specification.schema.Method import io.github.bgmsound.documentify.core.specification.schema.document.DocumentSpec import io.github.bgmsound.documentify.reactive.ReactiveDocumentContextEnvironment -import org.hamcrest.Matchers import org.springframework.http.HttpMethod import org.springframework.restdocs.RestDocumentationContextProvider import org.springframework.restdocs.operation.preprocess.Preprocessors.* @@ -30,13 +26,17 @@ class WebTestClientReactiveDocumentEmitter( override suspend fun emitDocument(): BodyContentSpec { val snippets = documentSpec.build() + val samplePathVariables = sampleAggregator.aggregate(documentSpec.request.pathVariables) + val sampleHeaders = sampleAggregator.aggregate(documentSpec.request.headers) + val sampleFields = sampleAggregator.aggregate(documentSpec.request.fields) + return webTestClient .method(method()) - .uri(uri(), documentSpec.request.pathVariables.associatedSample()) + .uri(requestUri, samplePathVariables) .headers { headers -> - headers.addAll(documentSpec.request.headers.associatedSample().toMultiValueMap()) + headers.addAll(sampleHeaders.toMultiValueMap()) } - .bodyIfExist(documentSpec.request.fields.associatedSample()) + .bodyIfExist(sampleFields) .exchange() .expectStatus() .isEqualTo(documentSpec.response.statusCode) @@ -52,24 +52,25 @@ class WebTestClientReactiveDocumentEmitter( *snippets.toTypedArray() ) ) - .validateExpectPayload() } override suspend fun emitAlternativeResponseDocument() { documentSpec.otherResponses.forEachIndexed { index, response -> + val sampleResponseFields = sampleAggregator.aggregate(response.fields) val api = AlternativeReactiveResponseDocumentController.new( response.statusCode, - response.fields.associatedFieldSample() + sampleResponseFields ) val webTestClient = WebTestClient .bindToController(api) .configureClient() .filter(WebTestClientRestDocumentation.documentationConfiguration(provider)) .build() + + val samplePathVariables = sampleAggregator.aggregate(documentSpec.request.pathVariables) webTestClient .method(method()) - .uri(uri(), documentSpec.request.pathVariables.associatedSample()) - .bodyIfExist(documentSpec.request.fields.associatedSample()) + .uri(requestUri, samplePathVariables) .exchange() .expectStatus() .isEqualTo(response.statusCode) @@ -86,6 +87,18 @@ class WebTestClientReactiveDocumentEmitter( } } + private val requestUri get(): String { + return StringBuilder().apply { + append(documentSpec.request.url) + if (documentSpec.request.queryParameters.isNotEmpty()) { + append("?") + append(documentSpec.request.queryParameters.joinToString("&") { parameter -> + "${parameter.key}=${parameter.sample}" + }) + } + }.toString() + } + private fun RequestBodySpec.bodyIfExist( fields: Map, ): WebTestClient.RequestHeadersSpec<*> = if (fields.isEmpty()) { @@ -111,33 +124,4 @@ class WebTestClientReactiveDocumentEmitter( } return linkedMultiValueMap } - - private fun uri(): String { - return StringBuilder().apply { - append(documentSpec.request.url) - if (documentSpec.request.queryParameters.isNotEmpty()) { - append("?") - append(documentSpec.request.queryParameters.joinToString("&") { parameter -> - "${parameter.key}=${parameter.sample}" - }) - } - }.toString() - } - - private fun BodyContentSpec.validateExpectPayload(): BodyContentSpec { - val matchers = documentSpec.response.fields.associatedMatchers() - for ((key, value) in matchers) { - if (key.endsWith("[*]")) { - if (value !is List<*>) { - throw IllegalArgumentException("sample value type must be List") - } - jsonPath(key.substringBeforeLast("[*]")).value(Matchers.containsInAnyOrder(*value.toTypedArray())) - } else if (key.contains("[*]") && !key.endsWith("[*]")) { - jsonPath(key).value(Matchers.hasItem(value)) - } else { - jsonPath(key).value(Matchers.equalToObject(value)) - } - } - return this - } } diff --git a/documentify-project/documentify-reactive/src/main/kotlin/io/github/bgmsound/documentify/reactive/environment/StandaloneReactiveContextEnvironment.kt b/documentify-project/documentify-reactive/src/main/kotlin/io/github/bgmsound/documentify/reactive/environment/StandaloneReactiveContextEnvironment.kt index 9fc3e5f..6b9220d 100644 --- a/documentify-project/documentify-reactive/src/main/kotlin/io/github/bgmsound/documentify/reactive/environment/StandaloneReactiveContextEnvironment.kt +++ b/documentify-project/documentify-reactive/src/main/kotlin/io/github/bgmsound/documentify/reactive/environment/StandaloneReactiveContextEnvironment.kt @@ -40,13 +40,13 @@ class StandaloneReactiveContextEnvironment private constructor( .argumentResolvers { configurer -> configurer.addCustomResolver(*argumentResolvers.toTypedArray()) } - .httpMessageCodecs { configurer -> if (objectMapper != null) { - val objectMapper = objectMapper!! + .httpMessageCodecs { configurer -> if (codec != null) { + val objectMapper = codec!! configurer.defaultCodecs().jackson2JsonDecoder(Jackson2JsonDecoder(objectMapper, MediaType.APPLICATION_JSON)) configurer.defaultCodecs().jackson2JsonEncoder(Jackson2JsonEncoder(objectMapper, MediaType.APPLICATION_JSON)) }} .configureClient() - .include(objectMapper) + .include(codec) .filter(WebTestClientRestDocumentation.documentationConfiguration(provider)) .build() } diff --git a/documentify-sample/build.gradle.kts b/documentify-sample/build.gradle.kts index 40938e6..a1e2c2d 100644 --- a/documentify-sample/build.gradle.kts +++ b/documentify-sample/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("io.github.bgmsound.documentify") version "1.2.4" apply false + id("io.github.bgmsound.documentify") version "1.2.5" apply false } subprojects { diff --git a/documentify-sample/mvc-sample/src/test/kotlin/io/github/bgmsound/documentify/sample/mvc/documentation/SnakeSampleDocs.kt b/documentify-sample/mvc-sample/src/test/kotlin/io/github/bgmsound/documentify/sample/mvc/documentation/SnakeSampleDocs.kt index a1394f1..c8e0a58 100644 --- a/documentify-sample/mvc-sample/src/test/kotlin/io/github/bgmsound/documentify/sample/mvc/documentation/SnakeSampleDocs.kt +++ b/documentify-sample/mvc-sample/src/test/kotlin/io/github/bgmsound/documentify/sample/mvc/documentation/SnakeSampleDocs.kt @@ -22,7 +22,7 @@ class SnakeSampleDocs : Documentify() { fun setUp(provider: RestDocumentationContextProvider) { standalone(provider) { controller(api) - objectMapper(objectMapper) + codec(objectMapper) } } diff --git a/documentify-sample/reactive-sample/src/main/kotlin/io/github/bgmsound/documentify/sample/reactive/SampleController.kt b/documentify-sample/reactive-sample/src/main/kotlin/io/github/bgmsound/documentify/sample/reactive/SampleController.kt index ae9f52d..16a8e85 100644 --- a/documentify-sample/reactive-sample/src/main/kotlin/io/github/bgmsound/documentify/sample/reactive/SampleController.kt +++ b/documentify-sample/reactive-sample/src/main/kotlin/io/github/bgmsound/documentify/sample/reactive/SampleController.kt @@ -14,6 +14,7 @@ class SampleController { @PathVariable("integerField") integerField: Int, @RequestParam("stringField") stringField: String ): SampleResponse { + "asfd" return Mono.just(SampleResponse(integerField, stringField)).awaitFirst() } diff --git a/documentify-sample/reactive-sample/src/test/kotlin/io/github/bgmsound/documentify/sample/reactive/documentation/SnakeSampleDocs.kt b/documentify-sample/reactive-sample/src/test/kotlin/io/github/bgmsound/documentify/sample/reactive/documentation/SnakeSampleDocs.kt index 02bdc66..54814c3 100644 --- a/documentify-sample/reactive-sample/src/test/kotlin/io/github/bgmsound/documentify/sample/reactive/documentation/SnakeSampleDocs.kt +++ b/documentify-sample/reactive-sample/src/test/kotlin/io/github/bgmsound/documentify/sample/reactive/documentation/SnakeSampleDocs.kt @@ -24,7 +24,7 @@ class SnakeSampleDocs : Documentify() { fun setUp(provider: RestDocumentationContextProvider) { standalone(provider) { controller(api) - objectMapper(objectMapper) + codec(objectMapper) } } diff --git a/gradle.properties b/gradle.properties index f23c2a9..669118d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ project.name=documentify project.group=io.github.bgmsound -project.version.id=1.2.5 +project.version.id=1.3.5 project.artifact=documentify project.description=Documentify is a tool to generate API documentation from source code. diff --git a/settings.gradle.kts b/settings.gradle.kts index 0079e46..9f75a59 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,7 +13,7 @@ plugins { include("documentify-project") include("documentify-project:documentify-core") -include("documentify-project:documentify-plugin") +include("documentify-project:documentify-gradle-plugin") include("documentify-project:documentify-mvc") include("documentify-project:documentify-reactive") include("documentify-sample")