Add an endpoint for posting to Grafana
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from fastapi import APIRouter, HTTPException, Request, Response
|
from fastapi import APIRouter, HTTPException, Response
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
from sqlmodel import select
|
from sqlmodel import select, Session
|
||||||
from slixmpp.jid import JID
|
from slixmpp.jid import JID
|
||||||
|
|
||||||
from xmpp_api.api.types.v1.bot import (
|
from xmpp_api.api.types.v1.bot import (
|
||||||
@@ -15,15 +15,15 @@ from xmpp_api.api.types.v1.bot import (
|
|||||||
SendMessageRequest,
|
SendMessageRequest,
|
||||||
BotConstraint,
|
BotConstraint,
|
||||||
BotDomainConstraint,
|
BotDomainConstraint,
|
||||||
|
SendMessageGrafanaWebhook,
|
||||||
)
|
)
|
||||||
from xmpp_api.db import BotDep, SessionDep, UserDep
|
from xmpp_api.db import BotDep, SessionDep, UserDep, AuthorizationBotDep
|
||||||
from xmpp_api.db.bot import AllowedJid, Bot, JIDType
|
from xmpp_api.db.bot import AllowedJid, Bot, JIDType
|
||||||
import xmpp_api.db.bot as db_bot
|
import xmpp_api.db.bot as db_bot
|
||||||
from xmpp_api.util.token import generate_token
|
from xmpp_api.util.token import generate_token
|
||||||
from xmpp_api.db import get_bot_by_id, get_jids_by_bot_id, get_jid_by_jid_token
|
from xmpp_api.db import get_bot_by_id, get_jids_by_bot_id, get_jid_by_jid_token
|
||||||
from xmpp_api.util.constraints import bot_constraint_to_db, bot_constraint_from_db
|
from xmpp_api.util.constraints import bot_constraint_to_db, bot_constraint_from_db
|
||||||
from xmpp_api.xmpp.component import XmppApiComponentDep
|
from xmpp_api.xmpp.component import XmppApiComponentDep, XmppApiComponent
|
||||||
|
|
||||||
|
|
||||||
router = APIRouter(prefix="/bot")
|
router = APIRouter(prefix="/bot")
|
||||||
|
|
||||||
@@ -157,11 +157,41 @@ def delete_bot_jid_token(
|
|||||||
def post_bot_message(
|
def post_bot_message(
|
||||||
jid_token: str,
|
jid_token: str,
|
||||||
message: SendMessageRequest,
|
message: SendMessageRequest,
|
||||||
request: Request,
|
|
||||||
bot: BotDep,
|
bot: BotDep,
|
||||||
session: SessionDep,
|
session: SessionDep,
|
||||||
component: XmppApiComponentDep,
|
component: XmppApiComponentDep,
|
||||||
):
|
):
|
||||||
|
_post_message(jid_token, message.body, bot, session, component)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/message/{jid_token}/grafana")
|
||||||
|
def post_bot_message_grafana_webhook(
|
||||||
|
jid_token: str,
|
||||||
|
message: SendMessageGrafanaWebhook,
|
||||||
|
bot: AuthorizationBotDep,
|
||||||
|
session: SessionDep,
|
||||||
|
component: XmppApiComponentDep,
|
||||||
|
):
|
||||||
|
_post_message(jid_token, message.body, bot, session, component)
|
||||||
|
|
||||||
|
|
||||||
|
def _post_message(
|
||||||
|
jid_token: str,
|
||||||
|
body: str,
|
||||||
|
bot: Bot,
|
||||||
|
session: Session,
|
||||||
|
component: XmppApiComponent,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Performs the message sending.
|
||||||
|
|
||||||
|
Args
|
||||||
|
:jid_token The recipient's JID token.
|
||||||
|
:body The body of the message.
|
||||||
|
:bot The bot to send the message as.
|
||||||
|
:session The database session.
|
||||||
|
:component The XMPP component instance.
|
||||||
|
"""
|
||||||
# Is the bot allowed to access this JID?
|
# Is the bot allowed to access this JID?
|
||||||
jid = session.exec(
|
jid = session.exec(
|
||||||
select(AllowedJid).where(
|
select(AllowedJid).where(
|
||||||
@@ -184,7 +214,7 @@ def post_bot_message(
|
|||||||
match jid.type:
|
match jid.type:
|
||||||
case JIDType.DIRECT:
|
case JIDType.DIRECT:
|
||||||
component.send_direct_message(
|
component.send_direct_message(
|
||||||
body=message.body,
|
body=body,
|
||||||
localpart=bot.localpart,
|
localpart=bot.localpart,
|
||||||
nick=bot.name,
|
nick=bot.name,
|
||||||
recipient=jid.jid,
|
recipient=jid.jid,
|
||||||
|
|||||||
@@ -109,6 +109,18 @@ class SendMessageRequest(BaseModel):
|
|||||||
body: str
|
body: str
|
||||||
|
|
||||||
|
|
||||||
|
class SendMessageGrafanaWebhook(BaseModel):
|
||||||
|
"""
|
||||||
|
Model for the request to send a grafana webhook to a JID.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# The alert title
|
||||||
|
title: str
|
||||||
|
|
||||||
|
# The alert message
|
||||||
|
body: str
|
||||||
|
|
||||||
|
|
||||||
class GetBotInformation(BotInformation):
|
class GetBotInformation(BotInformation):
|
||||||
"""
|
"""
|
||||||
Response for an information request about a given bot.
|
Response for an information request about a given bot.
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
from enum import auto, Enum
|
||||||
from typing import Annotated, Generator, Sequence, cast
|
from typing import Annotated, Generator, Sequence, cast
|
||||||
|
|
||||||
from fastapi import Depends, Request, HTTPException
|
from fastapi import Depends, Request, HTTPException
|
||||||
@@ -27,10 +28,35 @@ def get_session(engine: EngineDep) -> Generator[Session]:
|
|||||||
SessionDep = Annotated[Session, Depends(get_session)]
|
SessionDep = Annotated[Session, Depends(get_session)]
|
||||||
|
|
||||||
|
|
||||||
|
class TokenType(Enum):
|
||||||
|
"""
|
||||||
|
Types of authentication tokens.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Token is put into the X-Token header
|
||||||
|
X_TOKEN = auto()
|
||||||
|
|
||||||
|
# Token is put into the "Authorization" header with type "Bearer".
|
||||||
|
BEARER = auto()
|
||||||
|
|
||||||
|
|
||||||
def get_by_token(
|
def get_by_token(
|
||||||
cls: type[SQLModel], request: Request, session: SessionDep
|
cls: type[SQLModel],
|
||||||
|
request: Request,
|
||||||
|
session: SessionDep,
|
||||||
|
token_type: TokenType = TokenType.X_TOKEN,
|
||||||
) -> SQLModel:
|
) -> SQLModel:
|
||||||
token = request.headers.get("X-Token")
|
token: str | None = None
|
||||||
|
match token_type:
|
||||||
|
case TokenType.X_TOKEN:
|
||||||
|
token = request.headers.get("X-Token")
|
||||||
|
case TokenType.BEARER:
|
||||||
|
token = request.headers.get("Authorization")
|
||||||
|
if token is not None:
|
||||||
|
parts = token.split(" ")
|
||||||
|
if len(parts) == 2 and parts[0] == "Bearer":
|
||||||
|
token = parts[1]
|
||||||
|
|
||||||
if token is None:
|
if token is None:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
detail="No token provided",
|
detail="No token provided",
|
||||||
@@ -47,19 +73,28 @@ def get_by_token(
|
|||||||
|
|
||||||
|
|
||||||
def get_user(request: Request, session: SessionDep) -> User:
|
def get_user(request: Request, session: SessionDep) -> User:
|
||||||
return cast(User, get_by_token(User, request, session))
|
return cast(
|
||||||
|
User, get_by_token(User, request, session, token_type=TokenType.X_TOKEN)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
UserDep = Annotated[User, Depends(get_user)]
|
UserDep = Annotated[User, Depends(get_user)]
|
||||||
|
|
||||||
|
|
||||||
def get_bot(request: Request, session: SessionDep) -> Bot:
|
def get_bot(request: Request, session: SessionDep) -> Bot:
|
||||||
return cast(Bot, get_by_token(Bot, request, session))
|
return cast(Bot, get_by_token(Bot, request, session, token_type=TokenType.X_TOKEN))
|
||||||
|
|
||||||
|
|
||||||
BotDep = Annotated[Bot, Depends(get_bot)]
|
BotDep = Annotated[Bot, Depends(get_bot)]
|
||||||
|
|
||||||
|
|
||||||
|
def get_authorization_bot(request: Request, session: SessionDep) -> Bot:
|
||||||
|
return cast(Bot, get_by_token(Bot, request, session, token_type=TokenType.BEARER))
|
||||||
|
|
||||||
|
|
||||||
|
AuthorizationBotDep = Annotated[Bot, Depends(get_authorization_bot)]
|
||||||
|
|
||||||
|
|
||||||
def get_bot_by_id(bot_id: str, user_id: str, session: SessionDep) -> Bot | None:
|
def get_bot_by_id(bot_id: str, user_id: str, session: SessionDep) -> Bot | None:
|
||||||
"""
|
"""
|
||||||
Fetches the specified bot from the database.
|
Fetches the specified bot from the database.
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ def startup():
|
|||||||
if config.sentry is not None:
|
if config.sentry is not None:
|
||||||
logging.getLogger("root").info("Setting up Sentry")
|
logging.getLogger("root").info("Setting up Sentry")
|
||||||
import sentry_sdk
|
import sentry_sdk
|
||||||
|
|
||||||
sentry_sdk.init(
|
sentry_sdk.init(
|
||||||
config.sentry.dsn,
|
config.sentry.dsn,
|
||||||
send_default_pii=True,
|
send_default_pii=True,
|
||||||
|
|||||||
Reference in New Issue
Block a user