Ejemplo n.º 1
0
class UnitTest_CloudGemFramework_ResourceManagerCode_swagger_processor_interfaces(
        unittest.TestCase):
    def __init__(self, *args, **kwargs):
        super(
            UnitTest_CloudGemFramework_ResourceManagerCode_swagger_processor_interfaces,
            self).__init__(*args, **kwargs)

    def setUp(self):

        self.mock_context = mock.MagicMock('context')

        self.target_swagger = {
            "swagger": "2.0",
            "info": {
                "version": "1.0.0",
                "title": ""
            },
            "paths": {}
        }

        self.target_swagger_navigator = SwaggerNavigator(self.target_swagger)

        self.interface_swagger = {
            "swagger": "2.0",
            "info": {
                "version": "1.0.0",
                "title": ""
            },
            "paths": {}
        }

        self.interface_swagger_navigator = SwaggerNavigator(
            self.interface_swagger)

        self.test_interface_id = 'test_interface_1_2_3'
        self.test_path = '/test_path'
        self.test_definition_name = 'test-definition-name'
        self.test_definition_value = {
            'test-definition-property': 'test-definition-value'
        }
        self.test_mappings = {}
        self.test_lambda_dispatch_for_paths = {}
        self.test_external_parameters = None

    def add_target_path(self, path, content=None):
        content = content or {}
        path_object = self.target_swagger_navigator.get_object(
            'paths').add_object(path, content)
        return path_object

    def add_interface_path(self, path, content=None):
        content = content or {}
        path_object = self.interface_swagger_navigator.get_object(
            'paths').add_object(path, content)
        return path_object

    def add_target_interface_implementation_object(self,
                                                   path,
                                                   interface,
                                                   path_content=None,
                                                   lambda_dispatch=None,
                                                   external_parameters=None):

        if lambda_dispatch:
            interface_implementation_object_value = {
                'interface': {
                    'id': interface,
                    'lambda_dispatch': lambda_dispatch
                }
            }
        else:
            interface_implementation_object_value = {'interface': interface}

        path_object = self.add_target_path(path, path_content)

        interface_implementation_object = path_object.add_object(
            resource_manager_common.service_interface.
            INTERFACE_IMPLEMENTATION_OBJECT_NAME,
            interface_implementation_object_value)

        if external_parameters:
            path_object.get_or_add_array('parameters',
                                         default=external_parameters)

        return interface_implementation_object

    def add_interface_definition(self, definition_name, definition_value):
        definitions_object = self.interface_swagger_navigator.get_or_add_object(
            'definitions')
        definitions_object.value[definition_name] = definition_value
        return definitions_object.get(definition_name)

    def add_target_definition(self, definition_name, definition_value):
        definitions_object = self.target_swagger_navigator.get_or_add_object(
            'definitions')
        definitions_object.value[definition_name] = definition_value

    @mock.patch(
        'swagger_processor.interface.process_interface_implementation_object')
    def test_process_interface_implementation_objects_processes_all_objects(
            self, mock_process_interface_implementation_object):

        self.add_target_path('/path-without-interface-implemenation')

        interface_implementation_object_1 = self.add_target_interface_implementation_object(
            self.test_path + '1', self.test_interface_id)

        interface_implementation_object_2 = self.add_target_interface_implementation_object(
            self.test_path + '2', self.test_interface_id)

        swagger_processor.interface.process_interface_implementation_objects(
            self.mock_context, self.target_swagger_navigator)

        mock_process_interface_implementation_object.assert_has_calls(
            [
                mock.call(
                    self.mock_context,
                    SwaggerNavigatorMatcher(
                        interface_implementation_object_1)),
                mock.call(
                    self.mock_context,
                    SwaggerNavigatorMatcher(interface_implementation_object_2))
            ],
            any_order=True)

    @mock.patch('swagger_processor.interface.load_interface_swagger')
    @mock.patch('swagger_processor.interface.insert_interface_definitions')
    @mock.patch('swagger_processor.interface.insert_interface_paths')
    def test_process_interface_implementation_object(
            self, mock_insert_interface_paths,
            mock_insert_interface_definitions, mock_load_interface_swagger):

        mock_lambda_dispatch = {'paths': {}}

        mock_external_parameters = []

        interface_implementation_object = self.add_target_interface_implementation_object(
            self.test_path,
            self.test_interface_id,
            lambda_dispatch=mock_lambda_dispatch,
            external_parameters=mock_external_parameters)

        mock_mappings = mock_insert_interface_definitions.return_value
        mock_interface_swagger = mock_load_interface_swagger.return_value

        swagger_processor.interface.process_interface_implementation_object(
            self.mock_context, interface_implementation_object)

        mock_load_interface_swagger.assert_called_once_with(
            self.mock_context, self.test_interface_id)

        mock_insert_interface_definitions.assert_called_once_with(
            SwaggerNavigatorMatcher(interface_implementation_object.root),
            mock_interface_swagger, self.test_interface_id,
            mock_external_parameters)

        mock_insert_interface_paths.assert_called_once_with(
            SwaggerNavigatorMatcher(interface_implementation_object.parent),
            mock_interface_swagger, self.test_interface_id, mock_mappings,
            mock_lambda_dispatch['paths'], mock_external_parameters)

        self.assertEquals(
            self.target_swagger[resource_manager_common.service_interface.
                                INTERFACE_METADATA_OBJECT_NAME],
            {self.test_interface_id: {
                'basePath': self.test_path
            }})

    def test_insert_interface_definitions_with_no_source_or_target_definitions_does_not_add_definitions(
            self):

        external_parameters = {}

        swagger_processor.interface.insert_interface_definitions(
            self.target_swagger_navigator, self.interface_swagger_navigator,
            self.test_interface_id, external_parameters)

        self.assertNotIn('definitions', self.target_swagger.keys())
        self.assertNotIn(
            resource_manager_common.service_interface.
            INTERFACE_METADATA_OBJECT_NAME, self.target_swagger.keys())

    def test_insert_interface_definitions_with_only_target_definitions_does_not_modify_definitions(
            self):

        self.add_target_definition(self.test_definition_name,
                                   self.test_definition_value)
        target_definitions_copy = copy.deepcopy(
            self.target_swagger_navigator.get_object('definitions').value)

        external_parameters = {}

        swagger_processor.interface.insert_interface_definitions(
            self.target_swagger_navigator, self.interface_swagger_navigator,
            self.test_interface_id, external_parameters)

        self.assertEquals(
            self.target_swagger_navigator.get_object('definitions').value,
            target_definitions_copy)
        self.assertNotIn(
            resource_manager_common.service_interface.
            INTERFACE_METADATA_OBJECT_NAME, self.target_swagger.keys())

    def test_insert_interface_definitions_with_multiple_source_definitions_inserts_all_definitions(
            self):

        self.add_interface_definition(self.test_definition_name + '1',
                                      self.test_definition_value)
        self.add_interface_definition(self.test_definition_name + '2',
                                      self.test_definition_value)

        external_parameters = {}

        swagger_processor.interface.insert_interface_definitions(
            self.target_swagger_navigator, self.interface_swagger_navigator,
            self.test_interface_id, external_parameters)

        target_definitions_object = self.target_swagger_navigator.get_object(
            'definitions')

        result_definition = target_definitions_object.get_object(
            self.test_definition_name + '1')
        self.assertEquals(result_definition.value, self.test_definition_value)

        result_definition = target_definitions_object.get_object(
            self.test_definition_name + '2')
        self.assertEquals(result_definition.value, self.test_definition_value)

        self.assertEquals(
            self.target_swagger[resource_manager_common.service_interface.
                                INTERFACE_METADATA_OBJECT_NAME], {
                                    self.test_interface_id: {
                                        'definitions':
                                        AnyOrderListMatcher([
                                            self.test_definition_name + '1',
                                            self.test_definition_name + '2'
                                        ])
                                    }
                                })

    def test_insert_interface_definitions_preserves_existing_definition(self):

        interface_definition = {'interface': 'definition'}
        self.add_interface_definition(self.test_definition_name,
                                      interface_definition)

        target_definition = {'target': 'definition'}
        self.add_target_definition(self.test_definition_name,
                                   target_definition)

        external_parameters = []

        swagger_processor.interface.insert_interface_definitions(
            self.target_swagger_navigator, self.interface_swagger_navigator,
            self.test_interface_id, external_parameters)

        target_definitions_object = self.target_swagger_navigator.get_object(
            'definitions')

        result_definition = target_definitions_object.get_object(
            self.test_definition_name + '1')
        self.assertEquals(result_definition,
                          SwaggerNavigatorMatcher(interface_definition))

        result_definition = target_definitions_object.get_object(
            self.test_definition_name)
        self.assertEquals(result_definition,
                          SwaggerNavigatorMatcher(target_definition))

        self.assertEquals(
            self.target_swagger[resource_manager_common.service_interface.
                                INTERFACE_METADATA_OBJECT_NAME],
            {
                self.test_interface_id: {
                    'definitions': [self.test_definition_name + '1']
                }
            })

    def test_insert_interface_definitions_generates_unique_defintion_names(
            self):

        interface_definition = {'interface': 'definition'}
        self.add_interface_definition(self.test_definition_name,
                                      interface_definition)

        target_definition = {'target': 'definition'}
        self.add_target_definition(self.test_definition_name,
                                   target_definition)

        target_definition_1 = {'target': 'definition1'}
        self.add_target_definition(self.test_definition_name + '1',
                                   target_definition_1)

        target_definition_2 = {'target': 'definition2'}
        self.add_target_definition(self.test_definition_name + '2',
                                   target_definition_2)

        external_parameters = []

        swagger_processor.interface.insert_interface_definitions(
            self.target_swagger_navigator, self.interface_swagger_navigator,
            self.test_interface_id, external_parameters)

        target_definitions_object = self.target_swagger_navigator.get_object(
            'definitions')

        result_definition = target_definitions_object.get_object(
            self.test_definition_name + '3')
        self.assertEquals(result_definition,
                          SwaggerNavigatorMatcher(interface_definition))

        result_definition = target_definitions_object.get_object(
            self.test_definition_name + '1')
        self.assertEquals(result_definition,
                          SwaggerNavigatorMatcher(target_definition_1))

        result_definition = target_definitions_object.get_object(
            self.test_definition_name + '2')
        self.assertEquals(result_definition,
                          SwaggerNavigatorMatcher(target_definition_2))

        result_definition = target_definitions_object.get_object(
            self.test_definition_name)
        self.assertEquals(result_definition,
                          SwaggerNavigatorMatcher(target_definition))

        self.assertEquals(
            self.target_swagger[resource_manager_common.service_interface.
                                INTERFACE_METADATA_OBJECT_NAME],
            {
                self.test_interface_id: {
                    'definitions': [self.test_definition_name + '3']
                }
            })

    def test_insert_interface_paths_inserts_paths(self):

        interface_paths = ['/foo', '/bar']
        interface_paths_content = {}
        lambda_dispatch_for_paths = {}
        expected_interface_paths_content = {}
        external_parameters = []

        for interface_path in interface_paths:
            interface_path_content = {
                'get': {
                    'interface_path': interface_path
                },
                'parameters': [{
                    'internal': 'parameter'
                }]
            }
            lambda_dispatch_for_path = {
                'get': {
                    'lambda_dispatch': interface_path
                }
            }
            interface_paths_content[interface_path] = interface_path_content
            self.add_interface_path(interface_path, interface_path_content)
            lambda_dispatch_for_paths[
                interface_path] = lambda_dispatch_for_path
            expected_interface_paths_content[interface_path] = copy.deepcopy(
                interface_path_content)
            expected_interface_paths_content[interface_path]['get'][
                swagger_processor.lambda_dispatch.
                LAMBDA_DISPATCH_OBJECT_NAME] = lambda_dispatch_for_path['get']
            expected_parameters = []
            expected_parameters.extend(external_parameters)
            expected_parameters.extend(interface_path_content['parameters'])
            expected_interface_paths_content[interface_path][
                'parameters'] = AnyOrderListMatcher(expected_parameters)

        target_path_content = {'target_path': self.test_path}

        interface_implementation_object = self.add_target_interface_implementation_object(
            self.test_path,
            self.test_interface_id,
            path_content=target_path_content)

        swagger_processor.interface.insert_interface_paths(
            interface_implementation_object.parent,
            self.interface_swagger_navigator, self.test_interface_id,
            self.test_mappings, lambda_dispatch_for_paths, external_parameters)

        target_paths = self.target_swagger_navigator.get_object('paths')

        self.assertTrue(target_paths.contains(self.test_path))
        self.assertEqual(
            target_paths.get_object(self.test_path).value, target_path_content)

        for interface_path in interface_paths:
            self.assertTrue(
                target_paths.contains(self.test_path + interface_path))
            self.assertEquals(
                target_paths.get_object(self.test_path + interface_path).value,
                expected_interface_paths_content[interface_path])

        self.assertEquals(
            self.target_swagger[resource_manager_common.service_interface.
                                INTERFACE_METADATA_OBJECT_NAME],
            {
                self.test_interface_id: {
                    'paths':
                    AnyOrderListMatcher([
                        self.test_path + interface_path
                        for interface_path in interface_paths
                    ])
                }
            })

    def test_insert_interface_paths_works_on_root_path(self):

        interface_paths = ['/foo', '/bar']
        interface_paths_content = {}

        for interface_path in interface_paths:
            interface_path_content = {'interface_path': interface_path}
            interface_paths_content[interface_path] = interface_path_content
            self.add_interface_path(interface_path, interface_path_content)

        target_path_content = {'target_path': self.test_path}

        interface_implementation_object = self.add_target_interface_implementation_object(
            '/', self.test_interface_id, path_content=target_path_content)

        swagger_processor.interface.insert_interface_paths(
            interface_implementation_object.parent,
            self.interface_swagger_navigator, self.test_interface_id,
            self.test_mappings, self.test_lambda_dispatch_for_paths,
            self.test_external_parameters)

        target_paths = self.target_swagger_navigator.get_object('paths')

        self.assertTrue(target_paths.contains('/'))
        self.assertEqual(
            target_paths.get_object('/').value, target_path_content)

        for interface_path in interface_paths:
            self.assertTrue(target_paths.contains(interface_path))
            self.assertEquals(
                target_paths.get_object(interface_path).value,
                interface_paths_content[interface_path])

        self.assertEquals(
            self.target_swagger[resource_manager_common.service_interface.
                                INTERFACE_METADATA_OBJECT_NAME],
            {
                self.test_interface_id: {
                    'paths': AnyOrderListMatcher(interface_paths)
                }
            })

    def test_insert_interface_paths_fails_with_duplicate_paths(self):

        interface_path = '/foo'
        self.add_interface_path(interface_path)

        interface_implementation_object = self.add_target_interface_implementation_object(
            self.test_path, self.test_interface_id)

        self.add_target_path(self.test_path + interface_path)

        with self.assertRaisesRegexp(RuntimeError,
                                     self.test_path + interface_path):
            swagger_processor.interface.insert_interface_paths(
                interface_implementation_object.parent,
                self.interface_swagger_navigator, self.test_interface_id,
                self.test_mappings, self.test_lambda_dispatch_for_paths,
                self.test_external_parameters)

    def test_copy_with_updated_refs_with_ref(self):

        test_target = '#/definition/test-target'
        definition_object = self.add_interface_definition(
            self.test_definition_name, {'$ref': test_target})
        mappings = {'test-target': 'mapped-target'}

        result = swagger_processor.interface.copy_with_updated_refs(
            definition_object, mappings)
        self.assertEquals(result, {'$ref': '#/definition/mapped-target'})

    def test_copy_with_updated_refs_with_nested_ref_in_object(self):

        test_target = '#/definition/test-target'
        definition_object = self.add_interface_definition(
            self.test_definition_name,
            {'container': {
                'the-ref': {
                    '$ref': test_target
                }
            }})
        mappings = {'test-target': 'mapped-target'}

        result = swagger_processor.interface.copy_with_updated_refs(
            definition_object, mappings)

        self.assertEquals(
            result,
            {'container': {
                'the-ref': {
                    '$ref': '#/definition/mapped-target'
                }
            }})

    def test_copy_with_updated_refs_with_nested_ref_in_array(self):

        test_target = '#/definition/test-target'
        definition_object = self.add_interface_definition(
            self.test_definition_name, {'container': [{
                '$ref': test_target
            }]})
        mappings = {'test-target': 'mapped-target'}

        result = swagger_processor.interface.copy_with_updated_refs(
            definition_object, mappings)

        self.assertEquals(
            result, {'container': [{
                '$ref': '#/definition/mapped-target'
            }]})

    def test_copy_with_updated_refs_with_nested_ref_in_object_in_array(self):

        test_target = '#/definition/test-target'
        definition_object = self.add_interface_definition(
            self.test_definition_name,
            {'container': [{
                'the-ref': {
                    '$ref': test_target
                }
            }]})
        mappings = {'test-target': 'mapped-target'}

        result = swagger_processor.interface.copy_with_updated_refs(
            definition_object, mappings)

        self.assertEquals(result, {
            'container': [{
                'the-ref': {
                    '$ref': '#/definition/mapped-target'
                }
            }]
        })

    def test_copy_with_updated_refs_with_string(self):
        mappings = {'string': 'should-not-be-used'}
        string_value = 'string'
        string_navigator = self.add_interface_definition(
            self.test_definition_name, 'string')
        result = swagger_processor.interface.copy_with_updated_refs(
            string_navigator, mappings)
        self.assertEquals(result, string_value)

    def test_parse_interface_id_with_valid_id(self):

        expected_gem_name = 'TestGemName'
        expected_interface_name = 'TestInterfaceName'
        expected_interface_version = Version('1.2.3')

        interface_id = expected_gem_name + '_' + expected_interface_name + '_' + str(
            expected_interface_version).replace('.', '_')

        actual_gem_name, actual_interface_name, actual_interface_version = swagger_processor.interface.parse_interface_id(
            interface_id)

        self.assertEquals(actual_gem_name, expected_gem_name)
        self.assertEquals(actual_interface_name, expected_interface_name)
        self.assertEquals(actual_interface_version, expected_interface_version)
