def test_message_transformation():
    template = APIGATEWAY_TRANSFORMATION_TEMPLATE
    records = [{
        'data': {
            'foo': 'foo1',
            'bar': 'bar2'
        }
    }, {
        'data': {
            'foo': 'foo1',
            'bar': 'bar2'
        },
        'partitionKey': 'key123'
    }]
    context = {'records': records}
    # try rendering the template
    result = render_velocity_template(template, context, as_json=True)
    result_decoded = json.loads(base64.b64decode(result['Records'][0]['Data']))
    assert result_decoded == records[0]['data']
    assert len(result['Records'][0]['PartitionKey']) > 0
    assert result['Records'][1]['PartitionKey'] == 'key123'
    # try again with context as string
    context = json.dumps(context)
    result = render_velocity_template(template, context, as_json=True)
    result_decoded = json.loads(base64.b64decode(result['Records'][0]['Data']))
    assert result_decoded == records[0]['data']
    assert len(result['Records'][0]['PartitionKey']) > 0
    assert result['Records'][1]['PartitionKey'] == 'key123'

    # test with empty array
    records = []
    context = {'records': records}
    # try rendering the template
    result = render_velocity_template(template, context, as_json=True)
    assert result['Records'] == []
Esempio n. 2
0
 def test_special_chars(self):
     template1 = 'test#${foo.bar}'
     template2 = 'test#$foo.bar'
     context = {'foo': {'bar': 'baz'}}
     result = render_velocity_template(template1, {}, variables=context)
     self.assertEqual('test#baz', result)
     result = render_velocity_template(template2, {}, variables=context)
     self.assertEqual('test#baz', result)
Esempio n. 3
0
    def test_array_in_set_expr(self):
        template = "#set ($bar = $input.path('$.foo')[1]) \n $bar"
        context = {'foo': ['e1', 'e2', 'e3', 'e4']}
        result = render_velocity_template(template, context).strip()
        self.assertEqual('e2', result)

        template = "#set ($bar = $input.path('$.foo')[1][1][1]) $bar"
        context = {'foo': [['e1'], ['e2', ['e3', 'e4']]]}
        result = render_velocity_template(template, context).strip()
        self.assertEqual('e4', result)
Esempio n. 4
0
    def test_array_in_set_expr(self):
        template = "#set ($bar = $input.path('$.foo')[1]) \n $bar"
        context = {"foo": ["e1", "e2", "e3", "e4"]}
        result = render_velocity_template(template, context).strip()
        self.assertEqual("e2", result)

        template = "#set ($bar = $input.path('$.foo')[1][1][1]) $bar"
        context = {"foo": [["e1"], ["e2", ["e3", "e4"]]]}
        result = render_velocity_template(template, context).strip()
        self.assertEqual("e4", result)
Esempio n. 5
0
def test_array_size():
    template = "#set($list = $input.path('$.records')) $list.size()"
    context = {
        'records': [{
            'data': {'foo': 'bar1'}
        }, {
            'data': {'foo': 'bar2'}
        }]
    }
    result = render_velocity_template(template, context)
    assert(result == ' 2')
    result = render_velocity_template(template, json.dumps(context))
    assert(result == ' 2')
Esempio n. 6
0
 def do_test(context):
     result = render_velocity_template(template, context, as_json=True)
     result_decoded = json.loads(
         to_str(base64.b64decode(result["Records"][0]["Data"])))
     self.assertEqual(records[0]["data"], result_decoded)
     self.assertGreater(len(result["Records"][0]["PartitionKey"]), 0)
     self.assertEqual("key123", result["Records"][1]["PartitionKey"])
Esempio n. 7
0
def apply_template(
    integration: Dict[str, Any],
    req_res_type: str,
    data: InvocationPayload,
    path_params={},
    query_params={},
    headers={},
    context={},
):
    integration_type = integration.get("type") or integration.get(
        "integrationType")
    if integration_type in ["HTTP", "AWS"]:
        # apply custom request template
        content_type = APPLICATION_JSON  # TODO: make configurable!
        template = integration.get("%sTemplates" % req_res_type,
                                   {}).get(content_type)
        if template:
            variables = {"context": context or {}}
            input_ctx = {"body": data}

            def _params(name=None):
                # See https://docs.aws.amazon.com/apigateway/latest/developerguide/
                #    api-gateway-mapping-template-reference.html#input-variable-reference
                # Returns "request parameter from the path, query string, or header value (searched in that order)"
                combined = {}
                combined.update(path_params or {})
                combined.update(query_params or {})
                combined.update(headers or {})
                return combined if not name else combined.get(name)

            input_ctx["params"] = _params
            data = aws_stack.render_velocity_template(template,
                                                      input_ctx,
                                                      variables=variables)
    return data
Esempio n. 8
0
def update_apigateway(method,
                      path,
                      data,
                      headers,
                      response=None,
                      return_forward_info=False):
    if return_forward_info:
        regex1 = r'^/restapis/[A-Za-z0-9\-]+/deployments$'
        if method == 'POST' and re.match(regex1, path):
            # this is a request to deploy the API gateway, simply return HTTP code 200
            return 200

        regex2 = r'^/restapis/([A-Za-z0-9_\-]+)/([A-Za-z0-9_\-]+)/%s/([^/]+)$' % PATH_USER_REQUEST
        if method == 'POST' and re.match(regex2, path):
            api_id = re.search(regex2, path).group(1)
            sub_path = '/%s' % re.search(regex2, path).group(3)
            integration = aws_stack.get_apigateway_integration(
                api_id, method, sub_path)
            template = integration['requestTemplates'][APPLICATION_JSON]
            new_request = aws_stack.render_velocity_template(template, data)

            # forward records to our main kinesis stream
            # TODO check whether the target of this API method is 'kinesis'
            headers = aws_stack.mock_aws_request_headers(service='kinesis')
            headers['X-Amz-Target'] = KINESIS_ACTION_PUT_RECORDS
            result = common.make_http_request(url=TEST_KINESIS_URL,
                                              method='POST',
                                              data=new_request,
                                              headers=headers)
            return 200
        return True
Esempio n. 9
0
 def do_test(context):
     result = render_velocity_template(template, context, as_json=True)
     result_decoded = json.loads(
         to_str(base64.b64decode(result['Records'][0]['Data'])))
     self.assertEqual(records[0]['data'], result_decoded)
     self.assertGreater(len(result['Records'][0]['PartitionKey']), 0)
     self.assertEqual('key123', result['Records'][1]['PartitionKey'])
Esempio n. 10
0
 def test_string_methods(self):
     context = {"foo": {"bar": "BAZ baz"}}
     template1 = "${foo.bar.strip().lower().replace(' ','-')}"
     template2 = "${foo.bar.trim().toLowerCase().replace(' ','-')}"
     for template in [template1, template2]:
         result = render_velocity_template(template, {}, variables=context)
         self.assertEqual("baz-baz", result)
Esempio n. 11
0
    def test_message_transformation(self):
        template = APIGATEWAY_TRANSFORMATION_TEMPLATE
        records = [{
            'data': {
                'foo': 'foo1',
                'bar': 'bar2'
            }
        }, {
            'data': {
                'foo': 'foo1',
                'bar': 'bar2'
            },
            'partitionKey': 'key123'
        }]
        context = {'records': records}

        def do_test(context):
            result = render_velocity_template(template, context, as_json=True)
            result_decoded = json.loads(
                to_str(base64.b64decode(result['Records'][0]['Data'])))
            self.assertEqual(records[0]['data'], result_decoded)
            self.assertGreater(len(result['Records'][0]['PartitionKey']), 0)
            self.assertEqual('key123', result['Records'][1]['PartitionKey'])

        # try rendering the template
        do_test(context)
        # try again with context as string
        do_test(json.dumps(context))

        # test with empty array
        records = []
        context = {'records': records}
        # try rendering the template
        result = render_velocity_template(template, context, as_json=True)
        self.assertEqual([], result['Records'])
Esempio n. 12
0
def apply_template(integration,
                   req_res_type,
                   data,
                   path_params={},
                   query_params={},
                   headers={}):
    if integration['type'] in ['HTTP', 'AWS']:
        # apply custom request template
        template = integration.get('%sTemplates' % req_res_type,
                                   {}).get(APPLICATION_JSON)
        if template:
            context = {}
            context['body'] = data

            def _params(name=None):
                # See https://docs.aws.amazon.com/apigateway/latest/developerguide/
                #    api-gateway-mapping-template-reference.html#input-variable-reference
                # Returns "request parameter from the path, query string, or header value (searched in that order)"
                combined = {}
                combined.update(path_params or {})
                combined.update(query_params or {})
                combined.update(headers or {})
                return combined if not name else combined.get(name)

            context['params'] = _params
            data = aws_stack.render_velocity_template(template, context)
    return data
Esempio n. 13
0
 def test_array_size(self):
     template = "#set($list = $input.path('$.records')) $list.size()"
     context = {
         "records": [{
             "data": {
                 "foo": "bar1"
             }
         }, {
             "data": {
                 "foo": "bar2"
             }
         }]
     }
     result = render_velocity_template(template, context)
     self.assertEqual(" 2", result)
     result = render_velocity_template(template, json.dumps(context))
     self.assertEqual(" 2", result)
