Description
Describe the bug
When using the annotation @ApiResponse
with a content and a mediatype, the schema of my response object is being replaced by a String (instead of the reference to the actual schema).
Example:
@GetMapping("/test")
@ResponseStatus(HttpStatus.OK)
@Operation(summary = "get", description = "Provides a response.")
@ApiResponse(content = @Content(mediaType = MediaTypes.HAL_JSON_VALUE,
schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = Response.class)),
responseCode = "200")
public Response get() {
return new Response("value");
}
This is due to a NullPointerException
occuring in the new HateoasLinksConverter
.
The stacktrace (not displayed as catched in SpringDocAnnotationsUtils
line 442) :
java.lang.NullPointerException: Cannot invoke "String.substring(int)" because the return value of "io.swagger.v3.oas.models.media.Schema.get$ref()" is null
at org.springdoc.core.converters.HateoasLinksConverter.resolve(HateoasLinksConverter.java:74)
at org.springdoc.core.converters.PageableOpenAPIConverter.resolve(PageableOpenAPIConverter.java:97)
at org.springdoc.core.converters.PageOpenAPIConverter.resolve(PageOpenAPIConverter.java:102)
at org.springdoc.core.converters.SortOpenAPIConverter.resolve(SortOpenAPIConverter.java:92)
at io.swagger.v3.core.converter.ModelConverterContextImpl.resolve(ModelConverterContextImpl.java:97)
at io.swagger.v3.core.jackson.ModelResolver.resolve(ModelResolver.java:493)
at org.springdoc.core.converters.WebFluxSupportConverter.resolve(WebFluxSupportConverter.java:89)
at org.springdoc.core.converters.AdditionalModelsConverter.resolve(AdditionalModelsConverter.java:163)
at org.springdoc.core.converters.FileSupportConverter.resolve(FileSupportConverter.java:72)
at org.springdoc.core.converters.ResponseSupportConverter.resolve(ResponseSupportConverter.java:84)
at org.springdoc.core.converters.SchemaPropertyDeprecatingConverter.resolve(SchemaPropertyDeprecatingConverter.java:95)
at org.springdoc.core.converters.PolymorphicModelConverter.resolve(PolymorphicModelConverter.java:141)
at org.springdoc.core.converters.OAS31ModelConverter.resolve(OAS31ModelConverter.java:49)
at org.springdoc.core.converters.CollectionModelContentConverter.resolve(CollectionModelContentConverter.java:84)
at org.springdoc.core.converters.HateoasLinksConverter.resolve(HateoasLinksConverter.java:87)
at org.springdoc.core.converters.PageableOpenAPIConverter.resolve(PageableOpenAPIConverter.java:97)
at org.springdoc.core.converters.PageOpenAPIConverter.resolve(PageOpenAPIConverter.java:102)
at org.springdoc.core.converters.SortOpenAPIConverter.resolve(SortOpenAPIConverter.java:92)
at io.swagger.v3.core.converter.ModelConverterContextImpl.resolve(ModelConverterContextImpl.java:97)
at io.swagger.v3.core.jackson.ModelResolver.resolve(ModelResolver.java:745)
at org.springdoc.core.converters.WebFluxSupportConverter.resolve(WebFluxSupportConverter.java:89)
at org.springdoc.core.converters.AdditionalModelsConverter.resolve(AdditionalModelsConverter.java:163)
at org.springdoc.core.converters.FileSupportConverter.resolve(FileSupportConverter.java:72)
at org.springdoc.core.converters.ResponseSupportConverter.resolve(ResponseSupportConverter.java:84)
at org.springdoc.core.converters.SchemaPropertyDeprecatingConverter.resolve(SchemaPropertyDeprecatingConverter.java:95)
at org.springdoc.core.converters.PolymorphicModelConverter.resolve(PolymorphicModelConverter.java:141)
at org.springdoc.core.converters.OAS31ModelConverter.resolve(OAS31ModelConverter.java:49)
at org.springdoc.core.converters.CollectionModelContentConverter.resolve(CollectionModelContentConverter.java:84)
at org.springdoc.core.converters.HateoasLinksConverter.resolve(HateoasLinksConverter.java:73)
at org.springdoc.core.converters.PageableOpenAPIConverter.resolve(PageableOpenAPIConverter.java:97)
at org.springdoc.core.converters.PageOpenAPIConverter.resolve(PageOpenAPIConverter.java:102)
at org.springdoc.core.converters.SortOpenAPIConverter.resolve(SortOpenAPIConverter.java:92)
at io.swagger.v3.core.converter.ModelConverterContextImpl.resolve(ModelConverterContextImpl.java:97)
at io.swagger.v3.core.converter.ModelConverters.resolveAsResolvedSchema(ModelConverters.java:192)
at org.springdoc.core.utils.SpringDocAnnotationsUtils.extractSchema(SpringDocAnnotationsUtils.java:137)
at org.springdoc.core.service.GenericResponseService.calculateSchema(GenericResponseService.java:563)
at org.springdoc.core.service.GenericResponseService.buildContent(GenericResponseService.java:541)
at org.springdoc.core.service.GenericResponseService.buildContent(GenericResponseService.java:522)
at org.springdoc.core.service.GenericResponseService.buildApiResponses(GenericResponseService.java:595)
at org.springdoc.core.service.GenericResponseService.buildApiResponses(GenericResponseService.java:469)
at org.springdoc.core.service.GenericResponseService.build(GenericResponseService.java:264)
at org.springdoc.api.AbstractOpenApiResource.calculatePath(AbstractOpenApiResource.java:626)
at org.springdoc.api.AbstractOpenApiResource.calculatePath(AbstractOpenApiResource.java:816)
at org.springdoc.webmvc.api.OpenApiResource.lambda$calculatePath$11(OpenApiResource.java:222)
at java.base/java.util.Optional.ifPresent(Optional.java:178)
at org.springdoc.webmvc.api.OpenApiResource.calculatePath(OpenApiResource.java:203)
at org.springdoc.webmvc.api.OpenApiResource.lambda$getPaths$2(OpenApiResource.java:173)
at java.base/java.util.Optional.ifPresent(Optional.java:178)
at org.springdoc.webmvc.api.OpenApiResource.getPaths(OpenApiResource.java:152)
at org.springdoc.api.AbstractOpenApiResource.getOpenApi(AbstractOpenApiResource.java:370)
at org.springdoc.webmvc.api.OpenApiResource.openapiJson(OpenApiResource.java:127)
at org.springdoc.webmvc.api.OpenApiWebMvcResource.openapiJson(OpenApiWebMvcResource.java:117)
How to reproduce
git clone https://github.com/didjoman/Springdoc-String-Schema-returned-mediatype-issue
Run the application (spring boot).
Run curl --location --request GET 'http://localhost:8080/api-docs' --header 'Content-Type: application/json'
Expected behavior
I expect to have the same OAS generated as before v2.8.2, that is to say:
{
"openapi": "3.1.0",
"info": {
"title": "OpenAPI definition",
"version": "v0"
},
"servers": [
{
"url": "http://localhost:8080",
"description": "Generated server url"
}
],
"paths": {
"/test": {
"get": {
"tags": [
"basic-controller"
],
"summary": "get",
"description": "Provides a response.",
"operationId": "get",
"responses": {
"200": {
"description": "OK",
"content": {
"application/hal+json": {
"schema": {
"$ref": "#/components/schemas/Response"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"Link": {
"type": "object",
"properties": {
"href": {
"type": "string"
},
"hreflang": {
"type": "string"
},
"title": {
"type": "string"
},
"type": {
"type": "string"
},
"deprecation": {
"type": "string"
},
"profile": {
"type": "string"
},
"name": {
"type": "string"
},
"templated": {
"type": "boolean"
}
}
},
"Response": {
"type": "object",
"description": "test description",
"properties": {
"value2": {
"type": "string"
},
"_links": {
"$ref": "#/components/schemas/Links"
}
}
},
"Links": {
"type": "object",
"additionalProperties": {
"$ref": "#/components/schemas/Link"
}
}
}
}
}
Actual behavior
Since v2.8.2 I have this, the Response
schema is not generated and its reference in the path is replaced by the type String:
{
"openapi": "3.1.0",
"info": {
"title": "OpenAPI definition",
"version": "v0"
},
"servers": [
{
"url": "http://localhost:8080",
"description": "Generated server url"
}
],
"paths": {
"/test": {
"get": {
"tags": [
"basic-controller"
],
"summary": "get",
"description": "Provides a response.",
"operationId": "get",
"responses": {
"200": {
"description": "OK",
"content": {
"application/hal+json": {
"schema": {
"type": "string"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"Link": {
"type": "object",
"properties": {
"href": {
"type": "string"
},
"hreflang": {
"type": "string"
},
"title": {
"type": "string"
},
"type": {
"type": "string"
},
"deprecation": {
"type": "string"
},
"profile": {
"type": "string"
},
"name": {
"type": "string"
},
"templated": {
"type": "boolean"
}
}
},
"Links": {
"type": "object",
"additionalProperties": {
"$ref": "#/components/schemas/Link"
}
}
}
}
}
Additional context
The issue did not occur before v2.8.2, and it is still present in v2.8.4.
I think it may be related to this commit 88f5da0 introducing the HateoasLinkConverter. But it's not clear what should be done to fix it.
Maybe, the swagger-core.ModelResolver should be called with a new AnnotatedType(returnType) .resolveAsRef(true).jsonViewAnnotation(jsonView).ctxAnnotations(annotations)
as it is done in SpringDocAnnotationsUtils.extractSchema()
line 138 ? Because, here it gets called with resolveAsRef(false)
, then the ref
is not added to the schema at line ModelResolver.resolve()
line 1060 and then it breaks in HateoasLinksConverter.resolve()
line 74.
Thank you for your help :)