Ejemplo n.º 2
0
def process_lambda_dispatch_objects(swagger):

    swagger_navigator = SwaggerNavigator(swagger)

    # The x-amazon-cloud-canvas-lambda-dispatch objects can appear in the swagger object (root)
    # a path object or an operation object. We use these to generate x-amazon-apigateway-integration
    # objects for each operation that doesn't have one already.
    #
    # For the lambda, module, and function properties, the value specified by the "lowest" object
    # override any values provided by the "higher" object (so you can set a defaults at the top
    # and override then for individual paths or operations).
    #
    # For additional_properties, addnitional_request_template_cntent, and additional_response_template_content
    # properties, the aggregate values are injected into the x-amazon-apigateway-integration.
    #
    # We keep track of the lambda dispatch objects that are "in scope" in the displatch_object_stack.
    # This starts with the one in the swagger object, then the one for a "current" path, then
    # one for the "current" operation (the stack will never have more than three entries).
    #
    # As part of this processing, we need to know an operation's current parameters. Swagger
    # allows parameters to be defined in the path or operation object, with parameters in the
    # operation object overriding those in the path object. Swagger lets you put paramter
    # definitions in the swagger object, but these are not used for any path/operation unless
    # the path/operation parameters use $ref to identify one of these definitions. We use the
    # parameter_object_stack to keep track of the "current" path and operation parameters.

    dispatch_object_stack = []
    parameters_object_stack = []

    dispatch_object_stack.append(
        swagger_navigator.remove_object(LAMBDA_DISPATCH_OBJECT_NAME, {}))

    missing_security_warnings = []

    global_security_object = swagger_navigator.get_object('security',
                                                          default=None)

    for path, path_object in swagger_navigator.get_object('paths').items():

        dispatch_object_stack.append(
            path_object.remove_object(LAMBDA_DISPATCH_OBJECT_NAME, {}))
        parameters_object_stack.append(path_object.get_array('parameters', []))

        options_operation_needed_for_cors = False
        options_operation_found = False

        for operation, operation_object in path_object.items():

            # Swagger allows a path object to have properties that don't represent an
            # operation. Only the following properties define operations.

            if operation not in [
                    'get', 'put', 'post', 'delete', 'options', 'head', 'patch'
            ]:
                continue

            if operation == 'options':
                options_operation_found = True

            dispatch_object_stack.append(
                operation_object.remove_object(LAMBDA_DISPATCH_OBJECT_NAME,
                                               {}))
            parameters_object_stack.append(
                operation_object.get_array('parameters', []))

            # Create an x-amazon-apigateway-intergration object only if the operation object
            # doesn't have one already.

            if not operation_object.contains(
                    API_GATEWAY_INTEGRATION_OBJECT_NAME):

                # We assume executing a lambda can result in both internal server errors and
                # client errors, in addition to success. We add definitions for these
                # responses to the operation object if they aren't already present.
                if _add_error_response(operation_object):
                    _ensure_error_definition(swagger_navigator)

                # By default we want all APIs to be callable only when using valid AWS IAM
                # credentails. If no security object is present, add add one.
                if _determine_if_iam_security_enabled(dispatch_object_stack):
                    if _add_iam_security_to_operation(operation_object):
                        _ensure_iam_security_definition(swagger_navigator)

                # Construct the x-amazon-apigateway-intergration object using the information
                # we have in the x-amazon-cloud-canvas-lambda-dispatch objects that are currently
                # in scope.
                integration_object = _make_integration_object(
                    dispatch_object_stack, parameters_object_stack, path,
                    operation)
                operation_object.value[
                    API_GATEWAY_INTEGRATION_OBJECT_NAME] = integration_object

                if _determine_if_cors_enabled(dispatch_object_stack):
                    options_operation_needed_for_cors = True
                    __add_cores_response_headers_to_operation(operation_object)

            # If no security has been declared or inserted above, API Gateway will make the operation public.
            if global_security_object.is_none:
                security_array = operation_object.get_array('security',
                                                            default=None)
                if security_array.is_none:
                    missing_security_warnings.append('    {:<7} {}'.format(
                        operation, path))

            dispatch_object_stack.pop()  # operation scope
            parameters_object_stack.pop()

        if options_operation_needed_for_cors and not options_operation_found:
            __add_options_operation_for_cors(path, path_object)

        dispatch_object_stack.pop()  # path scope
        parameters_object_stack.pop()

    dispatch_object_stack.pop()  # swagger scope

    if missing_security_warnings:
        print ''
        print 'WARNING: the following operations do not specify a swagger "security" object.'
        print 'The Service APIs for these operations will be publically accessible.'
        print ''
        for warning in missing_security_warnings:
            print warning
        print ''