springdoc-openapi-gradle-plugin icon indicating copy to clipboard operation
springdoc-openapi-gradle-plugin copied to clipboard

generateOpenApiDocs removes null values from the api-docs JSON

Open haapaan-vr opened this issue 6 months ago • 0 comments

I want to create the API spec without removing any fields, regardless if their value is null or not. But currently this plugin removes null values from API DTO examples.

You need to describe your context (the title of an issue is not enough)

I am trying to use generateOpenApiDocs to produce the JSON api-spec to disk

What version of spring-boot you are using?

3.4.0-SNAPSHOT

but problem exists also with Spring Boot 2.7

What modules and versions of springdoc-openapi are you using?

implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0'

What are the actual and the expected result using OpenAPI Description (yml or json)?

Actual:

{
  "openapi": "3.0.1",
  "info": {
    "title": "OpenAPI definition",
    "version": "v0"
  },
  "servers": [
    {
      "url": "http://localhost:8080",
      "description": "Generated server url"
    }
  ],
  "paths": {
    "/": {
      "get": {
        "tags": [
          "demo-controller"
        ],
        "summary": "Test API",
        "description": "This is a test api",
        "operationId": "hello",
        "responses": {
          "404": {
            "description": "Not Found",
            "content": {
              "*/*": {
                "example": {}
              }
            }
          }
        }
      },
...

Expected:

{
  "openapi": "3.0.1",
  "info": {
    "title": "OpenAPI definition",
    "version": "v0"
  },
  "servers": [
    {
      "url": "http://localhost:8080",
      "description": "Generated server url"
    }
  ],
  "paths": {
    "/": {
      "get": {
        "tags": [
          "demo-controller"
        ],
        "summary": "Test API",
        "description": "This is a test api",
        "operationId": "hello",
        "responses": {
          "404": {
            "description": "Not Found",
            "content": {
              "*/*": {
                "example": {
                  "errorMessage": null
                }
              }
            }
          }
        }
      },
...

Provide with a sample code (HelloController) or Test that reproduces the problem

To reproduce the problem, I created new Spring Boot app here: https://start.spring.io/

with

  • Project: Gradle - Groovy
  • Language: Java
  • Spring Boot 3.4.0-SNAPSHOT
  • Java 17
  • Packaging: jar

and modified build.gradle to this:

plugins {
	id 'java'
	id 'org.springframework.boot' version '3.4.0-SNAPSHOT'
	id 'io.spring.dependency-management' version '1.1.6'
    id 'org.springdoc.openapi-gradle-plugin' version '1.9.0'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'

java {
	toolchain {
		languageVersion = JavaLanguageVersion.of(17)
	}
}

repositories {
	mavenCentral()
	maven { url 'https://repo.spring.io/milestone' }
	maven { url 'https://repo.spring.io/snapshot' }
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0'

	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

}

tasks.named('test') {
	useJUnitPlatform()
}

I added DemoController.java:

package com.example.demo;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.models.OpenAPI;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class DemoController {

    @RequestMapping("/")
    @Operation(summary = "Test API", description = "This is a test api")
    @ApiResponses({
        @ApiResponse(responseCode = "404", content = @Content(examples = @ExampleObject(value = "{\"errorMessage\": null}")))
    })
    public String hello() {
        return "Hello world";
    }

    @Bean
    public OpenAPI customOpenAPI() {
        return new OpenAPI();
    }
}

and executed:

./gradlew generateOpenApiDocs

and API spec JSON was created under build/openapi.json, and that file has the problem.

Probable cause: I think the problem is in OpenApiGeneratorTask.kt that tries to prettyPrint the json:

	private fun prettifyJson(response: String): String {
		val gson = GsonBuilder().setPrettyPrinting().create()

The problem is that GsonBuilder has default NOT to serialize nulls. So a call to .serializeNulls() is missing when configuring the builder.

PS. It would be also great that pretty printing could be disabled altogether, to work around any future bugs related to that.

haapaan-vr avatar Aug 18 '24 12:08 haapaan-vr