Repository: elastic/apm-agent-python

Issue:
capture_serverless makes lambda fail when input is not a JSON-dict
**Describe the bug**: 

Hi! 

I have encountered a bug when using `@capture_serverless` decorator on a lambda inside a Step Function that processes the output of a Map state.

Elastic APM Python package version: 6.14.0 (latest)

**To Reproduce**

1. Implement a Step Function with a Map state followed by a lambda function that takes the output of the map. As specified in AWS docs: `When complete, the output of the Map state is a JSON array, where each item is the output of an iteration.` - https://docs.aws.amazon.com/step-functions/latest/dg/concepts-asl-use-map-state-inline.html#:~:text=the%20ItemSelector%20field.-,When%20complete%2C%20the%20output%20of%20the%20Map%20state%20is%20a%20JSON%20array%2C%20where%20each%20item%20is%20the%20output%20of%20an%20iteration.,-For%20more%20information 
2. Add the `capture_serverless` setup to your lambda.

Expected: lambda to run and log to APM Server

Actual: Error from Step Function logs:
```
{
    "errorMessage": "'list' object has no attribute 'get'",
    "errorType": "AttributeError",
    "stackTrace": [
      "  File \"/var/task/elasticapm/contrib/serverless/aws.py\", line 116, in decorated\n    with _lambda_transaction(func, name, client, event, context) as sls:\n",
      "  File \"/var/task/elasticapm/contrib/serverless/aws.py\", line 147, in __enter__\n    trace_parent = TraceParent.from_headers(self.event.get(\"headers\", {}))\n"
    ]
  }
```

I think the fix is as simple as updating `elasticapm.xxx.aws`  `_lambda_transaction` to accept `event` being either a dictionary or a list, as this is an acceptable input to lambdas within Step Functions.

**Environment (please complete the following information)**
- OS: [e.g. Linux] - AWS Lambda function for Python
- Python version: 3.8
- APM Server version: 6.14.0

**Severity**
- Critical issue as this breaks all our lambdas processing events coming from Map states in Step Functions.


Discussion:
Thanks for the report! We'll get this fixed.

Resolving patch:
diff --git a/elasticapm/contrib/serverless/aws.py b/elasticapm/contrib/serverless/aws.py
--- a/elasticapm/contrib/serverless/aws.py
+++ b/elasticapm/contrib/serverless/aws.py
@@ -144,6 +144,11 @@ def __enter__(self):
         """
         Transaction setup
         """
+        if isinstance(self.event, list):
+            # When `event` is a list, it's likely the output of another AWS
+            # service like Step Functions, and is unlikely to be standardized
+            # in any way. We just have to rely on our defaults in this case.
+            self.event = {}
         trace_parent = TraceParent.from_headers(self.event.get("headers", {}))
 
         global COLD_START


Test patch:
diff --git a/tests/contrib/serverless/aws_tests.py b/tests/contrib/serverless/aws_tests.py
--- a/tests/contrib/serverless/aws_tests.py
+++ b/tests/contrib/serverless/aws_tests.py
@@ -95,6 +95,16 @@ def event_sns():
         return json.load(f)
 
 
+@pytest.fixture
+def event_list():
+    """
+    Lambda functions can receive the raw output of Step Functions as the event.
+    Because it's any valid JSON, it can be a list as well, with no useful
+    context information.
+    """
+    return ["foo", "bar", "baz"]
+
+
 @pytest.fixture
 def context():
     return SampleContext()
@@ -383,3 +393,18 @@ def test_func(event, context):
     test_func(event_api, context)
 
     assert elasticapm_client._transport._metadata["service"]["name"] == "override"
+
+
+def test_capture_serverless_list_event(event_list, context, elasticapm_client):
+    os.environ["AWS_LAMBDA_FUNCTION_NAME"] = "test_func"
+
+    @capture_serverless
+    def test_func(event, context):
+        return {"statusCode": 200, "headers": {"foo": "bar"}}
+
+    test_func(event_list, context)
+
+    assert len(elasticapm_client.events[constants.TRANSACTION]) == 1
+    transaction = elasticapm_client.events[constants.TRANSACTION][0]
+
+    assert transaction["name"] == "test_func"