Esempio n. 14
0
def apply_template(
    integration: Dict[str, Any],
    req_res_type: str,
    data: InvocationPayload,
    path_params=None,
    query_params=None,
    headers=None,
    context=None,
):
    if path_params is None:
        path_params = {}
    if query_params is None:
        query_params = {}
    if headers is None:
        headers = {}
    if context is None:
        context = {}
    integration_type = integration.get("type") or integration.get(
        "integrationType")
    if integration_type in ["HTTP", "AWS"]:
        # apply custom request template
        content_type = APPLICATION_JSON  # TODO: make configurable!
        template = integration.get("%sTemplates" % req_res_type,
                                   {}).get(content_type)
        if template:
            variables = {"context": context or {}}
            input_ctx = {"body": data}
            # little trick to flatten the input context so velocity templates
            # work from the root.
            # orig - { "body": '{"action": "$default","message":"foobar"}'
            # after - {
            #   "body": '{"action": "$default","message":"foobar"}',
            #   "action": "$default",
            #   "message": "foobar"
            # }
            if data:
                dict_pack = try_json(data)
                if isinstance(dict_pack, dict):
                    for k, v in dict_pack.items():
                        input_ctx.update({k: v})

            def _params(name=None):
                # See https://docs.aws.amazon.com/apigateway/latest/developerguide/
                #    api-gateway-mapping-template-reference.html#input-variable-reference
                # Returns "request parameter from the path, query string, or header value (searched in that order)"
                combined = {}
                combined.update(path_params or {})
                combined.update(query_params or {})
                combined.update(headers or {})
                return combined if not name else combined.get(name)

            input_ctx["params"] = _params
            data = aws_stack.render_velocity_template(template,
                                                      input_ctx,
                                                      variables=variables)
    return data
def test_message_transformation():
    template = APIGATEWAY_TRANSFORMATION_TEMPLATE
    records = [
        {
            'data': {
                'foo': 'foo1',
                'bar': 'bar2'
            }
        },
        {
            'data': {
                'foo': 'foo1',
                'bar': 'bar2'
            },
            'partitionKey': 'key123'
        }
    ]
    context = {
        'records': records
    }
    # try rendering the template
    result = render_velocity_template(template, context, as_json=True)
    result_decoded = json.loads(to_str(base64.b64decode(result['Records'][0]['Data'])))
    assert result_decoded == records[0]['data']
    assert len(result['Records'][0]['PartitionKey']) > 0
    assert result['Records'][1]['PartitionKey'] == 'key123'
    # try again with context as string
    context = json.dumps(context)
    result = render_velocity_template(template, context, as_json=True)
    result_decoded = json.loads(to_str(base64.b64decode(result['Records'][0]['Data'])))
    assert result_decoded == records[0]['data']
    assert len(result['Records'][0]['PartitionKey']) > 0
    assert result['Records'][1]['PartitionKey'] == 'key123'

    # test with empty array
    records = []
    context = {
        'records': records
    }
    # try rendering the template
    result = render_velocity_template(template, context, as_json=True)
    assert result['Records'] == []
Esempio n. 16
0
def update_apigateway(method,
                      path,
                      data,
                      headers,
                      response=None,
                      return_forward_info=False):
    if return_forward_info:

        regex1 = r'^/restapis/[A-Za-z0-9\-]+/deployments$'
        if method == 'POST' and re.match(regex1, path):
            # this is a request to deploy the API gateway, simply return HTTP code 200
            return 200

        regex2 = r'^/restapis/([A-Za-z0-9_\-]+)/([A-Za-z0-9_\-]+)/%s/([^/]+)$' % PATH_USER_REQUEST
        if method == 'POST' and re.match(regex2, path):
            api_id = re.search(regex2, path).group(1)
            sub_path = '/%s' % re.search(regex2, path).group(3)
            integration = aws_stack.get_apigateway_integration(
                api_id, method, sub_path)
            if integration['type'] == 'AWS':
                if integration['uri'].endswith('kinesis:action/PutRecords'):
                    template = integration['requestTemplates'][
                        APPLICATION_JSON]
                    new_request = aws_stack.render_velocity_template(
                        template, data)

                    # forward records to target kinesis stream
                    headers = aws_stack.mock_aws_request_headers(
                        service='kinesis')
                    headers['X-Amz-Target'] = KINESIS_ACTION_PUT_RECORDS
                    result = common.make_http_request(url=TEST_KINESIS_URL,
                                                      method='POST',
                                                      data=new_request,
                                                      headers=headers)
                    return result
                else:
                    LOGGER.warning(
                        'API Gateway action uri "%s" not yet implemented' %
                        integration['uri'])
            else:
                LOGGER.warning(
                    'API Gateway integration type "%s" not yet implemented' %
                    integration['type'])

            return 200

        return True
Esempio n. 17
0
    def test_message_transformation(self):
        template = APIGATEWAY_TRANSFORMATION_TEMPLATE
        records = [
            {
                "data": {
                    "foo": "foo1",
                    "bar": "bar2"
                }
            },
            {
                "data": {
                    "foo": "foo1",
                    "bar": "bar2"
                },
                "partitionKey": "key123"
            },
        ]
        context = {"records": records}

        def do_test(context):
            result = render_velocity_template(template, context, as_json=True)
            result_decoded = json.loads(
                to_str(base64.b64decode(result["Records"][0]["Data"])))
            self.assertEqual(records[0]["data"], result_decoded)
            self.assertGreater(len(result["Records"][0]["PartitionKey"]), 0)
            self.assertEqual("key123", result["Records"][1]["PartitionKey"])

        # try rendering the template
        do_test(context)
        # try again with context as string
        do_test(json.dumps(context))

        # test with empty array
        records = []
        context = {"records": records}
        # try rendering the template
        result = render_velocity_template(template, context, as_json=True)
        self.assertEqual([], result["Records"])
Esempio n. 18
0
def apply_request_response_templates(
    data: Union[Response, bytes],
    templates: Dict[str, str],
    content_type: str = None,
    as_json: bool = False,
):
    """Apply the matching request/response template (if it exists) to the payload data and return the result"""

    content_type = content_type or APPLICATION_JSON
    is_response = isinstance(data, Response)
    templates = templates or {}
    template = templates.get(content_type)
    if not template:
        return data
    content = (data.content if is_response else data) or ""
    result = aws_stack.render_velocity_template(template,
                                                content,
                                                as_json=as_json)
    if is_response:
        data._content = result
        update_content_length(data)
        return data
    return result
Esempio n. 19
0
 def test_render_urlencoded_string_data(self):
     template = "MessageBody=$util.base64Encode($input.json('$'))"
     result = render_velocity_template(template, b'{"spam": "eggs"}')
     self.assertEqual("MessageBody=eyJzcGFtIjogImVnZ3MifQ==", result)
Esempio n. 20
0
def invoke_rest_api(api_id, stage, method, invocation_path, data, headers, path=None):
    path = path or invocation_path
    relative_path, query_string_params = extract_query_string_params(path=invocation_path)

    path_map = helpers.get_rest_api_paths(rest_api_id=api_id)
    try:
        extracted_path, resource = get_resource_for_path(path=relative_path, path_map=path_map)
    except Exception:
        return make_error('Unable to find path %s' % path, 404)

    integrations = resource.get('resourceMethods', {})
    integration = integrations.get(method, {})
    if not integration:
        integration = integrations.get('ANY', {})
    integration = integration.get('methodIntegration')
    if not integration:
        if method == 'OPTIONS' and 'Origin' in headers:
            # default to returning CORS headers if this is an OPTIONS request
            return get_cors_response(headers)
        return make_error('Unable to find integration for path %s' % path, 404)

    uri = integration.get('uri')
    if method == 'POST' and integration['type'] == 'AWS':
        if uri.endswith('kinesis:action/PutRecords'):
            template = integration['requestTemplates'][APPLICATION_JSON]
            new_request = aws_stack.render_velocity_template(template, data)

            # forward records to target kinesis stream
            headers = aws_stack.mock_aws_request_headers(service='kinesis')
            headers['X-Amz-Target'] = kinesis_listener.ACTION_PUT_RECORDS
            result = common.make_http_request(url=TEST_KINESIS_URL,
                method='POST', data=new_request, headers=headers)
            return result

        elif uri.startswith('arn:aws:apigateway:') and ':sqs:path' in uri:
            template = integration['requestTemplates'][APPLICATION_JSON]
            account_id, queue = uri.split('/')[-2:]
            region_name = uri.split(':')[3]

            new_request = aws_stack.render_velocity_template(template, data) + '&QueueName=%s' % queue
            headers = aws_stack.mock_aws_request_headers(service='sqs', region_name=region_name)

            url = urljoin(TEST_SQS_URL, '%s/%s' % (account_id, queue))
            result = common.make_http_request(url, method='POST', headers=headers, data=new_request)
            return result

        else:
            msg = 'API Gateway action uri "%s" not yet implemented' % uri
            LOGGER.warning(msg)
            return make_error(msg, 404)

    elif integration['type'] == 'AWS_PROXY':
        if uri.startswith('arn:aws:apigateway:') and ':lambda:path' in uri:
            func_arn = uri.split(':lambda:path')[1].split('functions/')[1].split('/invocations')[0]
            data_str = json.dumps(data) if isinstance(data, (dict, list)) else data
            account_id = uri.split(':lambda:path')[1].split(':function:')[0].split(':')[-1]

            source_ip = headers['X-Forwarded-For'].split(',')[-2]

            # Sample request context:
            # https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html#api-gateway-create-api-as-simple-proxy-for-lambda-test
            request_context = {
                'path': relative_path,
                'accountId': account_id,
                'resourceId': resource.get('id'),
                'stage': stage,
                'identity': {
                    'accountId': account_id,
                    'sourceIp': source_ip,
                    'userAgent': headers['User-Agent'],
                }
            }

            try:
                path_params = extract_path_params(path=relative_path, extracted_path=extracted_path)
            except Exception:
                path_params = {}

            result = lambda_api.process_apigateway_invocation(func_arn, relative_path, data_str,
                headers, path_params=path_params, query_string_params=query_string_params,
                method=method, resource_path=path, request_context=request_context)

            if isinstance(result, FlaskResponse):
                return flask_to_requests_response(result)
            if isinstance(result, Response):
                return result

            response = Response()
            parsed_result = result if isinstance(result, dict) else json.loads(result)
            parsed_result = common.json_safe(parsed_result)
            parsed_result = {} if parsed_result is None else parsed_result
            response.status_code = int(parsed_result.get('statusCode', 200))
            response.headers.update(parsed_result.get('headers', {}))
            try:
                if isinstance(parsed_result['body'], dict):
                    response._content = json.dumps(parsed_result['body'])
                else:
                    response._content = to_bytes(parsed_result['body'])
            except Exception:
                response._content = '{}'
            response.headers['Content-Length'] = len(response._content)
            return response
        else:
            msg = 'API Gateway action uri "%s" not yet implemented' % uri
            LOGGER.warning(msg)
            return make_error(msg, 404)

    elif integration['type'] == 'HTTP':
        function = getattr(requests, method.lower())
        if isinstance(data, dict):
            data = json.dumps(data)
        result = function(integration['uri'], data=data, headers=headers)
        return result

    else:
        msg = ('API Gateway integration type "%s" for method "%s" not yet implemented' %
               (integration['type'], method))
        LOGGER.warning(msg)
        return make_error(msg, 404)

    return 200
