Skip to content

Commit efef989

Browse files
authored
Revert "handle 'Invalid Swagger Document' and refactor some validation into Swagger Editor constructor (#2263)" (#2346)
This reverts commit 59df2db.
1 parent 4f7649e commit efef989

30 files changed

+159
-166
lines changed

samtranslator/model/api/api_generator.py

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,6 @@ def __init__(
236236
self.template_conditions = template_conditions
237237
self.mode = mode
238238

239-
self.swagger_editor = SwaggerEditor(self.definition_body) if self.definition_body else None
240-
241239
def _construct_rest_api(self):
242240
"""Constructs and returns the ApiGateway RestApi.
243241
@@ -284,7 +282,7 @@ def _construct_rest_api(self):
284282
rest_api.BodyS3Location = self._construct_body_s3_dict()
285283
elif self.definition_body:
286284
# # Post Process OpenApi Auth Settings
287-
self.definition_body = self._openapi_postprocess(self.swagger_editor.swagger)
285+
self.definition_body = self._openapi_postprocess(self.definition_body)
288286
rest_api.Body = self.definition_body
289287

290288
if self.name:
@@ -310,7 +308,9 @@ def _add_endpoint_extension(self):
310308
raise InvalidResourceException(
311309
self.logical_id, "DisableExecuteApiEndpoint works only within 'DefinitionBody' property."
312310
)
313-
self.swagger_editor.add_disable_execute_api_endpoint_extension(self.disable_execute_api_endpoint)
311+
editor = SwaggerEditor(self.definition_body)
312+
editor.add_disable_execute_api_endpoint_extension(self.disable_execute_api_endpoint)
313+
self.definition_body = editor.swagger
314314

315315
def _construct_body_s3_dict(self):
316316
"""Constructs the RestApi's `BodyS3Location property`_, from the SAM Api's DefinitionUri property.
@@ -626,6 +626,13 @@ def _add_cors(self):
626626
else:
627627
raise InvalidResourceException(self.logical_id, INVALID_ERROR)
628628

629+
if not SwaggerEditor.is_valid(self.definition_body):
630+
raise InvalidResourceException(
631+
self.logical_id,
632+
"Unable to add Cors configuration because "
633+
"'DefinitionBody' does not contain a valid Swagger definition.",
634+
)
635+
629636
if properties.AllowCredentials is True and properties.AllowOrigin == _CORS_WILDCARD:
630637
raise InvalidResourceException(
631638
self.logical_id,
@@ -634,9 +641,10 @@ def _add_cors(self):
634641
"'AllowOrigin' is \"'*'\" or not set",
635642
)
636643

637-
for path in self.swagger_editor.iter_on_path():
644+
editor = SwaggerEditor(self.definition_body)
645+
for path in editor.iter_on_path():
638646
try:
639-
self.swagger_editor.add_cors(
647+
editor.add_cors(
640648
path,
641649
properties.AllowOrigin,
642650
properties.AllowHeaders,
@@ -647,6 +655,9 @@ def _add_cors(self):
647655
except InvalidTemplateException as ex:
648656
raise InvalidResourceException(self.logical_id, ex.message)
649657

658+
# Assign the Swagger back to template
659+
self.definition_body = editor.swagger
660+
650661
def _add_binary_media_types(self):
651662
"""
652663
Add binary media types to Swagger
@@ -659,7 +670,11 @@ def _add_binary_media_types(self):
659670
if self.binary_media and not self.definition_body:
660671
return
661672

662-
self.swagger_editor.add_binary_media_types(self.binary_media)
673+
editor = SwaggerEditor(self.definition_body)
674+
editor.add_binary_media_types(self.binary_media)
675+
676+
# Assign the Swagger back to template
677+
self.definition_body = editor.swagger
663678

664679
def _add_auth(self):
665680
"""
@@ -678,31 +693,40 @@ def _add_auth(self):
678693
if not all(key in AuthProperties._fields for key in self.auth.keys()):
679694
raise InvalidResourceException(self.logical_id, "Invalid value for 'Auth' property")
680695

696+
if not SwaggerEditor.is_valid(self.definition_body):
697+
raise InvalidResourceException(
698+
self.logical_id,
699+
"Unable to add Auth configuration because "
700+
"'DefinitionBody' does not contain a valid Swagger definition.",
701+
)
702+
swagger_editor = SwaggerEditor(self.definition_body)
681703
auth_properties = AuthProperties(**self.auth)
682704
authorizers = self._get_authorizers(auth_properties.Authorizers, auth_properties.DefaultAuthorizer)
683705

684706
if authorizers:
685-
self.swagger_editor.add_authorizers_security_definitions(authorizers)
707+
swagger_editor.add_authorizers_security_definitions(authorizers)
686708
self._set_default_authorizer(
687-
self.swagger_editor,
709+
swagger_editor,
688710
authorizers,
689711
auth_properties.DefaultAuthorizer,
690712
auth_properties.AddDefaultAuthorizerToCorsPreflight,
691713
auth_properties.Authorizers,
692714
)
693715

694716
if auth_properties.ApiKeyRequired:
695-
self.swagger_editor.add_apikey_security_definition()
696-
self._set_default_apikey_required(self.swagger_editor)
717+
swagger_editor.add_apikey_security_definition()
718+
self._set_default_apikey_required(swagger_editor)
697719

698720
if auth_properties.ResourcePolicy:
699721
SwaggerEditor.validate_is_dict(
700722
auth_properties.ResourcePolicy, "ResourcePolicy must be a map (ResourcePolicyStatement)."
701723
)
702-
for path in self.swagger_editor.iter_on_path():
703-
self.swagger_editor.add_resource_policy(auth_properties.ResourcePolicy, path, self.stage_name)
724+
for path in swagger_editor.iter_on_path():
725+
swagger_editor.add_resource_policy(auth_properties.ResourcePolicy, path, self.stage_name)
704726
if auth_properties.ResourcePolicy.get("CustomStatements"):
705-
self.swagger_editor.add_custom_statements(auth_properties.ResourcePolicy.get("CustomStatements"))
727+
swagger_editor.add_custom_statements(auth_properties.ResourcePolicy.get("CustomStatements"))
728+
729+
self.definition_body = self._openapi_postprocess(swagger_editor.swagger)
706730

707731
def _construct_usage_plan(self, rest_api_stage=None):
708732
"""Constructs and returns the ApiGateway UsagePlan, ApiGateway UsagePlanKey, ApiGateway ApiKey for Auth.
@@ -905,6 +929,15 @@ def _add_gateway_responses(self):
905929
),
906930
)
907931

932+
if not SwaggerEditor.is_valid(self.definition_body):
933+
raise InvalidResourceException(
934+
self.logical_id,
935+
"Unable to add Auth configuration because "
936+
"'DefinitionBody' does not contain a valid Swagger definition.",
937+
)
938+
939+
swagger_editor = SwaggerEditor(self.definition_body)
940+
908941
# The dicts below will eventually become part of swagger/openapi definition, thus requires using Py27Dict()
909942
gateway_responses = Py27Dict()
910943
for response_type, response in self.gateway_responses.items():
@@ -916,7 +949,10 @@ def _add_gateway_responses(self):
916949
)
917950

918951
if gateway_responses:
919-
self.swagger_editor.add_gateway_responses(gateway_responses)
952+
swagger_editor.add_gateway_responses(gateway_responses)
953+
954+
# Assign the Swagger back to template
955+
self.definition_body = swagger_editor.swagger
920956

921957
def _add_models(self):
922958
"""
@@ -932,10 +968,22 @@ def _add_models(self):
932968
self.logical_id, "Models works only with inline Swagger specified in " "'DefinitionBody' property."
933969
)
934970

971+
if not SwaggerEditor.is_valid(self.definition_body):
972+
raise InvalidResourceException(
973+
self.logical_id,
974+
"Unable to add Models definitions because "
975+
"'DefinitionBody' does not contain a valid Swagger definition.",
976+
)
977+
935978
if not all(isinstance(model, dict) for model in self.models.values()):
936979
raise InvalidResourceException(self.logical_id, "Invalid value for 'Models' property")
937980

938-
self.swagger_editor.add_models(self.models)
981+
swagger_editor = SwaggerEditor(self.definition_body)
982+
swagger_editor.add_models(self.models)
983+
984+
# Assign the Swagger back to template
985+
986+
self.definition_body = self._openapi_postprocess(swagger_editor.swagger)
939987

940988
def _openapi_postprocess(self, definition_body):
941989
"""

samtranslator/swagger/swagger.py

Lines changed: 10 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,19 @@ def __init__(self, doc):
4545
modifications on this copy.
4646
4747
:param dict doc: Swagger document as a dictionary
48-
:raises InvalidDocumentException if doc is invalid
48+
:raises ValueError: If the input Swagger document does not meet the basic Swagger requirements.
4949
"""
5050

51+
if not SwaggerEditor.is_valid(doc):
52+
raise ValueError("Invalid Swagger document")
53+
5154
self._doc = copy.deepcopy(doc)
52-
self.validate_definition_body(doc)
55+
self.paths = self._doc["paths"]
56+
self.security_definitions = self._doc.get("securityDefinitions", Py27Dict())
57+
self.gateway_responses = self._doc.get(self._X_APIGW_GATEWAY_RESPONSES, Py27Dict())
58+
self.resource_policy = self._doc.get(self._X_APIGW_POLICY, Py27Dict())
59+
self.definitions = self._doc.get("definitions", Py27Dict())
5360

54-
self.paths = self._doc.get("paths")
5561
# https://swagger.io/specification/#path-item-object
5662
# According to swagger spec,
5763
# each path item object must be a dict (even it is empty).
@@ -61,11 +67,6 @@ def __init__(self, doc):
6167
for path_item in self.get_conditional_contents(self.paths.get(path)):
6268
SwaggerEditor.validate_path_item_is_dict(path_item, path)
6369

64-
self.security_definitions = self._doc.get("securityDefinitions", Py27Dict())
65-
self.gateway_responses = self._doc.get(self._X_APIGW_GATEWAY_RESPONSES, Py27Dict())
66-
self.resource_policy = self._doc.get(self._X_APIGW_POLICY, Py27Dict())
67-
self.definitions = self._doc.get("definitions", Py27Dict())
68-
6970
def get_conditional_contents(self, item):
7071
"""
7172
Returns the contents of the given item.
@@ -165,6 +166,7 @@ def add_path(self, path, method=None):
165166
166167
:param string path: Path name
167168
:param string method: HTTP method
169+
:raises ValueError: If the value of `path` in Swagger is not a dictionary
168170
"""
169171
method = self._normalize_method_name(method)
170172

@@ -1284,32 +1286,6 @@ def is_valid(data):
12841286
)
12851287
return False
12861288

1287-
def validate_definition_body(self, definition_body):
1288-
"""
1289-
Checks if definition_body is a valid Swagger document
1290-
1291-
:param dict definition_body: Data to be validated
1292-
:raises InvalidDocumentException if definition_body is invalid
1293-
"""
1294-
1295-
SwaggerEditor.validate_is_dict(definition_body, "DefinitionBody must be a dictionary.")
1296-
SwaggerEditor.validate_is_dict(
1297-
definition_body.get("paths"), "The 'paths' property of DefinitionBody must be present and be a dictionary."
1298-
)
1299-
1300-
has_swagger = definition_body.get("swagger")
1301-
has_openapi3 = definition_body.get("openapi") and SwaggerEditor.safe_compare_regex_with_string(
1302-
SwaggerEditor.get_openapi_version_3_regex(), definition_body["openapi"]
1303-
)
1304-
if not (has_swagger) and not (has_openapi3):
1305-
raise InvalidDocumentException(
1306-
[
1307-
InvalidTemplateException(
1308-
"DefinitionBody must have either: (1) a 'swagger' property or (2) an 'openapi' property with version 3.x or 3.x.x"
1309-
)
1310-
]
1311-
)
1312-
13131289
@staticmethod
13141290
def validate_is_dict(obj, exception_message):
13151291
"""

tests/model/api/test_api_generator.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def test_construct_usage_plan_with_invalid_usage_plan_type(self, invalid_usage_p
1818
Mock(),
1919
Mock(),
2020
Mock(),
21-
{"paths": {}, "openapi": "3.0.1"},
21+
Mock(),
2222
Mock(),
2323
Mock(),
2424
Mock(),
@@ -39,7 +39,7 @@ def test_construct_usage_plan_with_invalid_usage_plan_fields(self, AuthPropertie
3939
Mock(),
4040
Mock(),
4141
Mock(),
42-
{"paths": {}, "openapi": "3.0.1"},
42+
Mock(),
4343
Mock(),
4444
Mock(),
4545
Mock(),

tests/swagger/test_swagger.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class TestSwaggerEditor_init(TestCase):
1818
def test_must_raise_on_invalid_swagger(self):
1919

2020
invalid_swagger = {"paths": {}} # Missing "Swagger" keyword
21-
with self.assertRaises(InvalidDocumentException):
21+
with self.assertRaises(ValueError):
2222
SwaggerEditor(invalid_swagger)
2323

2424
def test_must_succeed_on_valid_swagger(self):
@@ -32,13 +32,13 @@ def test_must_succeed_on_valid_swagger(self):
3232
def test_must_fail_on_invalid_openapi_version(self):
3333
invalid_swagger = {"openapi": "2.3.0", "paths": {"/foo": {}, "/bar": {}}}
3434

35-
with self.assertRaises(InvalidDocumentException):
35+
with self.assertRaises(ValueError):
3636
SwaggerEditor(invalid_swagger)
3737

3838
def test_must_fail_on_invalid_openapi_version_2(self):
3939
invalid_swagger = {"openapi": "3.1.1.1", "paths": {"/foo": {}, "/bar": {}}}
4040

41-
with self.assertRaises(InvalidDocumentException):
41+
with self.assertRaises(ValueError):
4242
SwaggerEditor(invalid_swagger)
4343

4444
def test_must_succeed_on_valid_openapi3(self):
@@ -53,7 +53,7 @@ def test_must_succeed_on_valid_openapi3(self):
5353
def test_must_fail_with_bad_values_for_path(self, invalid_path_item):
5454
invalid_swagger = {"openapi": "3.1.1.1", "paths": {"/foo": {}, "/bad": invalid_path_item}}
5555

56-
with self.assertRaises(InvalidDocumentException):
56+
with self.assertRaises(ValueError):
5757
SwaggerEditor(invalid_swagger)
5858

5959

tests/translator/input/api_with_resource_refs.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ Resources:
66
Properties:
77
StageName: foo
88
DefinitionBody:
9-
paths: {}
10-
openapi: "3.0.1"
9+
"this": "is"
10+
"a": "swagger"
1111

1212
MyFunction:
1313
Type: "AWS::Serverless::Function"

tests/translator/input/error_api_definition_body_invalid_openapi_version.yaml

Lines changed: 0 additions & 13 deletions
This file was deleted.

tests/translator/input/error_api_definition_body_invalid_paths.yaml

Lines changed: 0 additions & 11 deletions
This file was deleted.

tests/translator/input/error_api_definition_body_missing_openapi_or_swagger.yaml

Lines changed: 0 additions & 12 deletions
This file was deleted.

tests/translator/input/error_api_definition_body_missing_paths.yaml

Lines changed: 0 additions & 10 deletions
This file was deleted.

tests/translator/input/error_api_invalid_auth.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,14 @@ Resources:
121121
Auth:
122122
MyBad: Foo
123123

124+
AuthWithInvalidDefinitionBodyApi:
125+
Type: AWS::Serverless::Api
126+
Properties:
127+
StageName: Prod
128+
DefinitionBody: { invalid: true }
129+
Auth:
130+
DefaultAuthorizer: Foo
131+
124132
AuthWithMissingDefaultAuthorizerApi:
125133
Type: AWS::Serverless::Api
126134
Properties:

0 commit comments

Comments
 (0)