[RES-E002] - Remove a required property from an object used in responses

Rationale

Removing a required property from an object leads to false expectation on the client receiving the object. If the client is using “old” service’s Swagger spec it will expect the property to be presentand so it could throw errors. It could be valid to assume that the client won’t perform response validation and this leads to unexpected errors while parsing the response and/or using the missing property.

Mitigation

A known mitigation to this issue consists of keeping the required property in the Swagger specs and ensuring that the service populate the field with a placeholder (that has to be valid with the property spec).

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-E002 Rule
  version: '1.0'
paths:
  /endpoint:
    get:
      responses:
        default:
          description: ''
          schema:
            properties:
              property:
                type: string
            required:
            - property
            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
swagger: '2.0'
info:
  title: Minimal Case of RES-E002 Rule
  version: '1.0'
paths:
  /endpoint:
    get:
      responses:
        default:
          description: ''
          schema:
            properties:
              property:
                type: string
            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 = {}

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