Repository: getmoto/moto

Issue:
Incomplete "LastEvaluatedKey" in response from dynamodb2 query() when using Global Secondary Index.
Hi,

Trying to create a pagination over dynamodb by using `LastEvaluatedKey` and `ExclusiveStartKey` with `qury()` [documentation](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.Pagination.html).
Querying is performed over GSI of the table. When running the tests using `mock_dynamodb2` returned `LastEvaluatedKey` does not contain attributes from global secondary index.

Actual behavior should be that response's `LastEvaluatedKey` should contain index partition and sort key . Tested with live dynamodb table.

Below script reproduces the issue:

```python
import boto3
from moto import mock_dynamodb2
from boto3.dynamodb.conditions import Key

@mock_dynamodb2
def index_test():
    dynamo_db_resource = boto3.resource("dynamodb", region_name="eu-west-1")
    dynamo_db_resource.create_table(
        TableName="TestTable",
        KeySchema=[
            {"AttributeName": "TestPartitionKey", "KeyType": "HASH"},
            {"AttributeName": "TestSortKey", "KeyType": "RANGE"},
        ],
        AttributeDefinitions=[
            {"AttributeName": "TestPartitionKey", "AttributeType": "S"},
            {"AttributeName": "TestSortKey", "AttributeType": "N"},
            {"AttributeName": "IndexPartitionKey", "AttributeType": "S"},
            {"AttributeName": "IndexSortKey", "AttributeType": "S"},
        ],
        GlobalSecondaryIndexes=[
            {
                "IndexName": "TestGlobalSecondaryIndex",
                "KeySchema": [
                    {"AttributeName": "IndexPartitionKey", "KeyType": "HASH"},
                    {"AttributeName": "IndexSortKey", "KeyType": "RANGE"},
                ],
                "Projection": {
                    "ProjectionType": "INCLUDE",
                    "NonKeyAttributes": ["TestSortKey, TestPartitionKey", "SomeKey"],
                },
            }
        ],
    )
    table = dynamo_db_resource.Table("TestTable")

    itmes = [
        {"TestPartitionKey": "TEST","TestSortKey": 1,"IndexPartitionKey": "PART_A","IndexSortKey": "A", "SomeKey": "XYZ"},
        {"TestPartitionKey": "TEST","TestSortKey": 2,"IndexPartitionKey": "PART_A","IndexSortKey": "A","SomeKey": "XYZ"},
        {"TestPartitionKey": "TEST","TestSortKey": 3,"IndexPartitionKey": "PART_B","IndexSortKey": "A", "SomeKey": "XYZ"},
        {"TestPartitionKey": "TEST","TestSortKey": 4,"IndexPartitionKey": "PART_B","IndexSortKey": "A", "SomeKey": "XYZ"},
        {"TestPartitionKey": "TEST","TestSortKey": 5,"IndexPartitionKey": "PART_B","IndexSortKey": "A", "SomeKey": "XYZ"}
    ]

    for item in itmes:
        table.put_item(
            Item=item
        )
    query_args = {
        "Limit": 1,
        "IndexName": "TestGlobalSecondaryIndex",
        "KeyConditionExpression": Key("TestGlobalSecondaryIndex").eq("PART_B"),
    }

    query_result = table.query(**query_args)
    print(query_result)


if __name__ == "__main__":
    index_test()
```

moto == 2.0.7
boto3 == 1.17.75
botocore == 1.20.75

Possible fix: after adding `index_name` in call to `_trim_results` the issue seems to be resolved:
https://github.com/spulec/moto/blob/7947b1e00133985775add3dd62b20d4fe9b2e652/moto/dynamodb2/models/__init__.py#L824-L826


Discussion:
That seems like an easy fix - will mark it as a bug. Thanks for providing the test case + fix @questsul 👍 

Resolving patch:
diff --git a/moto/dynamodb2/models/__init__.py b/moto/dynamodb2/models/__init__.py
--- a/moto/dynamodb2/models/__init__.py
+++ b/moto/dynamodb2/models/__init__.py
@@ -825,7 +825,7 @@ def conv(x):
                 result.filter(projection_expression)
 
         results, last_evaluated_key = self._trim_results(
-            results, limit, exclusive_start_key
+            results, limit, exclusive_start_key, scanned_index=index_name
         )
         return results, scanned_count, last_evaluated_key
 
@@ -917,7 +917,7 @@ def scan(
                 result.filter(projection_expression)
 
         results, last_evaluated_key = self._trim_results(
-            results, limit, exclusive_start_key, index_name
+            results, limit, exclusive_start_key, scanned_index=index_name
         )
         return results, scanned_count, last_evaluated_key
 


Test patch:
diff --git a/tests/test_dynamodb2/test_dynamodb.py b/tests/test_dynamodb2/test_dynamodb.py
--- a/tests/test_dynamodb2/test_dynamodb.py
+++ b/tests/test_dynamodb2/test_dynamodb.py
@@ -5965,3 +5965,63 @@ def test_batch_write_item():
         table = conn.Table(f"table-{idx}")
         scan = table.scan()
         assert scan["Count"].should.equal(0)
+
+
+@mock_dynamodb2
+def test_gsi_lastevaluatedkey():
+    # github.com/spulec/moto/issues/3968
+    conn = boto3.resource("dynamodb", region_name="us-west-2")
+    name = "test-table"
+    table = conn.Table(name)
+
+    conn.create_table(
+        TableName=name,
+        KeySchema=[{"AttributeName": "main_key", "KeyType": "HASH"}],
+        AttributeDefinitions=[
+            {"AttributeName": "main_key", "AttributeType": "S"},
+            {"AttributeName": "index_key", "AttributeType": "S"},
+        ],
+        ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
+        GlobalSecondaryIndexes=[
+            {
+                "IndexName": "test_index",
+                "KeySchema": [{"AttributeName": "index_key", "KeyType": "HASH"}],
+                "Projection": {"ProjectionType": "ALL",},
+                "ProvisionedThroughput": {
+                    "ReadCapacityUnits": 1,
+                    "WriteCapacityUnits": 1,
+                },
+            }
+        ],
+    )
+
+    table.put_item(
+        Item={
+            "main_key": "testkey1",
+            "extra_data": "testdata",
+            "index_key": "indexkey",
+        },
+    )
+    table.put_item(
+        Item={
+            "main_key": "testkey2",
+            "extra_data": "testdata",
+            "index_key": "indexkey",
+        },
+    )
+
+    response = table.query(
+        Limit=1,
+        KeyConditionExpression=Key("index_key").eq("indexkey"),
+        IndexName="test_index",
+    )
+
+    items = response["Items"]
+    items.should.have.length_of(1)
+    items[0].should.equal(
+        {"main_key": "testkey1", "extra_data": "testdata", "index_key": "indexkey"}
+    )
+
+    last_evaluated_key = response["LastEvaluatedKey"]
+    last_evaluated_key.should.have.length_of(2)
+    last_evaluated_key.should.equal({"main_key": "testkey1", "index_key": "indexkey"})