| Index: third_party/gsutil/third_party/boto/tests/integration/dynamodb2/test_highlevel.py
|
| diff --git a/third_party/gsutil/third_party/boto/tests/integration/dynamodb2/test_highlevel.py b/third_party/gsutil/third_party/boto/tests/integration/dynamodb2/test_highlevel.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..833400783ee98aad066494e6302dc4fa36468b55
|
| --- /dev/null
|
| +++ b/third_party/gsutil/third_party/boto/tests/integration/dynamodb2/test_highlevel.py
|
| @@ -0,0 +1,821 @@
|
| +# Copyright (c) 2013 Amazon.com, Inc. or its affiliates.
|
| +# All rights reserved.
|
| +#
|
| +# Permission is hereby granted, free of charge, to any person obtaining a
|
| +# copy of this software and associated documentation files (the
|
| +# "Software"), to deal in the Software without restriction, including
|
| +# without limitation the rights to use, copy, modify, merge, publish, dis-
|
| +# tribute, sublicense, and/or sell copies of the Software, and to permit
|
| +# persons to whom the Software is furnished to do so, subject to the fol-
|
| +# lowing conditions:
|
| +#
|
| +# The above copyright notice and this permission notice shall be included
|
| +# in all copies or substantial portions of the Software.
|
| +#
|
| +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
| +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
|
| +# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
| +# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
| +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
| +# IN THE SOFTWARE.
|
| +
|
| +"""
|
| +Tests for DynamoDB v2 high-level abstractions.
|
| +"""
|
| +import os
|
| +import time
|
| +
|
| +from tests.unit import unittest
|
| +from boto.dynamodb2 import exceptions
|
| +from boto.dynamodb2.fields import (HashKey, RangeKey, KeysOnlyIndex,
|
| + GlobalKeysOnlyIndex, GlobalIncludeIndex,
|
| + GlobalAllIndex)
|
| +from boto.dynamodb2.items import Item
|
| +from boto.dynamodb2.table import Table
|
| +from boto.dynamodb2.types import NUMBER, STRING
|
| +
|
| +try:
|
| + import json
|
| +except ImportError:
|
| + import simplejson as json
|
| +
|
| +
|
| +class DynamoDBv2Test(unittest.TestCase):
|
| + dynamodb = True
|
| +
|
| + def test_integration(self):
|
| + # Test creating a full table with all options specified.
|
| + users = Table.create('users', schema=[
|
| + HashKey('username'),
|
| + RangeKey('friend_count', data_type=NUMBER)
|
| + ], throughput={
|
| + 'read': 5,
|
| + 'write': 5,
|
| + }, indexes=[
|
| + KeysOnlyIndex('LastNameIndex', parts=[
|
| + HashKey('username'),
|
| + RangeKey('last_name')
|
| + ]),
|
| + ])
|
| + self.addCleanup(users.delete)
|
| +
|
| + self.assertEqual(len(users.schema), 2)
|
| + self.assertEqual(users.throughput['read'], 5)
|
| +
|
| + # Wait for it.
|
| + time.sleep(60)
|
| +
|
| + # Make sure things line up if we're introspecting the table.
|
| + users_hit_api = Table('users')
|
| + users_hit_api.describe()
|
| + self.assertEqual(len(users.schema), len(users_hit_api.schema))
|
| + self.assertEqual(users.throughput, users_hit_api.throughput)
|
| + self.assertEqual(len(users.indexes), len(users_hit_api.indexes))
|
| +
|
| + # Test putting some items individually.
|
| + users.put_item(data={
|
| + 'username': 'johndoe',
|
| + 'first_name': 'John',
|
| + 'last_name': 'Doe',
|
| + 'friend_count': 4
|
| + })
|
| +
|
| + users.put_item(data={
|
| + 'username': 'alice',
|
| + 'first_name': 'Alice',
|
| + 'last_name': 'Expert',
|
| + 'friend_count': 2
|
| + })
|
| +
|
| + time.sleep(5)
|
| +
|
| + # Test batch writing.
|
| + with users.batch_write() as batch:
|
| + batch.put_item({
|
| + 'username': 'jane',
|
| + 'first_name': 'Jane',
|
| + 'last_name': 'Doe',
|
| + 'friend_count': 3
|
| + })
|
| + batch.delete_item(username='alice', friend_count=2)
|
| + batch.put_item({
|
| + 'username': 'bob',
|
| + 'first_name': 'Bob',
|
| + 'last_name': 'Smith',
|
| + 'friend_count': 1
|
| + })
|
| +
|
| + time.sleep(5)
|
| +
|
| + # Does it exist? It should?
|
| + self.assertTrue(users.has_item(username='jane', friend_count=3))
|
| + # But this shouldn't be there...
|
| + self.assertFalse(users.has_item(
|
| + username='mrcarmichaeljones',
|
| + friend_count=72948
|
| + ))
|
| +
|
| + # Test getting an item & updating it.
|
| + # This is the "safe" variant (only write if there have been no
|
| + # changes).
|
| + jane = users.get_item(username='jane', friend_count=3)
|
| + self.assertEqual(jane['first_name'], 'Jane')
|
| + jane['last_name'] = 'Doh'
|
| + self.assertTrue(jane.save())
|
| +
|
| + # Test strongly consistent getting of an item.
|
| + # Additionally, test the overwrite behavior.
|
| + client_1_jane = users.get_item(
|
| + username='jane',
|
| + friend_count=3,
|
| + consistent=True
|
| + )
|
| + self.assertEqual(jane['first_name'], 'Jane')
|
| + client_2_jane = users.get_item(
|
| + username='jane',
|
| + friend_count=3,
|
| + consistent=True
|
| + )
|
| + self.assertEqual(jane['first_name'], 'Jane')
|
| +
|
| + # Write & assert the ``first_name`` is gone, then...
|
| + del client_1_jane['first_name']
|
| + self.assertTrue(client_1_jane.save())
|
| + check_name = users.get_item(
|
| + username='jane',
|
| + friend_count=3,
|
| + consistent=True
|
| + )
|
| + self.assertEqual(check_name['first_name'], None)
|
| +
|
| + # ...overwrite the data with what's in memory.
|
| + client_2_jane['first_name'] = 'Joan'
|
| + # Now a write that fails due to default expectations...
|
| + self.assertRaises(exceptions.JSONResponseError, client_2_jane.save)
|
| + # ... so we force an overwrite.
|
| + self.assertTrue(client_2_jane.save(overwrite=True))
|
| + check_name_again = users.get_item(
|
| + username='jane',
|
| + friend_count=3,
|
| + consistent=True
|
| + )
|
| + self.assertEqual(check_name_again['first_name'], 'Joan')
|
| +
|
| + # Reset it.
|
| + jane['username'] = 'jane'
|
| + jane['first_name'] = 'Jane'
|
| + jane['last_name'] = 'Doe'
|
| + jane['friend_count'] = 3
|
| + self.assertTrue(jane.save(overwrite=True))
|
| +
|
| + # Test the partial update behavior.
|
| + client_3_jane = users.get_item(
|
| + username='jane',
|
| + friend_count=3,
|
| + consistent=True
|
| + )
|
| + client_4_jane = users.get_item(
|
| + username='jane',
|
| + friend_count=3,
|
| + consistent=True
|
| + )
|
| + client_3_jane['favorite_band'] = 'Feed Me'
|
| + # No ``overwrite`` needed due to new data.
|
| + self.assertTrue(client_3_jane.save())
|
| + # Expectations are only checked on the ``first_name``, so what wouldn't
|
| + # have succeeded by default does succeed here.
|
| + client_4_jane['first_name'] = 'Jacqueline'
|
| + self.assertTrue(client_4_jane.partial_save())
|
| + partial_jane = users.get_item(
|
| + username='jane',
|
| + friend_count=3,
|
| + consistent=True
|
| + )
|
| + self.assertEqual(partial_jane['favorite_band'], 'Feed Me')
|
| + self.assertEqual(partial_jane['first_name'], 'Jacqueline')
|
| +
|
| + # Reset it.
|
| + jane['username'] = 'jane'
|
| + jane['first_name'] = 'Jane'
|
| + jane['last_name'] = 'Doe'
|
| + jane['friend_count'] = 3
|
| + self.assertTrue(jane.save(overwrite=True))
|
| +
|
| + # Ensure that partial saves of a brand-new object work.
|
| + sadie = Item(users, data={
|
| + 'username': 'sadie',
|
| + 'first_name': 'Sadie',
|
| + 'favorite_band': 'Zedd',
|
| + 'friend_count': 7
|
| + })
|
| + self.assertTrue(sadie.partial_save())
|
| + serverside_sadie = users.get_item(
|
| + username='sadie',
|
| + friend_count=7,
|
| + consistent=True
|
| + )
|
| + self.assertEqual(serverside_sadie['first_name'], 'Sadie')
|
| +
|
| + # Test the eventually consistent query.
|
| + results = users.query_2(
|
| + username__eq='johndoe',
|
| + last_name__eq='Doe',
|
| + index='LastNameIndex',
|
| + attributes=('username',),
|
| + reverse=True
|
| + )
|
| +
|
| + for res in results:
|
| + self.assertTrue(res['username'] in ['johndoe',])
|
| + self.assertEqual(list(res.keys()), ['username'])
|
| +
|
| + # Ensure that queries with attributes don't return the hash key.
|
| + results = users.query_2(
|
| + username__eq='johndoe',
|
| + friend_count__eq=4,
|
| + attributes=('first_name',)
|
| + )
|
| +
|
| + for res in results:
|
| + self.assertEqual(res['first_name'], 'John')
|
| + self.assertEqual(list(res.keys()), ['first_name'])
|
| +
|
| + # Test the strongly consistent query.
|
| + c_results = users.query_2(
|
| + username__eq='johndoe',
|
| + last_name__eq='Doe',
|
| + index='LastNameIndex',
|
| + reverse=True,
|
| + consistent=True
|
| + )
|
| +
|
| + for res in c_results:
|
| + self.assertEqual(res['username'], 'johndoe')
|
| +
|
| + # Test a query with query filters
|
| + results = users.query_2(
|
| + username__eq='johndoe',
|
| + query_filter={
|
| + 'first_name__beginswith': 'J'
|
| + },
|
| + attributes=('first_name',)
|
| + )
|
| +
|
| + for res in results:
|
| + self.assertTrue(res['first_name'] in ['John'])
|
| +
|
| + # Test scans without filters.
|
| + all_users = users.scan(limit=7)
|
| + self.assertEqual(next(all_users)['username'], 'bob')
|
| + self.assertEqual(next(all_users)['username'], 'jane')
|
| + self.assertEqual(next(all_users)['username'], 'johndoe')
|
| +
|
| + # Test scans with a filter.
|
| + filtered_users = users.scan(limit=2, username__beginswith='j')
|
| + self.assertEqual(next(filtered_users)['username'], 'jane')
|
| + self.assertEqual(next(filtered_users)['username'], 'johndoe')
|
| +
|
| + # Test deleting a single item.
|
| + johndoe = users.get_item(username='johndoe', friend_count=4)
|
| + johndoe.delete()
|
| +
|
| + # Set batch get limit to ensure keys with no results are
|
| + # handled correctly.
|
| + users.max_batch_get = 2
|
| +
|
| + # Test the eventually consistent batch get.
|
| + results = users.batch_get(keys=[
|
| + {'username': 'noone', 'friend_count': 4},
|
| + {'username': 'nothere', 'friend_count': 10},
|
| + {'username': 'bob', 'friend_count': 1},
|
| + {'username': 'jane', 'friend_count': 3}
|
| + ])
|
| + batch_users = []
|
| +
|
| + for res in results:
|
| + batch_users.append(res)
|
| + self.assertIn(res['first_name'], ['Bob', 'Jane'])
|
| +
|
| + self.assertEqual(len(batch_users), 2)
|
| +
|
| + # Test the strongly consistent batch get.
|
| + c_results = users.batch_get(keys=[
|
| + {'username': 'bob', 'friend_count': 1},
|
| + {'username': 'jane', 'friend_count': 3}
|
| + ], consistent=True)
|
| + c_batch_users = []
|
| +
|
| + for res in c_results:
|
| + c_batch_users.append(res)
|
| + self.assertTrue(res['first_name'] in ['Bob', 'Jane'])
|
| +
|
| + self.assertEqual(len(c_batch_users), 2)
|
| +
|
| + # Test count, but in a weak fashion. Because lag time.
|
| + self.assertTrue(users.count() > -1)
|
| +
|
| + # Test query count
|
| + count = users.query_count(
|
| + username__eq='bob',
|
| + )
|
| +
|
| + self.assertEqual(count, 1)
|
| +
|
| + # Test without LSIs (describe calls shouldn't fail).
|
| + admins = Table.create('admins', schema=[
|
| + HashKey('username')
|
| + ])
|
| + self.addCleanup(admins.delete)
|
| + time.sleep(60)
|
| + admins.describe()
|
| + self.assertEqual(admins.throughput['read'], 5)
|
| + self.assertEqual(admins.indexes, [])
|
| +
|
| + # A single query term should fail on a table with *ONLY* a HashKey.
|
| + self.assertRaises(
|
| + exceptions.QueryError,
|
| + admins.query,
|
| + username__eq='johndoe'
|
| + )
|
| + # But it shouldn't break on more complex tables.
|
| + res = users.query_2(username__eq='johndoe')
|
| +
|
| + # Test putting with/without sets.
|
| + mau5_created = users.put_item(data={
|
| + 'username': 'mau5',
|
| + 'first_name': 'dead',
|
| + 'last_name': 'mau5',
|
| + 'friend_count': 2,
|
| + 'friends': set(['skrill', 'penny']),
|
| + })
|
| + self.assertTrue(mau5_created)
|
| +
|
| + penny_created = users.put_item(data={
|
| + 'username': 'penny',
|
| + 'first_name': 'Penny',
|
| + 'friend_count': 0,
|
| + 'friends': set([]),
|
| + })
|
| + self.assertTrue(penny_created)
|
| +
|
| + # Test attributes.
|
| + mau5 = users.get_item(
|
| + username='mau5',
|
| + friend_count=2,
|
| + attributes=['username', 'first_name']
|
| + )
|
| + self.assertEqual(mau5['username'], 'mau5')
|
| + self.assertEqual(mau5['first_name'], 'dead')
|
| + self.assertTrue('last_name' not in mau5)
|
| +
|
| + def test_unprocessed_batch_writes(self):
|
| + # Create a very limited table w/ low throughput.
|
| + users = Table.create('slow_users', schema=[
|
| + HashKey('user_id'),
|
| + ], throughput={
|
| + 'read': 1,
|
| + 'write': 1,
|
| + })
|
| + self.addCleanup(users.delete)
|
| +
|
| + # Wait for it.
|
| + time.sleep(60)
|
| +
|
| + with users.batch_write() as batch:
|
| + for i in range(500):
|
| + batch.put_item(data={
|
| + 'user_id': str(i),
|
| + 'name': 'Droid #{0}'.format(i),
|
| + })
|
| +
|
| + # Before ``__exit__`` runs, we should have a bunch of unprocessed
|
| + # items.
|
| + self.assertTrue(len(batch._unprocessed) > 0)
|
| +
|
| + # Post-__exit__, they should all be gone.
|
| + self.assertEqual(len(batch._unprocessed), 0)
|
| +
|
| + def test_gsi(self):
|
| + users = Table.create('gsi_users', schema=[
|
| + HashKey('user_id'),
|
| + ], throughput={
|
| + 'read': 5,
|
| + 'write': 3,
|
| + },
|
| + global_indexes=[
|
| + GlobalKeysOnlyIndex('StuffIndex', parts=[
|
| + HashKey('user_id')
|
| + ], throughput={
|
| + 'read': 2,
|
| + 'write': 1,
|
| + }),
|
| + ])
|
| + self.addCleanup(users.delete)
|
| +
|
| + # Wait for it.
|
| + time.sleep(60)
|
| +
|
| + users.update(
|
| + throughput={
|
| + 'read': 3,
|
| + 'write': 4
|
| + },
|
| + global_indexes={
|
| + 'StuffIndex': {
|
| + 'read': 1,
|
| + 'write': 2
|
| + }
|
| + }
|
| + )
|
| +
|
| + # Wait again for the changes to finish propagating.
|
| + time.sleep(150)
|
| +
|
| + def test_gsi_with_just_hash_key(self):
|
| + # GSI allows for querying off of different keys. This is behavior we
|
| + # previously disallowed (due to standard & LSI queries).
|
| + # See https://forums.aws.amazon.com/thread.jspa?threadID=146212&tstart=0
|
| + users = Table.create('gsi_query_users', schema=[
|
| + HashKey('user_id')
|
| + ], throughput={
|
| + 'read': 5,
|
| + 'write': 3,
|
| + },
|
| + global_indexes=[
|
| + GlobalIncludeIndex('UsernameIndex', parts=[
|
| + HashKey('username'),
|
| + ], includes=['user_id', 'username'], throughput={
|
| + 'read': 3,
|
| + 'write': 1,
|
| + })
|
| + ])
|
| + self.addCleanup(users.delete)
|
| +
|
| + # Wait for it.
|
| + time.sleep(60)
|
| +
|
| + users.put_item(data={
|
| + 'user_id': '7',
|
| + 'username': 'johndoe',
|
| + 'first_name': 'John',
|
| + 'last_name': 'Doe',
|
| + })
|
| + users.put_item(data={
|
| + 'user_id': '24',
|
| + 'username': 'alice',
|
| + 'first_name': 'Alice',
|
| + 'last_name': 'Expert',
|
| + })
|
| + users.put_item(data={
|
| + 'user_id': '35',
|
| + 'username': 'jane',
|
| + 'first_name': 'Jane',
|
| + 'last_name': 'Doe',
|
| + })
|
| +
|
| + # Try the main key. Should be fine.
|
| + rs = users.query_2(
|
| + user_id__eq='24'
|
| + )
|
| + results = sorted([user['username'] for user in rs])
|
| + self.assertEqual(results, ['alice'])
|
| +
|
| + # Now try the GSI. Also should work.
|
| + rs = users.query_2(
|
| + username__eq='johndoe',
|
| + index='UsernameIndex'
|
| + )
|
| + results = sorted([user['username'] for user in rs])
|
| + self.assertEqual(results, ['johndoe'])
|
| +
|
| + def test_query_with_limits(self):
|
| + # Per the DDB team, it's recommended to do many smaller gets with a
|
| + # reduced page size.
|
| + # Clamp down the page size while ensuring that the correct number of
|
| + # results are still returned.
|
| + posts = Table.create('posts', schema=[
|
| + HashKey('thread'),
|
| + RangeKey('posted_on')
|
| + ], throughput={
|
| + 'read': 5,
|
| + 'write': 5,
|
| + })
|
| + self.addCleanup(posts.delete)
|
| +
|
| + # Wait for it.
|
| + time.sleep(60)
|
| +
|
| + # Add some data.
|
| + test_data_path = os.path.join(
|
| + os.path.dirname(__file__),
|
| + 'forum_test_data.json'
|
| + )
|
| + with open(test_data_path, 'r') as test_data:
|
| + data = json.load(test_data)
|
| +
|
| + with posts.batch_write() as batch:
|
| + for post in data:
|
| + batch.put_item(post)
|
| +
|
| + time.sleep(5)
|
| +
|
| + # Test the reduced page size.
|
| + results = posts.query_2(
|
| + thread__eq='Favorite chiptune band?',
|
| + posted_on__gte='2013-12-24T00:00:00',
|
| + max_page_size=2
|
| + )
|
| +
|
| + all_posts = list(results)
|
| + self.assertEqual(
|
| + [post['posted_by'] for post in all_posts],
|
| + ['joe', 'jane', 'joe', 'joe', 'jane', 'joe']
|
| + )
|
| + self.assertTrue(results._fetches >= 3)
|
| +
|
| + def test_query_with_reverse(self):
|
| + posts = Table.create('more-posts', schema=[
|
| + HashKey('thread'),
|
| + RangeKey('posted_on')
|
| + ], throughput={
|
| + 'read': 5,
|
| + 'write': 5,
|
| + })
|
| + self.addCleanup(posts.delete)
|
| +
|
| + # Wait for it.
|
| + time.sleep(60)
|
| +
|
| + # Add some data.
|
| + test_data_path = os.path.join(
|
| + os.path.dirname(__file__),
|
| + 'forum_test_data.json'
|
| + )
|
| + with open(test_data_path, 'r') as test_data:
|
| + data = json.load(test_data)
|
| +
|
| + with posts.batch_write() as batch:
|
| + for post in data:
|
| + batch.put_item(post)
|
| +
|
| + time.sleep(5)
|
| +
|
| + # Test the default order (ascending).
|
| + results = posts.query_2(
|
| + thread__eq='Favorite chiptune band?',
|
| + posted_on__gte='2013-12-24T00:00:00'
|
| + )
|
| + self.assertEqual(
|
| + [post['posted_on'] for post in results],
|
| + [
|
| + '2013-12-24T12:30:54',
|
| + '2013-12-24T12:35:40',
|
| + '2013-12-24T13:45:30',
|
| + '2013-12-24T14:15:14',
|
| + '2013-12-24T14:25:33',
|
| + '2013-12-24T15:22:22',
|
| + ]
|
| + )
|
| +
|
| + # Test the explicit ascending order.
|
| + results = posts.query_2(
|
| + thread__eq='Favorite chiptune band?',
|
| + posted_on__gte='2013-12-24T00:00:00',
|
| + reverse=False
|
| + )
|
| + self.assertEqual(
|
| + [post['posted_on'] for post in results],
|
| + [
|
| + '2013-12-24T12:30:54',
|
| + '2013-12-24T12:35:40',
|
| + '2013-12-24T13:45:30',
|
| + '2013-12-24T14:15:14',
|
| + '2013-12-24T14:25:33',
|
| + '2013-12-24T15:22:22',
|
| + ]
|
| + )
|
| +
|
| + # Test the explicit descending order.
|
| + results = posts.query_2(
|
| + thread__eq='Favorite chiptune band?',
|
| + posted_on__gte='2013-12-24T00:00:00',
|
| + reverse=True
|
| + )
|
| + self.assertEqual(
|
| + [post['posted_on'] for post in results],
|
| + [
|
| + '2013-12-24T15:22:22',
|
| + '2013-12-24T14:25:33',
|
| + '2013-12-24T14:15:14',
|
| + '2013-12-24T13:45:30',
|
| + '2013-12-24T12:35:40',
|
| + '2013-12-24T12:30:54',
|
| + ]
|
| + )
|
| +
|
| + # Test the old, broken style.
|
| + results = posts.query(
|
| + thread__eq='Favorite chiptune band?',
|
| + posted_on__gte='2013-12-24T00:00:00'
|
| + )
|
| + self.assertEqual(
|
| + [post['posted_on'] for post in results],
|
| + [
|
| + '2013-12-24T15:22:22',
|
| + '2013-12-24T14:25:33',
|
| + '2013-12-24T14:15:14',
|
| + '2013-12-24T13:45:30',
|
| + '2013-12-24T12:35:40',
|
| + '2013-12-24T12:30:54',
|
| + ]
|
| + )
|
| + results = posts.query(
|
| + thread__eq='Favorite chiptune band?',
|
| + posted_on__gte='2013-12-24T00:00:00',
|
| + reverse=True
|
| + )
|
| + self.assertEqual(
|
| + [post['posted_on'] for post in results],
|
| + [
|
| + '2013-12-24T12:30:54',
|
| + '2013-12-24T12:35:40',
|
| + '2013-12-24T13:45:30',
|
| + '2013-12-24T14:15:14',
|
| + '2013-12-24T14:25:33',
|
| + '2013-12-24T15:22:22',
|
| + ]
|
| + )
|
| +
|
| + def test_query_after_describe_with_gsi(self):
|
| + # Create a table to using gsi to reproduce the error mentioned on issue
|
| + # https://github.com/boto/boto/issues/2828
|
| + users = Table.create('more_gsi_query_users', schema=[
|
| + HashKey('user_id')
|
| + ], throughput={
|
| + 'read': 5,
|
| + 'write': 5
|
| + }, global_indexes=[
|
| + GlobalAllIndex('EmailGSIIndex', parts=[
|
| + HashKey('email')
|
| + ], throughput={
|
| + 'read': 1,
|
| + 'write': 1
|
| + })
|
| + ])
|
| +
|
| + # Add this function to be called after tearDown()
|
| + self.addCleanup(users.delete)
|
| +
|
| + # Wait for it.
|
| + time.sleep(60)
|
| +
|
| + # populate a couple of items in it
|
| + users.put_item(data={
|
| + 'user_id': '7',
|
| + 'username': 'johndoe',
|
| + 'first_name': 'John',
|
| + 'last_name': 'Doe',
|
| + 'email': 'johndoe@johndoe.com',
|
| + })
|
| + users.put_item(data={
|
| + 'user_id': '24',
|
| + 'username': 'alice',
|
| + 'first_name': 'Alice',
|
| + 'last_name': 'Expert',
|
| + 'email': 'alice@alice.com',
|
| + })
|
| + users.put_item(data={
|
| + 'user_id': '35',
|
| + 'username': 'jane',
|
| + 'first_name': 'Jane',
|
| + 'last_name': 'Doe',
|
| + 'email': 'jane@jane.com',
|
| + })
|
| +
|
| + # Try the GSI. it should work.
|
| + rs = users.query_2(
|
| + email__eq='johndoe@johndoe.com',
|
| + index='EmailGSIIndex'
|
| + )
|
| +
|
| + for rs_item in rs:
|
| + self.assertEqual(rs_item['username'], ['johndoe'])
|
| +
|
| + # The issue arises when we're introspecting the table and try to
|
| + # query_2 after call describe method
|
| + users_hit_api = Table('more_gsi_query_users')
|
| + users_hit_api.describe()
|
| +
|
| + # Try the GSI. This is what os going wrong on #2828 issue. It should
|
| + # work fine now.
|
| + rs = users_hit_api.query_2(
|
| + email__eq='johndoe@johndoe.com',
|
| + index='EmailGSIIndex'
|
| + )
|
| +
|
| + for rs_item in rs:
|
| + self.assertEqual(rs_item['username'], ['johndoe'])
|
| +
|
| + def test_update_table_online_indexing_support(self):
|
| + # Create a table using gsi to test the DynamoDB online indexing support
|
| + # https://github.com/boto/boto/pull/2925
|
| + users = Table.create('online_indexing_support_users', schema=[
|
| + HashKey('user_id')
|
| + ], throughput={
|
| + 'read': 5,
|
| + 'write': 5
|
| + }, global_indexes=[
|
| + GlobalAllIndex('EmailGSIIndex', parts=[
|
| + HashKey('email')
|
| + ], throughput={
|
| + 'read': 2,
|
| + 'write': 2
|
| + })
|
| + ])
|
| +
|
| + # Add this function to be called after tearDown()
|
| + self.addCleanup(users.delete)
|
| +
|
| + # Wait for it.
|
| + time.sleep(60)
|
| +
|
| + # Fetch fresh table desc from DynamoDB
|
| + users.describe()
|
| +
|
| + # Assert if everything is fine so far
|
| + self.assertEqual(len(users.global_indexes), 1)
|
| + self.assertEqual(users.global_indexes[0].throughput['read'], 2)
|
| + self.assertEqual(users.global_indexes[0].throughput['write'], 2)
|
| +
|
| + # Update a GSI throughput. it should work.
|
| + users.update_global_secondary_index(global_indexes={
|
| + 'EmailGSIIndex': {
|
| + 'read': 2,
|
| + 'write': 1,
|
| + }
|
| + })
|
| +
|
| + # Wait for it.
|
| + time.sleep(60)
|
| +
|
| + # Fetch fresh table desc from DynamoDB
|
| + users.describe()
|
| +
|
| + # Assert if everything is fine so far
|
| + self.assertEqual(len(users.global_indexes), 1)
|
| + self.assertEqual(users.global_indexes[0].throughput['read'], 2)
|
| + self.assertEqual(users.global_indexes[0].throughput['write'], 1)
|
| +
|
| + # Update a GSI throughput using the old fashion way for compatibility
|
| + # purposes. it should work.
|
| + users.update(global_indexes={
|
| + 'EmailGSIIndex': {
|
| + 'read': 3,
|
| + 'write': 2,
|
| + }
|
| + })
|
| +
|
| + # Wait for it.
|
| + time.sleep(60)
|
| +
|
| + # Fetch fresh table desc from DynamoDB
|
| + users.describe()
|
| +
|
| + # Assert if everything is fine so far
|
| + self.assertEqual(len(users.global_indexes), 1)
|
| + self.assertEqual(users.global_indexes[0].throughput['read'], 3)
|
| + self.assertEqual(users.global_indexes[0].throughput['write'], 2)
|
| +
|
| + # Delete a GSI. it should work.
|
| + users.delete_global_secondary_index('EmailGSIIndex')
|
| +
|
| + # Wait for it.
|
| + time.sleep(60)
|
| +
|
| + # Fetch fresh table desc from DynamoDB
|
| + users.describe()
|
| +
|
| + # Assert if everything is fine so far
|
| + self.assertEqual(len(users.global_indexes), 0)
|
| +
|
| + # Create a GSI. it should work.
|
| + users.create_global_secondary_index(
|
| + global_index=GlobalAllIndex(
|
| + 'AddressGSIIndex', parts=[
|
| + HashKey('address', data_type=STRING)
|
| + ], throughput={
|
| + 'read': 1,
|
| + 'write': 1,
|
| + })
|
| + )
|
| + # Wait for it. This operation usually takes much longer than the others
|
| + time.sleep(60*10)
|
| +
|
| + # Fetch fresh table desc from DynamoDB
|
| + users.describe()
|
| +
|
| + # Assert if everything is fine so far
|
| + self.assertEqual(len(users.global_indexes), 1)
|
| + self.assertEqual(users.global_indexes[0].throughput['read'], 1)
|
| + self.assertEqual(users.global_indexes[0].throughput['write'], 1)
|
|
|