Esempio n. 21
0
def invoke_rest_api_integration(api_id,
                                stage,
                                integration,
                                method,
                                path,
                                invocation_path,
                                data,
                                headers,
                                resource_path,
                                context={},
                                resource_id=None,
                                response_templates={}):

    relative_path, query_string_params = extract_query_string_params(
        path=invocation_path)
    integration_type = integration.get('type') or integration.get(
        'integrationType')
    uri = integration.get('uri') or integration.get('integrationUri')

    if (uri.startswith('arn:aws:apigateway:')
            and ':lambda:path' in uri) or uri.startswith('arn:aws:lambda'):
        if integration_type in ['AWS', 'AWS_PROXY']:
            func_arn = uri
            if ':lambda:path' in uri:
                func_arn = uri.split(':lambda:path')[1].split(
                    'functions/')[1].split('/invocations')[0]
            data_str = json.dumps(data) if isinstance(data,
                                                      (dict,
                                                       list)) else to_str(data)

            try:
                path_params = extract_path_params(path=relative_path,
                                                  extracted_path=resource_path)
            except Exception:
                path_params = {}

            # apply custom request template
            data_str = apply_template(integration,
                                      'request',
                                      data_str,
                                      path_params=path_params,
                                      query_params=query_string_params,
                                      headers=headers)

            # Sample request context:
            # https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html#api-gateway-create-api-as-simple-proxy-for-lambda-test
            request_context = get_lambda_event_request_context(
                method,
                path,
                data,
                headers,
                integration_uri=uri,
                resource_id=resource_id)
            stage_variables = get_stage_variables(api_id, stage)

            result = lambda_api.process_apigateway_invocation(
                func_arn,
                relative_path,
                data_str,
                stage,
                api_id,
                headers,
                path_params=path_params,
                query_string_params=query_string_params,
                method=method,
                resource_path=path,
                request_context=request_context,
                event_context=context,
                stage_variables=stage_variables)

            if isinstance(result, FlaskResponse):
                response = flask_to_requests_response(result)
            elif isinstance(result, Response):
                response = result
            else:
                response = LambdaResponse()
                parsed_result = result if isinstance(
                    result, dict) else json.loads(str(result or '{}'))
                parsed_result = common.json_safe(parsed_result)
                parsed_result = {} if parsed_result is None else parsed_result
                response.status_code = int(parsed_result.get(
                    'statusCode', 200))
                parsed_headers = parsed_result.get('headers', {})
                if parsed_headers is not None:
                    response.headers.update(parsed_headers)
                try:
                    if isinstance(parsed_result['body'], dict):
                        response._content = json.dumps(parsed_result['body'])
                    else:
                        response._content = to_bytes(parsed_result['body'])
                except Exception:
                    response._content = '{}'
                update_content_length(response)
                response.multi_value_headers = parsed_result.get(
                    'multiValueHeaders') or {}

            # apply custom response template
            response._content = apply_template(integration, 'response',
                                               response._content)
            response.headers['Content-Length'] = str(
                len(response.content or ''))

            return response

        msg = 'API Gateway AWS integration action URI "%s", method "%s" not yet implemented' % (
            uri, method)
        LOGGER.warning(msg)
        return make_error_response(msg, 404)

    elif integration_type == 'AWS':
        if 'kinesis:action/' in uri:
            if uri.endswith('kinesis:action/PutRecords'):
                target = kinesis_listener.ACTION_PUT_RECORDS
            if uri.endswith('kinesis:action/ListStreams'):
                target = kinesis_listener.ACTION_LIST_STREAMS

            template = integration['requestTemplates'][APPLICATION_JSON]
            new_request = aws_stack.render_velocity_template(template, data)
            # forward records to target kinesis stream
            headers = aws_stack.mock_aws_request_headers(service='kinesis')
            headers['X-Amz-Target'] = target
            result = common.make_http_request(url=TEST_KINESIS_URL,
                                              method='POST',
                                              data=new_request,
                                              headers=headers)
            # TODO apply response template..?
            return result

        elif 'states:action/' in uri:
            if uri.endswith('states:action/StartExecution'):
                action = 'StartExecution'
            decoded_data = data.decode()
            if 'stateMachineArn' in decoded_data and 'input' in decoded_data:
                payload = json.loads(decoded_data)
            elif APPLICATION_JSON in integration.get('requestTemplates', {}):
                template = integration['requestTemplates'][APPLICATION_JSON]
                payload = aws_stack.render_velocity_template(template,
                                                             data,
                                                             as_json=True)
            client = aws_stack.connect_to_service('stepfunctions')

            kwargs = {'name': payload['name']} if 'name' in payload else {}
            result = client.start_execution(
                stateMachineArn=payload['stateMachineArn'],
                input=payload['input'],
                **kwargs)
            response = requests_response(
                content={
                    'executionArn': result['executionArn'],
                    'startDate': str(result['startDate'])
                },
                headers=aws_stack.mock_aws_request_headers())
            return response

        if method == 'POST':
            if uri.startswith('arn:aws:apigateway:') and ':sqs:path' in uri:
                template = integration['requestTemplates'][APPLICATION_JSON]
                account_id, queue = uri.split('/')[-2:]
                region_name = uri.split(':')[3]

                new_request = '%s&QueueName=%s' % (
                    aws_stack.render_velocity_template(template, data), queue)
                headers = aws_stack.mock_aws_request_headers(
                    service='sqs', region_name=region_name)

                url = urljoin(TEST_SQS_URL,
                              '%s/%s' % (TEST_AWS_ACCOUNT_ID, queue))
                result = common.make_http_request(url,
                                                  method='POST',
                                                  headers=headers,
                                                  data=new_request)
                return result

        msg = 'API Gateway AWS integration action URI "%s", method "%s" not yet implemented' % (
            uri, method)
        LOGGER.warning(msg)
        return make_error_response(msg, 404)

    elif integration_type == 'AWS_PROXY':
        if uri.startswith('arn:aws:apigateway:') and ':dynamodb:action' in uri:
            # arn:aws:apigateway:us-east-1:dynamodb:action/PutItem&Table=MusicCollection
            table_name = uri.split(':dynamodb:action')[1].split('&Table=')[1]
            action = uri.split(':dynamodb:action')[1].split('&Table=')[0]

            if 'PutItem' in action and method == 'PUT':
                response_template = response_templates.get(
                    'application/json', None)

                if response_template is None:
                    msg = 'Invalid response template defined in integration response.'
                    return make_error_response(msg, 404)

                response_template = json.loads(response_template)
                if response_template['TableName'] != table_name:
                    msg = 'Invalid table name specified in integration response template.'
                    return make_error_response(msg, 404)

                dynamo_client = aws_stack.connect_to_resource('dynamodb')
                table = dynamo_client.Table(table_name)

                event_data = {}
                data_dict = json.loads(data)
                for key, _ in response_template['Item'].items():
                    event_data[key] = data_dict[key]

                table.put_item(Item=event_data)
                response = requests_response(
                    event_data, headers=aws_stack.mock_aws_request_headers())
                return response
        else:
            msg = 'API Gateway action uri "%s", integration type %s not yet implemented' % (
                uri, integration_type)
            LOGGER.warning(msg)
            return make_error_response(msg, 404)

    elif integration_type in ['HTTP_PROXY', 'HTTP']:
        function = getattr(requests, method.lower())

        # apply custom request template
        data = apply_template(integration, 'request', data)

        if isinstance(data, dict):
            data = json.dumps(data)

        result = function(uri, data=data, headers=headers)

        # apply custom response template
        data = apply_template(integration, 'response', data)

        return result

    elif integration_type == 'MOCK':
        # TODO: add logic for MOCK responses
        pass

    if method == 'OPTIONS':
        # fall back to returning CORS headers if this is an OPTIONS request
        return get_cors_response(headers)

    msg = (
        'API Gateway integration type "%s", method "%s", URI "%s" not yet implemented'
        % (integration_type, method, uri))
    LOGGER.warning(msg)
    return make_error_response(msg, 404)
    def forward_request(self, method, path, data, headers):
        data = data and json.loads(data)

        # Paths to match
        regex2 = r'^/restapis/([A-Za-z0-9_\-]+)/([A-Za-z0-9_\-]+)/%s/(.*)$' % PATH_USER_REQUEST

        if re.match(regex2, path):
            search_match = re.search(regex2, path)
            api_id = search_match.group(1)
            relative_path = '/%s' % search_match.group(3)
            try:
                integration = aws_stack.get_apigateway_integration(
                    api_id, method, path=relative_path)
                assert integration
            except Exception:
                # if we have no exact match, try to find an API resource that contains path parameters
                path_map = get_rest_api_paths(rest_api_id=api_id)
                extracted_path, resource = get_resource_for_path(
                    path=relative_path, path_map=path_map) or {}
                integrations = resource.get('resourceMethods', {})
                integration = integrations.get(method, {})
                integration = integration.get('methodIntegration')
                if not integration:

                    if method == 'OPTIONS' and 'Origin' in headers:
                        # default to returning CORS headers if this is an OPTIONS request
                        return get_cors_response(headers)

                    return make_error(
                        'Unable to find integration for path %s' % path, 404)

            uri = integration.get('uri')
            if method == 'POST' and integration['type'] == 'AWS':
                if uri.endswith('kinesis:action/PutRecords'):
                    template = integration['requestTemplates'][
                        APPLICATION_JSON]
                    new_request = aws_stack.render_velocity_template(
                        template, data)

                    # forward records to target kinesis stream
                    headers = aws_stack.mock_aws_request_headers(
                        service='kinesis')
                    headers[
                        'X-Amz-Target'] = kinesis_listener.ACTION_PUT_RECORDS
                    result = common.make_http_request(url=TEST_KINESIS_URL,
                                                      method='POST',
                                                      data=new_request,
                                                      headers=headers)
                    return result
                else:
                    msg = 'API Gateway action uri "%s" not yet implemented' % uri
                    LOGGER.warning(msg)
                    return make_error(msg, 404)

            elif integration['type'] == 'AWS_PROXY':
                if uri.startswith(
                        'arn:aws:apigateway:') and ':lambda:path' in uri:
                    func_arn = uri.split(':lambda:path')[1].split(
                        'functions/')[1].split('/invocations')[0]
                    data_str = json.dumps(data) if isinstance(data,
                                                              dict) else data

                    try:
                        path_params = extract_path_params(
                            path=relative_path, extracted_path=extracted_path)
                    except Exception:
                        path_params = {}
                    result = lambda_api.process_apigateway_invocation(
                        func_arn,
                        relative_path,
                        data_str,
                        headers,
                        path_params=path_params,
                        method=method,
                        resource_path=path)

                    response = Response()
                    parsed_result = result if isinstance(
                        result, dict) else json.loads(result)
                    parsed_result = common.json_safe(parsed_result)
                    response.status_code = int(
                        parsed_result.get('statusCode', 200))
                    response.headers.update(parsed_result.get('headers', {}))
                    try:
                        response_body = parsed_result['body']
                        response._content = json.dumps(response_body)
                    except Exception:
                        response._content = '{}'
                    return response
                else:
                    msg = 'API Gateway action uri "%s" not yet implemented' % uri
                    LOGGER.warning(msg)
                    return make_error(msg, 404)

            elif integration['type'] == 'HTTP':
                function = getattr(requests, method.lower())
                if isinstance(data, dict):
                    data = json.dumps(data)
                result = function(integration['uri'],
                                  data=data,
                                  headers=headers)
                return result

            else:
                msg = (
                    'API Gateway integration type "%s" for method "%s" not yet implemented'
                    % (integration['type'], method))
                LOGGER.warning(msg)
                return make_error(msg, 404)

            return 200

        if re.match(PATH_REGEX_AUTHORIZERS, path):
            return handle_authorizers(method, path, data, headers)

        return True
    def forward_request(self, method, path, data, headers):

        regex2 = r'^/restapis/([A-Za-z0-9_\-]+)/([A-Za-z0-9_\-]+)/%s/(.*)$' % PATH_USER_REQUEST
        if re.match(regex2, path):
            search_match = re.search(regex2, path)
            api_id = search_match.group(1)
            path = '/%s' % search_match.group(3)
            try:
                integration = aws_stack.get_apigateway_integration(api_id, method, path)
            except Exception as e:
                msg = ('API Gateway endpoint "%s" for method "%s" not found' % (path, method))
                LOGGER.warning(msg)
                return make_error(msg, 404)
            uri = integration.get('uri')
            if method == 'POST' and integration['type'] in ['AWS']:
                if uri.endswith('kinesis:action/PutRecords'):
                    template = integration['requestTemplates'][APPLICATION_JSON]
                    new_request = aws_stack.render_velocity_template(template, data)

                    # forward records to target kinesis stream
                    headers = aws_stack.mock_aws_request_headers(service='kinesis')
                    headers['X-Amz-Target'] = kinesis_listener.ACTION_PUT_RECORDS
                    result = common.make_http_request(url=TEST_KINESIS_URL,
                        method='POST', data=new_request, headers=headers)
                    return result
                else:
                    msg = 'API Gateway action uri "%s" not yet implemented' % uri
                    LOGGER.warning(msg)
                    return make_error(msg, 404)

            elif integration['type'] == 'AWS_PROXY':
                if uri.startswith('arn:aws:apigateway:') and ':lambda:path' in uri:
                    func_arn = uri.split(':lambda:path')[1].split('functions/')[1].split('/invocations')[0]
                    data_str = json.dumps(data) if isinstance(data, dict) else data
                    result = lambda_api.process_apigateway_invocation(func_arn, path, data_str, headers)
                    response = Response()
                    parsed_result = json.loads(result)
                    response.status_code = int(parsed_result['statusCode'])
                    response.headers.update(parsed_result['headers'])
                    response_body = parsed_result['body']
                    response._content = json.dumps(response_body)
                    return response
                else:
                    msg = 'API Gateway action uri "%s" not yet implemented' % uri
                    LOGGER.warning(msg)
                    return make_error(msg, 404)

            elif integration['type'] == 'HTTP':
                function = getattr(requests, method.lower())
                if isinstance(data, dict):
                    data = json.dumps(data)
                result = function(integration['uri'], data=data, headers=headers)
                return result

            else:
                msg = ('API Gateway integration type "%s" for method "%s" not yet implemented' %
                    (integration['type'], method))
                LOGGER.warning(msg)
                return make_error(msg, 404)

            return 200

        if re.match(PATH_REGEX_AUTHORIZERS, path):
            return handle_authorizers(method, path, data, headers)

        return True
    def forward_request(self, method, path, data, headers):
        data = data and json.loads(to_str(data))

        # Paths to match
        regex2 = r'^/restapis/([A-Za-z0-9_\-]+)/([A-Za-z0-9_\-]+)/%s/(.*)$' % PATH_USER_REQUEST

        if re.match(regex2, path):
            search_match = re.search(regex2, path)
            api_id = search_match.group(1)
            stage = search_match.group(2)
            relative_path_w_query_params = '/%s' % search_match.group(3)

            relative_path, query_string_params = extract_query_string_params(
                path=relative_path_w_query_params)

            path_map = get_rest_api_paths(rest_api_id=api_id)
            try:
                extracted_path, resource = get_resource_for_path(
                    path=relative_path, path_map=path_map)
            except Exception:
                return make_error('Unable to find path %s' % path, 404)

            integrations = resource.get('resourceMethods', {})
            integration = integrations.get(method, {})
            if not integration:
                integration = integrations.get('ANY', {})
            integration = integration.get('methodIntegration')
            if not integration:

                if method == 'OPTIONS' and 'Origin' in headers:
                    # default to returning CORS headers if this is an OPTIONS request
                    return get_cors_response(headers)

                return make_error(
                    'Unable to find integration for path %s' % path, 404)

            uri = integration.get('uri')
            if method == 'POST' and integration['type'] == 'AWS':
                if uri.endswith('kinesis:action/PutRecords'):
                    template = integration['requestTemplates'][
                        APPLICATION_JSON]
                    new_request = aws_stack.render_velocity_template(
                        template, data)

                    # forward records to target kinesis stream
                    headers = aws_stack.mock_aws_request_headers(
                        service='kinesis')
                    headers[
                        'X-Amz-Target'] = kinesis_listener.ACTION_PUT_RECORDS
                    result = common.make_http_request(url=TEST_KINESIS_URL,
                                                      method='POST',
                                                      data=new_request,
                                                      headers=headers)
                    return result
                else:
                    msg = 'API Gateway action uri "%s" not yet implemented' % uri
                    LOGGER.warning(msg)
                    return make_error(msg, 404)

            elif integration['type'] == 'AWS_PROXY':
                if uri.startswith(
                        'arn:aws:apigateway:') and ':lambda:path' in uri:
                    func_arn = uri.split(':lambda:path')[1].split(
                        'functions/')[1].split('/invocations')[0]
                    account_id = uri.split(':lambda:path')[1].split(
                        ':function:')[0].split(':')[-1]
                    data_str = json.dumps(data) if isinstance(data,
                                                              dict) else data

                    source_ip = headers['X-Forwarded-For'].split(',')[-2]

                    # Sample request context:
                    # https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html#api-gateway-create-api-as-simple-proxy-for-lambda-test
                    request_context = {
                        'path': relative_path,
                        'accountId': account_id,
                        'resourceId': resource.get('id'),
                        'stage': stage,
                        'identity': {
                            'accountId': account_id,
                            'sourceIp': source_ip,
                            'userAgent': headers['User-Agent'],
                        }
                    }

                    try:
                        path_params = extract_path_params(
                            path=relative_path, extracted_path=extracted_path)
                    except Exception:
                        path_params = {}

                    result = lambda_api.process_apigateway_invocation(
                        func_arn,
                        relative_path,
                        data_str,
                        headers,
                        path_params=path_params,
                        query_string_params=query_string_params,
                        method=method,
                        resource_path=path,
                        request_context=request_context)

                    if isinstance(result, FlaskResponse):
                        return flask_to_requests_response(result)

                    response = Response()
                    parsed_result = result if isinstance(
                        result, dict) else json.loads(result)
                    parsed_result = common.json_safe(parsed_result)
                    response.status_code = int(
                        parsed_result.get('statusCode', 200))
                    response.headers.update(parsed_result.get('headers', {}))
                    try:
                        if isinstance(parsed_result['body'], dict):
                            response._content = json.dumps(
                                parsed_result['body'])
                        else:
                            response._content = parsed_result['body']
                    except Exception:
                        response._content = '{}'
                    return response
                else:
                    msg = 'API Gateway action uri "%s" not yet implemented' % uri
                    LOGGER.warning(msg)
                    return make_error(msg, 404)

            elif integration['type'] == 'HTTP':
                function = getattr(requests, method.lower())
                if isinstance(data, dict):
                    data = json.dumps(data)
                result = function(integration['uri'],
                                  data=data,
                                  headers=headers)
                return result

            else:
                msg = (
                    'API Gateway integration type "%s" for method "%s" not yet implemented'
                    % (integration['type'], method))
                LOGGER.warning(msg)
                return make_error(msg, 404)

            return 200

        if re.match(PATH_REGEX_AUTHORIZERS, path):
            return handle_authorizers(method, path, data, headers)

        return True
