-
Notifications
You must be signed in to change notification settings - Fork 10
Open
Description
When a response has a circular dependency in its definition, converter fails with a StackOverflowError. Stacktrace looks like this:
java.lang.StackOverflowError
at com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer$TableInfo.createInitial(CharsToNameCanonicalizer.java:809)
at com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer.<init>(CharsToNameCanonicalizer.java:243)
at com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer.createRoot(CharsToNameCanonicalizer.java:300)
at com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer.createRoot(CharsToNameCanonicalizer.java:296)
at com.fasterxml.jackson.core.JsonFactory.<init>(JsonFactory.java:191)
at com.fasterxml.jackson.databind.MappingJsonFactory.<init>(MappingJsonFactory.java:29)
at com.fasterxml.jackson.databind.ObjectMapper.<init>(ObjectMapper.java:549)
at com.fasterxml.jackson.databind.ObjectMapper.<init>(ObjectMapper.java:480)
// This is the loop start
at blog.svenbayer.springframework.cloud.contract.verifier.spec.swagger.builder.reference.SwaggerDefinitionsRefResolverSwagger.<init>(SwaggerDefinitionsRefResolverSwagger.java:25)
at blog.svenbayer.springframework.cloud.contract.verifier.spec.swagger.builder.reference.ReferenceResolverFactory.getReferenceResolver(ReferenceResolverFactory.java:32)
at blog.svenbayer.springframework.cloud.contract.verifier.spec.swagger.builder.ResponseHeaderValueBuilder.createResponseHeaderValue(ResponseHeaderValueBuilder.java:66)
at blog.svenbayer.springframework.cloud.contract.verifier.spec.swagger.builder.ResponseHeaderValueBuilder.createResponseHeaderValue(ResponseHeaderValueBuilder.java:74)
at blog.svenbayer.springframework.cloud.contract.verifier.spec.swagger.builder.reference.SwaggerDefinitionsRefResolverSwagger.lambda$resolveObjectDefinitionsRef$0(SwaggerDefinitionsRefResolverSwagger.java:110)
at java.base/java.util.stream.Collectors.lambda$uniqKeysMapAccumulator$1(Collectors.java:178)
at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
// This is the loop end
Here is the test and a json to reproduce this issue:
circular_dependency_swagger.yml
swagger: '2.0'
info:
title: COFFEE-ROCKET-SERVICE
description: A service that provides coffee bean rockets, bean planets, and other things the coffeeverse has to offer.
version: '1.0'
termsOfService: 'urn:tos'
contact: {}
license:
name: Apache 2.0
url: 'http://www.apache.org/licenses/LICENSE-2.0'
host: svenbayer.blog
schemes:
- https
- http
basePath: /coffee-rocket-service/v1.0
paths:
/takeoff:
post:
x-ignore: false
summary: Sends a coffee rocket to a bean planet and returns the bean planet.
description: API endpoint to send a coffee rocket to a bean planet and returns the bean planet.
consumes:
- application/json
produces:
- '*/*'
responses:
'201':
description: Created
schema:
$ref: '#/definitions/BeanPlanet'
definitions:
BeanPlanet:
type: object
properties:
neighbor_planets:
type: array
items:
$ref: '#/definitions/BeanPlanet'
title: BeanPlanetCircularDependencySwaggerContractSpec
class CircularDependencySwaggerContractSpec extends Specification {
@Subject
SwaggerContractConverter converter = new SwaggerContractConverter()
TestContractEquals testContractEquals = new TestContractEquals()
def "should convert swagger with circular dependency to contract"() {
given:
File singleSwaggerYaml = new File(SwaggerContractConverterSpec.getResource("/swagger/circular_dependency/circular_dependency_swagger.yml").toURI())
Contract expectedContract = Contract.make {
label("takeoff_coffee_bean_rocket")
name("1_takeoff_POST")
description("API endpoint to send a coffee rocket to a bean planet and returns the bean planet.")
priority(1)
request {
method(POST())
urlPath("/coffee-rocket-service/v1.0/takeoff")
}
response {
status(201)
body("""{
"neighbor_planets": [
null
]
}""")
}
}
when:
Collection<Contract> contracts = converter.convertFrom(singleSwaggerYaml)
then:
testContractEquals.assertContractEquals(Collections.singleton(expectedContract), contracts)
}
}Swagger UI solves this issue by setting the repeated item to null. Maybe it would be useful to let just one level to be generate, to show the contract more clearly, but any solution would work.
Metadata
Metadata
Assignees
Labels
No labels