2021-10-04 18:33:37 +02:00
|
|
|
import pytest
|
|
|
|
|
2015-11-24 12:34:53 +01:00
|
|
|
from botocore import model
|
|
|
|
from botocore.compat import OrderedDict
|
2022-05-26 00:10:07 +02:00
|
|
|
from tests import unittest
|
2015-11-24 12:34:53 +01:00
|
|
|
|
|
|
|
|
2021-10-04 18:33:37 +02:00
|
|
|
@pytest.mark.parametrize("property_name", ['api_version', 'protocol'])
|
|
|
|
def test_missing_model_attribute_raises_exception(property_name):
|
2015-11-24 12:34:53 +01:00
|
|
|
service_model = model.ServiceModel({'metadata': {'endpointPrefix': 'foo'}})
|
2021-10-04 18:33:37 +02:00
|
|
|
with pytest.raises(model.UndefinedModelAttributeError):
|
|
|
|
getattr(service_model, property_name)
|
2015-11-24 12:34:53 +01:00
|
|
|
|
|
|
|
|
2018-10-04 08:50:52 +02:00
|
|
|
class TestServiceId(unittest.TestCase):
|
|
|
|
def test_hypenize_replaces_spaces(self):
|
|
|
|
self.assertEqual(
|
|
|
|
model.ServiceId('my service').hyphenize(), 'my-service'
|
|
|
|
)
|
|
|
|
|
|
|
|
def test_hyphenize_lower_cases(self):
|
|
|
|
self.assertEqual(model.ServiceId('MyService').hyphenize(), 'myservice')
|
|
|
|
|
|
|
|
|
2015-11-24 12:34:53 +01:00
|
|
|
class TestServiceModel(unittest.TestCase):
|
|
|
|
def setUp(self):
|
|
|
|
self.model = {
|
2022-05-26 00:10:07 +02:00
|
|
|
'metadata': {
|
|
|
|
'protocol': 'query',
|
|
|
|
'endpointPrefix': 'endpoint-prefix',
|
|
|
|
'serviceId': 'MyService',
|
|
|
|
},
|
2015-11-24 12:34:53 +01:00
|
|
|
'documentation': 'Documentation value',
|
|
|
|
'operations': {},
|
2022-05-26 00:10:07 +02:00
|
|
|
'shapes': {'StringShape': {'type': 'string'}},
|
2015-11-24 12:34:53 +01:00
|
|
|
}
|
2020-05-30 12:49:33 +02:00
|
|
|
self.error_shapes = {
|
|
|
|
'ExceptionOne': {
|
|
|
|
'exception': True,
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {},
|
|
|
|
},
|
|
|
|
'ExceptionTwo': {
|
|
|
|
'exception': True,
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {},
|
2022-05-26 00:10:07 +02:00
|
|
|
'error': {'code': 'FooCode'},
|
2020-05-30 12:49:33 +02:00
|
|
|
},
|
|
|
|
}
|
2015-11-24 12:34:53 +01:00
|
|
|
self.service_model = model.ServiceModel(self.model)
|
|
|
|
|
|
|
|
def test_metadata_available(self):
|
|
|
|
# You should be able to access the metadata in a service description
|
|
|
|
# through the service model object.
|
|
|
|
self.assertEqual(self.service_model.metadata.get('protocol'), 'query')
|
|
|
|
|
|
|
|
def test_service_name_can_be_overriden(self):
|
2022-05-26 00:10:07 +02:00
|
|
|
service_model = model.ServiceModel(
|
|
|
|
self.model, service_name='myservice'
|
|
|
|
)
|
2015-11-24 12:34:53 +01:00
|
|
|
self.assertEqual(service_model.service_name, 'myservice')
|
|
|
|
|
|
|
|
def test_service_name_defaults_to_endpoint_prefix(self):
|
|
|
|
self.assertEqual(self.service_model.service_name, 'endpoint-prefix')
|
|
|
|
|
2018-07-11 08:25:50 +02:00
|
|
|
def test_service_id(self):
|
|
|
|
self.assertEqual(self.service_model.service_id, 'MyService')
|
|
|
|
|
2018-10-04 08:50:52 +02:00
|
|
|
def test_hyphenize_service_id(self):
|
|
|
|
self.assertEqual(
|
2022-05-26 00:10:07 +02:00
|
|
|
self.service_model.service_id.hyphenize(), 'myservice'
|
|
|
|
)
|
2018-10-04 08:50:52 +02:00
|
|
|
|
2018-12-28 08:05:06 +01:00
|
|
|
def test_service_id_does_not_exist(self):
|
|
|
|
service_model = {
|
|
|
|
'metadata': {
|
|
|
|
'protocol': 'query',
|
|
|
|
'endpointPrefix': 'endpoint-prefix',
|
|
|
|
},
|
|
|
|
'documentation': 'Documentation value',
|
|
|
|
'operations': {},
|
2022-05-26 00:10:07 +02:00
|
|
|
'shapes': {'StringShape': {'type': 'string'}},
|
2018-12-28 08:05:06 +01:00
|
|
|
}
|
|
|
|
service_name = 'myservice'
|
|
|
|
service_model = model.ServiceModel(service_model, service_name)
|
2022-05-26 00:10:07 +02:00
|
|
|
with self.assertRaisesRegex(
|
|
|
|
model.UndefinedModelAttributeError, service_name
|
|
|
|
):
|
2018-12-28 08:05:06 +01:00
|
|
|
service_model.service_id
|
|
|
|
|
2015-11-24 12:34:53 +01:00
|
|
|
def test_operation_does_not_exist(self):
|
|
|
|
with self.assertRaises(model.OperationNotFoundError):
|
|
|
|
self.service_model.operation_model('NoExistOperation')
|
|
|
|
|
|
|
|
def test_signing_name_defaults_to_endpoint_prefix(self):
|
|
|
|
self.assertEqual(self.service_model.signing_name, 'endpoint-prefix')
|
|
|
|
|
|
|
|
def test_documentation_exposed_as_property(self):
|
2022-05-26 00:10:07 +02:00
|
|
|
self.assertEqual(
|
|
|
|
self.service_model.documentation, 'Documentation value'
|
|
|
|
)
|
2015-11-24 12:34:53 +01:00
|
|
|
|
2017-02-02 09:27:08 +01:00
|
|
|
def test_shape_names(self):
|
|
|
|
self.assertEqual(self.service_model.shape_names, ['StringShape'])
|
|
|
|
|
2018-10-04 08:50:52 +02:00
|
|
|
def test_repr_has_service_name(self):
|
2022-05-26 00:10:07 +02:00
|
|
|
self.assertEqual(
|
|
|
|
repr(self.service_model), 'ServiceModel(endpoint-prefix)'
|
|
|
|
)
|
2018-10-04 08:50:52 +02:00
|
|
|
|
2020-05-30 12:49:33 +02:00
|
|
|
def test_shape_for_error_code(self):
|
|
|
|
self.model['shapes'].update(self.error_shapes)
|
|
|
|
self.service_model = model.ServiceModel(self.model)
|
|
|
|
shape = self.service_model.shape_for_error_code('ExceptionOne')
|
|
|
|
self.assertEqual(shape.name, 'ExceptionOne')
|
|
|
|
shape = self.service_model.shape_for_error_code('FooCode')
|
|
|
|
self.assertEqual(shape.name, 'ExceptionTwo')
|
|
|
|
|
|
|
|
def test_error_shapes(self):
|
|
|
|
self.model['shapes'].update(self.error_shapes)
|
|
|
|
self.service_model = model.ServiceModel(self.model)
|
|
|
|
error_shapes = self.service_model.error_shapes
|
|
|
|
error_shape_names = [shape.name for shape in error_shapes]
|
|
|
|
self.assertEqual(len(error_shape_names), 2)
|
|
|
|
self.assertIn('ExceptionOne', error_shape_names)
|
|
|
|
self.assertIn('ExceptionTwo', error_shape_names)
|
|
|
|
|
2015-11-24 12:34:53 +01:00
|
|
|
|
|
|
|
class TestOperationModelFromService(unittest.TestCase):
|
|
|
|
def setUp(self):
|
|
|
|
self.model = {
|
|
|
|
'metadata': {'protocol': 'query', 'endpointPrefix': 'foo'},
|
|
|
|
'documentation': '',
|
|
|
|
'operations': {
|
|
|
|
'OperationName': {
|
|
|
|
'http': {
|
|
|
|
'method': 'POST',
|
|
|
|
'requestUri': '/',
|
|
|
|
},
|
|
|
|
'name': 'OperationName',
|
2022-05-26 00:10:07 +02:00
|
|
|
'input': {'shape': 'OperationNameRequest'},
|
2015-11-24 12:34:53 +01:00
|
|
|
'output': {
|
|
|
|
'shape': 'OperationNameResponse',
|
|
|
|
},
|
|
|
|
'errors': [{'shape': 'NoSuchResourceException'}],
|
|
|
|
'documentation': 'Docs for OperationName',
|
2022-05-26 00:10:07 +02:00
|
|
|
'authtype': 'v4',
|
2017-06-27 11:52:19 +02:00
|
|
|
},
|
|
|
|
'OperationTwo': {
|
|
|
|
'http': {
|
|
|
|
'method': 'POST',
|
|
|
|
'requestUri': '/',
|
|
|
|
},
|
|
|
|
'name': 'OperationTwo',
|
2022-05-26 00:10:07 +02:00
|
|
|
'input': {'shape': 'OperationNameRequest'},
|
2017-06-27 11:52:19 +02:00
|
|
|
'output': {
|
|
|
|
'shape': 'OperationNameResponse',
|
|
|
|
},
|
|
|
|
'errors': [{'shape': 'NoSuchResourceException'}],
|
|
|
|
'documentation': 'Docs for OperationTwo',
|
2021-11-03 18:14:15 +01:00
|
|
|
},
|
|
|
|
'PayloadOperation': {
|
|
|
|
'http': {
|
|
|
|
'method': 'POST',
|
|
|
|
'requestUri': '/',
|
|
|
|
},
|
|
|
|
'name': 'PayloadOperation',
|
2022-05-26 00:10:07 +02:00
|
|
|
'input': {'shape': 'PayloadOperationRequest'},
|
2021-11-03 18:14:15 +01:00
|
|
|
'output': {
|
|
|
|
'shape': 'PayloadOperationResponse',
|
|
|
|
},
|
|
|
|
'errors': [{'shape': 'NoSuchResourceException'}],
|
|
|
|
'documentation': 'Docs for PayloadOperation',
|
|
|
|
},
|
|
|
|
'NoBodyOperation': {
|
|
|
|
'http': {
|
|
|
|
'method': 'GET',
|
|
|
|
'requestUri': '/',
|
|
|
|
},
|
|
|
|
'name': 'NoBodyOperation',
|
2022-05-26 00:10:07 +02:00
|
|
|
'input': {'shape': 'NoBodyOperationRequest'},
|
2021-11-03 18:14:15 +01:00
|
|
|
'output': {
|
|
|
|
'shape': 'OperationNameResponse',
|
|
|
|
},
|
|
|
|
'errors': [{'shape': 'NoSuchResourceException'}],
|
|
|
|
'documentation': 'Docs for NoBodyOperation',
|
2022-05-26 00:10:07 +02:00
|
|
|
},
|
2015-11-24 12:34:53 +01:00
|
|
|
},
|
|
|
|
'shapes': {
|
|
|
|
'OperationNameRequest': {
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {
|
|
|
|
'Arg1': {'shape': 'stringType'},
|
|
|
|
'Arg2': {'shape': 'stringType'},
|
2022-05-26 00:10:07 +02:00
|
|
|
},
|
2015-11-24 12:34:53 +01:00
|
|
|
},
|
|
|
|
'OperationNameResponse': {
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {
|
|
|
|
'String': {
|
|
|
|
'shape': 'stringType',
|
|
|
|
}
|
2022-05-26 00:10:07 +02:00
|
|
|
},
|
2015-11-24 12:34:53 +01:00
|
|
|
},
|
2021-11-03 18:14:15 +01:00
|
|
|
'PayloadOperationRequest': {
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {
|
|
|
|
'Arg1': {'shape': 'TestConfig'},
|
|
|
|
'Arg2': {'shape': 'stringType'},
|
|
|
|
},
|
2022-05-26 00:10:07 +02:00
|
|
|
'payload': 'Arg1',
|
2021-11-03 18:14:15 +01:00
|
|
|
},
|
|
|
|
'PayloadOperationResponse': {
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {
|
|
|
|
'String': {
|
|
|
|
'shape': 'stringType',
|
|
|
|
}
|
|
|
|
},
|
2022-05-26 00:10:07 +02:00
|
|
|
'payload': 'String',
|
2021-11-03 18:14:15 +01:00
|
|
|
},
|
|
|
|
'NoBodyOperationRequest': {
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {
|
|
|
|
'data': {
|
|
|
|
'location': 'header',
|
|
|
|
'locationName': 'x-amz-data',
|
2022-05-26 00:10:07 +02:00
|
|
|
'shape': 'stringType',
|
2021-11-03 18:14:15 +01:00
|
|
|
}
|
2022-05-26 00:10:07 +02:00
|
|
|
},
|
2021-11-03 18:14:15 +01:00
|
|
|
},
|
2015-11-24 12:34:53 +01:00
|
|
|
'NoSuchResourceException': {
|
|
|
|
'type': 'structure',
|
2022-05-26 00:10:07 +02:00
|
|
|
'members': {},
|
2015-11-24 12:34:53 +01:00
|
|
|
},
|
|
|
|
'stringType': {
|
|
|
|
'type': 'string',
|
2021-11-03 18:14:15 +01:00
|
|
|
},
|
|
|
|
'TestConfig': {
|
|
|
|
'type': 'structure',
|
2022-05-26 00:10:07 +02:00
|
|
|
'members': {'timeout': {'shape': 'stringType'}},
|
2021-11-03 18:14:15 +01:00
|
|
|
},
|
2022-05-26 00:10:07 +02:00
|
|
|
},
|
2015-11-24 12:34:53 +01:00
|
|
|
}
|
|
|
|
self.service_model = model.ServiceModel(self.model)
|
|
|
|
|
|
|
|
def test_wire_name_always_matches_model(self):
|
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
operation = model.OperationModel(
|
2022-05-26 00:10:07 +02:00
|
|
|
self.model['operations']['OperationName'], service_model, 'Foo'
|
|
|
|
)
|
2015-11-24 12:34:53 +01:00
|
|
|
self.assertEqual(operation.name, 'Foo')
|
|
|
|
self.assertEqual(operation.wire_name, 'OperationName')
|
|
|
|
|
2016-10-11 02:22:54 +02:00
|
|
|
def test_operation_name_in_repr(self):
|
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
operation = service_model.operation_model('OperationName')
|
|
|
|
self.assertIn('OperationName', repr(operation))
|
|
|
|
|
2015-11-24 12:34:53 +01:00
|
|
|
def test_name_and_wire_name_defaults_to_same_value(self):
|
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
operation = model.OperationModel(
|
2022-05-26 00:10:07 +02:00
|
|
|
self.model['operations']['OperationName'], service_model
|
|
|
|
)
|
2015-11-24 12:34:53 +01:00
|
|
|
self.assertEqual(operation.name, 'OperationName')
|
|
|
|
self.assertEqual(operation.wire_name, 'OperationName')
|
|
|
|
|
|
|
|
def test_name_from_service(self):
|
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
operation = service_model.operation_model('OperationName')
|
|
|
|
self.assertEqual(operation.name, 'OperationName')
|
|
|
|
|
|
|
|
def test_name_from_service_model_when_differs_from_name(self):
|
2022-05-26 00:10:07 +02:00
|
|
|
self.model['operations']['Foo'] = self.model['operations'][
|
|
|
|
'OperationName'
|
|
|
|
]
|
2015-11-24 12:34:53 +01:00
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
operation = service_model.operation_model('Foo')
|
|
|
|
self.assertEqual(operation.name, 'Foo')
|
|
|
|
|
|
|
|
def test_operation_input_model(self):
|
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
operation = service_model.operation_model('OperationName')
|
|
|
|
self.assertEqual(operation.name, 'OperationName')
|
|
|
|
# Operations should also have a reference to the top level metadata.
|
|
|
|
self.assertEqual(operation.metadata['protocol'], 'query')
|
|
|
|
self.assertEqual(operation.http['method'], 'POST')
|
|
|
|
self.assertEqual(operation.http['requestUri'], '/')
|
|
|
|
shape = operation.input_shape
|
|
|
|
self.assertEqual(shape.name, 'OperationNameRequest')
|
|
|
|
self.assertEqual(list(sorted(shape.members)), ['Arg1', 'Arg2'])
|
|
|
|
|
|
|
|
def test_has_documentation_property(self):
|
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
operation = service_model.operation_model('OperationName')
|
|
|
|
self.assertEqual(operation.documentation, 'Docs for OperationName')
|
|
|
|
|
|
|
|
def test_service_model_available_from_operation_model(self):
|
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
operation = service_model.operation_model('OperationName')
|
|
|
|
# This is an identity comparison because we don't implement
|
|
|
|
# __eq__, so we may need to change this in the future.
|
2022-05-26 00:10:07 +02:00
|
|
|
self.assertEqual(operation.service_model, service_model)
|
2015-11-24 12:34:53 +01:00
|
|
|
|
|
|
|
def test_operation_output_model(self):
|
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
operation = service_model.operation_model('OperationName')
|
|
|
|
output = operation.output_shape
|
|
|
|
self.assertEqual(list(output.members), ['String'])
|
|
|
|
self.assertFalse(operation.has_streaming_output)
|
|
|
|
|
|
|
|
def test_operation_shape_not_required(self):
|
|
|
|
# It's ok if there's no output shape. We'll just get a return value of
|
|
|
|
# None.
|
|
|
|
del self.model['operations']['OperationName']['output']
|
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
operation = service_model.operation_model('OperationName')
|
|
|
|
output_shape = operation.output_shape
|
|
|
|
self.assertIsNone(output_shape)
|
|
|
|
|
2017-02-02 09:27:08 +01:00
|
|
|
def test_error_shapes(self):
|
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
operation = service_model.operation_model('OperationName')
|
|
|
|
# OperationName only has a NoSuchResourceException
|
|
|
|
self.assertEqual(len(operation.error_shapes), 1)
|
|
|
|
self.assertEqual(
|
2022-05-26 00:10:07 +02:00
|
|
|
operation.error_shapes[0].name, 'NoSuchResourceException'
|
|
|
|
)
|
2017-02-02 09:27:08 +01:00
|
|
|
|
2017-06-27 11:52:19 +02:00
|
|
|
def test_has_auth_type(self):
|
|
|
|
operation = self.service_model.operation_model('OperationName')
|
|
|
|
self.assertEqual(operation.auth_type, 'v4')
|
|
|
|
|
|
|
|
def test_auth_type_not_set(self):
|
|
|
|
operation = self.service_model.operation_model('OperationTwo')
|
|
|
|
self.assertIsNone(operation.auth_type)
|
|
|
|
|
2018-01-15 17:34:17 +01:00
|
|
|
def test_deprecated_present(self):
|
|
|
|
self.model['operations']['OperationName']['deprecated'] = True
|
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
operation_name = service_model.operation_model('OperationName')
|
|
|
|
self.assertTrue(operation_name.deprecated)
|
|
|
|
|
|
|
|
def test_deprecated_present_false(self):
|
|
|
|
self.model['operations']['OperationName']['deprecated'] = False
|
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
operation_name = service_model.operation_model('OperationName')
|
|
|
|
self.assertFalse(operation_name.deprecated)
|
|
|
|
|
|
|
|
def test_deprecated_absent(self):
|
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
operation_two = service_model.operation_model('OperationTwo')
|
|
|
|
self.assertFalse(operation_two.deprecated)
|
|
|
|
|
2018-12-28 08:05:06 +01:00
|
|
|
def test_endpoint_operation_present(self):
|
|
|
|
self.model['operations']['OperationName']['endpointoperation'] = True
|
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
operation_name = service_model.operation_model('OperationName')
|
|
|
|
self.assertTrue(operation_name.is_endpoint_discovery_operation)
|
|
|
|
|
|
|
|
def test_endpoint_operation_present_false(self):
|
|
|
|
self.model['operations']['OperationName']['endpointoperation'] = False
|
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
operation_name = service_model.operation_model('OperationName')
|
|
|
|
self.assertFalse(operation_name.is_endpoint_discovery_operation)
|
|
|
|
|
|
|
|
def test_endpoint_operation_absent(self):
|
|
|
|
operation_two = self.service_model.operation_model('OperationName')
|
|
|
|
self.assertFalse(operation_two.is_endpoint_discovery_operation)
|
|
|
|
|
2021-01-26 16:12:20 +01:00
|
|
|
def test_endpoint_discovery_required(self):
|
|
|
|
operation = self.model['operations']['OperationName']
|
|
|
|
operation['endpointdiscovery'] = {'required': True}
|
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
self.assertTrue(service_model.endpoint_discovery_required)
|
|
|
|
|
|
|
|
def test_endpoint_discovery_required_false(self):
|
|
|
|
self.model['operations']['OperationName']['endpointdiscovery'] = {}
|
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
self.assertFalse(service_model.endpoint_discovery_required)
|
|
|
|
|
|
|
|
def test_endpoint_discovery_required_no_value(self):
|
|
|
|
operation = self.model['operations']['OperationName']
|
|
|
|
self.assertTrue(operation.get('endpointdiscovery') is None)
|
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
self.assertFalse(service_model.endpoint_discovery_required)
|
|
|
|
|
2018-12-28 08:05:06 +01:00
|
|
|
def test_endpoint_discovery_present(self):
|
|
|
|
operation = self.model['operations']['OperationName']
|
|
|
|
operation['endpointdiscovery'] = {'required': True}
|
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
operation_name = service_model.operation_model('OperationName')
|
|
|
|
self.assertTrue(operation_name.endpoint_discovery.get('required'))
|
|
|
|
|
|
|
|
def test_endpoint_discovery_absent(self):
|
|
|
|
operation_name = self.service_model.operation_model('OperationName')
|
|
|
|
self.assertIsNone(operation_name.endpoint_discovery)
|
|
|
|
|
2022-05-26 00:10:07 +02:00
|
|
|
def test_http_checksum_absent(self):
|
|
|
|
operation_name = self.service_model.operation_model('OperationName')
|
|
|
|
self.assertEqual(operation_name.http_checksum, {})
|
|
|
|
|
|
|
|
def test_http_checksum_present(self):
|
|
|
|
operation = self.model['operations']['OperationName']
|
|
|
|
operation['httpChecksum'] = {
|
|
|
|
"requestChecksumRequired": True,
|
|
|
|
"requestAlgorithmMember": "ChecksumAlgorithm",
|
|
|
|
"requestValidationModeMember": "ChecksumMode",
|
|
|
|
"responseAlgorithms": ["crc32", "crc32c", "sha256", "sha1"],
|
|
|
|
}
|
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
operation_model = service_model.operation_model('OperationName')
|
|
|
|
http_checksum = operation_model.http_checksum
|
|
|
|
self.assertEqual(
|
|
|
|
http_checksum["requestChecksumRequired"],
|
|
|
|
True,
|
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
http_checksum["requestAlgorithmMember"],
|
|
|
|
"ChecksumAlgorithm",
|
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
http_checksum["requestValidationModeMember"],
|
|
|
|
"ChecksumMode",
|
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
http_checksum["responseAlgorithms"],
|
|
|
|
["crc32", "crc32c", "sha256", "sha1"],
|
|
|
|
)
|
|
|
|
|
2016-10-11 02:22:54 +02:00
|
|
|
|
2018-05-08 03:57:43 +02:00
|
|
|
class TestOperationModelEventStreamTypes(unittest.TestCase):
|
|
|
|
def setUp(self):
|
2022-05-26 00:10:07 +02:00
|
|
|
super().setUp()
|
2018-05-08 03:57:43 +02:00
|
|
|
self.model = {
|
|
|
|
'metadata': {'protocol': 'rest-xml', 'endpointPrefix': 'foo'},
|
|
|
|
'documentation': '',
|
|
|
|
'operations': {
|
|
|
|
'OperationName': {
|
|
|
|
'http': {
|
|
|
|
'method': 'POST',
|
|
|
|
'requestUri': '/',
|
|
|
|
},
|
|
|
|
'name': 'OperationName',
|
|
|
|
'input': {'shape': 'OperationRequest'},
|
|
|
|
'output': {'shape': 'OperationResponse'},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'shapes': {
|
|
|
|
'NormalStructure': {
|
|
|
|
'type': 'structure',
|
2022-05-26 00:10:07 +02:00
|
|
|
'members': {'Input': {'shape': 'StringType'}},
|
2018-05-08 03:57:43 +02:00
|
|
|
},
|
|
|
|
'OperationRequest': {
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {
|
|
|
|
'String': {'shape': 'StringType'},
|
2022-05-26 00:10:07 +02:00
|
|
|
"Body": {'shape': 'EventStreamStructure'},
|
2018-05-08 03:57:43 +02:00
|
|
|
},
|
2022-05-26 00:10:07 +02:00
|
|
|
'payload': 'Body',
|
2018-05-08 03:57:43 +02:00
|
|
|
},
|
|
|
|
'OperationResponse': {
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {
|
|
|
|
'String': {'shape': 'StringType'},
|
2022-05-26 00:10:07 +02:00
|
|
|
"Body": {'shape': 'EventStreamStructure'},
|
2018-05-08 03:57:43 +02:00
|
|
|
},
|
2022-05-26 00:10:07 +02:00
|
|
|
'payload': 'Body',
|
2018-05-08 03:57:43 +02:00
|
|
|
},
|
|
|
|
'StringType': {'type': 'string'},
|
|
|
|
'BlobType': {'type': 'blob'},
|
|
|
|
'EventStreamStructure': {
|
|
|
|
'eventstream': True,
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {
|
|
|
|
'EventA': {'shape': 'EventAStructure'},
|
2022-05-26 00:10:07 +02:00
|
|
|
'EventB': {'shape': 'EventBStructure'},
|
|
|
|
},
|
2018-05-08 03:57:43 +02:00
|
|
|
},
|
|
|
|
'EventAStructure': {
|
|
|
|
'event': True,
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {
|
2022-05-26 00:10:07 +02:00
|
|
|
'Payload': {'shape': 'BlobType', 'eventpayload': True},
|
|
|
|
'Header': {'shape': 'StringType', 'eventheader': True},
|
|
|
|
},
|
2018-05-08 03:57:43 +02:00
|
|
|
},
|
|
|
|
'EventBStructure': {
|
|
|
|
'event': True,
|
|
|
|
'type': 'structure',
|
2022-05-26 00:10:07 +02:00
|
|
|
'members': {'Records': {'shape': 'StringType'}},
|
|
|
|
},
|
|
|
|
},
|
2018-05-08 03:57:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
def update_operation(self, **kwargs):
|
|
|
|
operation = self.model['operations']['OperationName']
|
|
|
|
operation.update(kwargs)
|
|
|
|
|
|
|
|
def test_event_stream_input_for_operation(self):
|
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
operation = service_model.operation_model('OperationName')
|
|
|
|
self.assertTrue(operation.has_event_stream_input)
|
|
|
|
event_stream_input = operation.get_event_stream_input()
|
|
|
|
self.assertEqual(event_stream_input.name, 'EventStreamStructure')
|
|
|
|
|
|
|
|
def test_no_event_stream_input_for_operation(self):
|
|
|
|
self.update_operation(input={'shape': 'NormalStructure'})
|
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
operation = service_model.operation_model('OperationName')
|
|
|
|
self.assertFalse(operation.has_event_stream_input)
|
|
|
|
self.assertEqual(operation.get_event_stream_input(), None)
|
|
|
|
|
|
|
|
def test_event_stream_output_for_operation(self):
|
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
operation = service_model.operation_model('OperationName')
|
|
|
|
self.assertTrue(operation.has_event_stream_output)
|
|
|
|
output = operation.get_event_stream_output()
|
|
|
|
self.assertEqual(output.name, 'EventStreamStructure')
|
|
|
|
|
|
|
|
def test_no_event_stream_output_for_operation(self):
|
|
|
|
self.update_operation(output={'shape': 'NormalStructure'})
|
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
operation = service_model.operation_model('OperationName')
|
|
|
|
self.assertFalse(operation.has_event_stream_output)
|
|
|
|
self.assertEqual(operation.get_event_stream_output(), None)
|
|
|
|
|
|
|
|
def test_no_output_shape(self):
|
|
|
|
self.update_operation(output=None)
|
|
|
|
del self.model['operations']['OperationName']['output']
|
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
operation = service_model.operation_model('OperationName')
|
|
|
|
self.assertFalse(operation.has_event_stream_output)
|
|
|
|
self.assertEqual(operation.get_event_stream_output(), None)
|
|
|
|
|
|
|
|
|
2016-10-11 02:22:54 +02:00
|
|
|
class TestOperationModelStreamingTypes(unittest.TestCase):
|
|
|
|
def setUp(self):
|
2022-05-26 00:10:07 +02:00
|
|
|
super().setUp()
|
2015-11-24 12:34:53 +01:00
|
|
|
self.model = {
|
|
|
|
'metadata': {'protocol': 'query', 'endpointPrefix': 'foo'},
|
|
|
|
'documentation': '',
|
|
|
|
'operations': {
|
|
|
|
'OperationName': {
|
|
|
|
'name': 'OperationName',
|
2016-10-11 02:22:54 +02:00
|
|
|
'input': {
|
|
|
|
'shape': 'OperationRequest',
|
|
|
|
},
|
2015-11-24 12:34:53 +01:00
|
|
|
'output': {
|
2016-10-11 02:22:54 +02:00
|
|
|
'shape': 'OperationResponse',
|
2015-11-24 12:34:53 +01:00
|
|
|
},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'shapes': {
|
2016-10-11 02:22:54 +02:00
|
|
|
'OperationRequest': {
|
2015-11-24 12:34:53 +01:00
|
|
|
'type': 'structure',
|
|
|
|
'members': {
|
|
|
|
'String': {
|
|
|
|
'shape': 'stringType',
|
|
|
|
},
|
|
|
|
"Body": {
|
|
|
|
'shape': 'blobType',
|
2022-05-26 00:10:07 +02:00
|
|
|
},
|
2015-11-24 12:34:53 +01:00
|
|
|
},
|
2022-05-26 00:10:07 +02:00
|
|
|
'payload': 'Body',
|
2015-11-24 12:34:53 +01:00
|
|
|
},
|
2016-10-11 02:22:54 +02:00
|
|
|
'OperationResponse': {
|
2015-11-24 12:34:53 +01:00
|
|
|
'type': 'structure',
|
|
|
|
'members': {
|
|
|
|
'String': {
|
|
|
|
'shape': 'stringType',
|
|
|
|
},
|
2016-10-11 02:22:54 +02:00
|
|
|
"Body": {
|
|
|
|
'shape': 'blobType',
|
2022-05-26 00:10:07 +02:00
|
|
|
},
|
2015-11-24 12:34:53 +01:00
|
|
|
},
|
2022-05-26 00:10:07 +02:00
|
|
|
'payload': 'Body',
|
2015-11-24 12:34:53 +01:00
|
|
|
},
|
|
|
|
'stringType': {
|
|
|
|
'type': 'string',
|
|
|
|
},
|
2022-05-26 00:10:07 +02:00
|
|
|
'blobType': {'type': 'blob'},
|
|
|
|
},
|
2015-11-24 12:34:53 +01:00
|
|
|
}
|
2016-10-11 02:22:54 +02:00
|
|
|
|
|
|
|
def remove_payload(self, type):
|
|
|
|
self.model['shapes']['Operation' + type].pop('payload')
|
|
|
|
|
|
|
|
def test_streaming_input_for_operation(self):
|
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
operation = service_model.operation_model('OperationName')
|
|
|
|
self.assertTrue(operation.has_streaming_input)
|
|
|
|
self.assertEqual(operation.get_streaming_input().name, 'blobType')
|
|
|
|
|
|
|
|
def test_not_streaming_input_for_operation(self):
|
|
|
|
self.remove_payload('Request')
|
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
operation = service_model.operation_model('OperationName')
|
|
|
|
self.assertFalse(operation.has_streaming_input)
|
|
|
|
self.assertEqual(operation.get_streaming_input(), None)
|
|
|
|
|
|
|
|
def test_streaming_output_for_operation(self):
|
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
operation = service_model.operation_model('OperationName')
|
|
|
|
self.assertTrue(operation.has_streaming_output)
|
|
|
|
self.assertEqual(operation.get_streaming_output().name, 'blobType')
|
|
|
|
|
|
|
|
def test_not_streaming_output_for_operation(self):
|
|
|
|
self.remove_payload('Response')
|
2015-11-24 12:34:53 +01:00
|
|
|
service_model = model.ServiceModel(self.model)
|
|
|
|
operation = service_model.operation_model('OperationName')
|
|
|
|
self.assertFalse(operation.has_streaming_output)
|
2016-10-11 02:22:54 +02:00
|
|
|
self.assertEqual(operation.get_streaming_output(), None)
|
2015-11-24 12:34:53 +01:00
|
|
|
|
|
|
|
|
|
|
|
class TestDeepMerge(unittest.TestCase):
|
|
|
|
def setUp(self):
|
|
|
|
self.shapes = {
|
|
|
|
'SetQueueAttributes': {
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {
|
2022-05-26 00:10:07 +02:00
|
|
|
'MapExample': {
|
|
|
|
'shape': 'StrToStrMap',
|
|
|
|
'locationName': 'Attribute',
|
|
|
|
},
|
|
|
|
},
|
2015-11-24 12:34:53 +01:00
|
|
|
},
|
|
|
|
'SetQueueAttributes2': {
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {
|
2022-05-26 00:10:07 +02:00
|
|
|
'MapExample': {
|
|
|
|
'shape': 'StrToStrMap',
|
|
|
|
'locationName': 'Attribute2',
|
|
|
|
},
|
|
|
|
},
|
2015-11-24 12:34:53 +01:00
|
|
|
},
|
|
|
|
'StrToStrMap': {
|
|
|
|
'type': 'map',
|
|
|
|
'key': {'shape': 'StringType', 'locationName': 'Name'},
|
|
|
|
'value': {'shape': 'StringType', 'locationName': 'Value'},
|
|
|
|
'flattened': True,
|
|
|
|
'name': 'NotAttribute',
|
|
|
|
},
|
2022-05-26 00:10:07 +02:00
|
|
|
'StringType': {'type': 'string'},
|
2015-11-24 12:34:53 +01:00
|
|
|
}
|
|
|
|
self.shape_resolver = model.ShapeResolver(self.shapes)
|
|
|
|
|
|
|
|
def test_deep_merge(self):
|
|
|
|
shape = self.shape_resolver.get_shape_by_name('SetQueueAttributes')
|
|
|
|
map_merged = shape.members['MapExample']
|
|
|
|
# map_merged has a serialization as a member trait as well as
|
|
|
|
# in the StrToStrMap.
|
|
|
|
# The member trait should have precedence.
|
2021-11-03 18:14:15 +01:00
|
|
|
self.assertEqual(
|
|
|
|
map_merged.serialization,
|
|
|
|
# member beats the definition.
|
|
|
|
{
|
|
|
|
'name': 'Attribute',
|
|
|
|
# From the definition.
|
|
|
|
'flattened': True,
|
2022-05-26 00:10:07 +02:00
|
|
|
},
|
2021-11-03 18:14:15 +01:00
|
|
|
)
|
2015-11-24 12:34:53 +01:00
|
|
|
# Ensure we don't merge/mutate the original dicts.
|
|
|
|
self.assertEqual(map_merged.key.serialization['name'], 'Name')
|
|
|
|
self.assertEqual(map_merged.value.serialization['name'], 'Value')
|
|
|
|
self.assertEqual(map_merged.key.serialization['name'], 'Name')
|
|
|
|
|
|
|
|
def test_merges_copy_dict(self):
|
|
|
|
shape = self.shape_resolver.get_shape_by_name('SetQueueAttributes')
|
|
|
|
map_merged = shape.members['MapExample']
|
|
|
|
self.assertEqual(map_merged.serialization.get('name'), 'Attribute')
|
|
|
|
|
|
|
|
shape2 = self.shape_resolver.get_shape_by_name('SetQueueAttributes2')
|
|
|
|
map_merged2 = shape2.members['MapExample']
|
|
|
|
self.assertEqual(map_merged2.serialization.get('name'), 'Attribute2')
|
|
|
|
|
|
|
|
|
|
|
|
class TestShapeResolver(unittest.TestCase):
|
|
|
|
def test_get_shape_by_name(self):
|
|
|
|
shape_map = {
|
|
|
|
'Foo': {
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {
|
|
|
|
'Bar': {'shape': 'StringType'},
|
|
|
|
'Baz': {'shape': 'StringType'},
|
2022-05-26 00:10:07 +02:00
|
|
|
},
|
2015-11-24 12:34:53 +01:00
|
|
|
},
|
2022-05-26 00:10:07 +02:00
|
|
|
"StringType": {"type": "string"},
|
2015-11-24 12:34:53 +01:00
|
|
|
}
|
|
|
|
resolver = model.ShapeResolver(shape_map)
|
|
|
|
shape = resolver.get_shape_by_name('Foo')
|
|
|
|
self.assertEqual(shape.name, 'Foo')
|
|
|
|
self.assertEqual(shape.type_name, 'structure')
|
|
|
|
|
|
|
|
def test_resolve_shape_reference(self):
|
|
|
|
shape_map = {
|
|
|
|
'Foo': {
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {
|
|
|
|
'Bar': {'shape': 'StringType'},
|
|
|
|
'Baz': {'shape': 'StringType'},
|
2022-05-26 00:10:07 +02:00
|
|
|
},
|
2015-11-24 12:34:53 +01:00
|
|
|
},
|
2022-05-26 00:10:07 +02:00
|
|
|
"StringType": {"type": "string"},
|
2015-11-24 12:34:53 +01:00
|
|
|
}
|
|
|
|
resolver = model.ShapeResolver(shape_map)
|
|
|
|
shape = resolver.resolve_shape_ref({'shape': 'StringType'})
|
|
|
|
self.assertEqual(shape.name, 'StringType')
|
|
|
|
self.assertEqual(shape.type_name, 'string')
|
|
|
|
|
|
|
|
def test_resolve_shape_references_with_member_traits(self):
|
|
|
|
shape_map = {
|
|
|
|
'Foo': {
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {
|
|
|
|
'Bar': {'shape': 'StringType'},
|
|
|
|
'Baz': {'shape': 'StringType', 'locationName': 'other'},
|
2022-05-26 00:10:07 +02:00
|
|
|
},
|
2015-11-24 12:34:53 +01:00
|
|
|
},
|
2022-05-26 00:10:07 +02:00
|
|
|
"StringType": {"type": "string"},
|
2015-11-24 12:34:53 +01:00
|
|
|
}
|
|
|
|
resolver = model.ShapeResolver(shape_map)
|
2022-05-26 00:10:07 +02:00
|
|
|
shape = resolver.resolve_shape_ref(
|
|
|
|
{'shape': 'StringType', 'locationName': 'other'}
|
|
|
|
)
|
2015-11-24 12:34:53 +01:00
|
|
|
self.assertEqual(shape.serialization['name'], 'other')
|
|
|
|
self.assertEqual(shape.name, 'StringType')
|
|
|
|
|
|
|
|
def test_serialization_cache(self):
|
|
|
|
shape_map = {
|
|
|
|
'Foo': {
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {
|
|
|
|
'Baz': {'shape': 'StringType', 'locationName': 'other'},
|
2022-05-26 00:10:07 +02:00
|
|
|
},
|
2015-11-24 12:34:53 +01:00
|
|
|
},
|
2022-05-26 00:10:07 +02:00
|
|
|
"StringType": {"type": "string"},
|
2015-11-24 12:34:53 +01:00
|
|
|
}
|
|
|
|
resolver = model.ShapeResolver(shape_map)
|
2022-05-26 00:10:07 +02:00
|
|
|
shape = resolver.resolve_shape_ref(
|
|
|
|
{'shape': 'StringType', 'locationName': 'other'}
|
|
|
|
)
|
2015-11-24 12:34:53 +01:00
|
|
|
self.assertEqual(shape.serialization['name'], 'other')
|
|
|
|
# serialization is computed on demand, and a cache is kept.
|
|
|
|
# This is just verifying that trying to access serialization again
|
|
|
|
# gives the same result. We don't actually care that it's cached,
|
|
|
|
# we just care that the cache doesn't mess with correctness.
|
|
|
|
self.assertEqual(shape.serialization['name'], 'other')
|
|
|
|
|
|
|
|
def test_shape_overrides(self):
|
|
|
|
shape_map = {
|
|
|
|
"StringType": {
|
|
|
|
"type": "string",
|
2022-05-26 00:10:07 +02:00
|
|
|
"documentation": "Original documentation",
|
2015-11-24 12:34:53 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
resolver = model.ShapeResolver(shape_map)
|
|
|
|
shape = resolver.get_shape_by_name('StringType')
|
|
|
|
self.assertEqual(shape.documentation, 'Original documentation')
|
|
|
|
|
2022-05-26 00:10:07 +02:00
|
|
|
shape = resolver.resolve_shape_ref(
|
|
|
|
{'shape': 'StringType', 'documentation': 'override'}
|
|
|
|
)
|
2015-11-24 12:34:53 +01:00
|
|
|
self.assertEqual(shape.documentation, 'override')
|
|
|
|
|
|
|
|
def test_shape_type_structure(self):
|
|
|
|
shapes = {
|
|
|
|
'ChangePasswordRequest': {
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {
|
|
|
|
'OldPassword': {'shape': 'passwordType'},
|
|
|
|
'NewPassword': {'shape': 'passwordType'},
|
2022-05-26 00:10:07 +02:00
|
|
|
},
|
2015-11-24 12:34:53 +01:00
|
|
|
},
|
|
|
|
'passwordType': {
|
2021-11-03 18:14:15 +01:00
|
|
|
"type": "string",
|
2022-05-26 00:10:07 +02:00
|
|
|
},
|
2015-11-24 12:34:53 +01:00
|
|
|
}
|
|
|
|
resolver = model.ShapeResolver(shapes)
|
|
|
|
shape = resolver.get_shape_by_name('ChangePasswordRequest')
|
|
|
|
self.assertEqual(shape.type_name, 'structure')
|
|
|
|
self.assertEqual(shape.name, 'ChangePasswordRequest')
|
2022-05-26 00:10:07 +02:00
|
|
|
self.assertEqual(
|
|
|
|
list(sorted(shape.members)), ['NewPassword', 'OldPassword']
|
|
|
|
)
|
2015-11-24 12:34:53 +01:00
|
|
|
self.assertEqual(shape.members['OldPassword'].name, 'passwordType')
|
|
|
|
self.assertEqual(shape.members['OldPassword'].type_name, 'string')
|
2020-05-30 12:49:33 +02:00
|
|
|
self.assertEqual(shape.error_code, None)
|
|
|
|
|
|
|
|
def test_exception_error_code(self):
|
|
|
|
shapes = {
|
|
|
|
'FooException': {
|
|
|
|
'exception': True,
|
|
|
|
'type': 'structure',
|
2022-05-26 00:10:07 +02:00
|
|
|
'members': {},
|
2020-05-30 12:49:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
# Test without explicit error code
|
|
|
|
resolver = model.ShapeResolver(shapes)
|
|
|
|
shape = resolver.get_shape_by_name('FooException')
|
|
|
|
self.assertTrue(shape.metadata['exception'])
|
|
|
|
self.assertEqual(shape.error_code, 'FooException')
|
|
|
|
# Test with explicit error code
|
|
|
|
shapes['FooException']['error'] = {'code': 'ExceptionCode'}
|
|
|
|
resolver = model.ShapeResolver(shapes)
|
|
|
|
shape = resolver.get_shape_by_name('FooException')
|
|
|
|
self.assertTrue(shape.metadata['exception'])
|
|
|
|
self.assertEqual(shape.error_code, 'ExceptionCode')
|
2015-11-24 12:34:53 +01:00
|
|
|
|
|
|
|
def test_shape_metadata(self):
|
|
|
|
shapes = {
|
|
|
|
'ChangePasswordRequest': {
|
|
|
|
'type': 'structure',
|
|
|
|
'required': ['OldPassword', 'NewPassword'],
|
|
|
|
'members': {
|
|
|
|
'OldPassword': {'shape': 'passwordType'},
|
|
|
|
'NewPassword': {'shape': 'passwordType'},
|
2022-05-26 00:10:07 +02:00
|
|
|
},
|
2015-11-24 12:34:53 +01:00
|
|
|
},
|
|
|
|
'passwordType': {
|
2021-11-03 18:14:15 +01:00
|
|
|
"type": "string",
|
|
|
|
"min": 1,
|
|
|
|
"max": 128,
|
2022-05-26 00:10:07 +02:00
|
|
|
"pattern": ".*",
|
|
|
|
"sensitive": True,
|
|
|
|
},
|
2015-11-24 12:34:53 +01:00
|
|
|
}
|
|
|
|
resolver = model.ShapeResolver(shapes)
|
|
|
|
shape = resolver.get_shape_by_name('ChangePasswordRequest')
|
2022-05-26 00:10:07 +02:00
|
|
|
self.assertEqual(
|
|
|
|
shape.metadata['required'], ['OldPassword', 'NewPassword']
|
|
|
|
)
|
2015-11-24 12:34:53 +01:00
|
|
|
member = shape.members['OldPassword']
|
|
|
|
self.assertEqual(member.metadata['min'], 1)
|
|
|
|
self.assertEqual(member.metadata['max'], 128)
|
2022-05-26 00:10:07 +02:00
|
|
|
self.assertEqual(member.metadata['pattern'], '.*')
|
2015-11-24 12:34:53 +01:00
|
|
|
self.assertEqual(member.metadata['sensitive'], True)
|
|
|
|
|
2020-03-22 13:12:42 +01:00
|
|
|
def test_error_shape_metadata(self):
|
|
|
|
shapes = {
|
|
|
|
'ResourceNotFoundException': {
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {
|
|
|
|
'message': {
|
|
|
|
'shape': 'ErrorMessage',
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'exception': True,
|
2022-05-26 00:10:07 +02:00
|
|
|
'retryable': {'throttling': True},
|
2020-03-22 13:12:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
resolver = model.ShapeResolver(shapes)
|
|
|
|
shape = resolver.get_shape_by_name('ResourceNotFoundException')
|
|
|
|
self.assertEqual(
|
|
|
|
shape.metadata,
|
2022-05-26 00:10:07 +02:00
|
|
|
{'exception': True, 'retryable': {'throttling': True}},
|
2020-03-22 13:12:42 +01:00
|
|
|
)
|
|
|
|
|
2015-11-24 12:34:53 +01:00
|
|
|
def test_shape_list(self):
|
|
|
|
shapes = {
|
|
|
|
'mfaDeviceListType': {
|
2021-11-03 18:14:15 +01:00
|
|
|
"type": "list",
|
2015-11-24 12:34:53 +01:00
|
|
|
"member": {"shape": "MFADevice"},
|
|
|
|
},
|
|
|
|
'MFADevice': {
|
|
|
|
'type': 'structure',
|
2022-05-26 00:10:07 +02:00
|
|
|
'members': {'UserName': {'shape': 'userNameType'}},
|
2015-11-24 12:34:53 +01:00
|
|
|
},
|
2022-05-26 00:10:07 +02:00
|
|
|
'userNameType': {'type': 'string'},
|
2015-11-24 12:34:53 +01:00
|
|
|
}
|
|
|
|
resolver = model.ShapeResolver(shapes)
|
|
|
|
shape = resolver.get_shape_by_name('mfaDeviceListType')
|
|
|
|
self.assertEqual(shape.member.type_name, 'structure')
|
|
|
|
self.assertEqual(shape.member.name, 'MFADevice')
|
|
|
|
self.assertEqual(list(shape.member.members), ['UserName'])
|
|
|
|
|
|
|
|
def test_shape_does_not_exist(self):
|
|
|
|
resolver = model.ShapeResolver({})
|
|
|
|
with self.assertRaises(model.NoShapeFoundError):
|
|
|
|
resolver.get_shape_by_name('NoExistShape')
|
|
|
|
|
|
|
|
def test_missing_type_key(self):
|
2022-05-26 00:10:07 +02:00
|
|
|
shapes = {'UnknownType': {'NotTheTypeKey': 'someUnknownType'}}
|
2015-11-24 12:34:53 +01:00
|
|
|
resolver = model.ShapeResolver(shapes)
|
|
|
|
with self.assertRaises(model.InvalidShapeError):
|
|
|
|
resolver.get_shape_by_name('UnknownType')
|
|
|
|
|
|
|
|
def test_bad_shape_ref(self):
|
|
|
|
# This is an example of a denormalized model,
|
|
|
|
# which should raise an exception.
|
|
|
|
shapes = {
|
|
|
|
'Struct': {
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {
|
|
|
|
'A': {'type': 'string'},
|
|
|
|
'B': {'type': 'string'},
|
2022-05-26 00:10:07 +02:00
|
|
|
},
|
2015-11-24 12:34:53 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
resolver = model.ShapeResolver(shapes)
|
|
|
|
with self.assertRaises(model.InvalidShapeReferenceError):
|
|
|
|
struct = resolver.get_shape_by_name('Struct')
|
|
|
|
# Resolving the members will fail because
|
|
|
|
# the 'A' and 'B' members are not shape refs.
|
|
|
|
struct.members
|
|
|
|
|
|
|
|
def test_shape_name_in_repr(self):
|
|
|
|
shapes = {
|
|
|
|
'StringType': {
|
|
|
|
'type': 'string',
|
|
|
|
}
|
|
|
|
}
|
|
|
|
resolver = model.ShapeResolver(shapes)
|
2022-05-26 00:10:07 +02:00
|
|
|
self.assertIn(
|
|
|
|
'StringType', repr(resolver.get_shape_by_name('StringType'))
|
|
|
|
)
|
2015-11-24 12:34:53 +01:00
|
|
|
|
|
|
|
|
|
|
|
class TestBuilders(unittest.TestCase):
|
|
|
|
def test_structure_shape_builder_with_scalar_types(self):
|
|
|
|
b = model.DenormalizedStructureBuilder()
|
2022-05-26 00:10:07 +02:00
|
|
|
shape = b.with_members(
|
|
|
|
{
|
|
|
|
'A': {'type': 'string'},
|
|
|
|
'B': {'type': 'integer'},
|
|
|
|
}
|
|
|
|
).build_model()
|
2015-11-24 12:34:53 +01:00
|
|
|
self.assertIsInstance(shape, model.StructureShape)
|
|
|
|
self.assertEqual(sorted(list(shape.members)), ['A', 'B'])
|
|
|
|
self.assertEqual(shape.members['A'].type_name, 'string')
|
|
|
|
self.assertEqual(shape.members['B'].type_name, 'integer')
|
|
|
|
|
|
|
|
def test_structure_shape_with_structure_type(self):
|
|
|
|
b = model.DenormalizedStructureBuilder()
|
2022-05-26 00:10:07 +02:00
|
|
|
shape = b.with_members(
|
|
|
|
{
|
|
|
|
'A': {
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {
|
|
|
|
'A-1': {'type': 'string'},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
).build_model()
|
2015-11-24 12:34:53 +01:00
|
|
|
self.assertIsInstance(shape, model.StructureShape)
|
|
|
|
self.assertEqual(list(shape.members), ['A'])
|
|
|
|
self.assertEqual(shape.members['A'].type_name, 'structure')
|
|
|
|
self.assertEqual(list(shape.members['A'].members), ['A-1'])
|
|
|
|
|
|
|
|
def test_structure_shape_with_list(self):
|
|
|
|
b = model.DenormalizedStructureBuilder()
|
2022-05-26 00:10:07 +02:00
|
|
|
shape = b.with_members(
|
|
|
|
{
|
|
|
|
'A': {'type': 'list', 'member': {'type': 'string'}},
|
|
|
|
}
|
|
|
|
).build_model()
|
2015-11-24 12:34:53 +01:00
|
|
|
self.assertIsInstance(shape.members['A'], model.ListShape)
|
|
|
|
self.assertEqual(shape.members['A'].member.type_name, 'string')
|
|
|
|
|
|
|
|
def test_structure_shape_with_map_type(self):
|
|
|
|
b = model.DenormalizedStructureBuilder()
|
2022-05-26 00:10:07 +02:00
|
|
|
shape = b.with_members(
|
|
|
|
{
|
|
|
|
'A': {
|
|
|
|
'type': 'map',
|
|
|
|
'key': {'type': 'string'},
|
|
|
|
'value': {'type': 'string'},
|
|
|
|
}
|
2015-11-24 12:34:53 +01:00
|
|
|
}
|
2022-05-26 00:10:07 +02:00
|
|
|
).build_model()
|
2015-11-24 12:34:53 +01:00
|
|
|
self.assertIsInstance(shape.members['A'], model.MapShape)
|
|
|
|
map_shape = shape.members['A']
|
|
|
|
self.assertEqual(map_shape.key.type_name, 'string')
|
|
|
|
self.assertEqual(map_shape.value.type_name, 'string')
|
|
|
|
|
|
|
|
def test_nested_structure(self):
|
|
|
|
b = model.DenormalizedStructureBuilder()
|
2022-05-26 00:10:07 +02:00
|
|
|
shape = b.with_members(
|
|
|
|
{
|
|
|
|
'A': {
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {
|
|
|
|
'B': {
|
|
|
|
'type': 'structure',
|
|
|
|
'members': {
|
|
|
|
'C': {
|
|
|
|
'type': 'string',
|
|
|
|
}
|
|
|
|
},
|
2015-11-24 12:34:53 +01:00
|
|
|
}
|
2022-05-26 00:10:07 +02:00
|
|
|
},
|
2015-11-24 12:34:53 +01:00
|
|
|
}
|
|
|
|
}
|
2022-05-26 00:10:07 +02:00
|
|
|
).build_model()
|
2015-11-24 12:34:53 +01:00
|
|
|
self.assertEqual(
|
2022-05-26 00:10:07 +02:00
|
|
|
shape.members['A'].members['B'].members['C'].type_name, 'string'
|
|
|
|
)
|
2015-11-24 12:34:53 +01:00
|
|
|
|
|
|
|
def test_enum_values_on_string_used(self):
|
|
|
|
b = model.DenormalizedStructureBuilder()
|
|
|
|
enum_values = ['foo', 'bar', 'baz']
|
2022-05-26 00:10:07 +02:00
|
|
|
shape = b.with_members(
|
|
|
|
{
|
|
|
|
'A': {
|
|
|
|
'type': 'string',
|
|
|
|
'enum': enum_values,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
).build_model()
|
2015-11-24 12:34:53 +01:00
|
|
|
self.assertIsInstance(shape, model.StructureShape)
|
|
|
|
string_shape = shape.members['A']
|
|
|
|
self.assertIsInstance(string_shape, model.StringShape)
|
|
|
|
self.assertEqual(string_shape.metadata['enum'], enum_values)
|
|
|
|
self.assertEqual(string_shape.enum, enum_values)
|
|
|
|
|
|
|
|
def test_documentation_on_shape_used(self):
|
|
|
|
b = model.DenormalizedStructureBuilder()
|
2022-05-26 00:10:07 +02:00
|
|
|
shape = b.with_members(
|
|
|
|
{
|
|
|
|
'A': {
|
|
|
|
'type': 'string',
|
|
|
|
'documentation': 'MyDocs',
|
|
|
|
},
|
|
|
|
}
|
|
|
|
).build_model()
|
|
|
|
self.assertEqual(shape.members['A'].documentation, 'MyDocs')
|
2015-11-24 12:34:53 +01:00
|
|
|
|
2019-08-03 07:08:36 +02:00
|
|
|
def test_min_max_used_in_metadata(self):
|
|
|
|
b = model.DenormalizedStructureBuilder()
|
2022-05-26 00:10:07 +02:00
|
|
|
shape = b.with_members(
|
|
|
|
{
|
|
|
|
'A': {
|
|
|
|
'type': 'string',
|
|
|
|
'documentation': 'MyDocs',
|
|
|
|
'min': 2,
|
|
|
|
'max': 3,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
).build_model()
|
2019-08-03 07:08:36 +02:00
|
|
|
metadata = shape.members['A'].metadata
|
|
|
|
self.assertEqual(metadata.get('min'), 2)
|
|
|
|
self.assertEqual(metadata.get('max'), 3)
|
|
|
|
|
2015-11-24 12:34:53 +01:00
|
|
|
def test_use_shape_name_when_provided(self):
|
|
|
|
b = model.DenormalizedStructureBuilder()
|
2022-05-26 00:10:07 +02:00
|
|
|
shape = b.with_members(
|
|
|
|
{
|
|
|
|
'A': {
|
|
|
|
'type': 'string',
|
|
|
|
'shape_name': 'MyStringShape',
|
|
|
|
},
|
|
|
|
}
|
|
|
|
).build_model()
|
2015-11-24 12:34:53 +01:00
|
|
|
self.assertEqual(shape.members['A'].name, 'MyStringShape')
|
|
|
|
|
|
|
|
def test_unknown_shape_type(self):
|
|
|
|
b = model.DenormalizedStructureBuilder()
|
|
|
|
with self.assertRaises(model.InvalidShapeError):
|
2022-05-26 00:10:07 +02:00
|
|
|
b.with_members(
|
|
|
|
{
|
|
|
|
'A': {
|
|
|
|
'type': 'brand-new-shape-type',
|
|
|
|
},
|
|
|
|
}
|
|
|
|
).build_model()
|
2015-11-24 12:34:53 +01:00
|
|
|
|
|
|
|
def test_ordered_shape_builder(self):
|
|
|
|
b = model.DenormalizedStructureBuilder()
|
2022-05-26 00:10:07 +02:00
|
|
|
shape = b.with_members(
|
|
|
|
OrderedDict(
|
|
|
|
[
|
|
|
|
('A', {'type': 'string'}),
|
|
|
|
(
|
|
|
|
'B',
|
|
|
|
{
|
|
|
|
'type': 'structure',
|
|
|
|
'members': OrderedDict(
|
|
|
|
[
|
|
|
|
('C', {'type': 'string'}),
|
|
|
|
('D', {'type': 'string'}),
|
|
|
|
]
|
|
|
|
),
|
|
|
|
},
|
|
|
|
),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
).build_model()
|
2015-11-24 12:34:53 +01:00
|
|
|
|
|
|
|
# Members should be in order
|
|
|
|
self.assertEqual(['A', 'B'], list(shape.members.keys()))
|
|
|
|
|
|
|
|
# Nested structure members should *also* stay ordered
|
|
|
|
self.assertEqual(['C', 'D'], list(shape.members['B'].members.keys()))
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
unittest.main()
|