def find_existing_item(put_item, table_name=None): table_name = table_name or put_item['TableName'] ddb_client = aws_stack.connect_to_service('dynamodb') search_key = {} if 'Key' in put_item: search_key = put_item['Key'] else: schema = get_table_schema(table_name) schemas = [schema['Table']['KeySchema']] for index in schema['Table'].get('GlobalSecondaryIndexes', []): # TODO # schemas.append(index['KeySchema']) pass for schema in schemas: for key in schema: key_name = key['AttributeName'] search_key[key_name] = put_item['Item'][key_name] if not search_key: return req = {'TableName': table_name, 'Key': search_key} existing_item = aws_stack.dynamodb_get_item_raw(req) if not existing_item: return existing_item if 'Item' not in existing_item: if 'message' in existing_item: table_names = ddb_client.list_tables()['TableNames'] msg = ( 'Unable to get item from DynamoDB (existing tables: %s ...truncated if >100 tables): %s' % (table_names, existing_item['message'])) LOGGER.warning(msg) return return existing_item.get('Item')
def find_existing_item(put_item, table_name=None): table_name = table_name or put_item["TableName"] ddb_client = aws_stack.connect_to_service("dynamodb") search_key = {} if "Key" in put_item: search_key = put_item["Key"] else: schema = get_table_schema(table_name) schemas = [schema["Table"]["KeySchema"]] for index in schema["Table"].get("GlobalSecondaryIndexes", []): # TODO # schemas.append(index['KeySchema']) pass for schema in schemas: for key in schema: key_name = key["AttributeName"] search_key[key_name] = put_item["Item"][key_name] if not search_key: return req = {"TableName": table_name, "Key": search_key} existing_item = aws_stack.dynamodb_get_item_raw(req) if not existing_item: return existing_item if "Item" not in existing_item: if "message" in existing_item: table_names = ddb_client.list_tables()["TableNames"] msg = "Unable to get item from DynamoDB (existing tables: %s ...truncated if >100 tables): %s" % ( table_names, existing_item["message"], ) LOGGER.warning(msg) return return existing_item.get("Item")
def find_existing_item(put_item): table_name = put_item['TableName'] ddb_client = aws_stack.connect_to_service('dynamodb') if 'Key' in put_item: key_attribute = list(put_item['Key'].keys())[0] key_value = put_item['Key'][key_attribute] else: schema = ddb_client.describe_table(TableName=table_name) schema = schema['Table']['KeySchema'] key_attribute = None for key in schema: if key['KeyType'] == 'HASH': key_attribute = key['AttributeName'] if not key_attribute: return key_value = put_item['Item'][key_attribute] if isinstance(key_value, dict): # extract <value> from {"<datatype>": <value>} string key_value = list(key_value.values())[0] req = {'TableName': table_name, 'Key': {key_attribute: key_value}} existing_item = aws_stack.dynamodb_get_item_raw(req) if 'Item' not in existing_item: if 'message' in existing_item: table_names = ddb_client.list_tables()['TableNames'] msg = ( 'Unable to get item from DynamoDB (existing tables: %s): %s' % (table_names, existing_item['message'])) LOGGER.warning(msg) return return existing_item.get('Item')
def find_existing_item(put_item): table_name = put_item['TableName'] ddb_client = aws_stack.connect_to_service('dynamodb') search_key = {} if 'Key' in put_item: search_key = put_item['Key'] else: schema = ddb_client.describe_table(TableName=table_name) schemas = [schema['Table']['KeySchema']] for index in schema['Table'].get('GlobalSecondaryIndexes', []): # schemas.append(index['KeySchema']) pass for schema in schemas: for key in schema: key_name = key['AttributeName'] search_key[key_name] = put_item['Item'][key_name] if not search_key: return req = {'TableName': table_name, 'Key': search_key} existing_item = aws_stack.dynamodb_get_item_raw(req) if 'Item' not in existing_item: if 'message' in existing_item: table_names = ddb_client.list_tables()['TableNames'] msg = ('Unable to get item from DynamoDB (existing tables: %s): %s' % (table_names, existing_item['message'])) LOGGER.warning(msg) return return existing_item.get('Item')
def return_response(self, method, path, data, headers, response): # update table definitions if data and 'TableName' in data and 'KeySchema' in data: TABLE_DEFINITIONS[data['TableName']] = data action = headers.get('X-Amz-Target') if not action: return response_data = json.loads(to_str(response.content)) record = { "eventID": "1", "eventVersion": "1.0", "dynamodb": { "StreamViewType": "NEW_AND_OLD_IMAGES", "SequenceNumber": "1", "SizeBytes": -1 }, "awsRegion": DEFAULT_REGION, "eventSource": "aws:dynamodb" } records = [record] if action == '%s.UpdateItem' % ACTION_PREFIX: req = {'TableName': data['TableName'], 'Key': data['Key']} new_item = aws_stack.dynamodb_get_item_raw(req) if 'Item' not in new_item: if 'message' in new_item: ddb_client = aws_stack.connect_to_service('dynamodb') table_names = ddb_client.list_tables()['TableNames'] msg = ( 'Unable to get item from DynamoDB (existing tables: %s): %s' % (table_names, new_item['message'])) LOGGER.warning(msg) return record['eventName'] = 'MODIFY' record['dynamodb']['Keys'] = data['Key'] record['dynamodb']['NewImage'] = new_item['Item'] elif action == '%s.BatchWriteItem' % ACTION_PREFIX: records = [] for table_name, requests in data['RequestItems'].items(): for request in requests: put_request = request.get('PutRequest') if put_request: keys = dynamodb_extract_keys(item=put_request['Item'], table_name=table_name) if isinstance(keys, Response): return keys new_record = clone(record) new_record['eventName'] = 'INSERT' new_record['dynamodb']['Keys'] = keys new_record['dynamodb']['NewImage'] = put_request[ 'Item'] new_record[ 'eventSourceARN'] = aws_stack.dynamodb_table_arn( table_name) records.append(new_record) elif action == '%s.PutItem' % ACTION_PREFIX: record['eventName'] = 'INSERT' keys = dynamodb_extract_keys(item=data['Item'], table_name=data['TableName']) if isinstance(keys, Response): return keys record['dynamodb']['Keys'] = keys record['dynamodb']['NewImage'] = data['Item'] elif action == '%s.GetItem' % ACTION_PREFIX: if response.status_code == 200: content = json.loads(to_str(response.content)) # make sure we append 'ConsumedCapacity', which is properly # returned by dynalite, but not by AWS's DynamoDBLocal if 'ConsumedCapacity' not in content and data.get( 'ReturnConsumedCapacity') in ('TOTAL', 'INDEXES'): content['ConsumedCapacity'] = { 'CapacityUnits': 0.5, # TODO hardcoded 'TableName': data['TableName'] } response._content = json.dumps(content) response.headers['content-length'] = len(response.content) response.headers['x-amz-crc32'] = calculate_crc32(response) elif action == '%s.DeleteItem' % ACTION_PREFIX: record['eventName'] = 'REMOVE' record['dynamodb']['Keys'] = data['Key'] elif action == '%s.CreateTable' % ACTION_PREFIX: if 'StreamSpecification' in data: create_dynamodb_stream(data) return elif action == '%s.UpdateTable' % ACTION_PREFIX: if 'StreamSpecification' in data: create_dynamodb_stream(data) return else: # nothing to do return if 'TableName' in data: record['eventSourceARN'] = aws_stack.dynamodb_table_arn( data['TableName']) forward_to_lambda(records) forward_to_ddb_stream(records)
def update_dynamodb(method, path, data, headers, response=None, return_forward_info=False): if return_forward_info: return True # update table definitions if data and 'TableName' in data and 'KeySchema' in data: TABLE_DEFINITIONS[data['TableName']] = data action = headers.get('X-Amz-Target') if not action: return response_data = json.loads(response.text) record = { "eventID": "1", "eventVersion": "1.0", "dynamodb": { "StreamViewType": "NEW_AND_OLD_IMAGES", "SequenceNumber": "1", "SizeBytes": -1 }, "awsRegion": DEFAULT_REGION, "eventSource": "aws:dynamodb" } event = { 'Records': [record] } if action == 'DynamoDB_20120810.UpdateItem': req = {'TableName': data['TableName']} req['Key'] = data['Key'] new_item = aws_stack.dynamodb_get_item_raw(TEST_DYNAMODB_URL, req) if 'Item' not in new_item: if 'message' in new_item: print('WARNING: Unable to get item from DynamoDB: %s' % new_item['message']) return record['eventName'] = 'MODIFY' record['dynamodb']['Keys'] = data['Key'] record['dynamodb']['NewImage'] = new_item['Item'] elif action == 'DynamoDB_20120810.PutItem': record['eventName'] = 'INSERT' keys = dynamodb_extract_keys(item=data['Item'], table_name=data['TableName']) record['dynamodb']['Keys'] = keys record['dynamodb']['NewImage'] = data['Item'] elif action == 'DynamoDB_20120810.DeleteItem': record['eventName'] = 'REMOVE' record['dynamodb']['Keys'] = data['Key'] elif action == 'DynamoDB_20120810.CreateTable': if 'StreamSpecification' in data: stream = data['StreamSpecification'] enabled = stream['StreamEnabled'] if enabled: table_name = data['TableName'] view_type = stream['StreamViewType'] dynamodbstreams_api.add_dynamodb_stream(table_name=table_name, view_type=view_type, enabled=enabled) return else: # nothing to do return record['eventSourceARN'] = aws_stack.dynamodb_table_arn(data['TableName']) sources = lambda_api.get_event_sources(source_arn=record['eventSourceARN']) if len(sources) > 0: pass for src in sources: func_to_call = lambda_api.lambda_arn_to_function[src['FunctionArn']] lambda_api.run_lambda(func_to_call, event=event, context={})
def update_dynamodb(method, path, data, headers, response=None, return_forward_info=False): if return_forward_info: if random.random() < config.DYNAMODB_ERROR_PROBABILITY: return dynamodb_error_response(data) return True # update table definitions if data and 'TableName' in data and 'KeySchema' in data: TABLE_DEFINITIONS[data['TableName']] = data action = headers.get('X-Amz-Target') if not action: return response_data = json.loads(to_str(response.content)) record = { "eventID": "1", "eventVersion": "1.0", "dynamodb": { "StreamViewType": "NEW_AND_OLD_IMAGES", "SequenceNumber": "1", "SizeBytes": -1 }, "awsRegion": DEFAULT_REGION, "eventSource": "aws:dynamodb" } records = [record] if action == 'DynamoDB_20120810.UpdateItem': req = {'TableName': data['TableName'], 'Key': data['Key']} new_item = aws_stack.dynamodb_get_item_raw(req) if 'Item' not in new_item: if 'message' in new_item: ddb_client = aws_stack.connect_to_service('dynamodb') table_names = ddb_client.list_tables()['TableNames'] msg = 'Unable to get item from DynamoDB (existing tables: %s): %s' % (table_names, new_item['message']) LOGGER.warning(msg) return record['eventName'] = 'MODIFY' record['dynamodb']['Keys'] = data['Key'] record['dynamodb']['NewImage'] = new_item['Item'] elif action == 'DynamoDB_20120810.BatchWriteItem': records = [] for table_name, requests in data['RequestItems'].items(): for request in requests: put_request = request.get('PutRequest') if put_request: keys = dynamodb_extract_keys(item=put_request['Item'], table_name=table_name) new_record = clone(record) new_record['eventName'] = 'INSERT' new_record['dynamodb']['Keys'] = keys new_record['dynamodb']['NewImage'] = put_request['Item'] new_record['eventSourceARN'] = aws_stack.dynamodb_table_arn(table_name) records.append(new_record) elif action == 'DynamoDB_20120810.PutItem': record['eventName'] = 'INSERT' keys = dynamodb_extract_keys(item=data['Item'], table_name=data['TableName']) record['dynamodb']['Keys'] = keys record['dynamodb']['NewImage'] = data['Item'] elif action == 'DynamoDB_20120810.DeleteItem': record['eventName'] = 'REMOVE' record['dynamodb']['Keys'] = data['Key'] elif action == 'DynamoDB_20120810.CreateTable': if 'StreamSpecification' in data: stream = data['StreamSpecification'] enabled = stream.get('StreamEnabled') if enabled not in [False, 'False']: table_name = data['TableName'] view_type = stream['StreamViewType'] dynamodbstreams_api.add_dynamodb_stream(table_name=table_name, view_type=view_type, enabled=enabled) return else: # nothing to do return if 'TableName' in data: record['eventSourceARN'] = aws_stack.dynamodb_table_arn(data['TableName']) for record in records: sources = lambda_api.get_event_sources(source_arn=record['eventSourceARN']) event = { 'Records': [record] } for src in sources: func_to_call = lambda_api.lambda_arn_to_function[src['FunctionArn']] lambda_api.run_lambda(func_to_call, event=event, context={}, func_arn=src['FunctionArn'])
def return_response(self, method, path, data, headers, response): data = json.loads(to_str(data)) # update table definitions if data and 'TableName' in data and 'KeySchema' in data: TABLE_DEFINITIONS[data['TableName']] = data if response._content: # fix the table ARN (DynamoDBLocal hardcodes "ddblocal" as the region) content_replaced = re.sub( r'"TableArn"\s*:\s*"arn:aws:dynamodb:ddblocal:([^"]+)"', r'"TableArn": "arn:aws:dynamodb:%s:\1"' % aws_stack.get_local_region(), to_str(response._content)) if content_replaced != response._content: response._content = content_replaced fix_headers_for_updated_response(response) action = headers.get('X-Amz-Target') if not action: return record = { 'eventID': '1', 'eventVersion': '1.0', 'dynamodb': { 'StreamViewType': 'NEW_AND_OLD_IMAGES', 'SizeBytes': -1 }, 'awsRegion': DEFAULT_REGION, 'eventSource': 'aws:dynamodb' } records = [record] if action == '%s.UpdateItem' % ACTION_PREFIX: req = {'TableName': data['TableName'], 'Key': data['Key']} new_item = aws_stack.dynamodb_get_item_raw(req) if 'Item' not in new_item: if 'message' in new_item: ddb_client = aws_stack.connect_to_service('dynamodb') table_names = ddb_client.list_tables()['TableNames'] msg = ( 'Unable to get item from DynamoDB (existing tables: %s): %s' % (table_names, new_item['message'])) LOGGER.warning(msg) return record['eventName'] = 'MODIFY' record['dynamodb']['Keys'] = data['Key'] record['dynamodb']['NewImage'] = new_item['Item'] elif action == '%s.BatchWriteItem' % ACTION_PREFIX: records = [] for table_name, requests in data['RequestItems'].items(): for request in requests: put_request = request.get('PutRequest') if put_request: keys = dynamodb_extract_keys(item=put_request['Item'], table_name=table_name) if isinstance(keys, Response): return keys new_record = clone(record) new_record['eventName'] = 'INSERT' new_record['dynamodb']['Keys'] = keys new_record['dynamodb']['NewImage'] = put_request[ 'Item'] new_record[ 'eventSourceARN'] = aws_stack.dynamodb_table_arn( table_name) records.append(new_record) elif action == '%s.PutItem' % ACTION_PREFIX: record['eventName'] = 'INSERT' keys = dynamodb_extract_keys(item=data['Item'], table_name=data['TableName']) if isinstance(keys, Response): return keys record['dynamodb']['Keys'] = keys record['dynamodb']['NewImage'] = data['Item'] elif action == '%s.GetItem' % ACTION_PREFIX: if response.status_code == 200: content = json.loads(to_str(response.content)) # make sure we append 'ConsumedCapacity', which is properly # returned by dynalite, but not by AWS's DynamoDBLocal if 'ConsumedCapacity' not in content and data.get( 'ReturnConsumedCapacity') in ('TOTAL', 'INDEXES'): content['ConsumedCapacity'] = { 'CapacityUnits': 0.5, # TODO hardcoded 'TableName': data['TableName'] } response._content = json.dumps(content) fix_headers_for_updated_response(response) elif action == '%s.DeleteItem' % ACTION_PREFIX: record['eventName'] = 'REMOVE' record['dynamodb']['Keys'] = data['Key'] elif action == '%s.CreateTable' % ACTION_PREFIX: if 'StreamSpecification' in data: create_dynamodb_stream(data) event_publisher.fire_event( event_publisher.EVENT_DYNAMODB_CREATE_TABLE, payload={'n': event_publisher.get_hash(data['TableName'])}) return elif action == '%s.DeleteTable' % ACTION_PREFIX: event_publisher.fire_event( event_publisher.EVENT_DYNAMODB_DELETE_TABLE, payload={'n': event_publisher.get_hash(data['TableName'])}) return elif action == '%s.UpdateTable' % ACTION_PREFIX: if 'StreamSpecification' in data: create_dynamodb_stream(data) return else: # nothing to do return if 'TableName' in data: record['eventSourceARN'] = aws_stack.dynamodb_table_arn( data['TableName']) forward_to_lambda(records) forward_to_ddb_stream(records)