[MIS-E002] - Changed type

Rationale

Changing the type of a field is not backward compatible as a client using “old” Swagger specs will send the field with a different type leading the service to fail to validate the request. On the other end, if the object containing the updated field is used in the response, it will lead to unexpected client errors when parsing the response and/or using the updated property.

Mitigation

There are no best practices for this type of issue. The general recommended approach is: don’t do it and add a new field to the spec.

Example

Old Swagger Specs

 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
swagger: '2.0'
info:
  title: Minimal Case of MIS-E002 Rule
  version: '1.0'
paths:
  /endpoint:
    get:
      responses:
        default:
          description: ''
          schema:
            properties:
              property:
                type: string
            type: object
            x-model: get_endpoint_response
    post:
      parameters:
      - in: body
        name: body
        required: true
        schema:
          properties:
            property:
              type: string
          type: object
      responses:
        default:
          description: ''

New Swagger Specs

 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
swagger: '2.0'
info:
  title: Minimal Case of MIS-E002 Rule
  version: '1.0'
paths:
  /endpoint:
    get:
      responses:
        default:
          description: ''
          schema:
            properties:
              property:
                type: boolean
            type: object
            x-model: get_endpoint_response
    post:
      parameters:
      - in: body
        name: body
        required: true
        schema:
          properties:
            property:
              type: boolean
          type: object
      responses:
        default:
          description: ''

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
40
41
42
43
44
45
46
47
48
49
# -*- 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': 'True'}

print('Validating the get endpoint response with old client: Succeeded')
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,
)

print('Validating the get endpoint response with the new client: Failed')
try:
    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,
    )
    raise RuntimeError('An error was expected')
except ValidationError:
    pass


print('Calling the post endpoint with old client: Succeeded')
old_client.endpoint.post_endpoint(body=object_to_validate)

print('Calling the post endpoint with the new client: Failed')
try:
    new_client.endpoint.post_endpoint(body=object_to_validate)
except ValidationError:
    pass

NOTE: The code is taking advantage of bravado