Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions app/core/utils/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from typing import Any

import uvicorn
from fastapi import BackgroundTasks

from app.core.utils.config import Settings

Expand Down Expand Up @@ -78,7 +79,7 @@ class console_color:

# Logging config
# See https://docs.python.org/3/library/logging.config.html#logging-config-dictschema
def get_config_dict(self, settings: Settings):
def _get_config_dict(self, settings: Settings):
# We can't use a dependency to access settings as this function is not an endpoint. The object must thus be passed as a parameter.

# /!\ WARNING /!\
Expand Down Expand Up @@ -121,6 +122,7 @@ def get_config_dict(self, settings: Settings):
# Send error to a Matrix server. If credentials are not set in settings, the handler will be disabled
"formatter": "matrix",
"class": "app.utils.loggers_tools.matrix_handler.MatrixHandler",
"background_tasks": BackgroundTasks,
"room_id": settings.MATRIX_LOG_ERROR_ROOM_ID,
"token": settings.MATRIX_TOKEN,
"server_base_url": settings.MATRIX_SERVER_BASE_URL,
Expand All @@ -133,6 +135,7 @@ def get_config_dict(self, settings: Settings):
# Send error to a Matrix server. If credentials are not set in settings, the handler will be disabled
"formatter": "matrix",
"class": "app.utils.loggers_tools.matrix_handler.MatrixHandler",
"background_tasks": BackgroundTasks,
"room_id": settings.MATRIX_LOG_AMAP_ROOM_ID,
"token": settings.MATRIX_TOKEN,
"server_base_url": settings.MATRIX_SERVER_BASE_URL,
Expand Down Expand Up @@ -398,7 +401,7 @@ def initialize_loggers(self, settings: Settings):
# If logs/ folder does not exist, the logging module won't be able to create file handlers
Path("logs/").mkdir(parents=True, exist_ok=True)

config_dict = self.get_config_dict(settings=settings)
config_dict = self._get_config_dict(settings=settings)
logging.config.dictConfig(config_dict)

loggers = [logging.getLogger(name) for name in config_dict["loggers"]]
Expand Down
18 changes: 12 additions & 6 deletions app/utils/communication/matrix.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Any

import requests
import httpx

from app.types.exceptions import MatrixRequestError, MatrixSendMessageError

Expand All @@ -24,7 +24,7 @@ def __init__(

self.access_token = token

def post(
async def post(
self,
url: str,
json: dict[str, Any],
Expand All @@ -43,15 +43,21 @@ def post(
if "Authorization" not in headers:
headers["Authorization"] = "Bearer " + self.access_token

response = requests.post(url, json=json, headers=headers, timeout=10)
try:
async with httpx.AsyncClient() as client:
response = await client.post(
url,
json=json,
headers=headers,
timeout=10,
)
response.raise_for_status()
except requests.exceptions.HTTPError as err:
except httpx.RequestError as err:
raise MatrixRequestError() from err

return response.json()

def send_message(self, room_id: str, formatted_body: str) -> None:
async def send_message(self, room_id: str, formatted_body: str) -> None:
"""
Send a message to the room `room_id`.
`formatted_body` can contain html formatted text
Expand All @@ -71,6 +77,6 @@ def send_message(self, room_id: str, formatted_body: str) -> None:
}

try:
self.post(url, json=data, headers=None)
await self.post(url, json=data, headers=None)
except MatrixRequestError as error:
raise MatrixSendMessageError(room_id=room_id) from error
10 changes: 8 additions & 2 deletions app/utils/loggers_tools/matrix_handler.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging
from logging import StreamHandler

from fastapi import BackgroundTasks
from typing_extensions import override

from app.utils.communication.matrix import Matrix
Expand All @@ -21,6 +22,7 @@ class MatrixHandler(StreamHandler):

def __init__(
self,
background_tasks: BackgroundTasks,
room_id: str,
token: str,
server_base_url: str | None,
Expand All @@ -30,6 +32,7 @@ def __init__(
super().__init__()
self.setLevel(level)

self.background_tasks = background_tasks
self.room_id = room_id
self.enabled = enabled
if self.enabled:
Expand All @@ -42,9 +45,12 @@ def __init__(
def emit(self, record):
if self.enabled:
msg = self.format(record)

try:
self.matrix.send_message(self.room_id, msg)
self.background_tasks.add_task(
self.matrix.send_message,
room_id=self.room_id,
formatted_body=msg,
)
# We should catch and log any error, as Python may discarded them in production
except Exception as err:
# We use warning level so that the message is not sent to matrix again
Expand Down
2 changes: 1 addition & 1 deletion requirements-common.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ fastapi[standard]==0.115.6
firebase-admin==6.5.0 # Firebase is used for push notification
google-auth-oauthlib==1.2.1
helloasso-python==1.0.5
httpx==0.27.0
icalendar==5.0.13
jellyfish==1.0.4 # String Matching
Jinja2==3.1.6 # template engine for html files
Expand All @@ -25,7 +26,6 @@ pypdf==4.3.1
python-dotenv==1.0.1 # load environment variables from .env file
python-multipart==0.0.18 # a form data parser, as oauth flow requires form-data parameters
redis==5.0.8
requests==2.32.4
sqlalchemy-utils == 0.41.2
SQLAlchemy[asyncio]==2.0.32 # [asyncio] allows greenlet to be installed on Apple M1 devices.
unidecode==1.3.8
Expand Down
2 changes: 0 additions & 2 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
aiosqlite==0.20.0
boto3-stubs[essential]==1.38.23
google-auth-stubs==0.3.0
httpx==0.27.0 # needed for tests as a replacement of requests in TestClient
mypy[faster-cache]==1.16.0
pytest-alembic==0.12.1
pytest-asyncio==0.26.0
Expand All @@ -15,4 +14,3 @@ types-Authlib==1.5.0.20250516
types-fpdf2==2.8.3.20250516
types-psutil==7.0.0.20250601
types-redis==4.6.0.20241004
types-requests==2.32.0.20250515
3 changes: 0 additions & 3 deletions tests/test_payment.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
HelloAssoApiV5ModelsCartsInitCheckoutResponse,
)
from pytest_mock import MockerFixture
from requests import Response
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.payment import cruds_payment, models_payment, schemas_payment
Expand Down Expand Up @@ -495,8 +494,6 @@ def init_a_checkout_side_effect(
init_checkout_body: HelloAssoApiV5ModelsCartsInitCheckoutBody,
):
if init_checkout_body.payer is not None:
r = Response()
r.status_code = 400
raise UnauthorizedException
return HelloAssoApiV5ModelsCartsInitCheckoutResponse(
id=7,
Expand Down