This commit is contained in:
2022-06-24 12:14:10 -07:00
commit 27fc1f5e16
31 changed files with 3769 additions and 0 deletions

View 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()

View 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}")

View 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}")

View 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}")