Esempio n. 25
0
def invoke_rest_api_integration_backend(api_id,
                                        stage,
                                        integration,
                                        method,
                                        path,
                                        invocation_path,
                                        data,
                                        headers,
                                        resource_path,
                                        context={},
                                        resource_id=None,
                                        response_templates={}):

    relative_path, query_string_params = extract_query_string_params(
        path=invocation_path)
    integration_type_orig = integration.get('type') or integration.get(
        'integrationType') or ''
    integration_type = integration_type_orig.upper()
    uri = integration.get('uri') or integration.get('integrationUri') or ''
    try:
        path_params = extract_path_params(path=relative_path,
                                          extracted_path=resource_path)
    except Exception:
        path_params = {}
    if (uri.startswith('arn:aws:apigateway:')
            and ':lambda:path' in uri) or uri.startswith('arn:aws:lambda'):
        if integration_type in ['AWS', 'AWS_PROXY']:
            func_arn = uri
            if ':lambda:path' in uri:
                func_arn = uri.split(':lambda:path')[1].split(
                    'functions/')[1].split('/invocations')[0]

            # apply custom request template
            data_str = data
            try:
                data_str = json.dumps(data) if isinstance(
                    data, (dict, list)) else to_str(data)
                data_str = apply_template(integration,
                                          'request',
                                          data_str,
                                          path_params=path_params,
                                          query_params=query_string_params,
                                          headers=headers)
            except Exception:
                pass

            # Sample request context:
            # https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html#api-gateway-create-api-as-simple-proxy-for-lambda-test
            request_context = get_lambda_event_request_context(
                method,
                path,
                data,
                headers,
                integration_uri=uri,
                resource_id=resource_id,
                resource_path=resource_path)
            stage_variables = get_stage_variables(api_id, stage)

            result = lambda_api.process_apigateway_invocation(
                func_arn,
                relative_path,
                data_str,
                stage,
                api_id,
                headers,
                path_params=path_params,
                query_string_params=query_string_params,
                method=method,
                resource_path=resource_path,
                request_context=request_context,
                event_context=context,
                stage_variables=stage_variables)

            if isinstance(result, FlaskResponse):
                response = flask_to_requests_response(result)
            elif isinstance(result, Response):
                response = result
            else:
                response = LambdaResponse()
                parsed_result = result if isinstance(
                    result, dict) else json.loads(str(result or '{}'))
                parsed_result = common.json_safe(parsed_result)
                parsed_result = {} if parsed_result is None else parsed_result
                response.status_code = int(parsed_result.get(
                    'statusCode', 200))
                parsed_headers = parsed_result.get('headers', {})
                if parsed_headers is not None:
                    response.headers.update(parsed_headers)
                try:
                    if isinstance(parsed_result['body'], dict):
                        response._content = json.dumps(parsed_result['body'])
                    else:
                        body_bytes = to_bytes(parsed_result.get('body') or '')
                        if parsed_result.get('isBase64Encoded', False):
                            body_bytes = base64.b64decode(body_bytes)
                        response._content = body_bytes
                except Exception as e:
                    LOG.warning("Couldn't set lambda response content: %s" % e)
                    response._content = '{}'
                update_content_length(response)
                response.multi_value_headers = parsed_result.get(
                    'multiValueHeaders') or {}

            # apply custom response template
            response._content = apply_template(integration, 'response',
                                               response._content)
            response.headers['Content-Length'] = str(
                len(response.content or ''))

            return response

        raise Exception(
            'API Gateway %s integration action "%s", method "%s" not yet implemented'
            % (integration_type, uri, method))

    elif integration_type == 'AWS':
        if 'kinesis:action/' in uri:
            if uri.endswith('kinesis:action/PutRecord'):
                target = kinesis_listener.ACTION_PUT_RECORD
            if uri.endswith('kinesis:action/PutRecords'):
                target = kinesis_listener.ACTION_PUT_RECORDS
            if uri.endswith('kinesis:action/ListStreams'):
                target = kinesis_listener.ACTION_LIST_STREAMS

            template = integration['requestTemplates'][APPLICATION_JSON]
            new_request = aws_stack.render_velocity_template(template, data)
            # forward records to target kinesis stream
            headers = aws_stack.mock_aws_request_headers(service='kinesis')
            headers['X-Amz-Target'] = target
            result = common.make_http_request(url=TEST_KINESIS_URL,
                                              method='POST',
                                              data=new_request,
                                              headers=headers)
            # TODO apply response template..?
            return result

        elif 'states:action/' in uri:
            if uri.endswith('states:action/StartExecution'):
                action = 'StartExecution'
            decoded_data = data.decode()
            payload = {}
            if 'stateMachineArn' in decoded_data and 'input' in decoded_data:
                payload = json.loads(decoded_data)
            elif APPLICATION_JSON in integration.get('requestTemplates', {}):
                template = integration['requestTemplates'][APPLICATION_JSON]
                payload = aws_stack.render_velocity_template(template,
                                                             data,
                                                             as_json=True)
            client = aws_stack.connect_to_service('stepfunctions')

            kwargs = {'name': payload['name']} if 'name' in payload else {}
            result = client.start_execution(
                stateMachineArn=payload['stateMachineArn'],
                input=payload['input'],
                **kwargs)
            response = requests_response(
                content={
                    'executionArn': result['executionArn'],
                    'startDate': str(result['startDate'])
                },
                headers=aws_stack.mock_aws_request_headers())
            response.headers['content-type'] = APPLICATION_JSON
            return response
        elif 's3:path/' in uri and method == 'GET':
            s3 = aws_stack.connect_to_service('s3')
            uri_match = re.match(TARGET_REGEX_S3_URI, uri)
            if uri_match:
                bucket, object_key = uri_match.group('bucket', 'object')
                LOG.debug('Getting request for bucket %s object %s', bucket,
                          object_key)
                try:
                    object = s3.get_object(Bucket=bucket, Key=object_key)
                except s3.exceptions.NoSuchKey:
                    msg = 'Object %s not found' % object_key
                    LOG.debug(msg)
                    return make_error_response(msg, 404)

                headers = aws_stack.mock_aws_request_headers(service='s3')

                if object.get('ContentType'):
                    headers['Content-Type'] = object['ContentType']

                # stream used so large files do not fill memory
                response = request_response_stream(stream=object['Body'],
                                                   headers=headers)
                return response
            else:
                msg = 'Request URI does not match s3 specifications'
                LOG.warning(msg)
                return make_error_response(msg, 400)

        if method == 'POST':
            if uri.startswith('arn:aws:apigateway:') and ':sqs:path' in uri:
                template = integration['requestTemplates'][APPLICATION_JSON]
                account_id, queue = uri.split('/')[-2:]
                region_name = uri.split(':')[3]

                new_request = '%s&QueueName=%s' % (
                    aws_stack.render_velocity_template(template, data), queue)
                headers = aws_stack.mock_aws_request_headers(
                    service='sqs', region_name=region_name)

                url = urljoin(TEST_SQS_URL,
                              '%s/%s' % (TEST_AWS_ACCOUNT_ID, queue))
                result = common.make_http_request(url,
                                                  method='POST',
                                                  headers=headers,
                                                  data=new_request)
                return result

        raise Exception(
            'API Gateway AWS integration action URI "%s", method "%s" not yet implemented'
            % (uri, method))

    elif integration_type == 'AWS_PROXY':
        if uri.startswith('arn:aws:apigateway:') and ':dynamodb:action' in uri:
            # arn:aws:apigateway:us-east-1:dynamodb:action/PutItem&Table=MusicCollection
            table_name = uri.split(':dynamodb:action')[1].split('&Table=')[1]
            action = uri.split(':dynamodb:action')[1].split('&Table=')[0]

            if 'PutItem' in action and method == 'PUT':
                response_template = response_templates.get('application/json')

                if response_template is None:
                    msg = 'Invalid response template defined in integration response.'
                    LOG.info('%s Existing: %s' % (msg, response_templates))
                    return make_error_response(msg, 404)

                response_template = json.loads(response_template)
                if response_template['TableName'] != table_name:
                    msg = 'Invalid table name specified in integration response template.'
                    return make_error_response(msg, 404)

                dynamo_client = aws_stack.connect_to_resource('dynamodb')
                table = dynamo_client.Table(table_name)

                event_data = {}
                data_dict = json.loads(data)
                for key, _ in response_template['Item'].items():
                    event_data[key] = data_dict[key]

                table.put_item(Item=event_data)
                response = requests_response(
                    event_data, headers=aws_stack.mock_aws_request_headers())
                return response
        else:
            raise Exception(
                'API Gateway action uri "%s", integration type %s not yet implemented'
                % (uri, integration_type))

    elif integration_type in ['HTTP_PROXY', 'HTTP']:

        if ':servicediscovery:' in uri:
            # check if this is a servicediscovery integration URI
            client = aws_stack.connect_to_service('servicediscovery')
            service_id = uri.split('/')[-1]
            instances = client.list_instances(
                ServiceId=service_id)['Instances']
            instance = (instances or [None])[0]
            if instance and instance.get('Id'):
                uri = 'http://%s/%s' % (instance['Id'],
                                        invocation_path.lstrip('/'))

        # apply custom request template
        data = apply_template(integration, 'request', data)
        if isinstance(data, dict):
            data = json.dumps(data)
        uri = apply_request_parameter(integration=integration,
                                      path_params=path_params)
        function = getattr(requests, method.lower())
        result = function(uri, data=data, headers=headers)
        # apply custom response template
        data = apply_template(integration, 'response', data)
        return result

    elif integration_type == 'MOCK':
        # return empty response - details filled in via responseParameters above...
        return requests_response({})

    if method == 'OPTIONS':
        # fall back to returning CORS headers if this is an OPTIONS request
        return get_cors_response(headers)

    raise Exception(
        'API Gateway integration type "%s", method "%s", URI "%s" not yet implemented'
        % (integration_type, method, uri))
