[RES-E003] - Added Enum value in Response contract

Rationale

Adding an enum value to a response parameter is backward incompatible as clients, using the “old” version of the Swagger specs, will not be able to properly validate the response.

Mitigation

A possible mitigation consists of modifying the endpoint to get the list of enum values supported for the specific request. This way we can ensure (at the business logic level, not on Swagger) that the response will not contain enum values that are not manageable by clients using “old” Swagger spec version.

Example

Old Swagger Specs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
swagger: '2.0'
info:
  title: Minimal Case of RES-E003 Rule
  version: '1.0'
paths:
  /endpoint:
    get:
      responses:
        default:
          description: ''
          schema:
            properties:
              property:
                type: string
                enum:
                - v1
            type: object
            x-model: get_endpoint_response

New Swagger Specs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
swagger: '2.0'
info:
  title: Minimal Case of RES-E003 Rule
  version: '1.0'
paths:
  /endpoint:
    get:
      responses:
        default:
          description: ''
          schema:
            properties:
              property:
                type: string
                enum:
                - v1
                - v2
            type: object
            x-model: get_endpoint_response

Backward Incompatibility

The following snippet triggers the incompatibility error.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals

from os.path import abspath

from bravado.client import SwaggerClient
from bravado_core.validate import validate_schema_object
from jsonschema import ValidationError
from six.moves.urllib.parse import urljoin
from six.moves.urllib.request import pathname2url

old_client = SwaggerClient.from_url(
    spec_url=urljoin('file:', pathname2url(abspath('old.yaml'))),
)
new_client = SwaggerClient.from_url(
    spec_url=urljoin('file:', pathname2url(abspath('new.yaml'))),
)

object_to_validate = {'property': 'v2'}

print('Validating the get endpoint response with the old client: Failed')
try:
    validate_schema_object(
        swagger_spec=old_client.swagger_spec,
        schema_object_spec=old_client.swagger_spec.definitions['get_endpoint_response']._model_spec,
        value=object_to_validate,
    )
    raise RuntimeError('An error was expected')
except ValidationError:
    pass

print('Validating the get endpoint response with the new client: Succeeded')
validate_schema_object(
    swagger_spec=new_client.swagger_spec,
    schema_object_spec=new_client.swagger_spec.definitions['get_endpoint_response']._model_spec,
    value=object_to_validate,
)

NOTE: The code is taking advantage of bravado