From b74625db0cf6676bd57cd09e610a202fe176117d Mon Sep 17 00:00:00 2001 From: Tomoya Iwata Date: Sun, 13 Jan 2019 17:38:38 +0900 Subject: [PATCH 1/4] add support for dynamodb transact_get_items --- IMPLEMENTATION_COVERAGE.md | 4 +- moto/dynamodb2/responses.py | 69 ++++++ tests/test_dynamodb2/test_dynamodb.py | 308 +++++++++++++++++++++++++- 3 files changed, 378 insertions(+), 3 deletions(-) diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md index a22cc3bfb..705618524 100644 --- a/IMPLEMENTATION_COVERAGE.md +++ b/IMPLEMENTATION_COVERAGE.md @@ -2237,7 +2237,7 @@ - [ ] verify_trust ## dynamodb -17% implemented +24% implemented - [ ] batch_get_item - [ ] batch_write_item - [ ] create_backup @@ -2268,7 +2268,7 @@ - [ ] restore_table_to_point_in_time - [X] scan - [ ] tag_resource -- [ ] transact_get_items +- [X] transact_get_items - [ ] transact_write_items - [ ] untag_resource - [ ] update_continuous_backups diff --git a/moto/dynamodb2/responses.py b/moto/dynamodb2/responses.py index d3767c3fd..c9b526121 100644 --- a/moto/dynamodb2/responses.py +++ b/moto/dynamodb2/responses.py @@ -10,6 +10,9 @@ from .exceptions import InvalidIndexNameError, InvalidUpdateExpression, ItemSize from .models import dynamodb_backends, dynamo_json_dump +TRANSACTION_MAX_ITEMS = 10 + + def has_empty_keys_or_values(_dict): if _dict == "": return True @@ -828,3 +831,69 @@ class DynamoHandler(BaseResponse): ttl_spec = self.dynamodb_backend.describe_ttl(name) return json.dumps({"TimeToLiveDescription": ttl_spec}) + + def transact_get_items(self): + transact_items = self.body['TransactItems'] + responses = list() + + if len(transact_items) > TRANSACTION_MAX_ITEMS: + msg = "1 validation error detected: Value '[" + err_list = list() + request_id = 268435456 + for _ in transact_items: + request_id += 1 + hex_request_id = format(request_id, 'x') + err_list.append('com.amazonaws.dynamodb.v20120810.TransactGetItem@%s' % hex_request_id) + msg += ', '.join(err_list) + msg += "'] at 'transactItems' failed to satisfy constraint: " \ + "Member must have length less than or equal to %s" % TRANSACTION_MAX_ITEMS + + return self.error('ValidationException', msg) + + dedup_list = [i for n, i in enumerate(transact_items) if i not in transact_items[n + 1:]] + if len(transact_items) != len(dedup_list): + er = 'com.amazon.coral.validate#ValidationException' + return self.error(er, 'Transaction request cannot include multiple operations on one item') + + ret_consumed_capacity = self.body.get('ReturnConsumedCapacity', 'NONE') + consumed_capacity = dict() + + for transact_item in transact_items: + + table_name = transact_item['Get']['TableName'] + key = transact_item['Get']['Key'] + try: + item = self.dynamodb_backend.get_item(table_name, key) + except ValueError as e: + er = 'com.amazonaws.dynamodb.v20111205#ResourceNotFoundException' + return self.error(er, 'Requested resource not found') + + if not item: + continue + + item_describe = item.describe_attrs(False) + responses.append(item_describe) + + table_capacity = consumed_capacity.get(table_name, {}) + table_capacity['TableName'] = table_name + capacity_units = table_capacity.get('CapacityUnits', 0) + 2.0 + table_capacity['CapacityUnits'] = capacity_units + read_capacity_units = table_capacity.get('ReadCapacityUnits', 0) + 2.0 + table_capacity['ReadCapacityUnits'] = read_capacity_units + consumed_capacity[table_name] = table_capacity + + if ret_consumed_capacity == 'INDEXES': + table_capacity['Table'] = { + 'CapacityUnits': capacity_units, + 'ReadCapacityUnits': read_capacity_units + } + + result = dict() + result.update({ + 'Responses': responses}) + if ret_consumed_capacity != 'NONE': + result.update({ + 'ConsumedCapacity': [v for v in consumed_capacity.values()] + }) + + return dynamo_json_dump(result) diff --git a/tests/test_dynamodb2/test_dynamodb.py b/tests/test_dynamodb2/test_dynamodb.py index 428b58f81..e439eeeb9 100644 --- a/tests/test_dynamodb2/test_dynamodb.py +++ b/tests/test_dynamodb2/test_dynamodb.py @@ -6,8 +6,9 @@ import six import boto import boto3 from boto3.dynamodb.conditions import Attr, Key -import sure # noqa +import re import requests +import sure # noqa from moto import mock_dynamodb2, mock_dynamodb2_deprecated from moto.dynamodb2 import dynamodb_backend2, dynamodb_backends2 from boto.exception import JSONResponseError @@ -3792,3 +3793,308 @@ def test_query_catches_when_no_filters(): ex.exception.response["Error"]["Message"].should.equal( "Either KeyConditions or QueryFilter should be present" ) + + +@mock_dynamodb2 +def test_invalid_transact_get_items(): + + dynamodb = boto3.resource('dynamodb', region_name='us-east-1') + dynamodb.create_table( + TableName='test1', + KeySchema=[{'AttributeName': 'id', 'KeyType': 'HASH'}], + AttributeDefinitions=[{'AttributeName': 'id', 'AttributeType': 'S'}], + ProvisionedThroughput={'ReadCapacityUnits': 5, 'WriteCapacityUnits': 5} + ) + table = dynamodb.Table('test1') + table.put_item(Item={ + 'id': '1', + 'val': '1', + }) + + table.put_item(Item={ + 'id': '1', + 'val': '2', + }) + + client = boto3.client('dynamodb', region_name='us-east-1') + + with assert_raises(ClientError) as ex: + client.transact_get_items(TransactItems=[ + { + 'Get': { + 'Key': { + 'id': {'S': '1'}, + }, + 'TableName': 'test1' + } + }, + { + 'Get': { + 'Key': { + 'id': {'S': '1'}, + }, + 'TableName': 'test1' + } + } + ]) + + ex.exception.response['Error']['Code'].should.equal('ValidationException') + ex.exception.response['ResponseMetadata']['HTTPStatusCode'].should.equal(400) + ex.exception.response['Error']['Message'].should.equal( + 'Transaction request cannot include multiple operations on one item' + ) + + with assert_raises(ClientError) as ex: + client.transact_get_items(TransactItems=[ + {'Get': {'Key': {'id': {'S': '1'}}, 'TableName': 'test1'}}, + {'Get': {'Key': {'id': {'S': '1'}}, 'TableName': 'test1'}}, + {'Get': {'Key': {'id': {'S': '1'}}, 'TableName': 'test1'}}, + {'Get': {'Key': {'id': {'S': '1'}}, 'TableName': 'test1'}}, + {'Get': {'Key': {'id': {'S': '1'}}, 'TableName': 'test1'}}, + {'Get': {'Key': {'id': {'S': '1'}}, 'TableName': 'test1'}}, + {'Get': {'Key': {'id': {'S': '1'}}, 'TableName': 'test1'}}, + {'Get': {'Key': {'id': {'S': '1'}}, 'TableName': 'test1'}}, + {'Get': {'Key': {'id': {'S': '1'}}, 'TableName': 'test1'}}, + {'Get': {'Key': {'id': {'S': '1'}}, 'TableName': 'test1'}}, + {'Get': {'Key': {'id': {'S': '1'}}, 'TableName': 'test1'}}, + ]) + + ex.exception.response['ResponseMetadata']['HTTPStatusCode'].should.equal(400) + ex.exception.response['Error']['Message'].should.match( + r'failed to satisfy constraint: Member must have length less than or equal to 10', re.I + ) + + with assert_raises(ClientError) as ex: + client.transact_get_items(TransactItems=[ + { + 'Get': { + 'Key': { + 'id': {'S': '1'}, + }, + 'TableName': 'test1' + } + }, + { + 'Get': { + 'Key': { + 'id': {'S': '1'}, + }, + 'TableName': 'non_exists_table' + } + } + ]) + + ex.exception.response['Error']['Code'].should.equal('ResourceNotFoundException') + ex.exception.response['ResponseMetadata']['HTTPStatusCode'].should.equal(400) + ex.exception.response['Error']['Message'].should.equal( + 'Requested resource not found' + ) + + +@mock_dynamodb2 +def test_valid_transact_get_items(): + dynamodb = boto3.resource('dynamodb', region_name='us-east-1') + dynamodb.create_table( + TableName='test1', + KeySchema=[ + {'AttributeName': 'id', 'KeyType': 'HASH'}, + {'AttributeName': 'sort_key', 'KeyType': 'RANGE'}, + ], + AttributeDefinitions=[ + {'AttributeName': 'id', 'AttributeType': 'S'}, + {'AttributeName': 'sort_key', 'AttributeType': 'S'}, + ], + ProvisionedThroughput={'ReadCapacityUnits': 5, 'WriteCapacityUnits': 5} + ) + table1 = dynamodb.Table('test1') + table1.put_item(Item={ + 'id': '1', + 'sort_key': '1', + }) + + table1.put_item(Item={ + 'id': '1', + 'sort_key': '2', + }) + + dynamodb.create_table( + TableName='test2', + KeySchema=[ + {'AttributeName': 'id', 'KeyType': 'HASH'}, + {'AttributeName': 'sort_key', 'KeyType': 'RANGE'}, + ], + AttributeDefinitions=[ + {'AttributeName': 'id', 'AttributeType': 'S'}, + {'AttributeName': 'sort_key', 'AttributeType': 'S'}, + ], + ProvisionedThroughput={'ReadCapacityUnits': 5, 'WriteCapacityUnits': 5} + ) + table2 = dynamodb.Table('test2') + table2.put_item(Item={ + 'id': '1', + 'sort_key': '1', + }) + + client = boto3.client('dynamodb', region_name='us-east-1') + res = client.transact_get_items(TransactItems=[ + { + 'Get': { + 'Key': { + 'id': {'S': '1'}, + 'sort_key': {'S': '1'} + }, + 'TableName': 'test1' + } + }, + { + 'Get': { + 'Key': { + 'id': {'S': 'non_exists_key'}, + 'sort_key': {'S': '2'} + }, + 'TableName': 'test1' + } + } + ]) + res['Responses'][0]['Item'].should.equal({ + 'id': {'S': '1'}, + 'sort_key': {'S': '1'} + }) + len(res['Responses']).should.equal(1) + + res = client.transact_get_items(TransactItems=[ + { + 'Get': { + 'Key': { + 'id': {'S': '1'}, + 'sort_key': {'S': '1'} + }, + 'TableName': 'test1' + } + }, + { + 'Get': { + 'Key': { + 'id': {'S': '1'}, + 'sort_key': {'S': '2'} + }, + 'TableName': 'test1' + } + }, + { + 'Get': { + 'Key': { + 'id': {'S': '1'}, + 'sort_key': {'S': '1'} + }, + 'TableName': 'test2' + } + }, + ]) + + res['Responses'][0]['Item'].should.equal({ + 'id': {'S': '1'}, + 'sort_key': {'S': '1'} + }) + + res['Responses'][1]['Item'].should.equal({ + 'id': {'S': '1'}, + 'sort_key': {'S': '2'} + }) + + res['Responses'][2]['Item'].should.equal({ + 'id': {'S': '1'}, + 'sort_key': {'S': '1'} + }) + + res = client.transact_get_items(TransactItems=[ + { + 'Get': { + 'Key': { + 'id': {'S': '1'}, + 'sort_key': {'S': '1'} + }, + 'TableName': 'test1' + } + }, + { + 'Get': { + 'Key': { + 'id': {'S': '1'}, + 'sort_key': {'S': '2'} + }, + 'TableName': 'test1' + } + }, + { + 'Get': { + 'Key': { + 'id': {'S': '1'}, + 'sort_key': {'S': '1'} + }, + 'TableName': 'test2' + } + }, + ], ReturnConsumedCapacity='TOTAL') + + res['ConsumedCapacity'][0].should.equal({ + 'TableName': 'test1', + 'CapacityUnits': 4.0, + 'ReadCapacityUnits': 4.0 + }) + + res['ConsumedCapacity'][1].should.equal({ + 'TableName': 'test2', + 'CapacityUnits': 2.0, + 'ReadCapacityUnits': 2.0 + }) + + res = client.transact_get_items(TransactItems=[ + { + 'Get': { + 'Key': { + 'id': {'S': '1'}, + 'sort_key': {'S': '1'} + }, + 'TableName': 'test1' + } + }, + { + 'Get': { + 'Key': { + 'id': {'S': '1'}, + 'sort_key': {'S': '2'} + }, + 'TableName': 'test1' + } + }, + { + 'Get': { + 'Key': { + 'id': {'S': '1'}, + 'sort_key': {'S': '1'} + }, + 'TableName': 'test2' + } + }, + ], ReturnConsumedCapacity='INDEXES') + + res['ConsumedCapacity'][0].should.equal({ + 'TableName': 'test1', + 'CapacityUnits': 4.0, + 'ReadCapacityUnits': 4.0, + 'Table': { + 'CapacityUnits': 4.0, + 'ReadCapacityUnits': 4.0, + } + }) + + res['ConsumedCapacity'][1].should.equal({ + 'TableName': 'test2', + 'CapacityUnits': 2.0, + 'ReadCapacityUnits': 2.0, + 'Table': { + 'CapacityUnits': 2.0, + 'ReadCapacityUnits': 2.0, + } + }) From 5a7da61833222c603113c5be03d3e117494efb8b Mon Sep 17 00:00:00 2001 From: Tomoya Iwata Date: Sun, 13 Jan 2019 18:32:27 +0900 Subject: [PATCH 2/4] remove unused local variable --- moto/dynamodb2/responses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moto/dynamodb2/responses.py b/moto/dynamodb2/responses.py index c9b526121..90cbcedda 100644 --- a/moto/dynamodb2/responses.py +++ b/moto/dynamodb2/responses.py @@ -864,7 +864,7 @@ class DynamoHandler(BaseResponse): key = transact_item['Get']['Key'] try: item = self.dynamodb_backend.get_item(table_name, key) - except ValueError as e: + except ValueError: er = 'com.amazonaws.dynamodb.v20111205#ResourceNotFoundException' return self.error(er, 'Requested resource not found') From caebe222d7846b87139a9eec08a1376db9c4d0d6 Mon Sep 17 00:00:00 2001 From: Bert Blommers Date: Thu, 12 Mar 2020 14:24:53 +0000 Subject: [PATCH 3/4] DynamoDB - Transact_get_items - Remove error condition --- moto/dynamodb2/responses.py | 7 +---- tests/test_dynamodb2/test_dynamodb.py | 40 ++------------------------- 2 files changed, 3 insertions(+), 44 deletions(-) diff --git a/moto/dynamodb2/responses.py b/moto/dynamodb2/responses.py index 90cbcedda..a5e465a1a 100644 --- a/moto/dynamodb2/responses.py +++ b/moto/dynamodb2/responses.py @@ -10,7 +10,7 @@ from .exceptions import InvalidIndexNameError, InvalidUpdateExpression, ItemSize from .models import dynamodb_backends, dynamo_json_dump -TRANSACTION_MAX_ITEMS = 10 +TRANSACTION_MAX_ITEMS = 25 def has_empty_keys_or_values(_dict): @@ -850,11 +850,6 @@ class DynamoHandler(BaseResponse): return self.error('ValidationException', msg) - dedup_list = [i for n, i in enumerate(transact_items) if i not in transact_items[n + 1:]] - if len(transact_items) != len(dedup_list): - er = 'com.amazon.coral.validate#ValidationException' - return self.error(er, 'Transaction request cannot include multiple operations on one item') - ret_consumed_capacity = self.body.get('ReturnConsumedCapacity', 'NONE') consumed_capacity = dict() diff --git a/tests/test_dynamodb2/test_dynamodb.py b/tests/test_dynamodb2/test_dynamodb.py index e439eeeb9..cfe071f44 100644 --- a/tests/test_dynamodb2/test_dynamodb.py +++ b/tests/test_dynamodb2/test_dynamodb.py @@ -3820,48 +3820,12 @@ def test_invalid_transact_get_items(): with assert_raises(ClientError) as ex: client.transact_get_items(TransactItems=[ - { - 'Get': { - 'Key': { - 'id': {'S': '1'}, - }, - 'TableName': 'test1' - } - }, - { - 'Get': { - 'Key': { - 'id': {'S': '1'}, - }, - 'TableName': 'test1' - } - } - ]) - - ex.exception.response['Error']['Code'].should.equal('ValidationException') - ex.exception.response['ResponseMetadata']['HTTPStatusCode'].should.equal(400) - ex.exception.response['Error']['Message'].should.equal( - 'Transaction request cannot include multiple operations on one item' - ) - - with assert_raises(ClientError) as ex: - client.transact_get_items(TransactItems=[ - {'Get': {'Key': {'id': {'S': '1'}}, 'TableName': 'test1'}}, - {'Get': {'Key': {'id': {'S': '1'}}, 'TableName': 'test1'}}, - {'Get': {'Key': {'id': {'S': '1'}}, 'TableName': 'test1'}}, - {'Get': {'Key': {'id': {'S': '1'}}, 'TableName': 'test1'}}, - {'Get': {'Key': {'id': {'S': '1'}}, 'TableName': 'test1'}}, - {'Get': {'Key': {'id': {'S': '1'}}, 'TableName': 'test1'}}, - {'Get': {'Key': {'id': {'S': '1'}}, 'TableName': 'test1'}}, - {'Get': {'Key': {'id': {'S': '1'}}, 'TableName': 'test1'}}, - {'Get': {'Key': {'id': {'S': '1'}}, 'TableName': 'test1'}}, - {'Get': {'Key': {'id': {'S': '1'}}, 'TableName': 'test1'}}, - {'Get': {'Key': {'id': {'S': '1'}}, 'TableName': 'test1'}}, + {'Get': {'Key': {'id': {'S': '1'}}, 'TableName': 'test1'}} for i in range(26) ]) ex.exception.response['ResponseMetadata']['HTTPStatusCode'].should.equal(400) ex.exception.response['Error']['Message'].should.match( - r'failed to satisfy constraint: Member must have length less than or equal to 10', re.I + r'failed to satisfy constraint: Member must have length less than or equal to 25', re.I ) with assert_raises(ClientError) as ex: From 71d3941daf09a9276dd29f3692585200bd6ea7fa Mon Sep 17 00:00:00 2001 From: Bert Blommers Date: Thu, 12 Mar 2020 14:26:23 +0000 Subject: [PATCH 4/4] Linting --- moto/dynamodb2/responses.py | 57 ++-- tests/test_dynamodb2/test_dynamodb.py | 402 +++++++++++--------------- 2 files changed, 204 insertions(+), 255 deletions(-) diff --git a/moto/dynamodb2/responses.py b/moto/dynamodb2/responses.py index a5e465a1a..3d25c7e49 100644 --- a/moto/dynamodb2/responses.py +++ b/moto/dynamodb2/responses.py @@ -833,7 +833,7 @@ class DynamoHandler(BaseResponse): return json.dumps({"TimeToLiveDescription": ttl_spec}) def transact_get_items(self): - transact_items = self.body['TransactItems'] + transact_items = self.body["TransactItems"] responses = list() if len(transact_items) > TRANSACTION_MAX_ITEMS: @@ -842,26 +842,32 @@ class DynamoHandler(BaseResponse): request_id = 268435456 for _ in transact_items: request_id += 1 - hex_request_id = format(request_id, 'x') - err_list.append('com.amazonaws.dynamodb.v20120810.TransactGetItem@%s' % hex_request_id) - msg += ', '.join(err_list) - msg += "'] at 'transactItems' failed to satisfy constraint: " \ - "Member must have length less than or equal to %s" % TRANSACTION_MAX_ITEMS + hex_request_id = format(request_id, "x") + err_list.append( + "com.amazonaws.dynamodb.v20120810.TransactGetItem@%s" + % hex_request_id + ) + msg += ", ".join(err_list) + msg += ( + "'] at 'transactItems' failed to satisfy constraint: " + "Member must have length less than or equal to %s" + % TRANSACTION_MAX_ITEMS + ) - return self.error('ValidationException', msg) + return self.error("ValidationException", msg) - ret_consumed_capacity = self.body.get('ReturnConsumedCapacity', 'NONE') + ret_consumed_capacity = self.body.get("ReturnConsumedCapacity", "NONE") consumed_capacity = dict() for transact_item in transact_items: - table_name = transact_item['Get']['TableName'] - key = transact_item['Get']['Key'] + table_name = transact_item["Get"]["TableName"] + key = transact_item["Get"]["Key"] try: item = self.dynamodb_backend.get_item(table_name, key) except ValueError: - er = 'com.amazonaws.dynamodb.v20111205#ResourceNotFoundException' - return self.error(er, 'Requested resource not found') + er = "com.amazonaws.dynamodb.v20111205#ResourceNotFoundException" + return self.error(er, "Requested resource not found") if not item: continue @@ -870,25 +876,22 @@ class DynamoHandler(BaseResponse): responses.append(item_describe) table_capacity = consumed_capacity.get(table_name, {}) - table_capacity['TableName'] = table_name - capacity_units = table_capacity.get('CapacityUnits', 0) + 2.0 - table_capacity['CapacityUnits'] = capacity_units - read_capacity_units = table_capacity.get('ReadCapacityUnits', 0) + 2.0 - table_capacity['ReadCapacityUnits'] = read_capacity_units + table_capacity["TableName"] = table_name + capacity_units = table_capacity.get("CapacityUnits", 0) + 2.0 + table_capacity["CapacityUnits"] = capacity_units + read_capacity_units = table_capacity.get("ReadCapacityUnits", 0) + 2.0 + table_capacity["ReadCapacityUnits"] = read_capacity_units consumed_capacity[table_name] = table_capacity - if ret_consumed_capacity == 'INDEXES': - table_capacity['Table'] = { - 'CapacityUnits': capacity_units, - 'ReadCapacityUnits': read_capacity_units + if ret_consumed_capacity == "INDEXES": + table_capacity["Table"] = { + "CapacityUnits": capacity_units, + "ReadCapacityUnits": read_capacity_units, } result = dict() - result.update({ - 'Responses': responses}) - if ret_consumed_capacity != 'NONE': - result.update({ - 'ConsumedCapacity': [v for v in consumed_capacity.values()] - }) + result.update({"Responses": responses}) + if ret_consumed_capacity != "NONE": + result.update({"ConsumedCapacity": [v for v in consumed_capacity.values()]}) return dynamo_json_dump(result) diff --git a/tests/test_dynamodb2/test_dynamodb.py b/tests/test_dynamodb2/test_dynamodb.py index cfe071f44..f67711689 100644 --- a/tests/test_dynamodb2/test_dynamodb.py +++ b/tests/test_dynamodb2/test_dynamodb.py @@ -3798,267 +3798,213 @@ def test_query_catches_when_no_filters(): @mock_dynamodb2 def test_invalid_transact_get_items(): - dynamodb = boto3.resource('dynamodb', region_name='us-east-1') + dynamodb = boto3.resource("dynamodb", region_name="us-east-1") dynamodb.create_table( - TableName='test1', - KeySchema=[{'AttributeName': 'id', 'KeyType': 'HASH'}], - AttributeDefinitions=[{'AttributeName': 'id', 'AttributeType': 'S'}], - ProvisionedThroughput={'ReadCapacityUnits': 5, 'WriteCapacityUnits': 5} + TableName="test1", + KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}], + AttributeDefinitions=[{"AttributeName": "id", "AttributeType": "S"}], + ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5}, + ) + table = dynamodb.Table("test1") + table.put_item( + Item={"id": "1", "val": "1",} ) - table = dynamodb.Table('test1') - table.put_item(Item={ - 'id': '1', - 'val': '1', - }) - table.put_item(Item={ - 'id': '1', - 'val': '2', - }) + table.put_item( + Item={"id": "1", "val": "2",} + ) - client = boto3.client('dynamodb', region_name='us-east-1') + client = boto3.client("dynamodb", region_name="us-east-1") with assert_raises(ClientError) as ex: - client.transact_get_items(TransactItems=[ - {'Get': {'Key': {'id': {'S': '1'}}, 'TableName': 'test1'}} for i in range(26) - ]) + client.transact_get_items( + TransactItems=[ + {"Get": {"Key": {"id": {"S": "1"}}, "TableName": "test1"}} + for i in range(26) + ] + ) - ex.exception.response['ResponseMetadata']['HTTPStatusCode'].should.equal(400) - ex.exception.response['Error']['Message'].should.match( - r'failed to satisfy constraint: Member must have length less than or equal to 25', re.I + ex.exception.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400) + ex.exception.response["Error"]["Message"].should.match( + r"failed to satisfy constraint: Member must have length less than or equal to 25", + re.I, ) with assert_raises(ClientError) as ex: - client.transact_get_items(TransactItems=[ - { - 'Get': { - 'Key': { - 'id': {'S': '1'}, - }, - 'TableName': 'test1' - } - }, - { - 'Get': { - 'Key': { - 'id': {'S': '1'}, - }, - 'TableName': 'non_exists_table' - } - } - ]) + client.transact_get_items( + TransactItems=[ + {"Get": {"Key": {"id": {"S": "1"},}, "TableName": "test1"}}, + {"Get": {"Key": {"id": {"S": "1"},}, "TableName": "non_exists_table"}}, + ] + ) - ex.exception.response['Error']['Code'].should.equal('ResourceNotFoundException') - ex.exception.response['ResponseMetadata']['HTTPStatusCode'].should.equal(400) - ex.exception.response['Error']['Message'].should.equal( - 'Requested resource not found' + ex.exception.response["Error"]["Code"].should.equal("ResourceNotFoundException") + ex.exception.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400) + ex.exception.response["Error"]["Message"].should.equal( + "Requested resource not found" ) @mock_dynamodb2 def test_valid_transact_get_items(): - dynamodb = boto3.resource('dynamodb', region_name='us-east-1') + dynamodb = boto3.resource("dynamodb", region_name="us-east-1") dynamodb.create_table( - TableName='test1', + TableName="test1", KeySchema=[ - {'AttributeName': 'id', 'KeyType': 'HASH'}, - {'AttributeName': 'sort_key', 'KeyType': 'RANGE'}, + {"AttributeName": "id", "KeyType": "HASH"}, + {"AttributeName": "sort_key", "KeyType": "RANGE"}, ], AttributeDefinitions=[ - {'AttributeName': 'id', 'AttributeType': 'S'}, - {'AttributeName': 'sort_key', 'AttributeType': 'S'}, + {"AttributeName": "id", "AttributeType": "S"}, + {"AttributeName": "sort_key", "AttributeType": "S"}, ], - ProvisionedThroughput={'ReadCapacityUnits': 5, 'WriteCapacityUnits': 5} + ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5}, + ) + table1 = dynamodb.Table("test1") + table1.put_item( + Item={"id": "1", "sort_key": "1",} ) - table1 = dynamodb.Table('test1') - table1.put_item(Item={ - 'id': '1', - 'sort_key': '1', - }) - table1.put_item(Item={ - 'id': '1', - 'sort_key': '2', - }) + table1.put_item( + Item={"id": "1", "sort_key": "2",} + ) dynamodb.create_table( - TableName='test2', + TableName="test2", KeySchema=[ - {'AttributeName': 'id', 'KeyType': 'HASH'}, - {'AttributeName': 'sort_key', 'KeyType': 'RANGE'}, + {"AttributeName": "id", "KeyType": "HASH"}, + {"AttributeName": "sort_key", "KeyType": "RANGE"}, ], AttributeDefinitions=[ - {'AttributeName': 'id', 'AttributeType': 'S'}, - {'AttributeName': 'sort_key', 'AttributeType': 'S'}, + {"AttributeName": "id", "AttributeType": "S"}, + {"AttributeName": "sort_key", "AttributeType": "S"}, ], - ProvisionedThroughput={'ReadCapacityUnits': 5, 'WriteCapacityUnits': 5} + ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5}, + ) + table2 = dynamodb.Table("test2") + table2.put_item( + Item={"id": "1", "sort_key": "1",} ) - table2 = dynamodb.Table('test2') - table2.put_item(Item={ - 'id': '1', - 'sort_key': '1', - }) - client = boto3.client('dynamodb', region_name='us-east-1') - res = client.transact_get_items(TransactItems=[ + client = boto3.client("dynamodb", region_name="us-east-1") + res = client.transact_get_items( + TransactItems=[ + { + "Get": { + "Key": {"id": {"S": "1"}, "sort_key": {"S": "1"}}, + "TableName": "test1", + } + }, + { + "Get": { + "Key": {"id": {"S": "non_exists_key"}, "sort_key": {"S": "2"}}, + "TableName": "test1", + } + }, + ] + ) + res["Responses"][0]["Item"].should.equal({"id": {"S": "1"}, "sort_key": {"S": "1"}}) + len(res["Responses"]).should.equal(1) + + res = client.transact_get_items( + TransactItems=[ + { + "Get": { + "Key": {"id": {"S": "1"}, "sort_key": {"S": "1"}}, + "TableName": "test1", + } + }, + { + "Get": { + "Key": {"id": {"S": "1"}, "sort_key": {"S": "2"}}, + "TableName": "test1", + } + }, + { + "Get": { + "Key": {"id": {"S": "1"}, "sort_key": {"S": "1"}}, + "TableName": "test2", + } + }, + ] + ) + + res["Responses"][0]["Item"].should.equal({"id": {"S": "1"}, "sort_key": {"S": "1"}}) + + res["Responses"][1]["Item"].should.equal({"id": {"S": "1"}, "sort_key": {"S": "2"}}) + + res["Responses"][2]["Item"].should.equal({"id": {"S": "1"}, "sort_key": {"S": "1"}}) + + res = client.transact_get_items( + TransactItems=[ + { + "Get": { + "Key": {"id": {"S": "1"}, "sort_key": {"S": "1"}}, + "TableName": "test1", + } + }, + { + "Get": { + "Key": {"id": {"S": "1"}, "sort_key": {"S": "2"}}, + "TableName": "test1", + } + }, + { + "Get": { + "Key": {"id": {"S": "1"}, "sort_key": {"S": "1"}}, + "TableName": "test2", + } + }, + ], + ReturnConsumedCapacity="TOTAL", + ) + + res["ConsumedCapacity"][0].should.equal( + {"TableName": "test1", "CapacityUnits": 4.0, "ReadCapacityUnits": 4.0} + ) + + res["ConsumedCapacity"][1].should.equal( + {"TableName": "test2", "CapacityUnits": 2.0, "ReadCapacityUnits": 2.0} + ) + + res = client.transact_get_items( + TransactItems=[ + { + "Get": { + "Key": {"id": {"S": "1"}, "sort_key": {"S": "1"}}, + "TableName": "test1", + } + }, + { + "Get": { + "Key": {"id": {"S": "1"}, "sort_key": {"S": "2"}}, + "TableName": "test1", + } + }, + { + "Get": { + "Key": {"id": {"S": "1"}, "sort_key": {"S": "1"}}, + "TableName": "test2", + } + }, + ], + ReturnConsumedCapacity="INDEXES", + ) + + res["ConsumedCapacity"][0].should.equal( { - 'Get': { - 'Key': { - 'id': {'S': '1'}, - 'sort_key': {'S': '1'} - }, - 'TableName': 'test1' - } - }, - { - 'Get': { - 'Key': { - 'id': {'S': 'non_exists_key'}, - 'sort_key': {'S': '2'} - }, - 'TableName': 'test1' - } + "TableName": "test1", + "CapacityUnits": 4.0, + "ReadCapacityUnits": 4.0, + "Table": {"CapacityUnits": 4.0, "ReadCapacityUnits": 4.0,}, } - ]) - res['Responses'][0]['Item'].should.equal({ - 'id': {'S': '1'}, - 'sort_key': {'S': '1'} - }) - len(res['Responses']).should.equal(1) + ) - res = client.transact_get_items(TransactItems=[ + res["ConsumedCapacity"][1].should.equal( { - 'Get': { - 'Key': { - 'id': {'S': '1'}, - 'sort_key': {'S': '1'} - }, - 'TableName': 'test1' - } - }, - { - 'Get': { - 'Key': { - 'id': {'S': '1'}, - 'sort_key': {'S': '2'} - }, - 'TableName': 'test1' - } - }, - { - 'Get': { - 'Key': { - 'id': {'S': '1'}, - 'sort_key': {'S': '1'} - }, - 'TableName': 'test2' - } - }, - ]) - - res['Responses'][0]['Item'].should.equal({ - 'id': {'S': '1'}, - 'sort_key': {'S': '1'} - }) - - res['Responses'][1]['Item'].should.equal({ - 'id': {'S': '1'}, - 'sort_key': {'S': '2'} - }) - - res['Responses'][2]['Item'].should.equal({ - 'id': {'S': '1'}, - 'sort_key': {'S': '1'} - }) - - res = client.transact_get_items(TransactItems=[ - { - 'Get': { - 'Key': { - 'id': {'S': '1'}, - 'sort_key': {'S': '1'} - }, - 'TableName': 'test1' - } - }, - { - 'Get': { - 'Key': { - 'id': {'S': '1'}, - 'sort_key': {'S': '2'} - }, - 'TableName': 'test1' - } - }, - { - 'Get': { - 'Key': { - 'id': {'S': '1'}, - 'sort_key': {'S': '1'} - }, - 'TableName': 'test2' - } - }, - ], ReturnConsumedCapacity='TOTAL') - - res['ConsumedCapacity'][0].should.equal({ - 'TableName': 'test1', - 'CapacityUnits': 4.0, - 'ReadCapacityUnits': 4.0 - }) - - res['ConsumedCapacity'][1].should.equal({ - 'TableName': 'test2', - 'CapacityUnits': 2.0, - 'ReadCapacityUnits': 2.0 - }) - - res = client.transact_get_items(TransactItems=[ - { - 'Get': { - 'Key': { - 'id': {'S': '1'}, - 'sort_key': {'S': '1'} - }, - 'TableName': 'test1' - } - }, - { - 'Get': { - 'Key': { - 'id': {'S': '1'}, - 'sort_key': {'S': '2'} - }, - 'TableName': 'test1' - } - }, - { - 'Get': { - 'Key': { - 'id': {'S': '1'}, - 'sort_key': {'S': '1'} - }, - 'TableName': 'test2' - } - }, - ], ReturnConsumedCapacity='INDEXES') - - res['ConsumedCapacity'][0].should.equal({ - 'TableName': 'test1', - 'CapacityUnits': 4.0, - 'ReadCapacityUnits': 4.0, - 'Table': { - 'CapacityUnits': 4.0, - 'ReadCapacityUnits': 4.0, + "TableName": "test2", + "CapacityUnits": 2.0, + "ReadCapacityUnits": 2.0, + "Table": {"CapacityUnits": 2.0, "ReadCapacityUnits": 2.0,}, } - }) - - res['ConsumedCapacity'][1].should.equal({ - 'TableName': 'test2', - 'CapacityUnits': 2.0, - 'ReadCapacityUnits': 2.0, - 'Table': { - 'CapacityUnits': 2.0, - 'ReadCapacityUnits': 2.0, - } - }) + )