Esempio n. 26
0
def invoke_rest_api(api_id,
                    stage,
                    method,
                    invocation_path,
                    data,
                    headers,
                    path=None):
    path = path or invocation_path
    relative_path, query_string_params = extract_query_string_params(
        path=invocation_path)

    # run gateway authorizers for this request
    authorize_invocation(api_id, headers)
    path_map = helpers.get_rest_api_paths(rest_api_id=api_id)
    try:
        extracted_path, resource = get_resource_for_path(path=relative_path,
                                                         path_map=path_map)
    except Exception:
        return make_error_response('Unable to find path %s' % path, 404)

    api_key_required = resource.get('resourceMethods',
                                    {}).get(method, {}).get('apiKeyRequired')
    if not is_api_key_valid(api_key_required, headers, stage):
        return make_error_response('Access denied - invalid API key', 403)

    integrations = resource.get('resourceMethods', {})
    integration = integrations.get(method, {})
    if not integration:
        integration = integrations.get('ANY', {})
    integration = integration.get('methodIntegration')
    if not integration:
        if method == 'OPTIONS' and 'Origin' in headers:
            # default to returning CORS headers if this is an OPTIONS request
            return get_cors_response(headers)
        return make_error_response(
            'Unable to find integration for path %s' % path, 404)

    uri = integration.get('uri') or ''
    integration_type = integration['type'].upper()

    if uri.startswith('arn:aws:apigateway:') and ':lambda:path' in uri:
        if integration_type in ['AWS', 'AWS_PROXY']:
            func_arn = uri.split(':lambda:path')[1].split(
                'functions/')[1].split('/invocations')[0]
            data_str = json.dumps(data) if isinstance(data,
                                                      (dict,
                                                       list)) else to_str(data)
            account_id = uri.split(':lambda:path')[1].split(
                ':function:')[0].split(':')[-1]
            source_ip = headers['X-Forwarded-For'].split(',')[-2]

            try:
                path_params = extract_path_params(
                    path=relative_path, extracted_path=extracted_path)
            except Exception:
                path_params = {}

            # apply custom request template
            data_str = apply_template(integration,
                                      'request',
                                      data_str,
                                      path_params=path_params,
                                      query_params=query_string_params,
                                      headers=headers)

            # Sample request context:
            # https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html#api-gateway-create-api-as-simple-proxy-for-lambda-test
            request_context = {
                # adding stage to the request context path.
                # https://github.com/localstack/localstack/issues/2210
                'path': '/' + stage + relative_path,
                'accountId': account_id,
                'resourceId': resource.get('id'),
                'stage': stage,
                'identity': {
                    'accountId': account_id,
                    'sourceIp': source_ip,
                    'userAgent': headers['User-Agent'],
                },
                'httpMethod': method,
                'protocol': 'HTTP/1.1',
                'requestTime': datetime.datetime.utcnow(),
                'requestTimeEpoch': int(time.time() * 1000),
            }

            result = lambda_api.process_apigateway_invocation(
                func_arn,
                relative_path,
                data_str,
                stage,
                api_id,
                headers,
                path_params=path_params,
                query_string_params=query_string_params,
                method=method,
                resource_path=path,
                request_context=request_context)

            if isinstance(result, FlaskResponse):
                response = flask_to_requests_response(result)
            elif isinstance(result, Response):
                response = result
            else:
                response = LambdaResponse()
                parsed_result = result if isinstance(
                    result, dict) else json.loads(str(result or '{}'))
                parsed_result = common.json_safe(parsed_result)
                parsed_result = {} if parsed_result is None else parsed_result
                response.status_code = int(parsed_result.get(
                    'statusCode', 200))
                parsed_headers = parsed_result.get('headers', {})
                if parsed_headers is not None:
                    response.headers.update(parsed_headers)
                try:
                    if isinstance(parsed_result['body'], dict):
                        response._content = json.dumps(parsed_result['body'])
                    else:
                        response._content = to_bytes(parsed_result['body'])
                except Exception:
                    response._content = '{}'
                update_content_length(response)
                response.multi_value_headers = parsed_result.get(
                    'multiValueHeaders') or {}

            # apply custom response template
            response._content = apply_template(integration, 'response',
                                               response._content)
            response.headers['Content-Length'] = str(
                len(response.content or ''))

            return response

        msg = 'API Gateway AWS integration action URI "%s", method "%s" not yet implemented' % (
            uri, method)
        LOGGER.warning(msg)
        return make_error_response(msg, 404)

    elif integration_type == 'AWS':
        if 'kinesis:action/' in uri:
            if uri.endswith('kinesis:action/PutRecords'):
                target = kinesis_listener.ACTION_PUT_RECORDS
            if uri.endswith('kinesis:action/ListStreams'):
                target = kinesis_listener.ACTION_LIST_STREAMS

            template = integration['requestTemplates'][APPLICATION_JSON]
            new_request = aws_stack.render_velocity_template(template, data)
            # forward records to target kinesis stream
            headers = aws_stack.mock_aws_request_headers(service='kinesis')
            headers['X-Amz-Target'] = target
            result = common.make_http_request(url=TEST_KINESIS_URL,
                                              method='POST',
                                              data=new_request,
                                              headers=headers)
            # TODO apply response template..?
            return result

        if method == 'POST':
            if uri.startswith('arn:aws:apigateway:') and ':sqs:path' in uri:
                template = integration['requestTemplates'][APPLICATION_JSON]
                account_id, queue = uri.split('/')[-2:]
                region_name = uri.split(':')[3]

                new_request = '%s&QueueName=%s' % (
                    aws_stack.render_velocity_template(template, data), queue)
                headers = aws_stack.mock_aws_request_headers(
                    service='sqs', region_name=region_name)

                url = urljoin(TEST_SQS_URL,
                              '%s/%s' % (TEST_AWS_ACCOUNT_ID, queue))
                result = common.make_http_request(url,
                                                  method='POST',
                                                  headers=headers,
                                                  data=new_request)
                return result

        msg = 'API Gateway AWS integration action URI "%s", method "%s" not yet implemented' % (
            uri, method)
        LOGGER.warning(msg)
        return make_error_response(msg, 404)

    elif integration_type == 'AWS_PROXY':
        if uri.startswith('arn:aws:apigateway:') and ':dynamodb:action' in uri:
            # arn:aws:apigateway:us-east-1:dynamodb:action/PutItem&Table=MusicCollection
            table_name = uri.split(':dynamodb:action')[1].split('&Table=')[1]
            action = uri.split(':dynamodb:action')[1].split('&Table=')[0]

            if 'PutItem' in action and method == 'PUT':
                response_template = path_map.get(relative_path, {}).get('resourceMethods', {})\
                    .get(method, {}).get('methodIntegration', {}).\
                    get('integrationResponses', {}).get('200', {}).get('responseTemplates', {})\
                    .get('application/json', None)

                if response_template is None:
                    msg = 'Invalid response template defined in integration response.'
                    return make_error_response(msg, 404)

                response_template = json.loads(response_template)
                if response_template['TableName'] != table_name:
                    msg = 'Invalid table name specified in integration response template.'
                    return make_error_response(msg, 404)

                dynamo_client = aws_stack.connect_to_resource('dynamodb')
                table = dynamo_client.Table(table_name)

                event_data = {}
                data_dict = json.loads(data)
                for key, _ in response_template['Item'].items():
                    event_data[key] = data_dict[key]

                table.put_item(Item=event_data)
                response = requests_response(
                    event_data, headers=aws_stack.mock_aws_request_headers())
                return response
        else:
            msg = 'API Gateway action uri "%s" not yet implemented' % uri
            LOGGER.warning(msg)
            return make_error_response(msg, 404)

    elif integration_type in ['HTTP_PROXY', 'HTTP']:
        function = getattr(requests, method.lower())

        # apply custom request template
        data = apply_template(integration, 'request', data)

        if isinstance(data, dict):
            data = json.dumps(data)

        result = function(integration['uri'], data=data, headers=headers)

        # apply custom response template
        data = apply_template(integration, 'response', data)

        return result

    elif integration_type == 'MOCK':
        # TODO: add logic for MOCK responses
        pass

    if method == 'OPTIONS':
        # fall back to returning CORS headers if this is an OPTIONS request
        return get_cors_response(headers)

    msg = (
        'API Gateway integration type "%s", method "%s", URI "%s" not yet implemented'
        % (integration['type'], method, integration.get('uri')))
    LOGGER.warning(msg)
    return make_error_response(msg, 404)
