Skip to content

Commit

Permalink
Fixed SpringConverter: Endpoints provide default media type for 'prod…
Browse files Browse the repository at this point in the history
…uces' if there is no return type (#24)
  • Loading branch information
cc-jhr committed Mar 11, 2019
1 parent eab2b43 commit d9647b4
Show file tree
Hide file tree
Showing 5 changed files with 556 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@ import org.springframework.web.bind.annotation.ResponseBody
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.method.HandlerMethod
import org.springframework.web.servlet.mvc.method.RequestMappingInfo
import java.lang.reflect.Method
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.instanceParameter
import kotlin.reflect.jvm.jvmErasure
import kotlin.reflect.jvm.kotlinFunction

internal fun Map.Entry<RequestMappingInfo, HandlerMethod>.produces(): Set<String> {
val isErrorPath = this.key.patternsCondition.patterns.contains("/error")
val isNotErrorPath = !this.key.patternsCondition.patterns.contains("/error")
val hasNoResponseBodyAnnotation = !this.value.providesResponseBodyAnnotation()
val hasNoRestControllerAnnotation = !this.value.providesRestControllerAnnotation()

if (!isErrorPath && !this.value.providesResponseBodyAnnotation() && !this.value.providesRestControllerAnnotation()) {
if (isNotErrorPath && ((hasNoResponseBodyAnnotation && hasNoRestControllerAnnotation) || this.value.method.hasNoReturnType())) {
return emptySet()
}

Expand Down Expand Up @@ -42,6 +45,8 @@ internal fun Map.Entry<RequestMappingInfo, HandlerMethod>.produces(): Set<String
}
}

private fun Method.hasNoReturnType() = this.returnType.name == "void" || this.returnType.name == "java.lang.Void"

private fun HandlerMethod.providesRestControllerAnnotation() = this.method
.kotlinFunction
?.instanceParameter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import org.springframework.web.bind.annotation.*
@SpringBootApplication
open class DummyApp

data class Todo(val description: String = "")

@Controller
@ResponseBody
@RequestMapping("/todos", produces = [APPLICATION_XML_VALUE])
Expand All @@ -35,51 +37,52 @@ open class RequestMappingMultipleMediaTypesAreInheritedByAllFunctionsController

