Repository: elastic/apm-agent-python

Issue:
Starlette/FastAPI python agent setup examples result in TypeError
When following the [example](https://www.elastic.co/guide/en/apm/agent/python/current/starlette-support.html#starlette-fastapi) of setting up the Elastic APM agent with python + FastAPI, we find that we can't use `make_apm_client` as described as it has a required positional argument, `config`. The documentation however says that we should be able to call `make_apm_client` with no arguments and in that case it should look for environment variables and/or use defaults. 

**To Reproduce**
Follow the example exactly: 
```python
from fastapi import FastAPI
from elasticapm.contrib.starlette import make_apm_client, ElasticAPM

apm = make_apm_client()
# Client fails to start due to TypeError: TypeError: make_apm_client() missing 1 required positional argument: 'config'

app = FastAPI()
app.add_middleware(ElasticAPM, client=apm)
```

**Environment (please complete the following information)**
- OS: Mac, Linux, Ubuntu
- Python version: 3.8.8
- Framework and version [e.g. Django 2.1]: fastapi==0.65.1
- APM Server version: latest
- Agent version: 6.2.2



Discussion:

Resolving patch:
diff --git a/elasticapm/contrib/starlette/__init__.py b/elasticapm/contrib/starlette/__init__.py
--- a/elasticapm/contrib/starlette/__init__.py
+++ b/elasticapm/contrib/starlette/__init__.py
@@ -31,6 +31,8 @@
 
 from __future__ import absolute_import
 
+from typing import Dict, Optional
+
 import starlette
 from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
 from starlette.requests import Request
@@ -50,7 +52,7 @@
 logger = get_logger("elasticapm.errors.client")
 
 
-def make_apm_client(config: dict, client_cls=Client, **defaults) -> Client:
+def make_apm_client(config: Optional[Dict] = None, client_cls=Client, **defaults) -> Client:
     """Builds ElasticAPM client.
 
     Args:


Test patch:
diff --git a/tests/contrib/asyncio/starlette_tests.py b/tests/contrib/asyncio/starlette_tests.py
--- a/tests/contrib/asyncio/starlette_tests.py
+++ b/tests/contrib/asyncio/starlette_tests.py
@@ -30,6 +30,8 @@
 
 import pytest  # isort:skip
 
+from tests.fixtures import TempStoreClient
+
 starlette = pytest.importorskip("starlette")  # isort:skip
 
 import types
@@ -43,7 +45,7 @@
 import elasticapm
 from elasticapm import async_capture_span
 from elasticapm.conf import constants
-from elasticapm.contrib.starlette import ElasticAPM
+from elasticapm.contrib.starlette import ElasticAPM, make_apm_client
 from elasticapm.utils import wrapt
 from elasticapm.utils.disttracing import TraceParent
 
@@ -367,3 +369,16 @@ def test_capture_body_error(app, elasticapm_client):
     client = TestClient(app)
     with pytest.raises(ValueError):
         response = client.post("/raise-exception", data="[0, 1]")
+
+
+def test_make_client_with_config():
+    c = make_apm_client(config={"SERVICE_NAME": "foo"}, client_cls=TempStoreClient)
+    c.close()
+    assert c.config.service_name == "foo"
+
+
+def test_make_client_without_config():
+    with mock.patch.dict("os.environ", {"ELASTIC_APM_SERVICE_NAME": "foo"}):
+        c = make_apm_client(client_cls=TempStoreClient)
+        c.close()
+        assert c.config.service_name == "foo"
diff --git a/tests/fixtures.py b/tests/fixtures.py
--- a/tests/fixtures.py
+++ b/tests/fixtures.py
@@ -331,9 +331,9 @@ def get_config(self, current_version=None, keys=None):
 
 
 class TempStoreClient(Client):
-    def __init__(self, **inline):
+    def __init__(self, config=None, **inline):
         inline.setdefault("transport_class", "tests.fixtures.DummyTransport")
-        super(TempStoreClient, self).__init__(**inline)
+        super(TempStoreClient, self).__init__(config, **inline)
 
     @property
     def events(self):