Esempio n. 27
0
def invoke_rest_api_integration_backend(
        invocation_context: ApiInvocationContext, integration: Dict):
    # define local aliases from invocation context
    invocation_path = invocation_context.path_with_query_string
    method = invocation_context.method
    path = invocation_context.path
    data = invocation_context.data
    headers = invocation_context.headers
    api_id = invocation_context.api_id
    stage = invocation_context.stage
    resource_path = invocation_context.resource_path
    response_templates = invocation_context.response_templates

    # extract integration type and path parameters
    relative_path, query_string_params = extract_query_string_params(
        path=invocation_path)
    integration_type_orig = integration.get("type") or integration.get(
        "integrationType") or ""
    integration_type = integration_type_orig.upper()
    uri = integration.get("uri") or integration.get("integrationUri") or ""
    try:
        path_params = extract_path_params(path=relative_path,
                                          extracted_path=resource_path)
    except Exception:
        path_params = {}

    if (uri.startswith("arn:aws:apigateway:")
            and ":lambda:path" in uri) or uri.startswith("arn:aws:lambda"):
        if integration_type in ["AWS", "AWS_PROXY"]:
            func_arn = uri
            if ":lambda:path" in uri:
                func_arn = (uri.split(":lambda:path")[1].split("functions/")
                            [1].split("/invocations")[0])

            # apply custom request template
            data_str = data
            is_base64_encoded = False
            try:
                data_str = json.dumps(data) if isinstance(
                    data, (dict, list)) else to_str(data)
                data_str = apply_template(
                    integration,
                    "request",
                    data_str,
                    path_params=path_params,
                    query_params=query_string_params,
                    headers=headers,
                )
            except UnicodeDecodeError:
                data_str = base64.b64encode(data_str)
                is_base64_encoded = True
            except Exception as e:
                LOG.warning(
                    "Unable to convert API Gateway payload to str: %s" % (e))
                pass

            # Sample request context:
            # https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html#api-gateway-create-api-as-simple-proxy-for-lambda-test
            request_context = get_lambda_event_request_context(
                invocation_context)
            stage_variables = (get_stage_variables(api_id, stage)
                               if not is_test_invoke_method(method, path) else
                               None)
            # TODO: change this signature to InvocationContext as well!
            result = lambda_api.process_apigateway_invocation(
                func_arn,
                relative_path,
                data_str,
                stage,
                api_id,
                headers,
                is_base64_encoded=is_base64_encoded,
                path_params=path_params,
                query_string_params=query_string_params,
                method=method,
                resource_path=resource_path,
                request_context=request_context,
                event_context=invocation_context.context,
                stage_variables=stage_variables,
            )

            if isinstance(result, FlaskResponse):
                response = flask_to_requests_response(result)
            elif isinstance(result, Response):
                response = result
            else:
                response = LambdaResponse()
                parsed_result = (result if isinstance(result, dict) else
                                 json.loads(str(result or "{}")))
                parsed_result = common.json_safe(parsed_result)
                parsed_result = {} if parsed_result is None else parsed_result
                response.status_code = int(parsed_result.get(
                    "statusCode", 200))
                parsed_headers = parsed_result.get("headers", {})
                if parsed_headers is not None:
                    response.headers.update(parsed_headers)
                try:
                    result_body = parsed_result.get("body")
                    if isinstance(result_body, dict):
                        response._content = json.dumps(result_body)
                    else:
                        body_bytes = to_bytes(to_str(result_body or ""))
                        if parsed_result.get("isBase64Encoded", False):
                            body_bytes = base64.b64decode(body_bytes)
                        response._content = body_bytes
                except Exception as e:
                    LOG.warning("Couldn't set Lambda response content: %s" % e)
                    response._content = "{}"
                update_content_length(response)
                response.multi_value_headers = parsed_result.get(
                    "multiValueHeaders") or {}

            # apply custom response template
            response._content = apply_template(integration, "response",
                                               response._content)
            response.headers["Content-Length"] = str(
                len(response.content or ""))

            return response

        raise Exception(
            'API Gateway integration type "%s", action "%s", method "%s" invalid or not yet implemented'
            % (integration_type, uri, method))

    elif integration_type == "AWS":
        if "kinesis:action/" in uri:
            if uri.endswith("kinesis:action/PutRecord"):
                target = kinesis_listener.ACTION_PUT_RECORD
            elif uri.endswith("kinesis:action/PutRecords"):
                target = kinesis_listener.ACTION_PUT_RECORDS
            elif uri.endswith("kinesis:action/ListStreams"):
                target = kinesis_listener.ACTION_LIST_STREAMS
            else:
                LOG.info(
                    "Unexpected API Gateway integration URI '%s' for integration type %s",
                    uri,
                    integration_type,
                )
                target = ""

            # apply request templates
            new_data = apply_request_response_templates(
                data,
                integration.get("requestTemplates"),
                content_type=APPLICATION_JSON)
            # forward records to target kinesis stream
            headers = aws_stack.mock_aws_request_headers(service="kinesis")
            headers["X-Amz-Target"] = target
            result = common.make_http_request(url=config.TEST_KINESIS_URL,
                                              method="POST",
                                              data=new_data,
                                              headers=headers)
            # apply response template
            result = apply_request_response_templates(
                result, response_templates, content_type=APPLICATION_JSON)
            return result

        elif "states:action/" in uri:
            action = uri.split("/")[-1]
            payload = {}

            if APPLICATION_JSON in integration.get("requestTemplates", {}):
                payload = apply_request_response_templates(
                    data,
                    integration.get("requestTemplates"),
                    content_type=APPLICATION_JSON,
                    as_json=True,
                )
            else:
                payload = json.loads(data.decode("utf-8"))
            client = aws_stack.connect_to_service("stepfunctions")

            # Hot fix since step functions local package responses: Unsupported Operation: 'StartSyncExecution'
            method_name = (camel_to_snake_case(action)
                           if action != "StartSyncExecution" else
                           "start_execution")

            try:
                method = getattr(client, method_name)
            except AttributeError:
                msg = "Invalid step function action: %s" % method_name
                LOG.error(msg)
                return make_error_response(msg, 400)

            result = method(**payload, )
            result = json_safe(
                {k: result[k]
                 for k in result if k not in "ResponseMetadata"})
            response = requests_response(
                content=result,
                headers=aws_stack.mock_aws_request_headers(),
            )

            if action == "StartSyncExecution":
                # poll for the execution result and return it
                result = await_sfn_execution_result(result["executionArn"])
                result_status = result.get("status")
                if result_status != "SUCCEEDED":
                    return make_error_response(
                        "StepFunctions execution %s failed with status '%s'" %
                        (result["executionArn"], result_status),
                        500,
                    )
                result = json_safe(result)
                response = requests_response(content=result)

            # apply response templates
            response = apply_request_response_templates(
                response, response_templates, content_type=APPLICATION_JSON)
            return response

        elif "s3:path/" in uri and method == "GET":
            s3 = aws_stack.connect_to_service("s3")
            uri_match = re.match(TARGET_REGEX_S3_URI, uri)
            if uri_match:
                bucket, object_key = uri_match.group("bucket", "object")
                LOG.debug("Getting request for bucket %s object %s", bucket,
                          object_key)
                try:
                    object = s3.get_object(Bucket=bucket, Key=object_key)
                except s3.exceptions.NoSuchKey:
                    msg = "Object %s not found" % object_key
                    LOG.debug(msg)
                    return make_error_response(msg, 404)

                headers = aws_stack.mock_aws_request_headers(service="s3")

                if object.get("ContentType"):
                    headers["Content-Type"] = object["ContentType"]

                # stream used so large files do not fill memory
                response = request_response_stream(stream=object["Body"],
                                                   headers=headers)
                return response
            else:
                msg = "Request URI does not match s3 specifications"
                LOG.warning(msg)
                return make_error_response(msg, 400)

        if method == "POST":
            if uri.startswith("arn:aws:apigateway:") and ":sqs:path" in uri:
                template = integration["requestTemplates"][APPLICATION_JSON]
                account_id, queue = uri.split("/")[-2:]
                region_name = uri.split(":")[3]

                new_request = "%s&QueueName=%s" % (
                    aws_stack.render_velocity_template(template, data),
                    queue,
                )
                headers = aws_stack.mock_aws_request_headers(
                    service="sqs", region_name=region_name)

                url = urljoin(config.TEST_SQS_URL,
                              "%s/%s" % (TEST_AWS_ACCOUNT_ID, queue))
                result = common.make_http_request(url,
                                                  method="POST",
                                                  headers=headers,
                                                  data=new_request)
                return result

        raise Exception(
            'API Gateway AWS integration action URI "%s", method "%s" not yet implemented'
            % (uri, method))

    elif integration_type == "AWS_PROXY":
        if uri.startswith("arn:aws:apigateway:") and ":dynamodb:action" in uri:
            # arn:aws:apigateway:us-east-1:dynamodb:action/PutItem&Table=MusicCollection
            table_name = uri.split(":dynamodb:action")[1].split("&Table=")[1]
            action = uri.split(":dynamodb:action")[1].split("&Table=")[0]

            if "PutItem" in action and method == "PUT":
                response_template = response_templates.get("application/json")

                if response_template is None:
                    msg = "Invalid response template defined in integration response."
                    LOG.info("%s Existing: %s" % (msg, response_templates))
                    return make_error_response(msg, 404)

                response_template = json.loads(response_template)
                if response_template["TableName"] != table_name:
                    msg = "Invalid table name specified in integration response template."
                    return make_error_response(msg, 404)

                dynamo_client = aws_stack.connect_to_resource("dynamodb")
                table = dynamo_client.Table(table_name)

                event_data = {}
                data_dict = json.loads(data)
                for key, _ in response_template["Item"].items():
                    event_data[key] = data_dict[key]

                table.put_item(Item=event_data)
                response = requests_response(event_data)
                return response
        else:
            raise Exception(
                'API Gateway action uri "%s", integration type %s not yet implemented'
                % (uri, integration_type))

    elif integration_type in ["HTTP_PROXY", "HTTP"]:

        if ":servicediscovery:" in uri:
            # check if this is a servicediscovery integration URI
            client = aws_stack.connect_to_service("servicediscovery")
            service_id = uri.split("/")[-1]
            instances = client.list_instances(
                ServiceId=service_id)["Instances"]
            instance = (instances or [None])[0]
            if instance and instance.get("Id"):
                uri = "http://%s/%s" % (instance["Id"],
                                        invocation_path.lstrip("/"))

        # apply custom request template
        data = apply_template(integration, "request", data)
        if isinstance(data, dict):
            data = json.dumps(data)
        uri = apply_request_parameter(uri,
                                      integration=integration,
                                      path_params=path_params)
        result = requests.request(method=method,
                                  url=uri,
                                  data=data,
                                  headers=headers)
        # apply custom response template
        result = apply_template(integration, "response", result)
        return result

    elif integration_type == "MOCK":
        # return empty response - details filled in via responseParameters above...
        return requests_response({})

    if method == "OPTIONS":
        # fall back to returning CORS headers if this is an OPTIONS request
        return get_cors_response(headers)

    raise Exception(
        'API Gateway integration type "%s", method "%s", URI "%s" not yet implemented'
        % (integration_type, method, uri))
