springdoc / springdoc-openapi

Library for OpenAPI 3 with spring-boot
https://springdoc.org
Apache License 2.0
3.29k stars 499 forks source link

NPEs when using @ParameterGroup with custom Pageable and adding descriptions to fields #1333

Closed arjunchhabra-invitae closed 3 years ago

arjunchhabra-invitae commented 3 years ago

Describe the bug

To Reproduce Steps to reproduce the behavior:

in Controller.kt...

    @GetMapping
    fun getSomething(@ParameterObject example: ExampleClass) {

in ExampleClass.kt...

data class ExampleClass (
    @field:Parameter(description = "Anything")
    val something: Int = 0
) : Pageable {

    override fun getPageNumber(): Int {
        TODO("Not yet implemented")
    }

    override fun getPageSize(): Int {
        TODO("Not yet implemented")
    }

    override fun getOffset(): Long {
        TODO("Not yet implemented")
    }

    override fun getSort(): Sort {
        TODO("Not yet implemented")
    }

    override fun next(): Pageable {
        TODO("Not yet implemented")
    }

    override fun previousOrFirst(): Pageable {
        TODO("Not yet implemented")
    }

    override fun first(): Pageable {
        TODO("Not yet implemented")
    }

    override fun withPage(pageNumber: Int): Pageable {
        TODO("Not yet implemented")
    }

    override fun hasPrevious(): Boolean {
        TODO("Not yet implemented")
    }
}

in ExampleClass2.kt...

data class ExampleClass2 (
    @field:Parameter(description = "Anything")
    val something: Int = 0
)

The above will fail with 500. If you replace ExampleClass with ExampleClass2 in Controller.kt, the docs will generate and include the description.

bnasslahsen commented 3 years ago

@arjun-chhabra,

Not reproducible. Make sure you provide a minimal reproducible simple as described in the contribution guide. This ticket will be closed and can be reopened if the relevant information are provided.

arjunchhabra-invitae commented 3 years ago

Hi, the below code should reproduce the issue. If you update the argument in ExampleController.kt to use ExampleNonPageable instead of ExamplePageable, the issue will resolve.

Please let me know if anything is unclear and I will provide more context.

full build.gradle.kts

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    val kotlinVersion = "1.5.31"
    id("org.springframework.boot") version "2.5.5"
    id("io.spring.dependency-management") version "1.0.11.RELEASE"
    kotlin("jvm") version kotlinVersion
    // Makes all Spring annotated (@Service, @Configuration, etc) classes & properties open
    kotlin("plugin.spring") version kotlinVersion
    // Kotlin annotation processor
    kotlin("kapt") version kotlinVersion
    // Adds generateOpenApiDocs task
    id("org.springdoc.openapi-gradle-plugin") version "1.3.3"
    id("com.github.johnrengelman.processes") version "0.5.0"
}

group = "com.example"
java.sourceCompatibility = JavaVersion.VERSION_11

configurations {
    compileOnly {
        extendsFrom(configurations.annotationProcessor.get())
    }
}

repositories {
    mavenCentral()
}

dependencies {
    // DB drivers
    runtimeOnly("com.h2database:h2")

    // Spring Boot
    implementation("org.springframework.boot:spring-boot-starter-actuator")
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")

    // QueryDSL
    implementation("com.querydsl:querydsl-jpa")
    kapt(group = "com.querydsl", name = "querydsl-apt", classifier = "jpa")

    // OpenAPI
    implementation("org.springdoc:springdoc-openapi-ui:1.5.11")
    implementation("org.springdoc:springdoc-openapi-kotlin:1.5.11")
    // Expand Pageable into page, size, sort
    implementation("org.springdoc:springdoc-openapi-data-rest:1.5.11")
    // Pull documentation from kdocs
    implementation("org.springdoc:springdoc-openapi-javadoc:1.5.11")
    kapt("com.github.therapi:therapi-runtime-javadoc-scribe:0.12.0")
}

tasks.withType<KotlinCompile> {
    kotlinOptions {
        freeCompilerArgs = listOf("-Xjsr305=strict", "-Xjvm-default=all")
        jvmTarget = "11"
    }
}

ExampleController.kt


import ExampleNonPageable
import ExamplePageable
import org.springdoc.api.annotations.ParameterObject
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
@RequestMapping("v0/example")
class ExampleController(
) {
    @GetMapping
    fun getEndpointPage(@ParameterObject params: ExamplePageable) {

    }
}

ExamplePageable.kt

import io.swagger.v3.oas.annotations.Parameter
import org.springframework.data.domain.Pageable
import org.springframework.data.domain.Sort

data class ExamplePageable(
    @field:Parameter(description = "Anything")
    val something: Int = 0,
) : Pageable {
    override fun getPageNumber(): Int {
        TODO("Not yet implemented")
    }

    override fun getPageSize(): Int {
        TODO("Not yet implemented")
    }

    override fun getOffset(): Long {
        TODO("Not yet implemented")
    }

    override fun getSort(): Sort {
        TODO("Not yet implemented")
    }

    override fun next(): Pageable {
        TODO("Not yet implemented")
    }

    override fun previousOrFirst(): Pageable {
        TODO("Not yet implemented")
    }

    override fun first(): Pageable {
        TODO("Not yet implemented")
    }

    override fun withPage(pageNumber: Int): Pageable {
        TODO("Not yet implemented")
    }

    override fun hasPrevious(): Boolean {
        TODO("Not yet implemented")
    }
}

ExampleNonPageable.kt

data class ExampleNonPageable(
@field:Parameter(description = "Anything")
val something: Int = 0,
)

@bnasslahsen

arjunchhabra-invitae commented 3 years ago

@bnasslahsen

Here is a self contained reproducer project.

exampleProject.zip

If you stand up the server with ./gradlew bootRun and access the docs, you should receive an NPE and a HTTP500.

If you replace ExamplePageable with ExampleNonPageable in ExampleController, the issue will resolve and the docs will be generated as expected.

Thank you again for your attention into this matter and my apologies if I am simply misconfiguring something.

bnasslahsen commented 3 years ago

@arjun-chhabra,

This will be fixed with the next release. You can already validate the fix, using the latest SNAPSHOT.

arjunchhabra-invitae commented 3 years ago

Thank you for the quick turnaround on this @bnasslahsen! Much appreciated!