diff --git a/packages/celery-library/src/celery_library/task_manager.py b/packages/celery-library/src/celery_library/task_manager.py index 72ca039f6ca2..0720a8b8fd9c 100644 --- a/packages/celery-library/src/celery_library/task_manager.py +++ b/packages/celery-library/src/celery_library/task_manager.py @@ -10,11 +10,14 @@ from common_library.async_tools import make_async from models_library.progress_bar import ProgressReport from servicelib.celery.models import ( + TASK_QUEUE_DEFAULT, Task, TaskFilter, TaskID, TaskInfoStore, TaskMetadata, + TaskName, + TaskQueue, TaskState, TaskStatus, TaskUUID, @@ -38,34 +41,43 @@ class CeleryTaskManager: _celery_settings: CelerySettings _task_info_store: TaskInfoStore - async def submit_task( + async def send_task( self, - task_metadata: TaskMetadata, - *, + task_name: TaskName, task_filter: TaskFilter, + *, + task_ephemeral: bool = True, + task_queue: TaskQueue = TASK_QUEUE_DEFAULT, **task_params, ) -> TaskUUID: with log_context( _logger, logging.DEBUG, - msg=f"Submit {task_metadata.name=}: {task_filter=} {task_params=}", + msg=f"Send {task_name=}: {task_filter=} {task_params=}", ): task_uuid = uuid4() task_id = build_task_id(task_filter, task_uuid) self._celery_app.send_task( - task_metadata.name, + task_name, task_id=task_id, kwargs={"task_id": task_id} | task_params, - queue=task_metadata.queue.value, + queue=task_queue, ) expiry = ( self._celery_settings.CELERY_EPHEMERAL_RESULT_EXPIRES - if task_metadata.ephemeral + if task_ephemeral else self._celery_settings.CELERY_RESULT_EXPIRES ) + await self._task_info_store.create_task( - task_id, task_metadata, expiry=expiry + task_id, + TaskMetadata( + name=task_name, + ephemeral=task_ephemeral, + queue=task_queue, + ), + expiry=expiry, ) return task_uuid @@ -97,6 +109,7 @@ async def get_task_result( msg=f"Get task result: {task_filter=} {task_uuid=}", ): task_id = build_task_id(task_filter, task_uuid) + async_result = self._celery_app.AsyncResult(task_id) result = async_result.result if async_result.ready(): @@ -106,7 +119,7 @@ async def get_task_result( await self._task_info_store.remove_task(task_id) return result - async def _get_task_progress_report( + async def _get_progress_report( self, task_filter: TaskFilter, task_uuid: TaskUUID, task_state: TaskState ) -> ProgressReport: if task_state in (TaskState.STARTED, TaskState.RETRY, TaskState.ABORTED): @@ -144,7 +157,7 @@ async def get_task_status( return TaskStatus( task_uuid=task_uuid, task_state=task_state, - progress_report=await self._get_task_progress_report( + progress_report=await self._get_progress_report( task_filter, task_uuid, task_state ), ) diff --git a/packages/celery-library/tests/conftest.py b/packages/celery-library/tests/conftest.py index e9fc599136a6..4deb7ae2a05a 100644 --- a/packages/celery-library/tests/conftest.py +++ b/packages/celery-library/tests/conftest.py @@ -121,7 +121,7 @@ def _on_worker_init_wrapper(sender: WorkController, **_kwargs): @pytest.fixture -async def celery_task_manager( +async def task_manager( celery_app: Celery, celery_settings: CelerySettings, with_celery_worker: TestWorkController, diff --git a/packages/celery-library/tests/unit/test_async_jobs.py b/packages/celery-library/tests/unit/test_async_jobs.py index 4a646a1fdb46..8ff6026299a4 100644 --- a/packages/celery-library/tests/unit/test_async_jobs.py +++ b/packages/celery-library/tests/unit/test_async_jobs.py @@ -27,7 +27,7 @@ from models_library.rabbitmq_basic_types import RPCNamespace from models_library.users import UserID from pydantic import TypeAdapter -from servicelib.celery.models import TaskFilter, TaskID, TaskMetadata +from servicelib.celery.models import TaskFilter, TaskID from servicelib.celery.task_manager import TaskManager from servicelib.rabbitmq import RabbitMQRPCClient, RPCRouter from servicelib.rabbitmq.rpc_interfaces.async_jobs import async_jobs @@ -83,8 +83,8 @@ async def rpc_sync_job( ) -> AsyncJobGet: task_name = sync_job.__name__ task_filter = TaskFilter.model_validate(job_filter.model_dump()) - task_uuid = await task_manager.submit_task( - TaskMetadata(name=task_name), task_filter=task_filter, **kwargs + task_uuid = await task_manager.send_task( + task_name=task_name, task_filter=task_filter, **kwargs ) return AsyncJobGet(job_id=task_uuid, job_name=task_name) @@ -96,8 +96,8 @@ async def rpc_async_job( ) -> AsyncJobGet: task_name = async_job.__name__ task_filter = TaskFilter.model_validate(job_filter.model_dump()) - task_uuid = await task_manager.submit_task( - TaskMetadata(name=task_name), task_filter=task_filter, **kwargs + task_uuid = await task_manager.send_task( + task_name=task_name, task_filter=task_filter, **kwargs ) return AsyncJobGet(job_id=task_uuid, job_name=task_name) @@ -141,13 +141,13 @@ async def async_job(task: Task, task_id: TaskID, action: Action, payload: Any) - @pytest.fixture async def register_rpc_routes( - async_jobs_rabbitmq_rpc_client: RabbitMQRPCClient, celery_task_manager: TaskManager + async_jobs_rabbitmq_rpc_client: RabbitMQRPCClient, task_manager: TaskManager ) -> None: await async_jobs_rabbitmq_rpc_client.register_router( - _async_jobs.router, ASYNC_JOBS_RPC_NAMESPACE, task_manager=celery_task_manager + _async_jobs.router, ASYNC_JOBS_RPC_NAMESPACE, task_manager=task_manager ) await async_jobs_rabbitmq_rpc_client.register_router( - router, ASYNC_JOBS_RPC_NAMESPACE, task_manager=celery_task_manager + router, ASYNC_JOBS_RPC_NAMESPACE, task_manager=task_manager ) diff --git a/packages/celery-library/tests/unit/test_tasks.py b/packages/celery-library/tests/unit/test_tasks.py index a4edfb7540ae..2f300f0118ab 100644 --- a/packages/celery-library/tests/unit/test_tasks.py +++ b/packages/celery-library/tests/unit/test_tasks.py @@ -22,7 +22,6 @@ from servicelib.celery.models import ( TaskFilter, TaskID, - TaskMetadata, TaskState, ) from servicelib.logging_utils import log_context @@ -91,14 +90,12 @@ def _(celery_app: Celery) -> None: async def test_submitting_task_calling_async_function_results_with_success_state( - celery_task_manager: CeleryTaskManager, + task_manager: CeleryTaskManager, ): task_filter = TaskFilter(user_id=42) - task_uuid = await celery_task_manager.submit_task( - TaskMetadata( - name=fake_file_processor.__name__, - ), + task_uuid = await task_manager.send_task( + task_name=fake_file_processor.__name__, task_filter=task_filter, files=[f"file{n}" for n in range(5)], ) @@ -109,26 +106,22 @@ async def test_submitting_task_calling_async_function_results_with_success_state stop=stop_after_delay(30), ): with attempt: - status = await celery_task_manager.get_task_status(task_filter, task_uuid) + status = await task_manager.get_task_status(task_filter, task_uuid) assert status.task_state == TaskState.SUCCESS assert ( - await celery_task_manager.get_task_status(task_filter, task_uuid) + await task_manager.get_task_status(task_filter, task_uuid) ).task_state == TaskState.SUCCESS - assert ( - await celery_task_manager.get_task_result(task_filter, task_uuid) - ) == "archive.zip" + assert (await task_manager.get_task_result(task_filter, task_uuid)) == "archive.zip" async def test_submitting_task_with_failure_results_with_error( - celery_task_manager: CeleryTaskManager, + task_manager: CeleryTaskManager, ): task_filter = TaskFilter(user_id=42) - task_uuid = await celery_task_manager.submit_task( - TaskMetadata( - name=failure_task.__name__, - ), + task_uuid = await task_manager.send_task( + task_name=failure_task.__name__, task_filter=task_filter, ) @@ -139,30 +132,26 @@ async def test_submitting_task_with_failure_results_with_error( ): with attempt: - raw_result = await celery_task_manager.get_task_result( - task_filter, task_uuid - ) + raw_result = await task_manager.get_task_result(task_filter, task_uuid) assert isinstance(raw_result, TransferrableCeleryError) - raw_result = await celery_task_manager.get_task_result(task_filter, task_uuid) + raw_result = await task_manager.get_task_result(task_filter, task_uuid) assert f"{raw_result}" == "Something strange happened: BOOM!" async def test_cancelling_a_running_task_aborts_and_deletes( - celery_task_manager: CeleryTaskManager, + task_manager: CeleryTaskManager, ): task_filter = TaskFilter(user_id=42) - task_uuid = await celery_task_manager.submit_task( - TaskMetadata( - name=dreamer_task.__name__, - ), + task_uuid = await task_manager.send_task( + task_name=dreamer_task.__name__, task_filter=task_filter, ) await asyncio.sleep(3.0) - await celery_task_manager.cancel_task(task_filter, task_uuid) + await task_manager.cancel_task(task_filter, task_uuid) for attempt in Retrying( retry=retry_if_exception_type(AssertionError), @@ -170,25 +159,23 @@ async def test_cancelling_a_running_task_aborts_and_deletes( stop=stop_after_delay(30), ): with attempt: - progress = await celery_task_manager.get_task_status(task_filter, task_uuid) + progress = await task_manager.get_task_status(task_filter, task_uuid) assert progress.task_state == TaskState.ABORTED assert ( - await celery_task_manager.get_task_status(task_filter, task_uuid) + await task_manager.get_task_status(task_filter, task_uuid) ).task_state == TaskState.ABORTED - assert task_uuid not in await celery_task_manager.list_tasks(task_filter) + assert task_uuid not in await task_manager.list_tasks(task_filter) async def test_listing_task_uuids_contains_submitted_task( - celery_task_manager: CeleryTaskManager, + task_manager: CeleryTaskManager, ): task_filter = TaskFilter(user_id=42) - task_uuid = await celery_task_manager.submit_task( - TaskMetadata( - name=dreamer_task.__name__, - ), + task_uuid = await task_manager.send_task( + task_name=dreamer_task.__name__, task_filter=task_filter, ) @@ -198,8 +185,8 @@ async def test_listing_task_uuids_contains_submitted_task( stop=stop_after_delay(10), ): with attempt: - tasks = await celery_task_manager.list_tasks(task_filter) + tasks = await task_manager.list_tasks(task_filter) assert any(task.uuid == task_uuid for task in tasks) - tasks = await celery_task_manager.list_tasks(task_filter) + tasks = await task_manager.list_tasks(task_filter) assert any(task.uuid == task_uuid for task in tasks) diff --git a/packages/common-library/src/common_library/pydantic_basic_types.py b/packages/common-library/src/common_library/pydantic_basic_types.py index 452c118dae95..fc5b10d17cfc 100644 --- a/packages/common-library/src/common_library/pydantic_basic_types.py +++ b/packages/common-library/src/common_library/pydantic_basic_types.py @@ -1,7 +1,7 @@ from re import Pattern from typing import Annotated, Final, TypeAlias -from pydantic import Field +from pydantic import Field, StringConstraints from pydantic_core import core_schema # https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers#Registered_ports @@ -77,3 +77,12 @@ class LongTruncatedStr(ConstrainedStr): # Analogous to ShortTruncatedStr strip_whitespace = True curtail_length = 65536 # same as github descripton + + +NotEmptyStr: TypeAlias = Annotated[ + str, + StringConstraints( + min_length=1, + strip_whitespace=True, + ), +] diff --git a/packages/models-library/requirements/_base.in b/packages/models-library/requirements/_base.in index 34141532522c..d0f947ed1105 100644 --- a/packages/models-library/requirements/_base.in +++ b/packages/models-library/requirements/_base.in @@ -7,6 +7,7 @@ arrow jsonschema orjson -pydantic-extra-types -pydantic-settings +phonenumbers pydantic[email] +pydantic-settings +pydantic-extra-types diff --git a/packages/models-library/requirements/_base.txt b/packages/models-library/requirements/_base.txt index 74c283766c8e..8aefa63059dc 100644 --- a/packages/models-library/requirements/_base.txt +++ b/packages/models-library/requirements/_base.txt @@ -22,6 +22,8 @@ orjson==3.10.15 # -c requirements/../../../requirements/constraints.txt # -r requirements/../../../packages/common-library/requirements/_base.in # -r requirements/_base.in +phonenumbers==9.0.9 + # via -r requirements/_base.in pydantic==2.11.7 # via # -c requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt diff --git a/packages/models-library/src/models_library/rpc/notifications/__init__.py b/packages/models-library/src/models_library/rpc/notifications/__init__.py new file mode 100644 index 000000000000..a26e186033dc --- /dev/null +++ b/packages/models-library/src/models_library/rpc/notifications/__init__.py @@ -0,0 +1,5 @@ +from ._notifications import Notification + +__all__: tuple[str, ...] = ("Notification",) + +# nopycln: file diff --git a/packages/models-library/src/models_library/rpc/notifications/_notifications.py b/packages/models-library/src/models_library/rpc/notifications/_notifications.py new file mode 100644 index 000000000000..65b69f2b042a --- /dev/null +++ b/packages/models-library/src/models_library/rpc/notifications/_notifications.py @@ -0,0 +1,11 @@ +from typing import Annotated + +from pydantic import BaseModel, Field + +from .channels import Channel +from .events import Event + + +class Notification(BaseModel): + event: Annotated[Event, Field(discriminator="type")] + channel: Annotated[Channel, Field(discriminator="type")] diff --git a/packages/models-library/src/models_library/rpc/notifications/channels/__init__.py b/packages/models-library/src/models_library/rpc/notifications/channels/__init__.py new file mode 100644 index 000000000000..708e3003df99 --- /dev/null +++ b/packages/models-library/src/models_library/rpc/notifications/channels/__init__.py @@ -0,0 +1,16 @@ +from typing import TypeAlias + +from ._email_channel import EmailAddress, EmailChannel +from ._sms_channel import SMSChannel + +Channel: TypeAlias = EmailChannel | SMSChannel + + +__all__: tuple[str, ...] = ( + "Channel", + "EmailAddress", + "EmailChannel", + "SMSChannel", +) + +# nopycln: file diff --git a/packages/models-library/src/models_library/rpc/notifications/channels/_email_channel.py b/packages/models-library/src/models_library/rpc/notifications/channels/_email_channel.py new file mode 100644 index 000000000000..e97b9e14ceba --- /dev/null +++ b/packages/models-library/src/models_library/rpc/notifications/channels/_email_channel.py @@ -0,0 +1,16 @@ +from typing import Literal + +from pydantic import BaseModel, EmailStr + + +class EmailAddress(BaseModel): + display_name: str = "" + addr_spec: EmailStr + + +class EmailChannel(BaseModel): + type: Literal["email"] = "email" + + from_addr: EmailAddress + to_addr: EmailAddress + reply_to_addr: EmailAddress | None = None diff --git a/packages/models-library/src/models_library/rpc/notifications/channels/_sms_channel.py b/packages/models-library/src/models_library/rpc/notifications/channels/_sms_channel.py new file mode 100644 index 000000000000..70ed96c5bdb3 --- /dev/null +++ b/packages/models-library/src/models_library/rpc/notifications/channels/_sms_channel.py @@ -0,0 +1,10 @@ +from typing import Literal + +from pydantic import BaseModel +from pydantic_extra_types.phone_numbers import PhoneNumber + + +class SMSChannel(BaseModel): + type: Literal["sms"] = "sms" + + phone_number: PhoneNumber diff --git a/packages/models-library/src/models_library/rpc/notifications/events/__init__.py b/packages/models-library/src/models_library/rpc/notifications/events/__init__.py new file mode 100644 index 000000000000..56d07abf0da4 --- /dev/null +++ b/packages/models-library/src/models_library/rpc/notifications/events/__init__.py @@ -0,0 +1,25 @@ +from typing import TypeAlias + +from ._account_events import ( + AccountApprovedEvent, + AccountRejectedEvent, + AccountRequestedEvent, + ProductData, + ProductUIData, + UserData, +) + +Event: TypeAlias = AccountRequestedEvent | AccountApprovedEvent | AccountRejectedEvent + + +__all__: tuple[str, ...] = ( + "AccountApprovedEvent", + "AccountRejectedEvent", + "AccountRequestedEvent", + "Event", + "ProductData", + "ProductUIData", + "UserData", +) + +# nopycln: file diff --git a/packages/models-library/src/models_library/rpc/notifications/events/_account_events.py b/packages/models-library/src/models_library/rpc/notifications/events/_account_events.py new file mode 100644 index 000000000000..e509e906719d --- /dev/null +++ b/packages/models-library/src/models_library/rpc/notifications/events/_account_events.py @@ -0,0 +1,61 @@ +from typing import Any, Literal + +from common_library.pydantic_basic_types import NotEmptyStr +from models_library.products import ProductName +from pydantic import BaseModel, EmailStr, HttpUrl + + +class UserData(BaseModel): + username: str + first_name: str + last_name: str + email: EmailStr + + # TODO: add more fields as needed + + +class ProductUIData(BaseModel): + project_alias: str + logo_url: str | None = ( + None # default_logo = "https://raw.githubusercontent.com/ITISFoundation/osparc-simcore/refs/heads/master/services/static-webserver/client/source/resource/osparc/osparc-white.svg" in base.html + ) + strong_color: str | None = ( + None # default_strong_color = "rgb(131, 0, 191)" in base.html + ) + + +class ProductData(BaseModel): + product_name: ProductName + display_name: str + vendor_display_inline: str + support_email: str + homepage_url: str | None # default_homepage = "https://osparc.io/" in base.html + ui: ProductUIData + + +class BaseAccountEvent(BaseModel): + user: UserData + product: ProductData + + +class AccountRequestedEvent(BaseAccountEvent): + type: Literal["account_requested"] = "account_requested" + + host: HttpUrl + + # NOTE: following are kept for backward compatibility + product_info: dict[str, Any] = {} + request_form: dict[str, Any] = {} + ipinfo: dict[str, Any] = {} + + +class AccountApprovedEvent(BaseAccountEvent): + type: Literal["account_approved"] = "account_approved" + + link: HttpUrl + + +class AccountRejectedEvent(BaseAccountEvent): + type: Literal["account_rejected"] = "account_rejected" + + reason: NotEmptyStr diff --git a/packages/notifications-library/src/notifications_library/_render.py b/packages/notifications-library/src/notifications_library/_render.py index 4be7ba3225c1..f6d40e3a9cfe 100644 --- a/packages/notifications-library/src/notifications_library/_render.py +++ b/packages/notifications-library/src/notifications_library/_render.py @@ -1,26 +1,39 @@ +import functools +import json import logging from pathlib import Path +from typing import Any import notifications_library +from common_library.json_serialization import pydantic_encoder from jinja2 import Environment, FileSystemLoader, PackageLoader, select_autoescape +from models_library.utils._original_fastapi_encoders import jsonable_encoder _logger = logging.getLogger(__name__) +def _safe_json_dumps(obj: Any, **kwargs): + return json.dumps(jsonable_encoder(obj), default=pydantic_encoder, **kwargs) + + def create_render_environment_from_notifications_library(**kwargs) -> Environment: - return Environment( + env = Environment( loader=PackageLoader(notifications_library.__name__, "templates"), autoescape=select_autoescape(["html", "xml"]), **kwargs ) + env.globals["dumps"] = functools.partial(_safe_json_dumps, indent=1) + return env def create_render_environment_from_folder(top_dir: Path) -> Environment: assert top_dir.exists() # nosec assert top_dir.is_dir() # nosec - return Environment( + env = Environment( loader=FileSystemLoader(top_dir), autoescape=select_autoescape( ["html", "xml"], ), ) + env.globals["dumps"] = functools.partial(_safe_json_dumps, indent=1) + return env diff --git a/packages/service-library/requirements/_fastapi.in b/packages/service-library/requirements/_fastapi.in index 3303e6043afa..940a289b3c55 100644 --- a/packages/service-library/requirements/_fastapi.in +++ b/packages/service-library/requirements/_fastapi.in @@ -3,7 +3,7 @@ # # - +asgi-lifespan fastapi[standard] fastapi-lifespan-manager httpx[http2] diff --git a/packages/service-library/requirements/_fastapi.txt b/packages/service-library/requirements/_fastapi.txt index 26484407f6b9..18d8d7e1ddf7 100644 --- a/packages/service-library/requirements/_fastapi.txt +++ b/packages/service-library/requirements/_fastapi.txt @@ -5,6 +5,8 @@ anyio==4.8.0 # httpx # starlette # watchfiles +asgi-lifespan==2.1.0 + # via -r requirements/_fastapi.in asgiref==3.8.1 # via opentelemetry-instrumentation-asgi certifi==2025.1.31 @@ -129,6 +131,9 @@ sentry-sdk==2.35.0 shellingham==1.5.4 # via typer sniffio==1.3.1 + # via + # anyio + # asgi-lifespan # via anyio starlette==0.47.2 # via fastapi diff --git a/packages/service-library/src/servicelib/celery/models.py b/packages/service-library/src/servicelib/celery/models.py index 407565533776..98460ece18f9 100644 --- a/packages/service-library/src/servicelib/celery/models.py +++ b/packages/service-library/src/servicelib/celery/models.py @@ -10,6 +10,17 @@ TaskName: TypeAlias = Annotated[ str, StringConstraints(strip_whitespace=True, min_length=1) ] + +TaskQueue: TypeAlias = Annotated[ + str, + StringConstraints( + strip_whitespace=True, + min_length=1, + max_length=64, + ), +] +TASK_QUEUE_DEFAULT: TaskQueue = "default" + TaskUUID: TypeAlias = UUID @@ -25,15 +36,10 @@ class TaskState(StrEnum): ABORTED = "ABORTED" -class TasksQueue(StrEnum): - CPU_BOUND = "cpu_bound" - DEFAULT = "default" - - class TaskMetadata(BaseModel): name: TaskName ephemeral: bool = True - queue: TasksQueue = TasksQueue.DEFAULT + queue: TaskQueue = TASK_QUEUE_DEFAULT class Task(BaseModel): diff --git a/packages/service-library/src/servicelib/celery/task_manager.py b/packages/service-library/src/servicelib/celery/task_manager.py index 93612e6845fe..660c374c08c6 100644 --- a/packages/service-library/src/servicelib/celery/task_manager.py +++ b/packages/service-library/src/servicelib/celery/task_manager.py @@ -3,18 +3,26 @@ from models_library.progress_bar import ProgressReport from ..celery.models import ( + TASK_QUEUE_DEFAULT, Task, TaskFilter, TaskID, - TaskMetadata, + TaskName, + TaskQueue, TaskStatus, TaskUUID, ) class TaskManager(Protocol): - async def submit_task( - self, task_metadata: TaskMetadata, *, task_filter: TaskFilter, **task_param + async def send_task( + self, + task_name: TaskName, + task_filter: TaskFilter, + *, + task_ephemeral: bool = True, + task_queue: TaskQueue = TASK_QUEUE_DEFAULT, + **task_params, ) -> TaskUUID: ... async def cancel_task( diff --git a/packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/notifications/__init__.py b/packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/notifications/__init__.py new file mode 100644 index 000000000000..11795609ea92 --- /dev/null +++ b/packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/notifications/__init__.py @@ -0,0 +1,3 @@ +from ._notifications import send_notification + +__all__: tuple[str, ...] = ("send_notification",) diff --git a/packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/notifications/_notifications.py b/packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/notifications/_notifications.py new file mode 100644 index 000000000000..a95df0977138 --- /dev/null +++ b/packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/notifications/_notifications.py @@ -0,0 +1,23 @@ +from typing import Final + +from models_library.api_schemas_notifications import NOTIFICATIONS_RPC_NAMESPACE +from models_library.rabbitmq_basic_types import RPCMethodName +from models_library.rpc.notifications import Notification +from pydantic import NonNegativeInt, TypeAdapter + +from ... import RabbitMQRPCClient + +_DEFAULT_TIMEOUT_S: Final[NonNegativeInt] = 30 + + +async def send_notification( + rabbitmq_rpc_client: RabbitMQRPCClient, + *, + notification: Notification, +) -> None: + await rabbitmq_rpc_client.request( + NOTIFICATIONS_RPC_NAMESPACE, + TypeAdapter(RPCMethodName).validate_python(send_notification.__name__), + timeout_s=_DEFAULT_TIMEOUT_S, + notification=notification, + ) diff --git a/services/docker-compose.yml b/services/docker-compose.yml index 7e263ed2139e..dbd5bef45488 100644 --- a/services/docker-compose.yml +++ b/services/docker-compose.yml @@ -1243,7 +1243,7 @@ services: init: true hostname: "{{.Node.Hostname}}-{{.Task.Slot}}" - environment: + environment: ¬ifications_environment LOG_FILTER_MAPPING: ${LOG_FILTER_MAPPING} LOG_FORMAT_LOCAL_DEV_ENABLED: ${LOG_FORMAT_LOCAL_DEV_ENABLED} @@ -1263,8 +1263,27 @@ services: RABBIT_SECURE: ${RABBIT_SECURE} RABBIT_USER: ${RABBIT_USER} + REDIS_HOST: ${REDIS_HOST} + REDIS_PORT: ${REDIS_PORT} + REDIS_SECURE: ${REDIS_SECURE} + REDIS_USER: ${REDIS_USER} + REDIS_PASSWORD: ${REDIS_PASSWORD} + + TRACING_OPENTELEMETRY_COLLECTOR_ENDPOINT: ${TRACING_OPENTELEMETRY_COLLECTOR_ENDPOINT} + TRACING_OPENTELEMETRY_COLLECTOR_PORT: ${TRACING_OPENTELEMETRY_COLLECTOR_PORT} <<: *tracing_open_telemetry_environs + not-worker: + image: ${DOCKER_REGISTRY:-itisfoundation}/notifications:${DOCKER_IMAGE_TAG:-master-github-latest} + init: true + hostname: "not-worker-{{.Node.Hostname}}-{{.Task.Slot}}" + environment: + <<: *notifications_environment + NOTIFICATIONS_WORKER_NAME: "not-worker-{{.Node.Hostname}}-{{.Task.Slot}}-{{.Task.ID}}" + NOTIFICATIONS_WORKER_MODE: "true" + CELERY_CONCURRENCY: 100 + CELERY_QUEUES: "notifications.default" + dask-sidecar: image: ${DOCKER_REGISTRY:-itisfoundation}/dask-sidecar:${DOCKER_IMAGE_TAG:-latest} init: true @@ -1383,7 +1402,7 @@ services: STORAGE_WORKER_NAME: "sto-worker-cpu-bound-{{.Node.Hostname}}-{{.Task.Slot}}-{{.Task.ID}}" STORAGE_WORKER_MODE: "true" CELERY_CONCURRENCY: 1 - CELERY_QUEUES: "cpu_bound" + CELERY_QUEUES: "storage.cpu_bound" networks: *storage_networks rabbit: diff --git a/services/notifications/docker/boot.sh b/services/notifications/docker/boot.sh index dbae76238cd8..29d6fbbba4c1 100755 --- a/services/notifications/docker/boot.sh +++ b/services/notifications/docker/boot.sh @@ -47,26 +47,52 @@ APP_LOG_LEVEL=${LOGLEVEL:-${LOG_LEVEL:-${LOGLEVEL:-INFO}}} SERVER_LOG_LEVEL=$(echo "${APP_LOG_LEVEL}" | tr '[:upper:]' '[:lower:]') echo "$INFO" "Log-level app/server: $APP_LOG_LEVEL/$SERVER_LOG_LEVEL" -if [ "${SC_BOOT_MODE}" = "debug" ]; then - reload_dir_packages=$(fdfind src /devel/packages --exec echo '--reload-dir {} ' | tr '\n' ' ') +if [ "${NOTIFICATIONS_WORKER_MODE:-}" = "true" ]; then + if [ "${SC_BOOT_MODE}" = "debug" ]; then + exec watchmedo auto-restart \ + --directory /devel/packages \ + --directory services/notifications \ + --pattern "*.py" \ + --recursive \ + -- \ + celery \ + --app=simcore_service_notifications.modules.celery.worker_main:the_celery_app \ + worker --pool=threads \ + --loglevel="${SERVER_LOG_LEVEL}" \ + --concurrency="${CELERY_CONCURRENCY}" \ + --hostname="${NOTIFICATIONS_WORKER_NAME}" \ + --queues="${CELERY_QUEUES:-default}" + else + exec celery \ + --app=simcore_service_notifications.modules.celery.worker_main:the_celery_app \ + worker --pool=threads \ + --loglevel="${SERVER_LOG_LEVEL}" \ + --concurrency="${CELERY_CONCURRENCY}" \ + --hostname="${NOTIFICATIONS_WORKER_NAME}" \ + --queues="${CELERY_QUEUES:-default}" + fi +else + if [ "${SC_BOOT_MODE}" = "debug" ]; then + reload_dir_packages=$(fdfind src /devel/packages --exec echo '--reload-dir {} ' | tr '\n' ' ') - exec sh -c " - cd services/notifications/src/simcore_service_notifications && \ - python -Xfrozen_modules=off -m debugpy --listen 0.0.0.0:${NOTIFICATIONS_REMOTE_DEBUGGING_PORT} -m \ - uvicorn \ - --factory main:app_factory \ + exec sh -c " + cd services/notifications/src/simcore_service_notifications && \ + python -Xfrozen_modules=off -m debugpy --listen 0.0.0.0:${NOTIFICATIONS_REMOTE_DEBUGGING_PORT} -m \ + uvicorn \ + --factory main:app_factory \ + --host 0.0.0.0 \ + --port 8000 \ + --reload \ + $reload_dir_packages \ + --reload-dir . \ + --log-level \"${SERVER_LOG_LEVEL}\" + " + else + exec uvicorn \ + --factory simcore_service_notifications.main:app_factory \ --host 0.0.0.0 \ --port 8000 \ - --reload \ - $reload_dir_packages \ - --reload-dir . \ - --log-level \"${SERVER_LOG_LEVEL}\" - " -else - exec uvicorn \ - --factory simcore_service_notifications.main:app_factory \ - --host 0.0.0.0 \ - --port 8000 \ - --log-level "${SERVER_LOG_LEVEL}" \ - --no-access-log + --log-level "${SERVER_LOG_LEVEL}" \ + --no-access-log + fi fi diff --git a/services/notifications/docker/healthcheck.py b/services/notifications/docker/healthcheck.py index 9e3f3274a292..6861107e4f37 100755 --- a/services/notifications/docker/healthcheck.py +++ b/services/notifications/docker/healthcheck.py @@ -16,18 +16,50 @@ - SEE https://blog.sixeyed.com/docker-healthchecks-why-not-to-use-curl-or-iwr/ """ import os +import subprocess import sys from urllib.request import urlopen +from simcore_service_notifications.core.application import ApplicationSettings + SUCCESS, UNHEALTHY = 0, 1 -# Disabled if boots with debugger (e.g. debug, pdb-debug, debug-ptvsd, debugpy, etc) -ok = "debug" in os.environ.get("SC_BOOT_MODE", "").lower() +# Disabled if boots with debugger +ok = os.getenv("SC_BOOT_MODE", "").lower() == "debug" # Queries host # pylint: disable=consider-using-with + +app_settings = ApplicationSettings.create_from_envs() + + +def _is_celery_worker_healthy(): + assert app_settings.NOTIFICATIONS_CELERY + broker_url = app_settings.NOTIFICATIONS_CELERY.CELERY_RABBIT_BROKER.dsn + + try: + result = subprocess.run( + [ + "celery", + "--broker", + broker_url, + "inspect", + "ping", + "--destination", + "celery@" + os.getenv("NOTIFICATIONS_WORKER_NAME", "worker"), + ], + capture_output=True, + text=True, + check=True, + ) + return "pong" in result.stdout + except subprocess.CalledProcessError: + return False + + ok = ( ok + or (app_settings.NOTIFICATIONS_WORKER_MODE and _is_celery_worker_healthy()) or urlopen( "{host}{baseurl}".format( host=sys.argv[1], baseurl=os.environ.get("SIMCORE_NODE_BASEPATH", "") diff --git a/services/notifications/requirements/_base.in b/services/notifications/requirements/_base.in index 77bb3fd4051a..b392120e16f7 100644 --- a/services/notifications/requirements/_base.in +++ b/services/notifications/requirements/_base.in @@ -6,7 +6,9 @@ --constraint ./constraints.txt # intra-repo required dependencies +--requirement ../../../packages/celery-library/requirements/_base.in --requirement ../../../packages/common-library/requirements/_base.in +--requirement ../../../packages/notifications-library/requirements/_base.in --requirement ../../../packages/models-library/requirements/_base.in --requirement ../../../packages/settings-library/requirements/_base.in --requirement ../../../packages/postgres-database/requirements/_base.in diff --git a/services/notifications/requirements/_base.txt b/services/notifications/requirements/_base.txt index 458ba8b757f4..74d898a5a9b7 100644 --- a/services/notifications/requirements/_base.txt +++ b/services/notifications/requirements/_base.txt @@ -1,20 +1,51 @@ aio-pika==9.5.5 - # via -r requirements/../../../packages/service-library/requirements/_base.in + # via + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/_base.in + # -r requirements/../../../packages/service-library/requirements/_base.in aiocache==0.12.3 - # via -r requirements/../../../packages/service-library/requirements/_base.in + # via + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/_base.in + # -r requirements/../../../packages/service-library/requirements/_base.in aiodebug==2.3.0 - # via -r requirements/../../../packages/service-library/requirements/_base.in + # via + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/_base.in + # -r requirements/../../../packages/service-library/requirements/_base.in aiodocker==0.24.0 - # via -r requirements/../../../packages/service-library/requirements/_base.in + # via + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/_base.in + # -r requirements/../../../packages/service-library/requirements/_base.in aiofiles==24.1.0 - # via -r requirements/../../../packages/service-library/requirements/_base.in + # via + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/_base.in + # -r requirements/../../../packages/notifications-library/requirements/_base.in + # -r requirements/../../../packages/service-library/requirements/_base.in aiohappyeyeballs==2.6.1 # via aiohttp aiohttp==3.12.12 # via + # -c requirements/../../../packages/celery-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt @@ -31,8 +62,14 @@ aiormq==6.8.1 # via aio-pika aiosignal==1.3.2 # via aiohttp +aiosmtplib==4.0.1 + # via -r requirements/../../../packages/notifications-library/requirements/_base.in alembic==1.15.1 - # via -r requirements/../../../packages/postgres-database/requirements/_base.in + # via + # -r requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/_base.in + # -r requirements/../../../packages/postgres-database/requirements/_base.in +amqp==5.3.1 + # via kombu annotated-types==0.7.0 # via pydantic anyio==4.9.0 @@ -44,9 +81,15 @@ anyio==4.9.0 # watchfiles arrow==1.3.0 # via + # -r requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/_base.in # -r requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/_base.in # -r requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/_base.in # -r requirements/../../../packages/service-library/requirements/_base.in +asgi-lifespan==2.1.0 + # via -r requirements/../../../packages/service-library/requirements/_fastapi.in asgiref==3.8.1 # via opentelemetry-instrumentation-asgi asyncpg==0.30.0 @@ -56,11 +99,35 @@ attrs==25.3.0 # aiohttp # jsonschema # referencing +billiard==4.2.1 + # via celery +celery==5.5.3 + # via -r requirements/../../../packages/celery-library/requirements/_base.in certifi==2025.1.31 # via + # -c requirements/../../../packages/celery-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt @@ -80,9 +147,19 @@ charset-normalizer==3.4.1 # via requests click==8.2.1 # via + # celery + # click-didyoumean + # click-plugins + # click-repl # rich-toolkit # typer # uvicorn +click-didyoumean==0.3.1 + # via celery +click-plugins==1.1.1.2 + # via celery +click-repl==0.3.0 + # via celery dnspython==2.7.0 # via email-validator email-validator==2.2.0 @@ -104,7 +181,9 @@ fastapi-cloud-cli==0.1.5 fastapi-lifespan-manager==0.1.4 # via -r requirements/../../../packages/service-library/requirements/_fastapi.in faststream==0.5.37 - # via -r requirements/../../../packages/service-library/requirements/_base.in + # via + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/_base.in + # -r requirements/../../../packages/service-library/requirements/_base.in frozenlist==1.5.0 # via # aiohttp @@ -131,9 +210,29 @@ httptools==0.6.4 # via uvicorn httpx==0.28.1 # via + # -c requirements/../../../packages/celery-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt @@ -161,9 +260,29 @@ importlib-metadata==8.6.1 # via opentelemetry-api jinja2==3.1.6 # via + # -c requirements/../../../packages/celery-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt @@ -175,18 +294,44 @@ jinja2==3.1.6 # -c requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../requirements/constraints.txt + # -r requirements/../../../packages/notifications-library/requirements/_base.in # fastapi jsonschema==4.23.0 # via + # -r requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/_base.in # -r requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/_base.in # -r requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/_base.in jsonschema-specifications==2024.10.1 # via jsonschema +kombu==5.5.4 + # via celery mako==1.3.10 # via + # -c requirements/../../../packages/celery-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt @@ -213,6 +358,7 @@ multidict==6.2.0 # yarl opentelemetry-api==1.34.1 # via + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/_base.in # -r requirements/../../../packages/service-library/requirements/_base.in # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-exporter-otlp-proto-http @@ -228,7 +374,9 @@ opentelemetry-api==1.34.1 # opentelemetry-sdk # opentelemetry-semantic-conventions opentelemetry-exporter-otlp==1.34.1 - # via -r requirements/../../../packages/service-library/requirements/_base.in + # via + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/_base.in + # -r requirements/../../../packages/service-library/requirements/_base.in opentelemetry-exporter-otlp-proto-common==1.34.1 # via # opentelemetry-exporter-otlp-proto-grpc @@ -248,21 +396,32 @@ opentelemetry-instrumentation==0.55b1 # opentelemetry-instrumentation-redis # opentelemetry-instrumentation-requests opentelemetry-instrumentation-aio-pika==0.55b1 - # via -r requirements/../../../packages/service-library/requirements/_base.in + # via + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/_base.in + # -r requirements/../../../packages/service-library/requirements/_base.in opentelemetry-instrumentation-asgi==0.55b1 # via opentelemetry-instrumentation-fastapi opentelemetry-instrumentation-asyncpg==0.55b1 - # via -r requirements/../../../packages/service-library/requirements/_base.in + # via + # -r requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/_base.in + # -r requirements/../../../packages/postgres-database/requirements/_base.in + # -r requirements/../../../packages/service-library/requirements/_base.in opentelemetry-instrumentation-fastapi==0.55b1 # via -r requirements/../../../packages/service-library/requirements/_fastapi.in opentelemetry-instrumentation-httpx==0.55b1 # via -r requirements/../../../packages/service-library/requirements/_fastapi.in opentelemetry-instrumentation-logging==0.55b1 - # via -r requirements/../../../packages/service-library/requirements/_base.in + # via + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/_base.in + # -r requirements/../../../packages/service-library/requirements/_base.in opentelemetry-instrumentation-redis==0.55b1 - # via -r requirements/../../../packages/service-library/requirements/_base.in + # via + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/_base.in + # -r requirements/../../../packages/service-library/requirements/_base.in opentelemetry-instrumentation-requests==0.55b1 - # via -r requirements/../../../packages/service-library/requirements/_base.in + # via + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/_base.in + # -r requirements/../../../packages/service-library/requirements/_base.in opentelemetry-proto==1.34.1 # via # opentelemetry-exporter-otlp-proto-common @@ -270,6 +429,7 @@ opentelemetry-proto==1.34.1 # opentelemetry-exporter-otlp-proto-http opentelemetry-sdk==1.34.1 # via + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/_base.in # -r requirements/../../../packages/service-library/requirements/_base.in # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-exporter-otlp-proto-http @@ -291,9 +451,29 @@ opentelemetry-util-http==0.55b1 # opentelemetry-instrumentation-requests orjson==3.10.16 # via + # -c requirements/../../../packages/celery-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt @@ -305,9 +485,22 @@ orjson==3.10.16 # -c requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../requirements/constraints.txt + # -r requirements/../../../packages/celery-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../packages/notifications-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/_base.in @@ -317,11 +510,21 @@ orjson==3.10.16 packaging==24.2 # via # -r requirements/_base.in + # kombu # opentelemetry-instrumentation pamqp==3.3.0 # via aiormq +phonenumbers==9.0.9 + # via + # -r requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/_base.in prometheus-client==0.21.1 # via -r requirements/../../../packages/service-library/requirements/_fastapi.in +prompt-toolkit==3.0.51 + # via click-repl propcache==0.3.1 # via # aiohttp @@ -331,16 +534,38 @@ protobuf==5.29.5 # googleapis-common-protos # opentelemetry-proto psutil==7.0.0 - # via -r requirements/../../../packages/service-library/requirements/_base.in + # via + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/_base.in + # -r requirements/../../../packages/service-library/requirements/_base.in psycopg2-binary==2.9.10 # via sqlalchemy pycryptodome==3.22.0 # via stream-zip pydantic==2.11.7 # via + # -c requirements/../../../packages/celery-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt @@ -352,9 +577,27 @@ pydantic==2.11.7 # -c requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../requirements/constraints.txt + # -r requirements/../../../packages/celery-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/_base.in # -r requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../packages/notifications-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/_base.in + # -r requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/_base.in # -r requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../packages/postgres-database/requirements/_base.in # -r requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/_base.in @@ -375,9 +618,22 @@ pydantic-core==2.33.2 # via pydantic pydantic-extra-types==2.10.5 # via + # -r requirements/../../../packages/celery-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../packages/notifications-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/_base.in + # -r requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/_base.in # -r requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/_base.in @@ -386,9 +642,29 @@ pydantic-extra-types==2.10.5 # -r requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/_base.in pydantic-settings==2.7.0 # via + # -c requirements/../../../packages/celery-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt @@ -400,16 +676,26 @@ pydantic-settings==2.7.0 # -c requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../requirements/constraints.txt + # -r requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/_base.in # -r requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/_base.in + # -r requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/_base.in # -r requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/_base.in # -r requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/_base.in # -r requirements/../../../packages/settings-library/requirements/_base.in pygments==2.19.1 # via rich pyinstrument==5.0.1 - # via -r requirements/../../../packages/service-library/requirements/_base.in + # via + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/_base.in + # -r requirements/../../../packages/service-library/requirements/_base.in python-dateutil==2.9.0.post0 - # via arrow + # via + # arrow + # celery python-dotenv==1.1.0 # via # pydantic-settings @@ -418,9 +704,29 @@ python-multipart==0.0.20 # via fastapi pyyaml==6.0.2 # via + # -c requirements/../../../packages/celery-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt @@ -432,13 +738,34 @@ pyyaml==6.0.2 # -c requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../requirements/constraints.txt + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/_base.in # -r requirements/../../../packages/service-library/requirements/_base.in # uvicorn redis==5.2.1 # via + # -c requirements/../../../packages/celery-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt @@ -450,12 +777,34 @@ redis==5.2.1 # -c requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../requirements/constraints.txt + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/_base.in # -r requirements/../../../packages/service-library/requirements/_base.in + # kombu referencing==0.35.1 # via + # -c requirements/../../../packages/celery-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt @@ -473,6 +822,9 @@ requests==2.32.4 # via opentelemetry-exporter-otlp-proto-http rich==14.1.0 # via + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/_base.in + # -r requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/_base.in # -r requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/_base.in # -r requirements/../../../packages/settings-library/requirements/_base.in # rich-toolkit @@ -494,12 +846,34 @@ shellingham==1.5.4 six==1.17.0 # via python-dateutil sniffio==1.3.1 - # via anyio + # via + # anyio + # asgi-lifespan sqlalchemy==1.4.54 # via + # -c requirements/../../../packages/celery-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt @@ -511,13 +885,34 @@ sqlalchemy==1.4.54 # -c requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../requirements/constraints.txt + # -r requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/_base.in # -r requirements/../../../packages/postgres-database/requirements/_base.in # alembic starlette==0.47.2 # via + # -c requirements/../../../packages/celery-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt @@ -531,15 +926,27 @@ starlette==0.47.2 # -c requirements/../../../requirements/constraints.txt # fastapi stream-zip==0.0.83 - # via -r requirements/../../../packages/service-library/requirements/_base.in + # via + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/_base.in + # -r requirements/../../../packages/service-library/requirements/_base.in tenacity==9.0.0 - # via -r requirements/../../../packages/service-library/requirements/_base.in + # via + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/_base.in + # -r requirements/../../../packages/service-library/requirements/_base.in toolz==1.0.0 - # via -r requirements/../../../packages/service-library/requirements/_base.in + # via + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/_base.in + # -r requirements/../../../packages/service-library/requirements/_base.in tqdm==4.67.1 + # via + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/_base.in + # -r requirements/../../../packages/service-library/requirements/_base.in # via -r requirements/../../../packages/service-library/requirements/_base.in typer==0.16.1 # via + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/_base.in + # -r requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/_base.in + # -r requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/_base.in # -r requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/_base.in # -r requirements/../../../packages/settings-library/requirements/_base.in # fastapi-cli @@ -567,11 +974,33 @@ typing-extensions==4.14.1 # typing-inspection typing-inspection==0.4.0 # via pydantic +tzdata==2025.2 + # via kombu urllib3==2.5.0 # via + # -c requirements/../../../packages/celery-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/celery-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../packages/settings-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../packages/notifications-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/postgres-database/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/postgres-database/requirements/../../../requirements/constraints.txt # -c requirements/../../../packages/service-library/requirements/../../../packages/common-library/requirements/../../../requirements/constraints.txt @@ -592,8 +1021,15 @@ uvicorn==0.34.2 # fastapi-cloud-cli uvloop==0.21.0 # via uvicorn +vine==5.1.0 + # via + # amqp + # celery + # kombu watchfiles==1.0.5 # via uvicorn +wcwidth==0.2.13 + # via prompt-toolkit websockets==15.0.1 # via uvicorn wrapt==1.17.2 @@ -604,6 +1040,8 @@ wrapt==1.17.2 # opentelemetry-instrumentation-redis yarl==1.18.3 # via + # -r requirements/../../../packages/celery-library/requirements/../../../packages/service-library/requirements/_base.in + # -r requirements/../../../packages/notifications-library/requirements/../../../packages/postgres-database/requirements/_base.in # -r requirements/../../../packages/postgres-database/requirements/_base.in # -r requirements/../../../packages/service-library/requirements/_base.in # aio-pika diff --git a/services/notifications/requirements/_test.in b/services/notifications/requirements/_test.in index 0269b0db4202..f27c540aaa50 100644 --- a/services/notifications/requirements/_test.in +++ b/services/notifications/requirements/_test.in @@ -18,6 +18,7 @@ faker httpx pytest pytest-asyncio +pytest-celery pytest-cov pytest-mock pytest-runner diff --git a/services/notifications/requirements/_test.txt b/services/notifications/requirements/_test.txt index b62b83d51d4a..6c6b4da278ef 100644 --- a/services/notifications/requirements/_test.txt +++ b/services/notifications/requirements/_test.txt @@ -1,9 +1,23 @@ +amqp==5.3.1 + # via + # -c requirements/_base.txt + # kombu anyio==4.9.0 # via # -c requirements/_base.txt # httpx asgi-lifespan==2.1.0 - # via -r requirements/_test.in + # via + # -c requirements/_base.txt + # -r requirements/_test.in +billiard==4.2.1 + # via + # -c requirements/_base.txt + # celery +celery==5.5.3 + # via + # -c requirements/_base.txt + # pytest-celery certifi==2025.1.31 # via # -c requirements/../../../requirements/constraints.txt @@ -15,12 +29,36 @@ charset-normalizer==3.4.1 # via # -c requirements/_base.txt # requests +click==8.2.1 + # via + # -c requirements/_base.txt + # celery + # click-didyoumean + # click-plugins + # click-repl +click-didyoumean==0.3.1 + # via + # -c requirements/_base.txt + # celery +click-plugins==1.1.1.2 + # via + # -c requirements/_base.txt + # celery +click-repl==0.3.0 + # via + # -c requirements/_base.txt + # celery coverage==7.7.1 # via # -r requirements/_test.in # pytest-cov +debugpy==1.8.14 + # via pytest-celery docker==7.1.0 - # via -r requirements/_test.in + # via + # -r requirements/_test.in + # pytest-celery + # pytest-docker-tools faker==37.1.0 # via -r requirements/_test.in h11==0.16.0 @@ -44,14 +82,28 @@ idna==3.10 # requests iniconfig==2.1.0 # via pytest +kombu==5.5.4 + # via + # -c requirements/_base.txt + # celery + # pytest-celery packaging==24.2 # via # -c requirements/_base.txt + # kombu # pytest pluggy==1.5.0 # via # pytest # pytest-cov +prompt-toolkit==3.0.51 + # via + # -c requirements/_base.txt + # click-repl +psutil==7.0.0 + # via + # -c requirements/_base.txt + # pytest-celery pygments==2.19.1 # via # -c requirements/_base.txt @@ -61,15 +113,24 @@ pytest==8.4.1 # -r requirements/_test.in # pytest-asyncio # pytest-cov + # pytest-docker-tools # pytest-mock pytest-asyncio==1.0.0 # via -r requirements/_test.in +pytest-celery==1.2.0 + # via -r requirements/_test.in pytest-cov==6.2.1 # via -r requirements/_test.in +pytest-docker-tools==3.1.9 + # via pytest-celery pytest-mock==3.14.1 # via -r requirements/_test.in pytest-runner==6.0.1 # via -r requirements/_test.in +python-dateutil==2.9.0.post0 + # via + # -c requirements/_base.txt + # celery python-dotenv==1.1.0 # via # -c requirements/_base.txt @@ -78,20 +139,43 @@ requests==2.32.4 # via # -c requirements/_base.txt # docker +setuptools==80.9.0 + # via pytest-celery +six==1.17.0 + # via + # -c requirements/_base.txt + # python-dateutil sniffio==1.3.1 # via # -c requirements/_base.txt # anyio # asgi-lifespan +tenacity==9.0.0 + # via + # -c requirements/_base.txt + # pytest-celery typing-extensions==4.14.1 # via # -c requirements/_base.txt # anyio tzdata==2025.2 - # via faker + # via + # -c requirements/_base.txt + # faker + # kombu urllib3==2.5.0 # via # -c requirements/../../../requirements/constraints.txt # -c requirements/_base.txt # docker # requests +vine==5.1.0 + # via + # -c requirements/_base.txt + # amqp + # celery + # kombu +wcwidth==0.2.13 + # via + # -c requirements/_base.txt + # prompt-toolkit diff --git a/services/notifications/requirements/_tools.txt b/services/notifications/requirements/_tools.txt index 44b443088762..4629426d8cbc 100644 --- a/services/notifications/requirements/_tools.txt +++ b/services/notifications/requirements/_tools.txt @@ -11,6 +11,7 @@ cfgv==3.4.0 click==8.2.1 # via # -c requirements/_base.txt + # -c requirements/_test.txt # black # pip-tools dill==0.3.9 @@ -70,7 +71,9 @@ pyyaml==6.0.2 ruff==0.11.2 # via -r requirements/../../../requirements/devenv.txt setuptools==80.9.0 - # via pip-tools + # via + # -c requirements/_test.txt + # pip-tools tomlkit==0.13.2 # via pylint typing-extensions==4.14.1 diff --git a/services/notifications/requirements/ci.txt b/services/notifications/requirements/ci.txt index 21975753559d..3a699961edc7 100644 --- a/services/notifications/requirements/ci.txt +++ b/services/notifications/requirements/ci.txt @@ -12,8 +12,10 @@ --requirement _tools.txt # installs this repo's packages +simcore-celery-library @ ../../packages/celery-library/ simcore-common-library @ ../../packages/common-library/ simcore-models-library @ ../../packages/models-library/ +simcore-notifications-library @ ../../packages/notifications-library/ simcore-postgres-database @ ../../packages/postgres-database/ pytest-simcore @ ../../packages/pytest-simcore/ simcore-service-library[fastapi] @ ../../packages/service-library/ diff --git a/services/notifications/requirements/dev.txt b/services/notifications/requirements/dev.txt index 4e73fc7a83a3..5e21f4fc4abd 100644 --- a/services/notifications/requirements/dev.txt +++ b/services/notifications/requirements/dev.txt @@ -12,8 +12,10 @@ --requirement _tools.txt # installs this repo's packages +--editable ../../packages/celery-library --editable ../../packages/common-library --editable ../../packages/models-library +--editable ../../packages/notifications-library --editable ../../packages/postgres-database --editable ../../packages/pytest-simcore --editable ../../packages/service-library[fastapi] diff --git a/services/notifications/requirements/prod.txt b/services/notifications/requirements/prod.txt index f203156b59cd..badd27a0079b 100644 --- a/services/notifications/requirements/prod.txt +++ b/services/notifications/requirements/prod.txt @@ -10,8 +10,10 @@ --requirement _base.txt # installs this repo's packages +simcore-celery-library @ ../../packages/celery-library/ simcore-common-library @ ../../packages/common-library/ simcore-models-library @ ../../packages/models-library/ +simcore-notifications-library @ ../../packages/notifications-library/ simcore-postgres-database @ ../../packages/postgres-database/ simcore-service-library[fastapi] @ ../../packages/service-library/ simcore-settings-library @ ../../packages/settings-library/ diff --git a/services/notifications/setup.py b/services/notifications/setup.py index c50365f3e259..23398f671226 100755 --- a/services/notifications/setup.py +++ b/services/notifications/setup.py @@ -23,7 +23,10 @@ def read_reqs(reqs_path: Path) -> set[str]: NAME = "simcore-service-notifications" VERSION = (CURRENT_DIR / "VERSION").read_text().strip() -AUTHORS = ("Andrei Neagu (GitHK)",) +AUTHORS = ( + "Giancarlo Romeo (giancarloromeo)", + "Andrei Neagu (GitHK)", +) DESCRIPTION = "Service used for sending notifications to users via different channels" PROD_REQUIREMENTS = tuple( diff --git a/services/notifications/src/simcore_service_notifications/api/rest/_health.py b/services/notifications/src/simcore_service_notifications/api/rest/_health.py index 5f38f21d5e03..d8f7e3e600e8 100644 --- a/services/notifications/src/simcore_service_notifications/api/rest/_health.py +++ b/services/notifications/src/simcore_service_notifications/api/rest/_health.py @@ -29,5 +29,4 @@ async def check_service_health( if not postgres_liveness.is_responsive: raise HealthCheckError(POSRGRES_DATABASE_UNHEALTHY_MSG) - return HealthCheckGet(timestamp=f"{__name__}@{arrow.utcnow().datetime.isoformat()}") diff --git a/services/notifications/src/simcore_service_notifications/api/rest/routing.py b/services/notifications/src/simcore_service_notifications/api/rest/routes.py similarity index 100% rename from services/notifications/src/simcore_service_notifications/api/rest/routing.py rename to services/notifications/src/simcore_service_notifications/api/rest/routes.py diff --git a/services/notifications/src/simcore_service_notifications/api/rpc/_notifications.py b/services/notifications/src/simcore_service_notifications/api/rpc/_notifications.py new file mode 100644 index 000000000000..f59c5db57761 --- /dev/null +++ b/services/notifications/src/simcore_service_notifications/api/rpc/_notifications.py @@ -0,0 +1,19 @@ +from models_library.rpc.notifications import Notification +from servicelib.celery.task_manager import TaskManager +from servicelib.rabbitmq import RPCRouter + +from ...services import notifications_service + +router = RPCRouter() + + +@router.expose(reraise_if_error_type=()) +async def send_notification( + task_manager: TaskManager, + *, + notification: Notification, +) -> None: + await notifications_service.send_notification( + task_manager, + notification=notification, + ) diff --git a/services/notifications/src/simcore_service_notifications/api/rpc/routing.py b/services/notifications/src/simcore_service_notifications/api/rpc/routes.py similarity index 64% rename from services/notifications/src/simcore_service_notifications/api/rpc/routing.py rename to services/notifications/src/simcore_service_notifications/api/rpc/routes.py index c43bcdb7c05a..08994ec187eb 100644 --- a/services/notifications/src/simcore_service_notifications/api/rpc/routing.py +++ b/services/notifications/src/simcore_service_notifications/api/rpc/routes.py @@ -1,23 +1,27 @@ from collections.abc import AsyncIterator +from celery_library.rpc import _async_jobs from fastapi import FastAPI from fastapi_lifespan_manager import State from models_library.api_schemas_notifications import NOTIFICATIONS_RPC_NAMESPACE from servicelib.rabbitmq import RPCRouter +from ...clients.celery import get_task_manager_from_app from ...clients.rabbitmq import get_rabbitmq_rpc_server +from . import _notifications ROUTERS: list[RPCRouter] = [ - # import and use all routers here + _async_jobs.router, + _notifications.router, ] async def rpc_api_routes_lifespan(app: FastAPI) -> AsyncIterator[State]: rpc_server = get_rabbitmq_rpc_server(app) - + task_manager = get_task_manager_from_app(app) for router in ROUTERS: await rpc_server.register_router( - router, NOTIFICATIONS_RPC_NAMESPACE, app + router, NOTIFICATIONS_RPC_NAMESPACE, task_manager=task_manager ) # pragma: no cover yield {} diff --git a/services/notifications/src/simcore_service_notifications/clients/celery.py b/services/notifications/src/simcore_service_notifications/clients/celery.py new file mode 100644 index 000000000000..5452a20b7e36 --- /dev/null +++ b/services/notifications/src/simcore_service_notifications/clients/celery.py @@ -0,0 +1,39 @@ +import logging +from collections.abc import AsyncIterator + +from celery_library.common import create_app, create_task_manager +from celery_library.task_manager import CeleryTaskManager +from celery_library.types import register_celery_types, register_pydantic_types +from fastapi import FastAPI +from fastapi_lifespan_manager import State +from models_library.rpc.notifications import Notification +from models_library.rpc.notifications.channels import ( + EmailChannel, + SMSChannel, +) +from settings_library.celery import CelerySettings + +from ..core.settings import ApplicationSettings + +_logger = logging.getLogger(__name__) + + +async def celery_lifespan(app: FastAPI) -> AsyncIterator[State]: + settings: ApplicationSettings = app.state.settings + if settings.NOTIFICATIONS_CELERY and not settings.NOTIFICATIONS_WORKER_MODE: + celery_settings: CelerySettings = settings.NOTIFICATIONS_CELERY + + app.state.task_manager = await create_task_manager( + create_app(celery_settings), celery_settings + ) + + register_celery_types() + register_pydantic_types(Notification, EmailChannel, SMSChannel) + yield {} + + +def get_task_manager_from_app(app: FastAPI) -> CeleryTaskManager: + assert hasattr(app.state, "task_manager") # nosec + task_manager = app.state.task_manager + assert isinstance(task_manager, CeleryTaskManager) # nosec + return task_manager diff --git a/services/notifications/src/simcore_service_notifications/clients/rabbitmq.py b/services/notifications/src/simcore_service_notifications/clients/rabbitmq.py index 3c205c401621..3594bb1af234 100644 --- a/services/notifications/src/simcore_service_notifications/clients/rabbitmq.py +++ b/services/notifications/src/simcore_service_notifications/clients/rabbitmq.py @@ -17,7 +17,7 @@ async def rabbitmq_lifespan(app: FastAPI) -> AsyncIterator[State]: await wait_till_rabbitmq_responsive(rabbit_settings.dsn) app.state.rabbitmq_rpc_server = await RabbitMQRPCClient.create( - client_name="dynamic_scheduler_rpc_server", settings=rabbit_settings + client_name="notifications_rpc_server", settings=rabbit_settings ) yield {} diff --git a/services/notifications/src/simcore_service_notifications/core/application.py b/services/notifications/src/simcore_service_notifications/core/application.py index 63517b52d5b4..545f94e15dad 100644 --- a/services/notifications/src/simcore_service_notifications/core/application.py +++ b/services/notifications/src/simcore_service_notifications/core/application.py @@ -15,7 +15,7 @@ ) from .._meta import API_VTAG, APP_NAME, SUMMARY, VERSION -from ..api.rest.routing import initialize_rest_api +from ..api.rest.routes import initialize_rest_api from . import events from .settings import ApplicationSettings @@ -35,7 +35,9 @@ def create_app( description=SUMMARY, version=f"{VERSION}", openapi_url=f"/api/{API_VTAG}/openapi.json", - lifespan=events.create_app_lifespan(logging_lifespan=logging_lifespan), + lifespan=events.create_app_lifespan( + settings, logging_lifespan=logging_lifespan + ), **get_common_oas_options(is_devel_mode=settings.SC_BOOT_MODE.is_devel_mode()), ) override_fastapi_openapi_method(app) diff --git a/services/notifications/src/simcore_service_notifications/core/events.py b/services/notifications/src/simcore_service_notifications/core/events.py index 2660e2f426ca..ac73d999af98 100644 --- a/services/notifications/src/simcore_service_notifications/core/events.py +++ b/services/notifications/src/simcore_service_notifications/core/events.py @@ -13,7 +13,8 @@ ) from .._meta import APP_FINISHED_BANNER_MSG, APP_STARTED_BANNER_MSG -from ..api.rpc.routing import rpc_api_routes_lifespan +from ..api.rpc.routes import rpc_api_routes_lifespan +from ..clients.celery import celery_lifespan from ..clients.postgres import postgres_lifespan from ..clients.rabbitmq import rabbitmq_lifespan from .settings import ApplicationSettings @@ -37,7 +38,7 @@ async def _settings_lifespan(app: FastAPI) -> AsyncIterator[State]: def create_app_lifespan( - logging_lifespan: Lifespan | None = None, + settings: ApplicationSettings, logging_lifespan: Lifespan | None = None ) -> LifespanManager[FastAPI]: # WARNING: order matters app_lifespan = LifespanManager() @@ -46,14 +47,19 @@ def create_app_lifespan( app_lifespan.add(_settings_lifespan) # - postgres + # NOTE: for now we will remove app_lifespan.add(postgres_database_lifespan) app_lifespan.add(postgres_lifespan) - # - rabbitmq - app_lifespan.add(rabbitmq_lifespan) + if not settings.NOTIFICATIONS_WORKER_MODE: + # - rabbitmq + app_lifespan.add(rabbitmq_lifespan) - # - rpc api routes - app_lifespan.add(rpc_api_routes_lifespan) + # - celery + app_lifespan.add(celery_lifespan) + + # - rpc api routes + app_lifespan.add(rpc_api_routes_lifespan) # - prometheus instrumentation app_lifespan.add(prometheus_instrumentation_lifespan) diff --git a/services/notifications/src/simcore_service_notifications/core/settings.py b/services/notifications/src/simcore_service_notifications/core/settings.py index 6f7e13a546e3..508ccc09f0d7 100644 --- a/services/notifications/src/simcore_service_notifications/core/settings.py +++ b/services/notifications/src/simcore_service_notifications/core/settings.py @@ -5,6 +5,7 @@ from pydantic import AliasChoices, Field, field_validator from servicelib.logging_utils_filtering import LoggerName, MessageSubstring from settings_library.base import BaseCustomSettings +from settings_library.celery import CelerySettings from settings_library.postgres import PostgresSettings from settings_library.rabbit import RabbitSettings from settings_library.tracing import TracingSettings @@ -25,11 +26,11 @@ class ApplicationSettings(BaseCustomSettings, MixinLoggingSettings): SC_BOOT_MODE: BootModeEnum | None - NOTIFICATIONS_VOLUMES_LOG_FORMAT_LOCAL_DEV_ENABLED: Annotated[ + NOTIFICATIONS_LOG_FORMAT_LOCAL_DEV_ENABLED: Annotated[ bool, Field( validation_alias=AliasChoices( - "NOTIFICATIONS_VOLUMES_LOG_FORMAT_LOCAL_DEV_ENABLED", + "NOTIFICATIONS_LOG_FORMAT_LOCAL_DEV_ENABLED", "LOG_FORMAT_LOCAL_DEV_ENABLED", ), description=( @@ -39,12 +40,12 @@ class ApplicationSettings(BaseCustomSettings, MixinLoggingSettings): ), ] = False - NOTIFICATIONS_VOLUMES_LOG_FILTER_MAPPING: Annotated[ + NOTIFICATIONS_LOG_FILTER_MAPPING: Annotated[ dict[LoggerName, list[MessageSubstring]], Field( default_factory=dict, validation_alias=AliasChoices( - "NOTIFICATIONS_VOLUMES_LOG_FILTER_MAPPING", "LOG_FILTER_MAPPING" + "NOTIFICATIONS_LOG_FILTER_MAPPING", "LOG_FILTER_MAPPING" ), description="is a dictionary that maps specific loggers (such as 'uvicorn.access' or 'gunicorn.access') to a list of log message patterns that should be filtered out.", ), @@ -58,6 +59,18 @@ class ApplicationSettings(BaseCustomSettings, MixinLoggingSettings): ), ] + NOTIFICATIONS_CELERY: Annotated[ + CelerySettings, + Field( + description="settings for service/celery", + json_schema_extra={"auto_default_from_env": True}, + ), + ] + + NOTIFICATIONS_WORKER_MODE: Annotated[ + bool, Field(description="If True, run as a worker") + ] = False + NOTIFICATIONS_POSTGRES: Annotated[ PostgresSettings, Field( diff --git a/services/notifications/src/simcore_service_notifications/main.py b/services/notifications/src/simcore_service_notifications/main.py index cda95f9dd9f5..8e12338728e1 100644 --- a/services/notifications/src/simcore_service_notifications/main.py +++ b/services/notifications/src/simcore_service_notifications/main.py @@ -20,8 +20,8 @@ def app_factory() -> FastAPI: app_settings = ApplicationSettings.create_from_envs() logging_lifespan = create_logging_lifespan( - log_format_local_dev_enabled=app_settings.NOTIFICATIONS_VOLUMES_LOG_FORMAT_LOCAL_DEV_ENABLED, - logger_filter_mapping=app_settings.NOTIFICATIONS_VOLUMES_LOG_FILTER_MAPPING, + log_format_local_dev_enabled=app_settings.NOTIFICATIONS_LOG_FORMAT_LOCAL_DEV_ENABLED, + logger_filter_mapping=app_settings.NOTIFICATIONS_LOG_FILTER_MAPPING, tracing_settings=app_settings.NOTIFICATIONS_TRACING, log_base_level=app_settings.log_level, noisy_loggers=_NOISY_LOGGERS, diff --git a/services/notifications/src/simcore_service_notifications/modules/__init__.py b/services/notifications/src/simcore_service_notifications/modules/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/services/notifications/src/simcore_service_notifications/modules/celery/__init__.py b/services/notifications/src/simcore_service_notifications/modules/celery/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/services/notifications/src/simcore_service_notifications/modules/celery/_email_tasks.py b/services/notifications/src/simcore_service_notifications/modules/celery/_email_tasks.py new file mode 100644 index 000000000000..25aebfe5b289 --- /dev/null +++ b/services/notifications/src/simcore_service_notifications/modules/celery/_email_tasks.py @@ -0,0 +1,58 @@ +# pylint: disable=unused-argument + +import logging +from email.headerregistry import Address + +from celery import Task # type: ignore[import-untyped] +from jinja2 import StrictUndefined +from models_library.rpc.notifications import Notification +from models_library.rpc.notifications.channels import EmailChannel +from notifications_library._email import compose_email, create_email_session +from notifications_library._email_render import render_email_parts +from notifications_library._render import ( + create_render_environment_from_notifications_library, +) +from servicelib.celery.models import TaskID +from settings_library.email import SMTPSettings + +_logger = logging.getLogger(__name__) + +EMAIL_CHANNEL_NAME = "email" + + +async def send_email_notification( + task: Task, + task_id: TaskID, + notification: Notification, +) -> None: + _ = task, task_id + + # + # NOTE: task can be used to provide progress + # + + assert isinstance(notification.channel, EmailChannel) # nosec + + _logger.info("Sending email notification to %s", notification.channel.to_addr) + + parts = render_email_parts( + env=create_render_environment_from_notifications_library( + undefined=StrictUndefined + ), + event_name=f"on_{notification.event.type}", + **notification.event.model_dump(), + ) + + msg = compose_email( + Address(**notification.channel.from_addr.model_dump()), + Address(**notification.channel.to_addr.model_dump()), + subject=parts.subject, + content_text=parts.text_content, + content_html=parts.html_content, + ) + + # if event_attachments: + # add_attachments(msg, event_attachments) + + async with create_email_session(settings=SMTPSettings.create_from_envs()) as smtp: + await smtp.send_message(msg) diff --git a/services/notifications/src/simcore_service_notifications/modules/celery/tasks.py b/services/notifications/src/simcore_service_notifications/modules/celery/tasks.py new file mode 100644 index 000000000000..1cee7388d6ed --- /dev/null +++ b/services/notifications/src/simcore_service_notifications/modules/celery/tasks.py @@ -0,0 +1,32 @@ +import logging +from enum import StrEnum + +from celery import Celery # type: ignore[import-untyped] +from celery_library.task import register_task +from celery_library.types import register_celery_types, register_pydantic_types +from models_library.rpc.notifications.channels import EmailChannel, SMSChannel +from models_library.rpc.notifications.events import AccountRequestedEvent +from servicelib.logging_utils import log_context + +from ...modules.celery._email_tasks import EMAIL_CHANNEL_NAME, send_email_notification + +_logger = logging.getLogger(__name__) + + +_NOTIFICATIONS_PREFIX: str = "notifications" + + +class TaskQueue(StrEnum): + DEFAULT = f"{_NOTIFICATIONS_PREFIX}.default" + + +def setup_worker_tasks(app: Celery) -> None: + register_celery_types() + register_pydantic_types(AccountRequestedEvent, EmailChannel, SMSChannel) + + with log_context(_logger, logging.INFO, msg="worker tasks registration"): + register_task( + app, + send_email_notification, + ".".join((_NOTIFICATIONS_PREFIX, EMAIL_CHANNEL_NAME)), + ) diff --git a/services/notifications/src/simcore_service_notifications/modules/celery/worker_main.py b/services/notifications/src/simcore_service_notifications/modules/celery/worker_main.py new file mode 100644 index 000000000000..467b08c53c09 --- /dev/null +++ b/services/notifications/src/simcore_service_notifications/modules/celery/worker_main.py @@ -0,0 +1,43 @@ +from functools import partial + +from celery.signals import worker_init, worker_shutdown # type: ignore[import-untyped] +from celery_library.common import create_app as create_celery_app +from celery_library.signals import ( + on_worker_init, + on_worker_shutdown, +) +from servicelib.fastapi.celery.app_server import FastAPIAppServer +from servicelib.logging_utils import setup_loggers + +from ...core.application import create_app +from ...core.settings import ApplicationSettings +from .tasks import setup_worker_tasks + +_settings = ApplicationSettings.create_from_envs() + +setup_loggers( + log_format_local_dev_enabled=_settings.NOTIFICATIONS_LOG_FORMAT_LOCAL_DEV_ENABLED, + logger_filter_mapping=_settings.NOTIFICATIONS_LOG_FILTER_MAPPING, + tracing_settings=_settings.NOTIFICATIONS_TRACING, + log_base_level=_settings.log_level, + noisy_loggers=None, +) + + +assert _settings.NOTIFICATIONS_CELERY # nosec +the_celery_app = create_celery_app(_settings.NOTIFICATIONS_CELERY) + +app_server = FastAPIAppServer(app=create_app(_settings)) + + +def worker_init_wrapper(sender, **_kwargs): + assert _settings.NOTIFICATIONS_CELERY # nosec + return partial(on_worker_init, app_server, _settings.NOTIFICATIONS_CELERY)( + sender, **_kwargs + ) + + +worker_init.connect(worker_init_wrapper) +worker_shutdown.connect(on_worker_shutdown) + +setup_worker_tasks(the_celery_app) diff --git a/services/notifications/src/simcore_service_notifications/services/notifications_service.py b/services/notifications/src/simcore_service_notifications/services/notifications_service.py new file mode 100644 index 000000000000..02ba7571145f --- /dev/null +++ b/services/notifications/src/simcore_service_notifications/services/notifications_service.py @@ -0,0 +1,23 @@ +import logging + +from models_library.rpc.notifications import Notification +from servicelib.celery.models import TaskFilter +from servicelib.celery.task_manager import TaskManager + +from ..modules.celery.tasks import TaskQueue + +_logger = logging.getLogger(__name__) + + +async def send_notification( + task_manager: TaskManager, + *, + notification: Notification, +) -> None: + await task_manager.send_task( + # send to the specific channel worker + task_name=f"notifications.{notification.channel.type}", + task_filter=TaskFilter(), # TODO: TaskFilter + task_queue=TaskQueue.DEFAULT, + notification=notification, + ) diff --git a/services/notifications/tests/conftest.py b/services/notifications/tests/conftest.py index 6091f50b9de0..7259727cf99e 100644 --- a/services/notifications/tests/conftest.py +++ b/services/notifications/tests/conftest.py @@ -1,7 +1,6 @@ # pylint: disable=redefined-outer-name # pylint: disable=unused-argument - from pathlib import Path import pytest @@ -16,6 +15,7 @@ "pytest_simcore.logging", "pytest_simcore.postgres_service", "pytest_simcore.rabbit_service", + "pytest_simcore.redis_service", "pytest_simcore.repository_paths", ] diff --git a/services/notifications/tests/unit/conftest.py b/services/notifications/tests/unit/conftest.py index 5f785451f12a..45d08d6235c4 100644 --- a/services/notifications/tests/unit/conftest.py +++ b/services/notifications/tests/unit/conftest.py @@ -1,48 +1,164 @@ # pylint: disable=redefined-outer-name # pylint: disable=unused-argument -from collections.abc import AsyncIterator +import datetime +from collections.abc import AsyncIterator, Awaitable, Callable +from functools import partial +from typing import Any, Final import pytest import sqlalchemy as sa from asgi_lifespan import LifespanManager +from celery import Celery +from celery.contrib.testing.worker import start_worker +from celery.signals import worker_init, worker_shutdown +from celery.worker.worker import WorkController +from celery_library.signals import on_worker_init, on_worker_shutdown +from faker import Faker from fastapi import FastAPI from fastapi.testclient import TestClient +from pytest_mock import MockerFixture from pytest_simcore.helpers.monkeypatch_envs import EnvVarsDict, setenvs_from_dict +from servicelib.fastapi.celery.app_server import FastAPIAppServer +from servicelib.rabbitmq import RabbitMQRPCClient from settings_library.rabbit import RabbitSettings +from settings_library.redis import RedisSettings from simcore_service_notifications.core.application import create_app +from simcore_service_notifications.core.settings import ApplicationSettings +from simcore_service_notifications.modules.celery.tasks import ( + TaskQueue, + setup_worker_tasks, +) + +_LIFESPAN_TIMEOUT: Final[int] = 30 @pytest.fixture def app_environment( monkeypatch: pytest.MonkeyPatch, mock_environment: EnvVarsDict, - rabbit_service: RabbitSettings, - postgres_db: sa.engine.Engine, # waiting for postgres service to start + postgres_db: sa.engine.Engine, # wait for postgres service to start postgres_env_vars_dict: EnvVarsDict, + rabbit_service: RabbitSettings, + redis_service: RedisSettings, ) -> EnvVarsDict: return setenvs_from_dict( monkeypatch, { **mock_environment, + **postgres_env_vars_dict, + "NOTIFICATIONS_TRACING": "null", "RABBIT_HOST": rabbit_service.RABBIT_HOST, "RABBIT_PASSWORD": rabbit_service.RABBIT_PASSWORD.get_secret_value(), "RABBIT_PORT": f"{rabbit_service.RABBIT_PORT}", "RABBIT_SECURE": f"{rabbit_service.RABBIT_SECURE}", "RABBIT_USER": rabbit_service.RABBIT_USER, - **postgres_env_vars_dict, + "REDIS_SECURE": redis_service.REDIS_SECURE, + "REDIS_HOST": redis_service.REDIS_HOST, + "REDIS_PORT": f"{redis_service.REDIS_PORT}", + "REDIS_PASSWORD": redis_service.REDIS_PASSWORD.get_secret_value(), }, ) @pytest.fixture -async def initialized_app(app_environment: EnvVarsDict) -> AsyncIterator[FastAPI]: - app: FastAPI = create_app() +def app_settings( + app_environment: EnvVarsDict, + monkeypatch: pytest.MonkeyPatch, +) -> ApplicationSettings: + monkeypatch.setenv("NOTIFICATIONS_WORKER_MODE", "false") + settings = ApplicationSettings.create_from_envs() + print(f"{settings.model_dump_json(indent=2)=}") + return settings + + +@pytest.fixture +async def mock_fastapi_app( + mock_celery_app: None, app_settings: ApplicationSettings +) -> AsyncIterator[FastAPI]: + app: FastAPI = create_app(app_settings) async with LifespanManager(app, startup_timeout=30, shutdown_timeout=30): yield app +@pytest.fixture(scope="session") +def celery_config() -> dict[str, Any]: + return { + "broker_connection_retry_on_startup": True, + "broker_url": "memory://localhost//", + "result_backend": "cache+memory://localhost//", + "result_expires": datetime.timedelta(days=7), + "result_extended": True, + "pool": "threads", + "task_default_queue": "default", + "task_send_sent_event": True, + "task_track_started": True, + "worker_send_task_events": True, + } + + +@pytest.fixture +def mock_celery_app(mocker: MockerFixture, celery_config: dict[str, Any]) -> Celery: + celery_app = Celery(**celery_config) + + for module in ("simcore_service_notifications.clients.celery.create_app",): + mocker.patch(module, return_value=celery_app) + + return celery_app + + +@pytest.fixture +async def mock_celery_worker( + app_environment: EnvVarsDict, + celery_app: Celery, + monkeypatch: pytest.MonkeyPatch, +) -> AsyncIterator[Any]: + monkeypatch.setenv("NOTIFICATIONS_WORKER_MODE", "true") + app_settings = ApplicationSettings.create_from_envs() + + def _on_worker_init_wrapper(sender: WorkController, **_kwargs): + assert app_settings.NOTIFICATIONS_CELERY # nosec + return partial( + on_worker_init, + FastAPIAppServer(app=create_app(app_settings)), + app_settings.NOTIFICATIONS_CELERY, + )(sender, **_kwargs) + + worker_init.connect(_on_worker_init_wrapper) + worker_shutdown.connect(on_worker_shutdown) + + setup_worker_tasks(celery_app) + + with start_worker( + celery_app, + pool="threads", + concurrency=1, + loglevel="debug", + perform_ping_check=False, + queues=",".join(queue.value for queue in TaskQueue), + ) as worker: + yield worker + + +@pytest.fixture +async def notifications_rabbitmq_rpc_client( + rabbitmq_rpc_client: Callable[[str], Awaitable[RabbitMQRPCClient]], +) -> RabbitMQRPCClient: + rpc_client = await rabbitmq_rpc_client("pytest_notifications_rpc_client") + assert rpc_client + return rpc_client + + +@pytest.fixture +def test_client(fastapi_app: FastAPI) -> TestClient: + return TestClient(fastapi_app) + + @pytest.fixture -def test_client(initialized_app: FastAPI) -> TestClient: - return TestClient(initialized_app) +def fake_ipinfo(faker: Faker) -> dict[str, Any]: + return { + "x-real-ip": faker.ipv4(), + "x-forwarded-for": faker.ipv4(), + "peername": faker.ipv4(), + } diff --git a/services/notifications/tests/unit/test_api_rest__health.py b/services/notifications/tests/unit/test_api_rest__health.py index ba418fe7bc3e..6461709b3f89 100644 --- a/services/notifications/tests/unit/test_api_rest__health.py +++ b/services/notifications/tests/unit/test_api_rest__health.py @@ -6,17 +6,14 @@ from fastapi import status from fastapi.testclient import TestClient from models_library.api_schemas__common.health import HealthCheckGet -from models_library.errors import ( - POSRGRES_DATABASE_UNHEALTHY_MSG, - RABBITMQ_CLIENT_UNHEALTHY_MSG, -) -from models_library.healthchecks import IsNonResponsive +from models_library.errors import RABBITMQ_CLIENT_UNHEALTHY_MSG from pytest_mock import MockerFixture from simcore_service_notifications.api.rest._health import HealthCheckError pytest_simcore_core_services_selection = [ "postgres", "rabbit", + "redis", ] @@ -26,23 +23,6 @@ def test_health_ok(test_client: TestClient): assert HealthCheckGet.model_validate(response.json()) -@pytest.fixture -def mock_postgres_liveness(mocker: MockerFixture, test_client: TestClient) -> None: - mocker.patch.object( - test_client.app.state.postgres_liveness, - "_liveness_result", - new=IsNonResponsive(reason="fake"), - ) - - -def test_health_postgres_unhealthy( - mock_postgres_liveness: None, test_client: TestClient -): - with pytest.raises(HealthCheckError) as exc: - test_client.get("/") - assert POSRGRES_DATABASE_UNHEALTHY_MSG in f"{exc.value}" - - @pytest.fixture def mock_rabbit_healthy(mocker: MockerFixture, test_client: TestClient) -> None: mocker.patch.object( diff --git a/services/notifications/tests/unit/test_send_email_tasks.py b/services/notifications/tests/unit/test_send_email_tasks.py new file mode 100644 index 000000000000..538be29c2c78 --- /dev/null +++ b/services/notifications/tests/unit/test_send_email_tasks.py @@ -0,0 +1,121 @@ +from typing import Any + +import pytest +from faker import Faker +from models_library.rpc.notifications import Notification +from models_library.rpc.notifications.channels import EmailAddress, EmailChannel +from models_library.rpc.notifications.events import ( + AccountApprovedEvent, + AccountRequestedEvent, + ProductData, + ProductUIData, + UserData, +) +from pydantic import HttpUrl +from servicelib.rabbitmq import RabbitMQRPCClient +from servicelib.rabbitmq.rpc_interfaces.notifications import ( + send_notification, +) + +pytest_simcore_core_services_selection = [ + "postgres", + "rabbit", + "redis", +] + + +@pytest.mark.usefixtures( + "mock_celery_worker", + "mock_fastapi_app", +) +async def test_account_requested( + notifications_rabbitmq_rpc_client: RabbitMQRPCClient, + fake_ipinfo: dict[str, Any], + faker: Faker, +): + user_email = faker.email() + + await send_notification( + notifications_rabbitmq_rpc_client, + notification=Notification( + event=AccountRequestedEvent( + user=UserData( + username=faker.user_name(), + first_name=faker.first_name(), + last_name=faker.last_name(), + email=user_email, + ), + product=ProductData( + product_name=faker.company(), + display_name=faker.company(), + vendor_display_inline=faker.company_suffix(), + support_email=faker.email(), + homepage_url=faker.url(), + ui=ProductUIData( + project_alias=faker.word(), + logo_url=faker.image_url(), + strong_color=faker.color_name(), + ), + ), + host=HttpUrl(faker.url()), + ipinfo=fake_ipinfo, + ), + channel=EmailChannel( + from_addr=EmailAddress(addr_spec=faker.email()), + to_addr=EmailAddress( + addr_spec=user_email, + ), + ), + ), + ) + + # TODO: wait for the email to be sent and check + + +@pytest.mark.usefixtures( + "mock_celery_worker", + "mock_fastapi_app", +) +async def test_account_approved( + notifications_rabbitmq_rpc_client: RabbitMQRPCClient, + faker: Faker, +): + user_email = faker.email() + + await send_notification( + notifications_rabbitmq_rpc_client, + notification=Notification( + event=AccountApprovedEvent( + user=UserData( + username=faker.user_name(), + first_name=faker.first_name(), + last_name=faker.last_name(), + email=user_email, + ), + product=ProductData( + product_name=faker.company(), + display_name=faker.company(), + vendor_display_inline=faker.company_suffix(), + support_email=faker.email(), + homepage_url=faker.url(), + ui=ProductUIData( + project_alias=faker.word(), + logo_url=faker.image_url(), + strong_color=faker.color(), + ), + ), + link=HttpUrl(faker.url()), + ), + channel=EmailChannel( + from_addr=EmailAddress( + display_name=faker.name(), addr_spec=faker.email() + ), + to_addr=EmailAddress( + display_name=faker.name(), + addr_spec=user_email, + ), + ), + ), + ) + + # TODO: wait for the email to be sent and check diff --git a/services/storage/src/simcore_service_storage/api/rest/_files.py b/services/storage/src/simcore_service_storage/api/rest/_files.py index 9981570fc5fa..f8ddb433faa5 100644 --- a/services/storage/src/simcore_service_storage/api/rest/_files.py +++ b/services/storage/src/simcore_service_storage/api/rest/_files.py @@ -20,7 +20,7 @@ from models_library.projects_nodes_io import LocationID, StorageFileID from pydantic import AnyUrl, ByteSize, TypeAdapter from servicelib.aiohttp import status -from servicelib.celery.models import TaskFilter, TaskMetadata, TaskUUID +from servicelib.celery.models import TaskFilter, TaskUUID from servicelib.celery.task_manager import TaskManager from servicelib.logging_utils import log_context from yarl import URL @@ -293,10 +293,8 @@ async def complete_upload_file( client_name=_ASYNC_JOB_CLIENT_NAME, ) task_filter = TaskFilter.model_validate(job_filter.model_dump()) - task_uuid = await task_manager.submit_task( - TaskMetadata( - name=remote_complete_upload_file.__name__, - ), + task_uuid = await task_manager.send_task( + task_name=remote_complete_upload_file.__name__, task_filter=task_filter, user_id=job_filter.user_id, location_id=location_id, @@ -352,7 +350,8 @@ async def is_completed_upload_file( ) task_filter = TaskFilter.model_validate(job_filter.model_dump()) task_status = await task_manager.get_task_status( - task_filter=task_filter, task_uuid=TaskUUID(future_id) + task_filter=task_filter, + task_uuid=TaskUUID(future_id), ) # first check if the task is in the app if task_status.is_done: diff --git a/services/storage/src/simcore_service_storage/api/rpc/_paths.py b/services/storage/src/simcore_service_storage/api/rpc/_paths.py index b34da2e7e7f8..cdf41d362397 100644 --- a/services/storage/src/simcore_service_storage/api/rpc/_paths.py +++ b/services/storage/src/simcore_service_storage/api/rpc/_paths.py @@ -6,7 +6,7 @@ AsyncJobGet, ) from models_library.projects_nodes_io import LocationID -from servicelib.celery.models import TaskFilter, TaskMetadata +from servicelib.celery.models import TaskFilter from servicelib.celery.task_manager import TaskManager from servicelib.rabbitmq import RPCRouter @@ -26,10 +26,8 @@ async def compute_path_size( ) -> AsyncJobGet: task_name = remote_compute_path_size.__name__ task_filter = TaskFilter.model_validate(job_filter.model_dump()) - task_uuid = await task_manager.submit_task( - task_metadata=TaskMetadata( - name=task_name, - ), + task_uuid = await task_manager.send_task( + task_name=task_name, task_filter=task_filter, user_id=job_filter.user_id, location_id=location_id, @@ -48,10 +46,8 @@ async def delete_paths( ) -> AsyncJobGet: task_name = remote_delete_paths.__name__ task_filter = TaskFilter.model_validate(job_filter.model_dump()) - task_uuid = await task_manager.submit_task( - task_metadata=TaskMetadata( - name=task_name, - ), + task_uuid = await task_manager.send_task( + task_name=task_name, task_filter=task_filter, user_id=job_filter.user_id, location_id=location_id, diff --git a/services/storage/src/simcore_service_storage/api/rpc/_simcore_s3.py b/services/storage/src/simcore_service_storage/api/rpc/_simcore_s3.py index 314bad0e00b0..1afecf36aad7 100644 --- a/services/storage/src/simcore_service_storage/api/rpc/_simcore_s3.py +++ b/services/storage/src/simcore_service_storage/api/rpc/_simcore_s3.py @@ -6,10 +6,11 @@ ) from models_library.api_schemas_storage.storage_schemas import FoldersBody from models_library.api_schemas_webserver.storage import PathToExport -from servicelib.celery.models import TaskFilter, TaskMetadata, TasksQueue +from servicelib.celery.models import TaskFilter from servicelib.celery.task_manager import TaskManager from servicelib.rabbitmq import RPCRouter +from ...modules.celery.tasks import TaskQueue from .._worker_tasks._simcore_s3 import ( deep_copy_files_from_project, export_data, @@ -27,10 +28,9 @@ async def copy_folders_from_project( ) -> AsyncJobGet: task_name = deep_copy_files_from_project.__name__ task_filter = TaskFilter.model_validate(job_filter.model_dump()) - task_uuid = await task_manager.submit_task( - task_metadata=TaskMetadata( - name=task_name, - ), + + task_uuid = await task_manager.send_task( + task_name=task_name, task_filter=task_filter, user_id=job_filter.user_id, body=body, @@ -53,13 +53,11 @@ async def start_export_data( else: raise ValueError(f"Invalid export_as value: {export_as}") task_filter = TaskFilter.model_validate(job_filter.model_dump()) - task_uuid = await task_manager.submit_task( - task_metadata=TaskMetadata( - name=task_name, - ephemeral=False, - queue=TasksQueue.CPU_BOUND, - ), + task_uuid = await task_manager.send_task( + task_name=task_name, task_filter=task_filter, + task_ephemeral=False, + task_queue=TaskQueue.CPU_BOUND, user_id=job_filter.user_id, paths_to_export=paths_to_export, ) diff --git a/services/storage/src/simcore_service_storage/modules/celery/tasks.py b/services/storage/src/simcore_service_storage/modules/celery/tasks.py new file mode 100644 index 000000000000..6882551c052a --- /dev/null +++ b/services/storage/src/simcore_service_storage/modules/celery/tasks.py @@ -0,0 +1,8 @@ +from enum import StrEnum + +_TASK_QUEUE_PREFIX: str = "storage." + + +class TaskQueue(StrEnum): + DEFAULT = f"{_TASK_QUEUE_PREFIX}.default" + CPU_BOUND = f"{_TASK_QUEUE_PREFIX}.cpu_bound" diff --git a/services/storage/tests/conftest.py b/services/storage/tests/conftest.py index 328136401972..4fd6b2df99c4 100644 --- a/services/storage/tests/conftest.py +++ b/services/storage/tests/conftest.py @@ -76,6 +76,7 @@ from simcore_service_storage.datcore_dsm import DatCoreDataManager from simcore_service_storage.dsm import get_dsm_provider from simcore_service_storage.models import FileMetaData, FileMetaDataAtDB, S3BucketName +from simcore_service_storage.modules.celery.tasks import TaskQueue from simcore_service_storage.modules.s3 import get_s3_client from simcore_service_storage.simcore_s3_dsm import SimcoreS3DataManager from sqlalchemy import literal_column @@ -1035,7 +1036,7 @@ def _on_worker_init_wrapper(sender: WorkController, **_kwargs): concurrency=1, loglevel="info", perform_ping_check=False, - queues="default,cpu_bound", + queues=",".join(queue.value for queue in TaskQueue), ) as worker: yield worker