Welcome to Swagger Spec Compatibility’s documentation!

swagger-spec-compatibility is a Yelp maintained library that provides tools to automatically detect the safety of Swagger/OpenAPI 2.0 specification changes with respect to backwards compatibility.

swagger-spec-compatibility aims to give developers confidence that their spec changes are safe and that clients built with previous versions of the Swagger spec can continue to communicate correctly.

Disclaimer

The library is not supposed to cover all the possible cases of backward incompatibility.
This is because OpenAPI 2.0 specifications are very expressive and flexible that leads to many cases of backward incompatibility.

The detection rules currently supported are built due to the need to cover common breaking changes (that we’ve experienced internally at Yelp) or support received from contributors.

If you’re experiencing breaking changes and you would have the tool help you figure it out before being late, feel free to open issues on the project. You can also open pull requests implementing the rules, we’re always open to contributors.

Errors description

Request Contract Changes

[REQ-E001] - Added Required Property in Request contract
Rationale

Adding a required property to an object used in requests leads client request to fail if the property is not present.

Mitigation

A possible mitigation consists of adding the property as optional with an associated default value. In this case, the client requests don’t fail to validate and the service can assume that the property is always set.

Example
Old 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 REQ-E001 Rule
  version: '1.0'
paths:
  /endpoint:
    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
swagger: '2.0'
info:
  title: Minimal Case of REQ-E001 Rule
  version: '1.0'
paths:
  /endpoint:
    post:
      parameters:
      - in: body
        name: body
        required: true
        schema:
          properties:
            property:
              type: string
          required:
          - property
          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
# -*- 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 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_send = {}

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

print('Calling the post endpoint with the new client: Failed')
try:
    new_client.endpoint.post_endpoint(body=object_to_send)
    raise RuntimeError('An error was expected')
except ValidationError:
    pass

NOTE: The code is taking advantage of bravado

[REQ-E002] - Removed Enum value from Request contract
Rationale

Removing an enum value from a request parameter is backward incompatible as a previously valid request will not be valid. This happens because a request containing the removed enum value, valid according to the “old” Swagger spec, is not valid according to the new specs.

Mitigation

There are no best practices for this type of issue. The general recommendation would be to avoid as much as possible this type of change.

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
swagger: '2.0'
info:
  title: Minimal Case of REQ-E002 Rule
  version: '1.0'
paths:
  /endpoint:
    post:
      parameters:
      - in: body
        name: body
        required: true
        schema:
          properties:
            property:
              type: string
              enum:
              - v1
              - v2
          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
swagger: '2.0'
info:
  title: Minimal Case of REQ-E002 Rule
  version: '1.0'
paths:
  /endpoint:
    post:
      parameters:
      - in: body
        name: body
        required: true
        schema:
          properties:
            property:
              type: string
              enum:
              - v1
          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
# -*- 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 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_send = {'property': 'v2'}

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

print('Calling the post endpoint with the new client: Failed')
try:
    new_client.endpoint.post_endpoint(body=object_to_send)
    raise RuntimeError('An error was expected')
except ValidationError:
    pass

NOTE: The code is taking advantage of bravado

[REQ-E003] - Removing properties from an object with additionalProperties set to False used as request parameter
Rationale

If the object is defined with additionalProperties set to False then the object will not allow presence of properties not defined on the properties section of the object definition. Removing a definition of an existing property makes objects sent from a client, that is using “old” Swagger specs, to the server be considered invalid by the backend.

Mitigation

A possible mitigation could be to not remove the property from the object schema, mark it as deprecated (mostly for documentation purposes) and make sure that your business logic fills the field with a placeholder.

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
swagger: '2.0'
info:
  title: Minimal Case of REQ-E003 Rule
  version: '1.0'
paths:
  /endpoint:
    post:
      parameters:
      - in: body
        name: body
        required: true
        schema:
          additionalProperties: false
          properties:
            property_1:
              type: string
            property_2:
              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
swagger: '2.0'
info:
  title: Minimal Case of REQ-E003 Rule
  version: '1.0'
paths:
  /endpoint:
    post:
      parameters:
      - in: body
        name: body
        required: true
        schema:
          additionalProperties: false
          properties:
            property_1:
              type: string
          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
# -*- 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 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_send = {'property_1': 'v1', 'property_2': 'v2'}

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

print('Calling the post endpoint with the old client: Failed')
try:
    new_client.endpoint.post_endpoint(body=object_to_send)
    raise RuntimeError('An error was expected')
except ValidationError:
    pass

NOTE: The code is taking advantage of bravado

Response Contract Changes

[RES-E001] - Added properties in an object with additionalProperties set to False used in response
Rationale

If the object is defined with additionalProperties set to False then the object will not allow presence of properties not defined on the properties section of the object definition. Adding a definition of a new property makes object sent from the server to the client be considered invalid by a client that is using “old” Swagger specs.

Mitigation

A general suggestion would be to avoid setting additionalProperties to False for request objects as this prevents backward compatible safe object modifications. A possible mitigation to this requires the implementation of a new endpoint that returns the new object schema.

NOTE: Implementing a new endpoint is usually cheap but comes with the complexity of handling multiple versions of similar endpoints.

Example
Old Swagger Specs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
swagger: '2.0'
info:
  title: Minimal Case of RES-E001 Rule
  version: '1.0'
paths:
  /endpoint:
    get:
      responses:
        default:
          description: ''
          schema:
            additionalProperties: false
            properties:
              property_1:
                type: string
            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-E001 Rule
  version: '1.0'
paths:
  /endpoint:
    get:
      responses:
        default:
          description: ''
          schema:
            additionalProperties: false
            properties:
              property_1:
                type: string
              property_2:
                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 = {'property_2': 'v1'}

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 old 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

