diff --git a/moto/dynamodb2/models.py b/moto/dynamodb2/models.py index 77b1434c3..43059265c 100644 --- a/moto/dynamodb2/models.py +++ b/moto/dynamodb2/models.py @@ -184,6 +184,7 @@ class Table(object): 'ItemCount': len(self), 'CreationDateTime': unix_time(self.created_at), 'GlobalSecondaryIndexes': [index for index in self.global_indexes], + 'LocalSecondaryIndexes': [index for index in self.indexes] } } return results diff --git a/moto/dynamodb2/responses.py b/moto/dynamodb2/responses.py index 48340405b..08a03c08c 100644 --- a/moto/dynamodb2/responses.py +++ b/moto/dynamodb2/responses.py @@ -100,12 +100,14 @@ class DynamoHandler(BaseResponse): attr = body["AttributeDefinitions"] # getting the indexes global_indexes = body.get("GlobalSecondaryIndexes", []) + local_secondary_indexes = body.get("LocalSecondaryIndexes", []) table = dynamodb_backend2.create_table(table_name, schema=key_schema, throughput=throughput, attr=attr, - global_indexes=global_indexes) + global_indexes=global_indexes, + indexes=local_secondary_indexes) if table is not None: return dynamo_json_dump(table.describe) else: diff --git a/tests/test_dynamodb2/test_dynamodb_table_with_range_key.py b/tests/test_dynamodb2/test_dynamodb_table_with_range_key.py index a90f06caf..bcda68f6d 100644 --- a/tests/test_dynamodb2/test_dynamodb_table_with_range_key.py +++ b/tests/test_dynamodb2/test_dynamodb_table_with_range_key.py @@ -11,8 +11,9 @@ from moto import mock_dynamodb2 from boto.exception import JSONResponseError from tests.helpers import requires_boto_gte try: - from boto.dynamodb2.fields import GlobalAllIndex, HashKey, RangeKey + from boto.dynamodb2.fields import GlobalAllIndex, HashKey, RangeKey, AllIndex from boto.dynamodb2.table import Item, Table + from boto.dynamodb2.types import STRING, NUMBER from boto.dynamodb2.exceptions import ValidationException from boto.dynamodb2.exceptions import ConditionalCheckFailedException except ImportError: @@ -30,6 +31,30 @@ def create_table(): return table +def create_table_with_local_indexes(): + table = Table.create( + 'messages', + schema=[ + HashKey('forum_name'), + RangeKey('subject'), + ], + throughput={ + 'read': 10, + 'write': 10, + }, + indexes=[ + AllIndex( + 'threads_index', + parts=[ + HashKey('forum_name', data_type=STRING), + RangeKey('threads', data_type=NUMBER), + ] + ) + ] + ) + return table + + def iterate_results(res): for i in res: pass @@ -56,6 +81,7 @@ def test_create_table(): {'KeyType': 'HASH', 'AttributeName': 'forum_name'}, {'KeyType': 'RANGE', 'AttributeName': 'subject'} ], + 'LocalSecondaryIndexes': [], 'ItemCount': 0, 'CreationDateTime': 1326499200.0, 'GlobalSecondaryIndexes': [], } @@ -63,6 +89,48 @@ def test_create_table(): table.describe().should.equal(expected) +@requires_boto_gte("2.9") +@mock_dynamodb2 +@freeze_time("2012-01-14") +def test_create_table_with_local_index(): + table = create_table_with_local_indexes() + expected = { + 'Table': { + 'AttributeDefinitions': [ + {'AttributeName': 'forum_name', 'AttributeType': 'S'}, + {'AttributeName': 'subject', 'AttributeType': 'S'}, + {'AttributeName': 'threads', 'AttributeType': 'N'} + ], + 'ProvisionedThroughput': { + 'NumberOfDecreasesToday': 0, + 'WriteCapacityUnits': 10, + 'ReadCapacityUnits': 10, + }, + 'TableSizeBytes': 0, + 'TableName': 'messages', + 'TableStatus': 'ACTIVE', + 'KeySchema': [ + {'KeyType': 'HASH', 'AttributeName': 'forum_name'}, + {'KeyType': 'RANGE', 'AttributeName': 'subject'} + ], + 'LocalSecondaryIndexes': [ + { + 'IndexName': 'threads_index', + 'KeySchema': [ + {'AttributeName': 'forum_name', 'KeyType': 'HASH'}, + {'AttributeName': 'threads', 'KeyType': 'RANGE'} + ], + 'Projection': {'ProjectionType': 'ALL'} + } + ], + 'ItemCount': 0, + 'CreationDateTime': 1326499200.0, + 'GlobalSecondaryIndexes': [], + } + } + table.describe().should.equal(expected) + + @requires_boto_gte("2.9") @mock_dynamodb2 def test_delete_table(): @@ -570,6 +638,26 @@ def test_query_with_global_indexes(): list(results).should.have.length_of(0) +@mock_dynamodb2 +def test_query_with_local_indexes(): + table = create_table_with_local_indexes() + item_data = { + 'forum_name': 'Cool Forum', + 'subject': 'Check this out!', + 'version': '1', + 'threads': 1, + 'status': 'inactive' + } + item = Item(table, item_data) + item.save(overwrite=True) + + item['version'] = '2' + item.save(overwrite=True) + # Revisit this query once support for QueryFilter is added + results = table.query(forum_name__eq='Cool Forum', index='threads_index') + list(results).should.have.length_of(1) + + @mock_dynamodb2 def test_reverse_query(): conn = boto.dynamodb2.layer1.DynamoDBConnection() diff --git a/tests/test_dynamodb2/test_dynamodb_table_without_range_key.py b/tests/test_dynamodb2/test_dynamodb_table_without_range_key.py index 6baeb8a12..36aac9a87 100644 --- a/tests/test_dynamodb2/test_dynamodb_table_without_range_key.py +++ b/tests/test_dynamodb2/test_dynamodb_table_without_range_key.py @@ -48,6 +48,7 @@ def test_create_table(): ], 'ItemCount': 0, 'CreationDateTime': 1326499200.0, 'GlobalSecondaryIndexes': [], + 'LocalSecondaryIndexes': [] } } conn = boto.dynamodb2.connect_to_region(