Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for nullable request parameters in Kotlin #2006

Closed
raphiz opened this issue Dec 15, 2022 · 4 comments
Closed

Support for nullable request parameters in Kotlin #2006

raphiz opened this issue Dec 15, 2022 · 4 comments
Labels
enhancement New feature or request question Further information is requested

Comments

@raphiz
Copy link

raphiz commented Dec 15, 2022

Is your feature request related to a problem? Please describe.

When using springdoc-openapi in a Kotlin code base, it is annoying that parameters with a nullable type have to be marked manually as optional (required = false):

@RestController
class MyController{
    @GetMapping("/")
    fun greet(@RequestParam(required = false) name: String?): String {
        if(name != null) return "Hello $name"
        return "Hello!"
    }
}

Describe the solution you'd like

It would be nice to just leave the required = false out and just write:

@RestController
class MyController{
    @GetMapping("/")
    fun greet(@RequestParam name: String?): String {
        if(name != null) return "Hello $name"
        return "Hello!"
    }
}

Describe alternatives you've considered

We were able to achieve this using a ParameterCustomizer. However, I believe that this should be part of springdoc-openapi instead.

    @Bean
    fun nullableKotlinRequestParameterCustomizer(): ParameterCustomizer {
        return ParameterCustomizer { parameterModel, methodParameter ->
            if (parameterModel == null) return@ParameterCustomizer null

            val kParameter = methodParameter.toKParameter()
            if (kParameter != null) {
                parameterModel.required = kParameter.type.isMarkedNullable == false
            }
            return@ParameterCustomizer parameterModel
        }
    }

    private fun MethodParameter.toKParameter(): KParameter? {
        // ignore return type, see org.springframework.core.MethodParameter.getParameterIndex
        if (parameterIndex == -1) return null
        val kotlinFunction = method?.kotlinFunction ?: return null

        // The first parameter of the kotlin function is the "this" reference and not needed here.
        // See also kotlin.reflect.KCallable.getParameters
        return kotlinFunction.parameters[parameterIndex + 1]
    }

PS: Thank you for your great work!

@bnasslahsen
Copy link
Contributor

bnasslahsen commented Dec 15, 2022

@raphiz,

Thank you for your sharing and encouragements!
Your expected behavior is already working out of the box with v1.6.13. can you please validate it ?

Maybe you can add a test case to show what is not working in your side ?

@bnasslahsen bnasslahsen added the question Further information is requested label Dec 16, 2022
@raphiz
Copy link
Author

raphiz commented Dec 19, 2022

Interesting! I created a simple sample project to reproduce it and - as you said - everything works out of the box. However, the same combination of versions (including springdoc 1.6.13) did not work when I configured it in our product.

The setup we use in our product is a bit special, because we do not have the actual controller implementations available when we generate the documentation, only the interfaces. We therefore generate dynamic proxies for each controller interface to be consumed by springdoc. I created a sample project to illustrate this behavior. This project demonstrates in a test, that it does not work for dynamic proxies.

If I'm correct, springdoc checks if the parameter is optional in the ParameterInfo class and uses MethodParameter#isOptional from Spring, which is aware of nullable Kotlin types. This works for normal controllers, but when using a proxy, isOptional returns false because the containing class (the proxy) is not a Kotlin class.

Everything else works perfectly fine by the way!

I was thinking about potential solutions, but I haven't really found something suitable here 🤔 ...

@bnasslahsen
Copy link
Contributor

bnasslahsen commented Dec 20, 2022

@raphiz,

Thank you for sharing your use case.
I have added your proposed ParameterCustomizer to the Kotlin module with few adaptations and a property to disable it if needed.

springdoc.nullable-request-parameter-enabled=false

@bnasslahsen bnasslahsen added the enhancement New feature or request label Dec 20, 2022
@raphiz
Copy link
Author

raphiz commented Dec 21, 2022

@bnasslahsen That's awesome, thank you 🎉

@springdoc springdoc locked as resolved and limited conversation to collaborators Dec 21, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants