[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