feat: add event watcher
This commit is contained in:
@@ -2,7 +2,7 @@ from datetime import datetime
|
||||
from enum import StrEnum
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import BigInteger, Column, DateTime, LargeBinary, func
|
||||
from sqlalchemy import BigInteger, Column, DateTime, LargeBinary, Text, func
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
@@ -471,6 +471,7 @@ class Alert(SQLModel, table=True):
|
||||
default_factory=dict, sa_column=Column(JSONB, nullable=False)
|
||||
)
|
||||
seen: bool = False
|
||||
dedup_key: str | None = Field(default=None, sa_column=Column(Text, nullable=True))
|
||||
created_at: datetime = Field(
|
||||
sa_column=Column(
|
||||
DateTime(timezone=True), nullable=False, server_default=func.now()
|
||||
|
||||
@@ -6,6 +6,9 @@ import asyncpg
|
||||
|
||||
from utils.read.models import AlertView, Page, WatchView
|
||||
|
||||
WATCHES_CHANGED_CHANNEL = "watches_changed"
|
||||
ALERTS_CHANGED_CHANNEL = "alerts_changed"
|
||||
|
||||
_WATCH_COLS = "id, account_id, kind, params, enabled, created_at, updated_at"
|
||||
_ALERT_COLS = "id, account_id, watch_id, ts, payload, seen, created_at"
|
||||
|
||||
@@ -55,6 +58,7 @@ async def create_watch(
|
||||
json.dumps(params),
|
||||
enabled,
|
||||
)
|
||||
await pool.execute(f"NOTIFY {WATCHES_CHANGED_CHANNEL}")
|
||||
return _to_watch(row)
|
||||
|
||||
|
||||
@@ -68,12 +72,18 @@ async def update_watch(
|
||||
json.dumps(params),
|
||||
enabled,
|
||||
)
|
||||
return _to_watch(row) if row else None
|
||||
if row is None:
|
||||
return None
|
||||
await pool.execute(f"NOTIFY {WATCHES_CHANGED_CHANNEL}")
|
||||
return _to_watch(row)
|
||||
|
||||
|
||||
async def delete_watch(pool: asyncpg.Pool, watch_id: int) -> bool:
|
||||
result = await pool.execute("DELETE FROM watches WHERE id = $1", watch_id)
|
||||
return result.endswith("1")
|
||||
if not result.endswith("1"):
|
||||
return False
|
||||
await pool.execute(f"NOTIFY {WATCHES_CHANGED_CHANNEL}")
|
||||
return True
|
||||
|
||||
|
||||
async def list_alerts(
|
||||
@@ -95,16 +105,27 @@ async def list_alerts(
|
||||
|
||||
|
||||
async def insert_alert(
|
||||
pool: asyncpg.Pool, account_id: int, watch_id: int, payload: dict[str, Any]
|
||||
) -> AlertView:
|
||||
pool: asyncpg.Pool,
|
||||
account_id: int,
|
||||
watch_id: int,
|
||||
payload: dict[str, Any],
|
||||
*,
|
||||
dedup_key: str | None = None,
|
||||
) -> AlertView | None:
|
||||
row = await pool.fetchrow(
|
||||
"INSERT INTO alerts (account_id, watch_id, ts, payload) " # noqa: S608
|
||||
f"VALUES ($1, $2, $3, $4::jsonb) RETURNING {_ALERT_COLS}",
|
||||
"INSERT INTO alerts (account_id, watch_id, ts, payload, seen, dedup_key) " # noqa: S608
|
||||
"VALUES ($1, $2, $3, $4::jsonb, false, $5) "
|
||||
"ON CONFLICT (watch_id, dedup_key) WHERE dedup_key IS NOT NULL "
|
||||
f"DO NOTHING RETURNING {_ALERT_COLS}",
|
||||
account_id,
|
||||
watch_id,
|
||||
datetime.now(UTC),
|
||||
json.dumps(payload),
|
||||
dedup_key,
|
||||
)
|
||||
if row is None:
|
||||
return None
|
||||
await pool.execute(f"NOTIFY {ALERTS_CHANGED_CHANNEL}")
|
||||
return _to_alert(row)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user