Repository: localstack/localstack

Issue:
bug: dynamodb to kinesis stream has `null` `OldImage` when deleting an item
### Is there an existing issue for this?

- [X] I have searched the existing issues

### Current Behavior

Here are the kinesis events from inserting/deleting a dynamodb item:

```
{"eventID":"cf1199b0","eventVersion":"1.1","dynamodb":{"ApproximateCreationDateTime":1.635284406858227E9,"SizeBytes":56,"Keys":{"hash":{"S":"hash-691"},"range":{"S":"range-153"}},"NewImage":{"hash":{"S":"hash-691"},"range":{"S":"range-153"}}},"awsRegion":"us-east-1","eventSource":"aws:dynamodb","eventName":"INSERT","tableName":"bug"}

{"eventID":"976b515b","eventVersion":"1.1","dynamodb":{"ApproximateCreationDateTime":1.6352844070969691E9,"SizeBytes":4,"Keys":{"hash":{"S":"hash-691"},"range":{"S":"range-153"}},"OldImage":null},"awsRegion":"us-east-1","eventSource":"aws:dynamodb","eventName":"REMOVE","tableName":"bug"}
```

Note that the remove event has `OldImage` = `null`

### Expected Behavior

I expected `OldImage` to be `{"hash":{"S":"hash-691"},"range":{"S":"range-153"}}`.

### How are you starting LocalStack?

With a docker-compose file

### Steps To Reproduce

https://github.com/achaphiv/issue-localstack-dynamodb-stream-to-kinesis

```
# Create table/stream and hook them up
docker-compose up -d

# Create/delete an item
./mvnw -P write-to-dynamodb

# See the produced records
./mvnw -P watch-kinesis
```

### Environment

```markdown
- OS: Manjaro Linux
- LocalStack: 0.12.19
```


### Anything else?

_No response_


Discussion:

Resolving patch:
diff --git a/localstack/services/dynamodb/dynamodb_listener.py b/localstack/services/dynamodb/dynamodb_listener.py
--- a/localstack/services/dynamodb/dynamodb_listener.py
+++ b/localstack/services/dynamodb/dynamodb_listener.py
@@ -914,7 +914,7 @@ def has_event_sources_or_streams_enabled(table_name, cache={}):
         result = True
     if not result and dynamodbstreams_api.get_stream_for_table(table_arn):
         result = True
-    cache[table_arn] = result
+
     # if kinesis streaming destination is enabled
     # get table name from table_arn
     # since batch_wrtie and transact write operations passing table_arn instead of table_name
@@ -923,6 +923,7 @@ def has_event_sources_or_streams_enabled(table_name, cache={}):
     if not result and table_definitions.get(table_name):
         if table_definitions[table_name].get("KinesisDataStreamDestinationStatus") == "ACTIVE":
             result = True
+    cache[table_arn] = result
     return result
 
 


Test patch:
diff --git a/tests/integration/test_dynamodb.py b/tests/integration/test_dynamodb.py
--- a/tests/integration/test_dynamodb.py
+++ b/tests/integration/test_dynamodb.py
@@ -446,6 +446,15 @@ def test_dynamodb_with_kinesis_stream(self):
         # put item into table
         dynamodb.put_item(TableName=table_name, Item={"Username": {"S": "Fred"}})
 
+        dynamodb.update_item(
+            TableName=table_name,
+            Key={"Username": {"S": "Fred"}},
+            UpdateExpression="set S=:r",
+            ExpressionAttributeValues={":r": {"S": "Fred_Modified"}},
+            ReturnValues="UPDATED_NEW",
+        )
+
+        dynamodb.delete_item(TableName=table_name, Key={"Username": {"S": "Fred"}})
         # get shard iterator of the stream
         shard_iterator = kinesis.get_shard_iterator(
             StreamName="kinesis_dest_stream",
@@ -454,15 +463,23 @@ def test_dynamodb_with_kinesis_stream(self):
         )["ShardIterator"]
 
         # get records from the stream
-        rec = kinesis.get_records(ShardIterator=shard_iterator)["Records"]
-        # assert records in stream
-        self.assertEqual(1, len(rec))
-
-        # check tableName exists in the stream record
-        record_data = json.loads(rec[0]["Data"])
-        self.assertEqual(record_data["tableName"], table_name)
-        # check eventSourceARN not exists in the stream record
-        self.assertNotIn("eventSourceARN", record_data)
+        records = kinesis.get_records(ShardIterator=shard_iterator)["Records"]
+        self.assertEqual(3, len(records))
+
+        for record in records:
+            record = json.loads(record["Data"])
+            self.assertEqual(record["tableName"], table_name)
+            # check eventSourceARN not exists in the stream record
+            self.assertNotIn("eventSourceARN", record)
+            if record["eventName"] == "INSERT":
+                self.assertNotIn("OldImage", record["dynamodb"])
+                self.assertIn("NewImage", record["dynamodb"])
+            elif record["eventName"] == "MODIFY":
+                self.assertIn("NewImage", record["dynamodb"])
+                self.assertIn("OldImage", record["dynamodb"])
+            elif record["eventName"] == "REMOVE":
+                self.assertNotIn("NewImage", record["dynamodb"])
+                self.assertIn("OldImage", record["dynamodb"])
         # describe kinesis streaming destination of the table
         describe = dynamodb.describe_kinesis_streaming_destination(TableName=table_name)[
             "KinesisDataStreamDestinations"