Init
This commit is contained in:
103
barcode_server/notifier/__init__.py
Normal file
103
barcode_server/notifier/__init__.py
Normal file
@ -0,0 +1,103 @@
|
||||
import asyncio
|
||||
import logging
|
||||
from asyncio import Task, QueueEmpty
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from barcode_server.barcode import BarcodeEvent
|
||||
from barcode_server.config import AppConfig
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BarcodeNotifier:
|
||||
"""
|
||||
Base class for a notifier.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.config = AppConfig()
|
||||
self.drop_event_queue_after = self.config.DROP_EVENT_QUEUE_AFTER.value
|
||||
self.retry_interval = self.config.RETRY_INTERVAL.value
|
||||
self.event_queue = asyncio.Queue()
|
||||
self.processor_task: Optional[Task] = None
|
||||
|
||||
def is_running(self) -> bool:
|
||||
return self.processor_task is not None
|
||||
|
||||
async def start(self):
|
||||
"""
|
||||
Starts the event processor of this notifier
|
||||
"""
|
||||
self.processor_task = asyncio.create_task(self.event_processor())
|
||||
|
||||
async def stop(self):
|
||||
"""
|
||||
Stops the event processor of this notifier
|
||||
"""
|
||||
if self.processor_task is None:
|
||||
return
|
||||
|
||||
self.processor_task.cancel()
|
||||
self.processor_task = None
|
||||
|
||||
async def drop_queue(self):
|
||||
"""
|
||||
Drops all items in the event queue
|
||||
"""
|
||||
running = self.is_running()
|
||||
# stop if currently running
|
||||
if running:
|
||||
await self.stop()
|
||||
|
||||
# mark all items as finished
|
||||
for _ in range(self.event_queue.qsize()):
|
||||
try:
|
||||
self.event_queue.get_nowait()
|
||||
self.event_queue.task_done()
|
||||
except QueueEmpty as ex:
|
||||
break
|
||||
|
||||
# restart if it was running
|
||||
if running:
|
||||
await self.start()
|
||||
|
||||
async def event_processor(self):
|
||||
"""
|
||||
Processes the event queue
|
||||
"""
|
||||
while True:
|
||||
try:
|
||||
event = await self.event_queue.get()
|
||||
|
||||
success = False
|
||||
while not success:
|
||||
if datetime.now() - event.date >= self.drop_event_queue_after:
|
||||
# event is older than threshold, so we just skip it
|
||||
self.event_queue.task_done()
|
||||
break
|
||||
|
||||
try:
|
||||
await self._send_event(event)
|
||||
success = True
|
||||
self.event_queue.task_done()
|
||||
except Exception as ex:
|
||||
LOGGER.exception(ex)
|
||||
await asyncio.sleep(self.retry_interval.total_seconds())
|
||||
|
||||
except Exception as ex:
|
||||
LOGGER.exception(ex)
|
||||
|
||||
async def add_event(self, event: BarcodeEvent):
|
||||
"""
|
||||
Adds an event to the event queue
|
||||
"""
|
||||
await self.event_queue.put(event)
|
||||
|
||||
async def _send_event(self, event: BarcodeEvent):
|
||||
"""
|
||||
Sends the given event to the notification target
|
||||
:param event: barcode event
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
30
barcode_server/notifier/http.py
Normal file
30
barcode_server/notifier/http.py
Normal file
@ -0,0 +1,30 @@
|
||||
import logging
|
||||
from typing import List
|
||||
|
||||
import aiohttp
|
||||
from prometheus_async.aio import time
|
||||
|
||||
from barcode_server.barcode import BarcodeEvent
|
||||
from barcode_server.notifier import BarcodeNotifier
|
||||
from barcode_server.stats import HTTP_NOTIFIER_TIME
|
||||
from barcode_server.util import barcode_event_to_json
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HttpNotifier(BarcodeNotifier):
|
||||
|
||||
def __init__(self, method: str, url: str, headers: List[str]):
|
||||
super().__init__()
|
||||
self.method = method
|
||||
self.url = url
|
||||
headers = list(map(lambda x: tuple(x.split(':', 1)), headers))
|
||||
self.headers = list(map(lambda x: (x[0].strip(), x[1].strip()), headers))
|
||||
|
||||
@time(HTTP_NOTIFIER_TIME)
|
||||
async def _send_event(self, event: BarcodeEvent):
|
||||
json = barcode_event_to_json(self.config.INSTANCE_ID.value, event)
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.request(self.method, self.url, headers=self.headers, data=json) as resp:
|
||||
resp.raise_for_status()
|
||||
LOGGER.debug(f"Notified {self.url}: {event.barcode}")
|
38
barcode_server/notifier/mqtt.py
Normal file
38
barcode_server/notifier/mqtt.py
Normal file
@ -0,0 +1,38 @@
|
||||
import logging
|
||||
|
||||
from asyncio_mqtt import Client
|
||||
from prometheus_async.aio import time
|
||||
|
||||
from barcode_server.barcode import BarcodeEvent
|
||||
from barcode_server.notifier import BarcodeNotifier
|
||||
from barcode_server.stats import MQTT_NOTIFIER_TIME
|
||||
from barcode_server.util import barcode_event_to_json
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MQTTNotifier(BarcodeNotifier):
|
||||
|
||||
def __init__(self, host: str, port: int = 1883,
|
||||
topic: str = "/barcode-server/barcode",
|
||||
client_id: str = "barcode-server",
|
||||
user: str = None, password: str = None,
|
||||
qos: int = 2, retain: bool = False):
|
||||
super().__init__()
|
||||
self.client_id = client_id
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.user = user
|
||||
self.password = password
|
||||
self.topic = topic
|
||||
self.qos = qos
|
||||
self.retain = retain
|
||||
|
||||
@time(MQTT_NOTIFIER_TIME)
|
||||
async def _send_event(self, event: BarcodeEvent):
|
||||
json = barcode_event_to_json(self.config.INSTANCE_ID.value, event)
|
||||
async with Client(hostname=self.host, port=self.port,
|
||||
username=self.user, password=self.password,
|
||||
client_id=self.client_id) as client:
|
||||
await client.publish(self.topic, json, self.qos, self.retain)
|
||||
LOGGER.debug(f"Notified {self.host}:{self.port}: {event.barcode}")
|
23
barcode_server/notifier/ws.py
Normal file
23
barcode_server/notifier/ws.py
Normal file
@ -0,0 +1,23 @@
|
||||
from prometheus_async.aio import time
|
||||
|
||||
from barcode_server.barcode import BarcodeEvent
|
||||
from barcode_server.notifier import BarcodeNotifier
|
||||
from barcode_server.stats import WEBSOCKET_NOTIFIER_TIME
|
||||
from barcode_server.util import barcode_event_to_json
|
||||
|
||||
|
||||
class WebsocketNotifier(BarcodeNotifier):
|
||||
|
||||
def __init__(self, websocket):
|
||||
super().__init__()
|
||||
self.websocket = websocket
|
||||
|
||||
@time(WEBSOCKET_NOTIFIER_TIME)
|
||||
async def _send_event(self, event: BarcodeEvent):
|
||||
json = barcode_event_to_json(self.config.INSTANCE_ID.value, event)
|
||||
await self.websocket.send_bytes(json)
|
||||
|
||||
# TODO: cant log websocket address here because we don't have access
|
||||
# to an unique identifier anymore, maybe we need to store one manually
|
||||
# when the websocket is connected initially...
|
||||
# LOGGER.debug(f"Notified {client.remote_address}")
|
Reference in New Issue
Block a user