Esempio n. 28
0
def update_apigateway(method,
                      path,
                      data,
                      headers,
                      response=None,
                      return_forward_info=False):
    if return_forward_info:

        regex2 = r'^/restapis/([A-Za-z0-9_\-]+)/([A-Za-z0-9_\-]+)/%s/([^/]*)$' % PATH_USER_REQUEST
        if re.match(regex2, path):
            search_match = re.search(regex2, path)
            api_id = search_match.group(1)
            sub_path = '/%s' % search_match.group(3)
            try:
                integration = aws_stack.get_apigateway_integration(
                    api_id, method, sub_path)
            except Exception as e:
                msg = ('API Gateway endpoint "%s" for method "%s" not found' %
                       (path, method))
                LOGGER.warning(msg)
                return make_error(msg, 404)
            if method == 'POST' and integration['type'] == 'AWS':
                if integration['uri'].endswith('kinesis:action/PutRecords'):
                    template = integration['requestTemplates'][
                        APPLICATION_JSON]
                    new_request = aws_stack.render_velocity_template(
                        template, data)

                    # forward records to target kinesis stream
                    headers = aws_stack.mock_aws_request_headers(
                        service='kinesis')
                    headers['X-Amz-Target'] = KINESIS_ACTION_PUT_RECORDS
                    result = common.make_http_request(url=TEST_KINESIS_URL,
                                                      method='POST',
                                                      data=new_request,
                                                      headers=headers)
                    return result
                else:
                    msg = 'API Gateway action uri "%s" not yet implemented' % integration[
                        'uri']
                    LOGGER.warning(msg)
                    return make_error(msg, 404)

            elif integration['type'] == 'HTTP':
                function = getattr(requests, method.lower())
                if isinstance(data, dict):
                    data = json.dumps(data)
                result = function(integration['uri'],
                                  data=data,
                                  headers=headers)
                return result

            else:
                msg = (
                    'API Gateway integration type "%s" for method "%s" not yet implemented'
                    % (integration['type'], method))
                LOGGER.warning(msg)
                return make_error(msg, 404)

            return 200

        return True