@Controller
@ResponseBody
open class RequestMappingOneMediaTypeIsExtractedCorrectlyController {
@RequestMapping("/todos")
open class RequestMappingOnClassDefaultValueController {

@RequestMapping("/todos", produces = [APPLICATION_XML_VALUE])
fun getAllTodos() = ResponseEntity.status(200).body(RequestMappingOneMediaTypeIsExtractedCorrectlyController())
@RequestMapping
fun getAllTodos() = ResponseEntity.status(200).body(RequestMappingOnClassDefaultValueController())
}

@Controller
@ResponseBody
open class RequestMappingMultipleMediaTypesAreExtractedCorrectlyController {
@RequestMapping("/todos")
open class RequestMappingWithoutProducesOnClassInfoAndStringAsResponseBodyValueController {

@RequestMapping("/todos", produces = [APPLICATION_XML_VALUE, TEXT_PLAIN_VALUE])
fun getAllTodos() = ResponseEntity.status(200).body(RequestMappingMultipleMediaTypesAreExtractedCorrectlyController())
@RequestMapping
fun getAllTodos() = ""
}

@Controller
@ResponseBody
@RequestMapping("/todos", produces = [APPLICATION_XML_VALUE])
open class RequestMappingOneMediaTypeIsOverwrittenByDeclarationOnFunctionController {
@RequestMapping("/todos")
open class RequestMappingOnClassWithoutResponseBodyAnnotationController {

@RequestMapping(produces = [TEXT_PLAIN_VALUE])
fun getAllTodos() = ResponseEntity.status(200).body(RequestMappingOneMediaTypeIsOverwrittenByDeclarationOnFunctionController())
@RequestMapping
fun getAllTodos() { }
}

@RequestMapping("/{id}")
fun getSpecificTodo() = ResponseEntity.status(200).body(RequestMappingOneMediaTypeIsOverwrittenByDeclarationOnFunctionController())
@RestController
@RequestMapping("/todos")
open class RequestMappingOnClassNoProducesInfoAndNoReturnTypeController {

@RequestMapping
fun todos() { }
}

@Controller
@ResponseBody
@RequestMapping("/todos", produces = [APPLICATION_XML_VALUE, APPLICATION_XHTML_XML_VALUE])
open class RequestMappingMultipleMediaTypesAreOverwrittenByDeclarationOnFunctionController {

@RequestMapping(produces = [TEXT_PLAIN_VALUE, "application/pdf"])
fun getAllTodos() = ResponseEntity.status(200).body(RequestMappingMultipleMediaTypesAreOverwrittenByDeclarationOnFunctionController())
open class RequestMappingOneMediaTypeIsExtractedCorrectlyController {

@RequestMapping("/{id}")
fun getSpecificTodo() = ResponseEntity.status(200).body(RequestMappingMultipleMediaTypesAreOverwrittenByDeclarationOnFunctionController())
@RequestMapping("/todos", produces = [APPLICATION_XML_VALUE])
fun getAllTodos() = ResponseEntity.status(200).body(RequestMappingOneMediaTypeIsExtractedCorrectlyController())
}

@Controller
@ResponseBody
@RequestMapping("/todos")
open class RequestMappingOnClassDefaultValueController {
open class RequestMappingMultipleMediaTypesAreExtractedCorrectlyController {

@RequestMapping
fun getAllTodos() = ResponseEntity.status(200).body(RequestMappingOnClassDefaultValueController())
@RequestMapping("/todos", produces = [APPLICATION_XML_VALUE, TEXT_PLAIN_VALUE])
fun getAllTodos() = ResponseEntity.status(200).body(RequestMappingMultipleMediaTypesAreExtractedCorrectlyController())
}

@Controller
Expand All @@ -91,11 +94,11 @@ open class RequestMappingOnFunctionDefaultValueController {
}

@Controller
@RequestMapping("/todos")
open class RequestMappingOnClassWithoutResponseBodyAnnotationController {
@ResponseBody
open class RequestMappingWithoutProducesOnFunctionInfoAndStringAsResponseBodyValueController {

@RequestMapping
fun getAllTodos() { }
@RequestMapping("/todos")
fun getAllTodos() = ""
}

@Controller
Expand All @@ -105,21 +108,12 @@ open class RequestMappingOnFunctionWithoutResponseBodyAnnotationController {
fun getAllTodos() { }
}

@Controller
@ResponseBody
@RestController
@RequestMapping("/todos")
open class RequestMappingWithoutProducesOnClassInfoAndStringAsResponseBodyValueController {
open class RequestMappingOnFunctionNoProducesInfoAndNoReturnTypeController {

@RequestMapping
fun getAllTodos() = ""
}

@Controller
@ResponseBody
open class RequestMappingWithoutProducesOnFunctionInfoAndStringAsResponseBodyValueController {

@RequestMapping("/todos")
fun getAllTodos() = ""
fun todos() { }
}

@Controller
Expand Down Expand Up @@ -161,6 +155,13 @@ open class GetMappingWithoutResponseBodyAnnotationController {
fun getAllTodos() { }
}

@RestController
open class GetMappingNoProducesInfoAndNoReturnTypeController {

@GetMapping("/todos")
fun todos() { }
}

@Controller
@ResponseBody
open class DeleteMappingOneMediaTypeIsExtractedCorrectlyController {
Expand Down Expand Up @@ -200,6 +201,13 @@ open class DeleteMappingWithoutResponseBodyAnnotationController {
fun getAllTodos() { }
}

@RestController
open class DeleteMappingNoProducesInfoAndNoReturnTypeController {

@DeleteMapping("/todos")
fun todos() { }
}

@Controller
@ResponseBody
open class PatchMappingOneMediaTypeIsExtractedCorrectlyController {
Expand Down Expand Up @@ -239,6 +247,13 @@ open class PatchMappingWithoutResponseBodyAnnotationController {
fun getAllTodos() { }
}

@RestController
open class PatchMappingNoProducesInfoAndNoReturnTypeController {

@PatchMapping("/todos")
fun todos() { }
}

@Controller
@ResponseBody
open class PostMappingOneMediaTypeIsExtractedCorrectlyController {
Expand Down Expand Up @@ -278,6 +293,13 @@ open class PostMappingWithoutProducesInfoAndStringAsResponseBodyValueController
fun getAllTodos() = ""
}

@RestController
open class PostMappingNoProducesInfoAndNoReturnTypeController {

@PostMapping("/todos")
fun todos() { }
}

@Controller
@ResponseBody
open class PutMappingOneMediaTypeIsExtractedCorrectlyController {
Expand Down Expand Up @@ -317,13 +339,44 @@ open class PutMappingWithoutResponseBodyAnnotationController {
fun getAllTodos() { }
}

@RestController
open class PutMappingNoProducesInfoAndNoReturnTypeController {

@PutMapping("/todos")
fun todos() { }
}

@Controller
@ResponseBody
@RequestMapping("/todos", produces = [APPLICATION_XML_VALUE])
open class RequestMappingOneMediaTypeIsOverwrittenByDeclarationOnFunctionController {

@RequestMapping(produces = [TEXT_PLAIN_VALUE])
fun getAllTodos() = ResponseEntity.status(200).body(RequestMappingOneMediaTypeIsOverwrittenByDeclarationOnFunctionController())

@RequestMapping("/{id}")
fun getSpecificTodo() = ResponseEntity.status(200).body(RequestMappingOneMediaTypeIsOverwrittenByDeclarationOnFunctionController())
}

@Controller
@ResponseBody
@RequestMapping("/todos", produces = [APPLICATION_XML_VALUE, APPLICATION_XHTML_XML_VALUE])
open class RequestMappingMultipleMediaTypesAreOverwrittenByDeclarationOnFunctionController {

@RequestMapping(produces = [TEXT_PLAIN_VALUE, "application/pdf"])
fun getAllTodos() = ResponseEntity.status(200).body(RequestMappingMultipleMediaTypesAreOverwrittenByDeclarationOnFunctionController())

@RequestMapping("/{id}")
fun getSpecificTodo() = ResponseEntity.status(200).body(RequestMappingMultipleMediaTypesAreOverwrittenByDeclarationOnFunctionController())
}

@Controller
@ResponseBody
@RequestMapping(produces = [APPLICATION_XML_VALUE])
open class GetMappingOneMediaTypeIsOverwrittenController {

@GetMapping("/todos", produces = [TEXT_PLAIN_VALUE])
fun todos() { }
fun todos() = Todo()
}

@Controller
Expand All @@ -332,7 +385,7 @@ open class GetMappingOneMediaTypeIsOverwrittenController {
open class DeleteMappingOneMediaTypeIsOverwrittenController {

@DeleteMapping("/todos", produces = [TEXT_PLAIN_VALUE])
fun todos() { }
fun todos() = Todo()
}

@Controller
Expand All @@ -341,7 +394,7 @@ open class DeleteMappingOneMediaTypeIsOverwrittenController {
open class PatchMappingOneMediaTypeIsOverwrittenController {

@PatchMapping("/todos", produces = [TEXT_PLAIN_VALUE])
fun todos() { }
fun todos() = Todo()
}

@Controller
Expand All @@ -350,7 +403,7 @@ open class PatchMappingOneMediaTypeIsOverwrittenController {
open class PostMappingOneMediaTypeIsOverwrittenController {

@PostMapping("/todos", produces = [TEXT_PLAIN_VALUE])
fun todos() { }
fun todos() = Todo()
}

@Controller
Expand All @@ -359,7 +412,7 @@ open class PostMappingOneMediaTypeIsOverwrittenController {
open class PutMappingOneMediaTypeIsOverwrittenController {

@PutMapping("/todos", produces = [TEXT_PLAIN_VALUE])
fun todos() { }
fun todos() = Todo()
}

@Controller
Expand All @@ -368,7 +421,7 @@ open class PutMappingOneMediaTypeIsOverwrittenController {
open class GetMappingMultipleMediaTypesAreOverwrittenController {

@GetMapping("/todos", produces = [TEXT_PLAIN_VALUE, APPLICATION_PDF_VALUE])
fun todos() { }
fun todos() = Todo()
}

@Controller
Expand All @@ -377,7 +430,7 @@ open class GetMappingMultipleMediaTypesAreOverwrittenController {
open class DeleteMappingMultipleMediaTypesAreOverwrittenController {

@DeleteMapping("/todos", produces = [TEXT_PLAIN_VALUE, APPLICATION_PDF_VALUE])
fun todos() { }
fun todos() = Todo()
}

@Controller
Expand All @@ -386,7 +439,7 @@ open class DeleteMappingMultipleMediaTypesAreOverwrittenController {
open class PatchMappingMultipleMediaTypesAreOverwrittenController {

@PatchMapping("/todos", produces = [TEXT_PLAIN_VALUE, APPLICATION_PDF_VALUE])
fun todos() { }
fun todos() = Todo()
}

@Controller
Expand All @@ -395,7 +448,7 @@ open class PatchMappingMultipleMediaTypesAreOverwrittenController {
open class PostMappingMultipleMediaTypesAreOverwrittenController {

@PostMapping("/todos", produces = [TEXT_PLAIN_VALUE, APPLICATION_PDF_VALUE])
fun todos() { }
fun todos() = Todo()
}

@Controller
Expand All @@ -404,7 +457,7 @@ open class PostMappingMultipleMediaTypesAreOverwrittenController {
open class PutMappingMultipleMediaTypesAreOverwrittenController {

@PutMapping("/todos", produces = [TEXT_PLAIN_VALUE, APPLICATION_PDF_VALUE])
fun todos() { }
fun todos() = Todo()
}

@Controller
Expand Down
Loading

0 comments on commit d9647b4

Please sign in to comment.