[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

[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

Miscellaneous Changes

[MIS-E001] - Delete an endpoint
Rationale

A Swagger service should be only serve endpoints defined via Swagger specs. Once an endpoint is removed from the Swagger specs this implies that the endpoint is not served anymore and any call to it will fail (as there is no implementation for it; HTTP/400, HTTP/404 or HTTP/500 could be expected in this case).

Mitigation

There are no general approaches to make this change without risks. The main recommendations are: ensure that the endpoint is marked as deprecated and notify all the clients about the endpoint removal timeline ensure that the endpoint is not called anymore.

Note: this is not always possible, think about the case of public APIs used by mobile devices. In such case validate with the clients product owners that is possible to returns errors.

PS: be aware that the fact that an endpoint is not called anymore does not implies that it won’t be called in the future (maybe the obsolete client is just not used at the moment)

Example
Old Swagger Specs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
swagger: '2.0'
info:
  title: Minimal Case of MIS-E001 Rule
  version: '1.0'
paths:
  /endpoint:
    get:
      responses:
        default:
          description: ''
    post:
      responses:
        default:
          description: ''
New Swagger Specs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
swagger: '2.0'
info:
  title: Minimal Case of MIS-E001 Rule
  version: '1.0'
paths:
  /endpoint:
    get:
      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
# -*- 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 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'))),
)

print('Calling the post endpoint with the old client: Succeeded')
old_client.endpoint.post_endpoint()
print('Calling the post endpoint with the new client: Failed')
try:
    new_client.endpoint.post_endpoint()
    raise RuntimeError('An error was expected')
except AttributeError:
    pass

NOTE: The code is taking advantage of bravado

[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

API reference

swagger_spec_compatibility Package

cache Module
class swagger_spec_compatibility.cache.typed_lru_cache(maxsize=None)[source]

Bases: object

cached_function
maxsize
uncached_function
cli Module
swagger_spec_compatibility.cli.parser()[source]
class swagger_spec_compatibility.cli.common.CLIProtocol(*args, **kwargs)[source]

Bases: typing_extensions.Protocol

command = None
func = None
class swagger_spec_compatibility.cli.common.CLIRulesProtocol(*args, **kwargs)[source]

Bases: typing_extensions.Protocol

blacklist_rules = None
rules = None
swagger_spec_compatibility.cli.common.add_rule_discovery_argument(argument_parser)[source]
swagger_spec_compatibility.cli.common.add_rules_arguments(argument_parser)[source]
swagger_spec_compatibility.cli.common.cli_rules()[source]
swagger_spec_compatibility.cli.common.post_process_rules_cli_arguments(args)[source]
swagger_spec_compatibility.cli.common.pre_process_cli_to_discover_rules(argv=None)[source]
swagger_spec_compatibility.cli.common.rules(cli_args)[source]
swagger_spec_compatibility.cli.common.uri(param)[source]
swagger_spec_compatibility.cli.explain.add_sub_parser(subparsers)[source]
swagger_spec_compatibility.cli.explain.execute(cli_args)[source]
swagger_spec_compatibility.cli.info.add_sub_parser(subparsers)[source]
swagger_spec_compatibility.cli.info.execute(cli_args)[source]
swagger_spec_compatibility.cli.run.add_sub_parser(subparsers)[source]
swagger_spec_compatibility.cli.run.execute(cli_args)[source]
rules Module
swagger_spec_compatibility.rules.compatibility_status(old_spec, new_spec, rules=<swagger_spec_compatibility.rules._ALL_RULES object>)[source]
class swagger_spec_compatibility.rules.added_properties_in_response_objects_with_additional_properties_set_to_false.AddedPropertiesInResponseObjectsWithAdditionalPropertiesSetToFalse[source]

Bases: swagger_spec_compatibility.rules.common.BaseRule

description = 'If the object is defined with additionalProperties set to False then the object will not allow presence of properties not defined on the properties section of the object definition. Adding a definition of a new property makes object sent from the server to the client be considered invalid by a client that is using "old" Swagger specs.'
error_code = 'RES-E001'
error_level = 2
rule_type = 1
short_name = 'Added properties in an object with additionalProperties set to False used in response'
classmethod validate(left_spec, right_spec)[source]
class swagger_spec_compatibility.rules.added_required_property_in_request.AddedRequiredPropertyInRequest[source]

Bases: swagger_spec_compatibility.rules.common.BaseRule

description = 'Adding a required property to an object used in requests leads client request to fail if the property is not present.'
error_code = 'REQ-E001'
error_level = 2
rule_type = 0
short_name = 'Added Required Property in Request contract'
classmethod validate(left_spec, right_spec)[source]
class swagger_spec_compatibility.rules.added_enum_value_in_response.AddedEnumValueInRequest[source]

Bases: swagger_spec_compatibility.rules.common.BaseRule

description = '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.'
error_code = 'RES-E003'
error_level = 2
rule_type = 1
short_name = 'Added Enum value in Response contract'
classmethod validate(left_spec, right_spec)[source]
class swagger_spec_compatibility.rules.common.BaseRule[source]

Bases: object

description = None
error_code = None
error_level = None
classmethod explain()[source]
rule_type = None
short_name = None
classmethod validate(left_spec, right_spec)[source]
classmethod validation_message(reference)[source]
class swagger_spec_compatibility.rules.common.Level[source]

Bases: enum.IntEnum

An enumeration.

ERROR = 2
INFO = 0
WARNING = 1
class swagger_spec_compatibility.rules.common.RuleProtocol(*args, **kwargs)[source]

Bases: typing_extensions.Protocol

description = None
error_code = None
error_level = None
rule_type = None
short_name = None
classmethod validate(left_spec, right_spec)[source]
class swagger_spec_compatibility.rules.common.RuleRegistry[source]

Bases: abc.ABCMeta

static has_rule(rule_name)[source]
static rule(rule_name)[source]
static rule_names()[source]
static rules()[source]
class swagger_spec_compatibility.rules.common.RuleType[source]

Bases: enum.IntEnum

An enumeration.

MISCELLANEOUS = 2
REQUEST_CONTRACT = 0
RESPONSE_CONTRACT = 1
class swagger_spec_compatibility.rules.common.ValidationMessage[source]

Bases: swagger_spec_compatibility.rules.common._ValidationMessage

json_representation()[source]
string_representation()[source]

Helper method that allows to extract documentation link related to a given rule.

If the rule is implemented within swagger-spec-compatibility library then the documentation link will fall back to the “default” read-the-docs link

class swagger_spec_compatibility.rules.deleted_endpoint.DeletedEndpoint[source]

Bases: swagger_spec_compatibility.rules.common.BaseRule

description = 'An endpoint has been removed. This change is not backward compatible as holders of stale swagger specs (like old mobile Apps) could continue to call the removed endpoint and this will cause an HTTP error status code (usually an HTTP/400 or HTTP/404)'
error_code = 'MIS-E001'
error_level = 2
rule_type = 2
short_name = 'Delete Endpoint'
classmethod validate(left_spec, right_spec)[source]
class swagger_spec_compatibility.rules.changed_type.ChangedType[source]

Bases: swagger_spec_compatibility.rules.common.BaseRule

description = '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.'
error_code = 'MIS-E002'
error_level = 2
rule_type = 2
short_name = 'Changed type'
classmethod validate(left_spec, right_spec)[source]
class swagger_spec_compatibility.rules.removed_enum_value_from_request.RemovedEnumValueFromRequest[source]

Bases: swagger_spec_compatibility.rules.common.BaseRule

description = 'Removing an enum value from a request parameter is backward incompatible as a previously valid request will not be valid. This happens because a request containing the removed enum value, valid according to the "old" Swagger spec, is not valid according to the new specs.'
error_code = 'REQ-E002'
error_level = 2
rule_type = 0
short_name = 'Removed Enum value from Request contract'
classmethod validate(left_spec, right_spec)[source]
class swagger_spec_compatibility.rules.removed_properties_from_request_objects_with_additional_properties_set_to_false.RemovedPropertiesFromRequestObjectsWithAdditionalPropertiesSetToFalse[source]

Bases: swagger_spec_compatibility.rules.common.BaseRule

description = 'If the object is defined with additionalProperties set to False then the object will not allow presence of properties not defined on the properties section of the object definition. Removing a definition of an existing property makes objects sent from a client, that is using "old" Swagger specs, to the server be considered invalid by the backend.'
error_code = 'REQ-E003'
error_level = 2
rule_type = 0
short_name = 'Removing properties from an object with additionalProperties set to False used as request parameter'
classmethod validate(left_spec, right_spec)[source]
class swagger_spec_compatibility.rules.removed_required_property_from_response.RemovedRequiredPropertyFromResponse[source]

Bases: swagger_spec_compatibility.rules.common.BaseRule

description = '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 present and so it could throw errors. It could be valid to assume that the client won\'t perform response validation and this to unexpected errors while parsing the response and/or using the missing property.'
error_code = 'RES-E002'
error_level = 2
rule_type = 1
short_name = 'Removed Required Property from Response contract'
classmethod validate(left_spec, right_spec)[source]
spec_utils Module
class swagger_spec_compatibility.spec_utils.Endpoint[source]

Bases: swagger_spec_compatibility.spec_utils._Endpoint

static from_swagger_operation(operation)[source]
class swagger_spec_compatibility.spec_utils.HTTPVerb[source]

Bases: enum.Enum

An enumeration.

DELETE = 'delete'
GET = 'get'
HEAD = 'head'
OPTIONS = 'options'
PARAMETERS = 'parameters'
PATCH = 'patch'
POST = 'post'
PUT = 'put'
from_swagger_operation = <function HTTPVerb.from_swagger_operation>[source]
class swagger_spec_compatibility.spec_utils.StatusCodeSchema(status_code, mapping)

Bases: tuple

mapping

Alias for field number 1

status_code

Alias for field number 0

swagger_spec_compatibility.spec_utils.get_endpoints[source]
swagger_spec_compatibility.spec_utils.get_operation_mappings(old_spec, new_spec)[source]
swagger_spec_compatibility.spec_utils.get_operations[source]
swagger_spec_compatibility.spec_utils.get_properties(swagger_spec, schema)[source]
swagger_spec_compatibility.spec_utils.get_required_properties(swagger_spec, schema)[source]
swagger_spec_compatibility.spec_utils.iterate_on_responses_status_codes(old_operation, new_operation)[source]
swagger_spec_compatibility.spec_utils.load_spec_from_spec_dict(spec_dict)[source]
swagger_spec_compatibility.spec_utils.load_spec_from_uri[source]
util Module
class swagger_spec_compatibility.util.EntityMapping(old, new)[source]

Bases: typing.Generic

new
old
swagger_spec_compatibility.util.is_path_in_top_level_paths(top_level_paths, path)[source]
swagger_spec_compatibility.util.wrap(text, width=120, indent='')[source]
walkers Module
class swagger_spec_compatibility.walkers.NoValue[source]

Bases: object

class swagger_spec_compatibility.walkers.SchemaWalker(left_spec, right_spec, **kwargs)[source]

Bases: swagger_spec_compatibility.walkers.Walker

Walker aware of how a Swagger schema looks like.

The main difference between this walker and Walker is that this walker keeps in consideration some peculiarity of the swagger specs.

The walker implementation should never worry about dereferencing as the traversing is performed on the fully flattened and dereferenced specs

fix_parameter_path(path, original_path, value)[source]

Fix an eventual path present on the value returned by the walker.

The SwaggerAwareWalker modifies the indexing approach used for parameters due to the fact that parameters are defined as arrays and modifying their order would not change the semantic.

class swagger_spec_compatibility.walkers.Walker(left, right, **kwargs)[source]

Bases: typing.Generic

Generic Walker over two objects.

The abstract class strips away the details related to dictionary vs list iterations, path update etc.

dict_check(path, left_dict, right_dict)[source]

Compare the left and right content of path in case both objects are dictionaries.

list_check(path, left_list, right_list)[source]

Compare the left and right content of path in case both objects are list.

should_path_be_walked_through(path)[source]

Determine whether to traverse or interrupt traversal of a given path.

This method allows Walkers to skip traversal of area of the specs that are “not interesting”. This will allow to write simpler methods and to avoid needless traversing of the Specs.

value_check(path, left_value, right_value)[source]

Compare the left and right content of path in case the objects have different types or are not dictionaries or lists.

walk()[source]

Fully traverse the left and right objects. NOTE: the traversing is internally cached such that all the subsequent calls

to walk() are equivalent to an attribute access
swagger_spec_compatibility.walkers.format_path(path)[source]
class swagger_spec_compatibility.walkers.additional_properties.AdditionalPropertiesDiff[source]

Bases: swagger_spec_compatibility.walkers.additional_properties.AdditionalPropertiesDiff

fix_parameter_path(path, original_path)[source]
class swagger_spec_compatibility.walkers.additional_properties.AdditionalPropertiesDifferWalker(left_spec, right_spec, **kwargs)[source]

Bases: swagger_spec_compatibility.walkers.SchemaWalker

additionalPropertiesValue = None
dict_check(path, left_dict, right_dict)[source]

Compare the left and right content of path in case both objects are dictionaries.

diffs = None
left_spec = None
list_check(path, left_list, right_list)[source]

Compare the left and right content of path in case both objects are list.

right_spec = None
value_check(path, left_value, right_value)[source]

Compare the left and right content of path in case the objects have different types or are not dictionaries or lists.

class swagger_spec_compatibility.walkers.additional_properties.DiffType[source]

Bases: enum.Enum

An enumeration.

PROPERTIES = 0
VALUE = 1
class swagger_spec_compatibility.walkers.changed_types.ChangedTypesDiff[source]

Bases: swagger_spec_compatibility.walkers.changed_types.ChangedTypesDiff

fix_parameter_path(path, original_path)[source]
class swagger_spec_compatibility.walkers.changed_types.ChangedTypesDifferWalker(left_spec, right_spec)[source]

Bases: swagger_spec_compatibility.walkers.SchemaWalker

dict_check(path, left_dict, right_dict)[source]

Compare the left and right content of path in case both objects are dictionaries.

diffs = None
left_spec = None
list_check(path, left_list, right_list)[source]

Compare the left and right content of path in case both objects are list.

right_spec = None
value_check(path, left_value, right_value)[source]

Compare the left and right content of path in case the objects have different types or are not dictionaries or lists.

class swagger_spec_compatibility.walkers.enum_values.EnumValuesDiff[source]

Bases: swagger_spec_compatibility.walkers.enum_values.EnumValuesDiff

fix_parameter_path(path, original_path)[source]
class swagger_spec_compatibility.walkers.enum_values.EnumValuesDifferWalker(left_spec, right_spec, **kwargs)[source]

Bases: swagger_spec_compatibility.walkers.SchemaWalker

dict_check(path, left_dict, right_dict)[source]

Compare the left and right content of path in case both objects are dictionaries.

left_spec = None
list_check(path, left_list, right_list)[source]

Compare the left and right content of path in case both objects are list.

right_spec = None
value_check(path, left_value, right_value)[source]

Compare the left and right content of path in case the objects have different types or are not dictionaries or lists.

class swagger_spec_compatibility.walkers.request_parameters.RequestParametersWalker(left_spec, right_spec)[source]

Bases: swagger_spec_compatibility.walkers.SchemaWalker

dict_check(path, left_dict, right_dict)[source]

Compare the left and right content of path in case both objects are dictionaries.

fix_parameter_path(path, original_path, value)[source]

Fix an eventual path present on the value returned by the walker.

The SwaggerAwareWalker modifies the indexing approach used for parameters due to the fact that parameters are defined as arrays and modifying their order would not change the semantic.

left_spec = None
list_check(path, left_list, right_list)[source]

Compare the left and right content of path in case both objects are list.

right_spec = None
should_path_be_walked_through(path)[source]

Determine whether to traverse or interrupt traversal of a given path.

This method allows Walkers to skip traversal of area of the specs that are “not interesting”. This will allow to write simpler methods and to avoid needless traversing of the Specs.

value_check(path, left_value, right_value)[source]

Compare the left and right content of path in case the objects have different types or are not dictionaries or lists.

class swagger_spec_compatibility.walkers.required_properties.RequiredPropertiesDiff[source]

Bases: swagger_spec_compatibility.walkers.required_properties.RequiredPropertiesDiff

fix_parameter_path(path, original_path)[source]
class swagger_spec_compatibility.walkers.required_properties.RequiredPropertiesDifferWalker(left_spec, right_spec)[source]

Bases: swagger_spec_compatibility.walkers.SchemaWalker

dict_check(path, left_dict, right_dict)[source]

Compare the left and right content of path in case both objects are dictionaries.

diffs = None
left_spec = None
list_check(path, left_list, right_list)[source]

Compare the left and right content of path in case both objects are list.

right_spec = None
value_check(path, left_value, right_value)[source]

Compare the left and right content of path in case the objects have different types or are not dictionaries or lists.

class swagger_spec_compatibility.walkers.response_paths.ResponsePathsWalker(left_spec, right_spec)[source]

Bases: swagger_spec_compatibility.walkers.SchemaWalker

dict_check(path, left_dict, right_dict)[source]

Compare the left and right content of path in case both objects are dictionaries.

left_spec = None
list_check(path, left_list, right_list)[source]

Compare the left and right content of path in case both objects are list.

paths = None
right_spec = None
should_path_be_walked_through(path)[source]

Determine whether to traverse or interrupt traversal of a given path.

This method allows Walkers to skip traversal of area of the specs that are “not interesting”. This will allow to write simpler methods and to avoid needless traversing of the Specs.

value_check(path, left_value, right_value)[source]

Compare the left and right content of path in case the objects have different types or are not dictionaries or lists.

Changelog

1.2.1 (2020-01-07)

  • Blacklist bravado-core==5.16.0 as not compatible with the library (PR #18)

1.2.0 (2019-03-27)

  • Expose read-the-docs documentation links for rules defined by the library (PR #9)

1.1.0 (2019-03-07)

  • Add --blacklist-rules CLI option to ignore certain rules during the checks (PR #6)
  • Fixed SchemaWalker to properly combine old and new parameters during the spec traversal. (PR #7)
  • Simplified creation and registration of new rules (PR #8)

1.0.1 (2019-02-26)

  • Updated package metadata

1.0.0 (2019-02-26)

  • Initial library implementation with definition of the following rules:
    • [REQ-E001] - Added Required Property in Request contract
    • [REQ-E002] - Removed Enum value from Request contract
    • [REQ-E003] - Removing properties from an object with additionalProperties set to False used as request parameter
    • [RES-E001] - Added properties in an object with additionalProperties set to False used in response
    • [RES-E002] - Remove a required property from an object used in responses
    • [RES-E003] - Added Enum value in Response contract
    • [MIS-E001] - Delete an endpoint
    • [MIS-E002] - Changed type

Indices and tables