Skeleton ready
This commit is contained in:
0
shared/__init__.py
Normal file
0
shared/__init__.py
Normal file
0
shared/application/__init__.py
Normal file
0
shared/application/__init__.py
Normal file
0
shared/application/factory/__init__.py
Normal file
0
shared/application/factory/__init__.py
Normal file
10
shared/application/factory/application.py
Normal file
10
shared/application/factory/application.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from typing import List
|
||||
|
||||
from kernel.feature.provider import Provider
|
||||
|
||||
from shared.application.rabbitmq_application import RabbitMQApplication
|
||||
from shared.application.rabbitmq_kernel import RabbitMQKernel
|
||||
|
||||
class Application:
|
||||
def create_rabbitmq(self, features: List[type[Provider]]) -> RabbitMQApplication:
|
||||
return RabbitMQApplication(RabbitMQKernel(), features)
|
||||
27
shared/application/factory/logger.py
Normal file
27
shared/application/factory/logger.py
Normal file
@@ -0,0 +1,27 @@
|
||||
import logging
|
||||
|
||||
from sys import stdout
|
||||
|
||||
|
||||
class Logger:
|
||||
def create_stdout(
|
||||
self,
|
||||
name: str,
|
||||
level: int = logging.INFO
|
||||
) -> logging.Logger:
|
||||
logger = logging.getLogger(name)
|
||||
logger.setLevel(level)
|
||||
|
||||
handler = logging.StreamHandler(stdout)
|
||||
handler.setLevel(level)
|
||||
handler.setFormatter(
|
||||
logging.Formatter(
|
||||
"%(asctime)s - %(levelname)s - %(message)s",
|
||||
datefmt="%Y-%m-%d %H:%M:%S"
|
||||
)
|
||||
)
|
||||
|
||||
logger.addHandler(handler)
|
||||
|
||||
return logger
|
||||
|
||||
0
shared/application/manager/__init__.py
Normal file
0
shared/application/manager/__init__.py
Normal file
40
shared/application/manager/listener_arguments_parser.py
Normal file
40
shared/application/manager/listener_arguments_parser.py
Normal file
@@ -0,0 +1,40 @@
|
||||
from typing import Any, Sequence
|
||||
from argparse import ArgumentParser, HelpFormatter
|
||||
|
||||
|
||||
class ListenerArgumentsParser(ArgumentParser):
|
||||
def __init__(
|
||||
self,
|
||||
prog: str | None = None,
|
||||
usage: str | None = None,
|
||||
description: str | None = None,
|
||||
epilog: str | None = None,
|
||||
parents: Sequence[ArgumentParser] = [],
|
||||
formatter_class: type[HelpFormatter] = HelpFormatter,
|
||||
prefix_chars: str = '-',
|
||||
fromfile_prefix_chars: str | None = None,
|
||||
argument_default: Any = None,
|
||||
conflict_handler: str = 'error',
|
||||
add_help: bool = True,
|
||||
allow_abbrev: bool = True,
|
||||
exit_on_error: bool = True
|
||||
) -> None:
|
||||
super().__init__(
|
||||
prog=prog,
|
||||
usage=usage,
|
||||
description=description,
|
||||
epilog=epilog,
|
||||
parents=parents,
|
||||
formatter_class=formatter_class,
|
||||
prefix_chars=prefix_chars,
|
||||
fromfile_prefix_chars=fromfile_prefix_chars,
|
||||
argument_default=argument_default,
|
||||
conflict_handler=conflict_handler,
|
||||
add_help=add_help,
|
||||
allow_abbrev=allow_abbrev,
|
||||
exit_on_error=exit_on_error
|
||||
)
|
||||
|
||||
self.add_argument('--queue', type=str, required=True)
|
||||
self.add_argument('--exchange', type=str, required=True)
|
||||
self.add_argument('--routing-key', type=str, default="")
|
||||
56
shared/application/rabbitmq_application.py
Normal file
56
shared/application/rabbitmq_application.py
Normal file
@@ -0,0 +1,56 @@
|
||||
from typing import Callable, Awaitable, override
|
||||
from asyncio import CancelledError
|
||||
from logging import Logger
|
||||
from punq import Container
|
||||
from aio_pika.abc import AbstractIncomingMessage
|
||||
|
||||
from kernel.application.stop_event import StopEvent
|
||||
from kernel.application.view.configuration import Configuration
|
||||
from kernel.application.application import Application
|
||||
from kernel.rabbitmq.client import Client
|
||||
|
||||
from shared.application.view.listener_arguments import ListenerArguments
|
||||
|
||||
|
||||
class RabbitMQApplication(Application):
|
||||
@override
|
||||
async def do(
|
||||
self,
|
||||
callback: Callable[[Container, AbstractIncomingMessage], Awaitable[None]]
|
||||
) -> None:
|
||||
rabbitmq = await self.__connect_rabbitmq_client()
|
||||
arguments = self.get_container().resolve(ListenerArguments)
|
||||
|
||||
async def consumer(message: AbstractIncomingMessage) -> None:
|
||||
await callback(self.get_container(), message)
|
||||
|
||||
queue = arguments.queue
|
||||
exchange = arguments.exchange
|
||||
routing_key = arguments.routing_key
|
||||
|
||||
await rabbitmq.consume(queue, exchange, consumer, routing_key)
|
||||
|
||||
logger = self.get_container().resolve(Logger)
|
||||
|
||||
logger.info(f"Queue: {queue}")
|
||||
logger.info(f"Exchange: `{exchange}`")
|
||||
|
||||
if routing_key != "":
|
||||
logger.info(f"Routing key: {routing_key}")
|
||||
|
||||
try:
|
||||
await self.get_container().resolve(StopEvent).wait()
|
||||
except CancelledError as e:
|
||||
logger.exception(e)
|
||||
|
||||
async def __connect_rabbitmq_client(self) -> Client:
|
||||
rabbitmq_client = self.get_container().resolve(Client)
|
||||
|
||||
stop_event = self.get_container().resolve(StopEvent)
|
||||
configuration = self.get_container().resolve(Configuration)
|
||||
|
||||
await rabbitmq_client\
|
||||
.connect(stop_event, configuration.rabbitmq().timeout)
|
||||
|
||||
return rabbitmq_client
|
||||
|
||||
139
shared/application/rabbitmq_kernel.py
Normal file
139
shared/application/rabbitmq_kernel.py
Normal file
@@ -0,0 +1,139 @@
|
||||
from os import getenv
|
||||
from logging import Logger
|
||||
from punq import Container
|
||||
from pyee import EventEmitter
|
||||
from pyee.asyncio import AsyncIOEventEmitter
|
||||
from aioredlock import Aioredlock
|
||||
from redis.asyncio import Redis as AsyncRedis
|
||||
|
||||
from kernel.application.manager.stop_event import StopEvent as StopEventManager
|
||||
from kernel.application.stop_event import StopEvent
|
||||
from kernel.application.view.application import Application
|
||||
from kernel.application.view.configuration import Configuration
|
||||
from kernel.application.view.rabbitmq import RabbitMQ
|
||||
from kernel.application.view.redis import Redis as RedisConfig
|
||||
|
||||
from kernel.application.kernel import Kernel
|
||||
from shared.application.service.environment_loader import EnvironmentLoader
|
||||
from shared.application.manager.listener_arguments_parser import ListenerArgumentsParser
|
||||
from shared.application.factory.logger import Logger as LoggerFactory
|
||||
from shared.application.view.listener_arguments import ListenerArguments
|
||||
from kernel.rabbitmq.client import Client
|
||||
from kernel.rabbitmq.view.authentication_params import AuthenticationParams
|
||||
from kernel.rabbitmq.view.connection_params import ConnectionParams
|
||||
|
||||
|
||||
class RabbitMQKernel(Kernel):
|
||||
__container: Container
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.__load_env()
|
||||
self.__init_container()
|
||||
self.__register_config()
|
||||
self.__register_arguments()
|
||||
self.__register_stop_event()
|
||||
self.__register_logger()
|
||||
self.__register_rabbitmq_client()
|
||||
self.__register_redis()
|
||||
self.__register_lock_provider()
|
||||
self.__register_event_emitter()
|
||||
|
||||
def get_container(self) -> Container:
|
||||
return self.__container
|
||||
|
||||
def __load_env(self) -> None:
|
||||
env_loader = EnvironmentLoader(override=False)
|
||||
env_loader.load()
|
||||
|
||||
def __init_container(self) -> None:
|
||||
self.__container = Container()
|
||||
|
||||
def __register_config(self) -> None:
|
||||
application = Application(
|
||||
name=getenv("APP_NAME", ""),
|
||||
env=getenv("APP_ENV", ""),
|
||||
)
|
||||
|
||||
rabbitmq = RabbitMQ(
|
||||
host=getenv("RABBITMQ_HOST", "localhost"),
|
||||
port=int(getenv("RABBITMQ_PORT", "5672")),
|
||||
user=getenv("RABBITMQ_USER", "guest"),
|
||||
password=getenv("RABBITMQ_PASS", "guest"),
|
||||
vhost=getenv("RABBITMQ_VHOST", "/")
|
||||
)
|
||||
|
||||
redis = RedisConfig(
|
||||
host=getenv("REDIS_HOST", "localhost"),
|
||||
port=int(getenv("REDIS_PORT", "6379")),
|
||||
db=int(getenv("REDIS_DB", "0"))
|
||||
)
|
||||
|
||||
config = Configuration(application, rabbitmq, redis)
|
||||
|
||||
self.__container.register(Configuration, instance=config)
|
||||
|
||||
def __register_arguments(self) -> None:
|
||||
argument_parser = ListenerArgumentsParser()
|
||||
arguments_parsed = argument_parser.parse_args()
|
||||
|
||||
arguments = ListenerArguments(
|
||||
queue=arguments_parsed.queue,
|
||||
exchange=arguments_parsed.exchange,
|
||||
routing_key=arguments_parsed.routing_key
|
||||
)
|
||||
|
||||
self.__container.register(ListenerArguments, instance=arguments)
|
||||
|
||||
def __register_stop_event(self) -> None:
|
||||
stop_event = StopEvent()
|
||||
|
||||
stop_event_manager = StopEventManager(stop_event)
|
||||
stop_event_manager.setup_stop_signals()
|
||||
|
||||
self.__container.register(StopEvent, instance=stop_event)
|
||||
|
||||
def __register_logger(self) -> None:
|
||||
logger_factory = LoggerFactory()
|
||||
logger = logger_factory.create_stdout(name="rbmq.listener")
|
||||
|
||||
self.__container.register(Logger, instance=logger)
|
||||
|
||||
def __register_rabbitmq_client(self) -> None:
|
||||
config = self.__container.resolve(Configuration).rabbitmq()
|
||||
|
||||
rabbitmq_client = Client(
|
||||
AuthenticationParams(config.user, config.password),
|
||||
ConnectionParams(config.host, config.port, config.vhost),
|
||||
self.__container.resolve(Logger)
|
||||
)
|
||||
|
||||
self.__container.register(Client, instance=rabbitmq_client)
|
||||
|
||||
def __register_redis(self) -> None:
|
||||
config = self.__container.resolve(Configuration).redis()
|
||||
|
||||
client = AsyncRedis(
|
||||
host=config.host,
|
||||
port=config.port,
|
||||
db=config.db,
|
||||
decode_responses=True
|
||||
)
|
||||
|
||||
self.__container.register(AsyncRedis, instance=client)
|
||||
|
||||
def __register_lock_provider(self) -> None:
|
||||
config = self.__container.resolve(Configuration).redis()
|
||||
|
||||
aioredlock = Aioredlock([
|
||||
{
|
||||
"host": config.host,
|
||||
"port": config.port,
|
||||
"db": config.db
|
||||
}
|
||||
])
|
||||
|
||||
self.__container.register(Aioredlock, instance=aioredlock)
|
||||
|
||||
def __register_event_emitter(self) -> None:
|
||||
event_emitter = AsyncIOEventEmitter()
|
||||
self.__container.register(EventEmitter, instance=event_emitter)
|
||||
0
shared/application/service/__init__.py
Normal file
0
shared/application/service/__init__.py
Normal file
29
shared/application/service/environment_loader.py
Normal file
29
shared/application/service/environment_loader.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from dotenv import find_dotenv, load_dotenv
|
||||
|
||||
|
||||
class EnvironmentLoader:
|
||||
__name: str
|
||||
__usecwd: bool
|
||||
__override: bool
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str = ".env",
|
||||
usecwd: bool = True,
|
||||
override: bool = True
|
||||
) -> None:
|
||||
self.__name = name
|
||||
self.__usecwd = usecwd
|
||||
self.__override = override
|
||||
|
||||
def load(self) -> None:
|
||||
dotenv = find_dotenv(
|
||||
filename=self.__name,
|
||||
raise_error_if_not_found=True,
|
||||
usecwd=self.__usecwd
|
||||
)
|
||||
|
||||
load_dotenv(
|
||||
dotenv_path=dotenv,
|
||||
override=self.__override
|
||||
)
|
||||
0
shared/application/view/__init__.py
Normal file
0
shared/application/view/__init__.py
Normal file
8
shared/application/view/listener_arguments.py
Normal file
8
shared/application/view/listener_arguments.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ListenerArguments:
|
||||
queue: str
|
||||
exchange: str
|
||||
routing_key: str
|
||||
0
shared/bus/__init__.py
Normal file
0
shared/bus/__init__.py
Normal file
0
shared/bus/view/__init__.py
Normal file
0
shared/bus/view/__init__.py
Normal file
9
shared/bus/view/message.py
Normal file
9
shared/bus/view/message.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Message:
|
||||
id: str
|
||||
type: str
|
||||
source: str
|
||||
message: str
|
||||
0
shared/event/__init__.py
Normal file
0
shared/event/__init__.py
Normal file
5
shared/event/type.py
Normal file
5
shared/event/type.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class Type(str, Enum):
|
||||
MESSAGE_RECEIVED = "message.received"
|
||||
Reference in New Issue
Block a user