Esempio n. 29
0
    def forward_request(self, method, path, data, headers):
        data = data and json.loads(to_str(data))

        # Paths to match
        regex2 = r'^/restapis/([A-Za-z0-9_\-]+)/([A-Za-z0-9_\-]+)/%s/(.*)$' % PATH_USER_REQUEST

        if re.match(regex2, path):
            search_match = re.search(regex2, path)
            api_id = search_match.group(1)
            relative_path = '/%s' % search_match.group(3)
            try:
                integration = aws_stack.get_apigateway_integration(api_id, method, path=relative_path)
                assert integration
            except Exception:
                # if we have no exact match, try to find an API resource that contains path parameters
                path_map = get_rest_api_paths(rest_api_id=api_id)
                try:
                    extracted_path, resource = get_resource_for_path(path=relative_path, path_map=path_map)
                except Exception:
                    return make_error('Unable to find path %s' % path, 404)

                integrations = resource.get('resourceMethods', {})
                integration = integrations.get(method, {})
                integration = integration.get('methodIntegration')
                if not integration:

                    if method == 'OPTIONS' and 'Origin' in headers:
                        # default to returning CORS headers if this is an OPTIONS request
                        return get_cors_response(headers)

                    return make_error('Unable to find integration for path %s' % path, 404)

            uri = integration.get('uri')
            if method == 'POST' and integration['type'] == 'AWS':
                if uri.endswith('kinesis:action/PutRecords'):
                    template = integration['requestTemplates'][APPLICATION_JSON]
                    new_request = aws_stack.render_velocity_template(template, data)

                    # forward records to target kinesis stream
                    headers = aws_stack.mock_aws_request_headers(service='kinesis')
                    headers['X-Amz-Target'] = kinesis_listener.ACTION_PUT_RECORDS
                    result = common.make_http_request(url=TEST_KINESIS_URL,
                        method='POST', data=new_request, headers=headers)
                    return result
                else:
                    msg = 'API Gateway action uri "%s" not yet implemented' % uri
                    LOGGER.warning(msg)
                    return make_error(msg, 404)

            elif integration['type'] == 'AWS_PROXY':
                if uri.startswith('arn:aws:apigateway:') and ':lambda:path' in uri:
                    func_arn = uri.split(':lambda:path')[1].split('functions/')[1].split('/invocations')[0]
                    data_str = json.dumps(data) if isinstance(data, dict) else data

                    try:
                        path_params = extract_path_params(path=relative_path, extracted_path=extracted_path)
                    except Exception:
                        path_params = {}
                    result = lambda_api.process_apigateway_invocation(func_arn, relative_path, data_str,
                        headers, path_params=path_params, method=method, resource_path=path)

                    if isinstance(result, FlaskResponse):
                        return flask_to_requests_response(result)

                    response = Response()
                    parsed_result = result if isinstance(result, dict) else json.loads(result)
                    parsed_result = common.json_safe(parsed_result)
                    response.status_code = int(parsed_result.get('statusCode', 200))
                    response.headers.update(parsed_result.get('headers', {}))
                    try:
                        if isinstance(parsed_result['body'], dict):
                            response._content = json.dumps(parsed_result['body'])
                        else:
                            response._content = parsed_result['body']
                    except Exception:
                        response._content = '{}'
                    return response
                else:
                    msg = 'API Gateway action uri "%s" not yet implemented' % uri
                    LOGGER.warning(msg)
                    return make_error(msg, 404)

            elif integration['type'] == 'HTTP':
                function = getattr(requests, method.lower())
                if isinstance(data, dict):
                    data = json.dumps(data)
                result = function(integration['uri'], data=data, headers=headers)
                return result

            else:
                msg = ('API Gateway integration type "%s" for method "%s" not yet implemented' %
                    (integration['type'], method))
                LOGGER.warning(msg)
                return make_error(msg, 404)

            return 200

        if re.match(PATH_REGEX_AUTHORIZERS, path):
            return handle_authorizers(method, path, data, headers)

        return True