def invoke_rest_api(api_id, stage, method, invocation_path, data, headers, path=None, context={}): 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) res_methods = path_map.get(relative_path, {}).get('resourceMethods', {}) meth_integration = res_methods.get(method, {}).get('methodIntegration', {}) int_responses = meth_integration.get('integrationResponses', {}) response_templates = int_responses.get('200', {}).get('responseTemplates', {}) return invoke_rest_api_integration(api_id, stage, integration, method, path, invocation_path, data, headers, resource_path=extracted_path, context=context, resource_id=resource.get('id'), response_templates=response_templates)
def invoke_rest_api(invocation_context: ApiInvocationContext): invocation_path = invocation_context.path_with_query_string raw_path = invocation_context.path or invocation_path method = invocation_context.method headers = invocation_context.headers # run gateway authorizers for this request authorize_invocation(invocation_context) extracted_path, resource = get_target_resource_details(invocation_context) if not resource: return make_error_response( "Unable to find path %s" % invocation_context.path, 404) # validate request validator = RequestValidator(invocation_context, aws_stack.connect_to_service("apigateway")) if not validator.is_request_valid(): return make_error_response("Invalid request body", 400) api_key_required = resource.get("resourceMethods", {}).get(method, {}).get("apiKeyRequired") if not is_api_key_valid(api_key_required, headers, invocation_context.stage): return make_error_response("Access denied - invalid API key", 403) integrations = resource.get("resourceMethods", {}) integration = integrations.get(method, {}) if not integration: # HttpMethod: '*' # ResourcePath: '/*' - produces 'X-AMAZON-APIGATEWAY-ANY-METHOD' integration = integrations.get("ANY", {}) or integrations.get( "X-AMAZON-APIGATEWAY-ANY-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_response( "Unable to find integration for: %s %s (%s)" % (method, invocation_path, raw_path), 404, ) res_methods = resource.get("resourceMethods", {}) meth_integration = res_methods.get(method, {}).get("methodIntegration", {}) int_responses = meth_integration.get("integrationResponses", {}) response_templates = int_responses.get("200", {}).get("responseTemplates", {}) # update fields in invocation context, then forward request to next handler invocation_context.resource = resource invocation_context.resource_path = extracted_path invocation_context.response_templates = response_templates invocation_context.integration = integration return invoke_rest_api_integration(invocation_context)
def invoke_rest_api(invocation_context: ApiInvocationContext): invocation_path = invocation_context.path_with_query_string raw_path = invocation_context.path or invocation_path method = invocation_context.method headers = invocation_context.headers # run gateway authorizers for this request authorize_invocation(invocation_context) extracted_path, resource = get_target_resource_details(invocation_context) if not resource: return make_error_response( "Unable to find path %s" % invocation_context.path, 404) api_key_required = resource.get("resourceMethods", {}).get(method, {}).get("apiKeyRequired") if not is_api_key_valid(api_key_required, headers, invocation_context.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: %s %s (%s)" % (method, invocation_path, raw_path), 404, ) res_methods = resource.get("resourceMethods", {}) meth_integration = res_methods.get(method, {}).get("methodIntegration", {}) int_responses = meth_integration.get("integrationResponses", {}) response_templates = int_responses.get("200", {}).get("responseTemplates", {}) # update fields in invocation context, then forward request to next handler invocation_context.resource = resource invocation_context.resource_path = extracted_path invocation_context.response_templates = response_templates invocation_context.integration = integration result = invoke_rest_api_integration(invocation_context) return result
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)
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
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 invoke_rest_api_integration_backend( invocation_context: ApiInvocationContext): # define local aliases from invocation context invocation_path = invocation_context.path_with_query_string method = invocation_context.method data = invocation_context.data headers = invocation_context.headers api_id = invocation_context.api_id stage = invocation_context.stage resource_path = invocation_context.resource_path integration = invocation_context.integration integration_response = integration.get("integrationResponses", {}) response_templates = integration_response.get("200", {}).get( "responseTemplates", {}) # 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 "" # XXX we need replace the internal Authorization header with an Authorization header set from # the customer, even if it's empty that's what's expected in the integration. custom_auth_header = invocation_context.headers.pop( HEADER_LOCALSTACK_AUTHORIZATION, "") invocation_context.headers["Authorization"] = custom_auth_header try: path_params = extract_path_params(path=relative_path, extracted_path=resource_path) invocation_context.path_params = path_params except Exception: path_params = {} if (uri.startswith("arn:aws:apigateway:") and ":lambda:path" in uri) or uri.startswith("arn:aws:lambda"): if integration_type == "AWS_PROXY": return LambdaProxyIntegration().invoke(invocation_context) elif integration_type == "AWS": func_arn = uri if ":lambda:path" in uri: func_arn = (uri.split(":lambda:path")[1].split("functions/") [1].split("/invocations")[0]) headers = helpers.create_invocation_headers(invocation_context) invocation_context.context = helpers.get_event_request_context( invocation_context) invocation_context.stage_variables = helpers.get_stage_variables( invocation_context) if invocation_context.authorizer_type: invocation_context.context[ "authorizer"] = invocation_context.auth_context request_templates = RequestTemplates() payload = request_templates.render(invocation_context) # TODO: change this signature to InvocationContext as well! result = lambda_api.process_apigateway_invocation( func_arn, relative_path, payload, stage, api_id, headers, is_base64_encoded=invocation_context.is_data_base64_encoded, path_params=path_params, query_string_params=query_string_params, method=method, resource_path=resource_path, request_context=invocation_context.context, stage_variables=invocation_context.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 = 200 response._content = parsed_result update_content_length(response) # apply custom response template invocation_context.response = response response_templates = ResponseTemplates() response_templates.render(invocation_context) invocation_context.response.headers["Content-Length"] = str( len(response.content or "")) return invocation_context.response raise Exception( f'API Gateway integration type "{integration_type}", action "{uri}", method "{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( f"Unexpected API Gateway integration URI '{uri}' for integration type {integration_type}", ) target = "" try: invocation_context.context = helpers.get_event_request_context( invocation_context) invocation_context.stage_variables = helpers.get_stage_variables( invocation_context) request_templates = RequestTemplates() payload = request_templates.render(invocation_context) except Exception as e: LOG.warning("Unable to convert API Gateway payload to str", e) raise # forward records to target kinesis stream headers = aws_stack.mock_aws_request_headers( service="kinesis", region_name=invocation_context.region_name) headers["X-Amz-Target"] = target result = common.make_http_request( url=config.service_url("kineses"), data=payload, headers=headers, method="POST") # apply response template invocation_context.response = result response_templates = ResponseTemplates() response_templates.render(invocation_context) return invocation_context.response elif "states:action/" in uri: action = uri.split("/")[-1] if APPLICATION_JSON in integration.get("requestTemplates", {}): request_templates = RequestTemplates() payload = request_templates.render(invocation_context) payload = json.loads(payload) else: # XXX decoding in py3 sounds wrong, this actually might break payload = json.loads(data.decode("utf-8")) client = aws_stack.connect_to_service("stepfunctions") if isinstance(payload.get("input"), dict): payload["input"] = json.dumps(payload["input"]) # 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 invocation_context.response = response response_templates = ResponseTemplates() response_templates.render(invocation_context) # response = apply_request_response_templates( # response, response_templates, content_type=APPLICATION_JSON # ) return response # https://docs.aws.amazon.com/apigateway/api-reference/resource/integration/ elif ("s3:path/" in uri or "s3:action/" in uri) and method == "GET": s3 = aws_stack.connect_to_service("s3") uri = apply_request_parameters( uri, integration=integration, path_params=path_params, query_params=query_string_params, ) uri_match = re.match(TARGET_REGEX_PATH_S3_URI, uri) or re.match( TARGET_REGEX_ACTION_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] if "GetQueueUrl" in template or "CreateQueue" in template: request_templates = RequestTemplates() payload = request_templates.render(invocation_context) new_request = f"{payload}&QueueName={queue}" else: request_templates = RequestTemplates() payload = request_templates.render(invocation_context) queue_url = f"{config.get_edge_url()}/{account_id}/{queue}" new_request = f"{payload}&QueueUrl={queue_url}" headers = aws_stack.mock_aws_request_headers( service="sqs", region_name=region_name) url = urljoin(config.service_url("sqs"), f"{TEST_AWS_ACCOUNT_ID}/{queue}") result = common.make_http_request(url, method="POST", headers=headers, data=new_request) return result elif uri.startswith("arn:aws:apigateway:") and ":sns:path" in uri: invocation_context.context = helpers.get_event_request_context( invocation_context) invocation_context.stage_variables = helpers.get_stage_variables( invocation_context) integration_response = SnsIntegration().invoke( invocation_context) return apply_request_response_templates( integration_response, response_templates, content_type=APPLICATION_JSON) 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 invocation_context.context = helpers.get_event_request_context( invocation_context) invocation_context.stage_variables = helpers.get_stage_variables( invocation_context) request_templates = RequestTemplates() payload = request_templates.render(invocation_context) if isinstance(payload, dict): payload = json.dumps(payload) uri = apply_request_parameters( uri, integration=integration, path_params=path_params, query_params=query_string_params, ) result = requests.request(method=method, url=uri, data=payload, headers=headers) # apply custom response template invocation_context.response = result response_templates = ResponseTemplates() response_templates.render(invocation_context) return invocation_context.response elif integration_type == "MOCK": mock_integration = MockIntegration() return mock_integration.invoke(invocation_context) 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))
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))
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))