Description
When response is an array of an existing response type, the converter is not able to produce a value for the response body and crashes with this stacktrace
blog.svenbayer.springframework.cloud.contract.verifier.spec.swagger.exception.SwaggerContractConverterException: Could not parse body for response
at blog.svenbayer.springframework.cloud.contract.verifier.spec.swagger.builder.ResponseBodyBuilder.createValueForResponseBody(ResponseBodyBuilder.java:46)
at blog.svenbayer.springframework.cloud.contract.verifier.spec.swagger.SwaggerContractConverter.createResponse(SwaggerContractConverter.java:173)
at blog.svenbayer.springframework.cloud.contract.verifier.spec.swagger.SwaggerContractConverter.createContract(SwaggerContractConverter.java:99)
...
Here is the test and a json to reproduce this issue
array/array_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:
type: array
items:
$ref: '#/definitions/BeanPlanet'
definitions:
BeanPlanet:
type: object
properties:
name:
type: string
size:
type: integer
asteroids:
type: array
items:
$ref: '#/definitions/BeanAsteroid'
title: BeanPlanet
BeanAsteroid:
type: object
properties:
name:
type: string
speed:
type: integer
shape:
type: string
enum:
- 'ROUND'
- 'SQUARE'
- 'BEAN'
title: BeanAsteroids
ArraySwaggerContractSpec:
class ArraySwaggerContractSpec extends Specification {
@Subject
SwaggerContractConverter converter = new SwaggerContractConverter()
TestContractEquals testContractEquals = new TestContractEquals()
def "should convert array response swagger to contract"() {
given:
File singleSwaggerYaml = new File(SwaggerContractConverterSpec.getResource("/swagger/array/array_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("""[
{
"size" : 1,
"asteroids" : [ {
"shape" : "ROUND",
"name" : "name",
"speed" : 1
} ],
"name" : "name"
}
]""")
}
}
when:
Collection<Contract> contracts = converter.convertFrom(singleSwaggerYaml)
then:
testContractEquals.assertContractEquals(Collections.singleton(expectedContract), contracts)
}
}
As I see this, the easiest and hackiest way to fix this would be to get the array type, create and use a resolver for it and then append array brackets to the start and end of the string.
By adding this clause to ResponseBodyBuilder
...
else if(response.getResponseSchema() instanceof ArrayModel && ((ArrayModel)response.getResponseSchema()).getItems() instanceof RefProperty) {
String simpleRefName = ((RefProperty) ((ArrayModel) response.getResponseSchema()).getItems()).getSimpleRef();
SwaggerReferenceResolver resolver = this.refFactory.getReferenceResolver(simpleRefName, response.getVendorExtensions());
return "[" + resolver.resolveReference(definitions) + "]";
}
...
An even hackier alternative would a creation of a new array type, saving it to definitions and then pass it to the resolver, but this sounds very bad.
If this is to be fixed, it should be trivial to check if the same issues are present for request body builder and fix it.