Skip to content

Converter fails when response is an array of known response types #20

Open
@joklek

Description

@joklek

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions