Skip to content

Commit

Permalink
feat: Allow change request timeout and limits (#59)
Browse files Browse the repository at this point in the history
  • Loading branch information
akalex authored Jul 11, 2023
1 parent b2df26d commit e056af2
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 3 deletions.
12 changes: 12 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Changelog

## 3.2.0
* ``AsyncFirebaseClient`` empower with advanced features to configure request behaviour such as timeout, or connection pooling.
Example:
```python

from async_firebase.client import AsyncFirebaseClient, RequestTimeout

# This will disable timeout
client = AsyncFirebaseClient(..., request_timeout=RequestTimeout(None))
client.send(...)
```

## 3.1.1
* [FIX] The push notification could not be sent to topic because ``messages.Message.token`` is declared as required attribute though it should be optional.
``messages.Message.token`` turned into Optional attribute.
Expand Down
39 changes: 39 additions & 0 deletions async_firebase/_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import typing as t
from dataclasses import dataclass


@dataclass()
class RequestTimeout:
"""
Request timeout configuration.
Arguments:
timeout: Timeout on all operations eg, read, write, connect.
Examples:
RequestTimeout(None) # No timeouts.
RequestTimeout(5.0) # 5s timeout on all operations.
"""

timeout: t.Optional[float] = None


@dataclass()
class RequestLimits:
"""
Configuration for request limits.
Attributes:
max_connections: The maximum number of concurrent connections that may be established.
max_keepalive_connections: Allow the connection pool to maintain keep-alive connections
below this point. Should be less than or equal to `max_connections`.
keepalive_expiry: Time limit on idle keep-alive connections in seconds.
"""

max_connections: t.Optional[int] = None
max_keepalive_connections: t.Optional[int] = None
keepalive_expiry: t.Optional[int] = None


DEFAULT_REQUEST_TIMEOUT = RequestTimeout(timeout=5.0)
DEFAULT_REQUEST_LIMITS = RequestLimits(max_connections=100, max_keepalive_connections=20, keepalive_expiry=5)
20 changes: 19 additions & 1 deletion async_firebase/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@
from google.oauth2 import service_account # type: ignore

import pkg_resources # type: ignore
from async_firebase._config import (
DEFAULT_REQUEST_LIMITS,
DEFAULT_REQUEST_TIMEOUT,
RequestLimits,
RequestTimeout,
)
from async_firebase.messages import FCMBatchResponse, FCMResponse
from async_firebase.utils import (
FCMBatchResponseHandler,
Expand All @@ -40,6 +46,9 @@ def __init__(
self,
credentials: t.Optional[service_account.Credentials] = None,
scopes: t.Optional[t.List[str]] = None,
*,
request_timeout: RequestTimeout = DEFAULT_REQUEST_TIMEOUT,
request_limits: RequestLimits = DEFAULT_REQUEST_LIMITS,
) -> None:
"""
:param credentials: instance of ``google.oauth2.service_account.Credentials``.
Expand All @@ -54,10 +63,15 @@ def __init__(
self.creds_from_service_account_info(service_account_info)
:param scopes: user-defined scopes to request during the authorization grant.
:param request_timeout: advanced feature that allows to change request timeout.
:param request_limits: advanced feature that allows to control the connection pool size.
"""
self._credentials: service_account.Credentials = credentials
self.scopes: t.List[str] = scopes or self.SCOPES

self._request_timeout = request_timeout
self._request_limits = request_limits

def creds_from_service_account_info(self, service_account_info: t.Dict[str, str]) -> None:
"""
Creates a Credentials instance from parsed service account info.
Expand Down Expand Up @@ -166,7 +180,11 @@ async def send_request(
:param content: request content
:return: HTTP response
"""
async with httpx.AsyncClient(base_url=self.BASE_URL) as client:
async with httpx.AsyncClient(
base_url=self.BASE_URL,
timeout=httpx.Timeout(**self._request_timeout.__dict__),
limits=httpx.Limits(**self._request_limits.__dict__),
) as client:
logging.debug(
"Requesting POST %s, payload: %s, content: %s, headers: %s",
urljoin(self.BASE_URL, self.FCM_ENDPOINT.format(project_id=self._credentials.project_id)),
Expand Down
6 changes: 5 additions & 1 deletion async_firebase/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@

import httpx

from async_firebase.base import AsyncClientBase
from async_firebase.base import ( # noqa: F401
AsyncClientBase,
RequestLimits,
RequestTimeout,
)
from async_firebase.encoders import aps_encoder
from async_firebase.messages import (
AndroidConfig,
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "async-firebase"
version = "3.1.1"
version = "3.2.0"
description = "Async Firebase Client - a Python asyncio client to interact with Firebase Cloud Messaging in an easy way."
license = "MIT"
authors = [
Expand Down

0 comments on commit e056af2

Please sign in to comment.