Skip to content

Commit

Permalink
Fixes http endpoint being overwritten by gRPC address argument in con…
Browse files Browse the repository at this point in the history
…structor (#621)

* Fixes bug of constructor argument being used as http endpoint

Signed-off-by: Elena Kolevska <[email protected]>

* Updates tests

Signed-off-by: Elena Kolevska <[email protected]>

* Updates the docs explaining how http service invocation should be configured

Signed-off-by: Elena Kolevska <[email protected]>

* Update daprdocs/content/en/python-sdk-docs/python-client.md

Signed-off-by: Bernd Verst <[email protected]>

---------

Signed-off-by: Elena Kolevska <[email protected]>
Signed-off-by: Bernd Verst <[email protected]>
Co-authored-by: Bernd Verst <[email protected]>
  • Loading branch information
elena-kolevska and berndverst authored Oct 31, 2023
1 parent ff08864 commit 4748755
Show file tree
Hide file tree
Showing 6 changed files with 28 additions and 56 deletions.
3 changes: 1 addition & 2 deletions dapr/clients/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,7 @@ def __init__(
if http_timeout_seconds is None:
http_timeout_seconds = settings.DAPR_HTTP_TIMEOUT_SECONDS
self.invocation_client = DaprInvocationHttpClient(headers_callback=headers_callback,
timeout=http_timeout_seconds,
address=address)
timeout=http_timeout_seconds)
elif invocation_protocol == 'GRPC':
pass
else:
Expand Down
12 changes: 4 additions & 8 deletions dapr/clients/http/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ class DaprHttpClient:
def __init__(self,
message_serializer: 'Serializer',
timeout: Optional[int] = 60,
headers_callback: Optional[Callable[[], Dict[str, str]]] = None,
address: Optional[str] = None):
headers_callback: Optional[Callable[[], Dict[str, str]]] = None):
"""Invokes Dapr over HTTP.
Args:
Expand All @@ -49,16 +48,13 @@ def __init__(self,
self._timeout = aiohttp.ClientTimeout(total=timeout)
self._serializer = message_serializer
self._headers_callback = headers_callback
self._address = address

def get_api_url(self) -> str:
if self._address:
return '{}/{}'.format(self._address, settings.DAPR_API_VERSION)
if settings.DAPR_HTTP_ENDPOINT:
return '{}/{}'.format(settings.DAPR_HTTP_ENDPOINT, settings.DAPR_API_VERSION)
else:
return 'http://{}:{}/{}'.format(settings.DAPR_RUNTIME_HOST,
settings.DAPR_HTTP_PORT, settings.DAPR_API_VERSION)

return 'http://{}:{}/{}'.format(settings.DAPR_RUNTIME_HOST,
settings.DAPR_HTTP_PORT, settings.DAPR_API_VERSION)

async def send_bytes(
self, method: str, url: str,
Expand Down
5 changes: 2 additions & 3 deletions dapr/clients/http/dapr_invocation_http_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,14 @@ class DaprInvocationHttpClient:
def __init__(
self,
timeout: int = 60,
headers_callback: Optional[Callable[[], Dict[str, str]]] = None,
address: Optional[str] = None):
headers_callback: Optional[Callable[[], Dict[str, str]]] = None):
"""Invokes Dapr's API for method invocation over HTTP.
Args:
timeout (int, optional): Timeout in seconds, defaults to 60.
headers_callback (lambda: Dict[str, str]], optional): Generates header for each request.
"""
self._client = DaprHttpClient(DefaultJSONSerializer(), timeout, headers_callback, address)
self._client = DaprHttpClient(DefaultJSONSerializer(), timeout, headers_callback)

async def invoke_method_async(
self,
Expand Down
8 changes: 8 additions & 0 deletions daprdocs/content/en/python-sdk-docs/python-client.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ The Python SDK allows you to interface with all of the [Dapr building blocks]({{

### Invoke a service

The Dapr Python SDK provides a simple API for invoking services via either HTTP or gRPC (deprecated). The protocol can be selected by setting the `DAPR_API_METHOD_INVOCATION_PROTOCOL` environment variable, defaulting to HTTP when unset. GRPC service invocation in Dapr is deprecated and GRPC proxying is recommended as an alternative.

```python
from dapr.clients import DaprClient

Expand All @@ -80,6 +82,12 @@ with DaprClient() as d:
resp = d.invoke_method('service-to-invoke', 'method-to-invoke', data='{"id":"100", "FirstName":"Value", "LastName":"Value"}', http_verb='post')
```

The base endpoint for HTTP api calls is specified in the `DAPR_HTTP_ENDPOINT` environment variable.
If this variable is not set, the endpoint value is derived from the `DAPR_RUNTIME_HOST` and `DAPR_HTTP_PORT` variables, whose default values are `127.0.0.1` and `3500` accordingly.

The base endpoint for gRPC calls is the one used for the client initialisation ([explained above](#initialising-the-client)).


- For a full guide on service invocation visit [How-To: Invoke a service]({{< ref howto-invoke-discover-services.md >}}).
- Visit [Python SDK examples](https://github.com/dapr/python-sdk/tree/master/examples/invoke-simple) for code samples and instructions to try out service invocation.

Expand Down
5 changes: 3 additions & 2 deletions tests/clients/test_http_service_invocation_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,10 @@ def test_get_api_url_default(self):
settings.DAPR_API_VERSION),
client.invocation_client._client.get_api_url())

def test_get_api_url_endpoint_as_argument(self):
@patch.object(settings, "DAPR_HTTP_ENDPOINT", "https://domain1.com:5000")
def test_dont_get_api_url_endpoint_as_argument(self):
client = DaprClient("http://localhost:5000")
self.assertEqual('http://localhost:5000/{}'.format(settings.DAPR_API_VERSION),
self.assertEqual('https://domain1.com:5000/{}'.format(settings.DAPR_API_VERSION),
client.invocation_client._client.get_api_url())

@patch.object(settings, "DAPR_HTTP_ENDPOINT", "https://domain1.com:5000")
Expand Down
51 changes: 10 additions & 41 deletions tests/clients/test_secure_http_service_invocation_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,13 @@
limitations under the License.
"""
import ssl
from unittest.mock import patch

from dapr.clients.http.client import DaprHttpClient
from .certs import CERTIFICATE_CHAIN_PATH
from .fake_http_server import FakeHttpServer
from dapr.conf import settings
from dapr.clients import DaprClient
from dapr.proto import common_v1
from asyncio import TimeoutError


from opencensus.trace.tracer import Tracer # type: ignore
from opencensus.trace import print_exporter, samplers

from .test_http_service_invocation_client import DaprInvocationHttpClientTests

Expand All @@ -45,7 +40,8 @@ def setUp(self):
self.server.start()
settings.DAPR_HTTP_PORT = self.server_port
settings.DAPR_API_METHOD_INVOCATION_PROTOCOL = 'http'
self.client = DaprClient("https://localhost:{}".format(self.server_port))
settings.DAPR_HTTP_ENDPOINT = "https://localhost:{}".format(self.server_port)
self.client = DaprClient()
self.app_id = 'fakeapp'
self.method_name = 'fakemethod'
self.invoke_url = f'/v1.0/invoke/{self.app_id}/method/{self.method_name}'
Expand All @@ -55,37 +51,10 @@ def tearDown(self):
settings.DAPR_API_TOKEN = None
settings.DAPR_API_METHOD_INVOCATION_PROTOCOL = 'http'

def test_global_timeout_setting_is_honored(self):
previous_timeout = settings.DAPR_HTTP_TIMEOUT_SECONDS
settings.DAPR_HTTP_TIMEOUT_SECONDS = 1
new_client = DaprClient("https://localhost:{}".format(self.server_port))
self.server.set_server_delay(1.5)
with self.assertRaises(TimeoutError):
new_client.invoke_method(self.app_id, self.method_name, "")

settings.DAPR_HTTP_TIMEOUT_SECONDS = previous_timeout

def test_invoke_method_with_tracer(self):
tracer = Tracer(sampler=samplers.AlwaysOnSampler(), exporter=print_exporter.PrintExporter())

self.client = DaprClient("https://localhost:{}".format(self.server_port),
headers_callback=lambda: tracer.propagator.to_headers(
tracer.span_context))
self.server.set_response(b"FOO")

with tracer.span(name="test"):
req = common_v1.StateItem(key='test')
resp = self.client.invoke_method(self.app_id, self.method_name, http_verb='PUT',
data=req, )

request_headers = self.server.get_request_headers()

self.assertIn('Traceparent', request_headers)
self.assertEqual(b'FOO', resp.data)

def test_timeout_exception_thrown_when_timeout_reached(self):
new_client = DaprClient("https://localhost:{}".format(self.server_port),
http_timeout_seconds=1)
self.server.set_server_delay(1.5)
with self.assertRaises(TimeoutError):
new_client.invoke_method(self.app_id, self.method_name, "")
@patch.object(settings, "DAPR_HTTP_ENDPOINT", None)
def test_get_api_url_default(self):
client = DaprClient()
self.assertEqual(
'http://{}:{}/{}'.format(settings.DAPR_RUNTIME_HOST, settings.DAPR_HTTP_PORT,
settings.DAPR_API_VERSION),
client.invocation_client._client.get_api_url())

0 comments on commit 4748755

Please sign in to comment.