chore: whereby & s3 settings env error reporting (#637)

Co-authored-by: Igor Loskutov <igor.loskutoff@gmail.com>
This commit is contained in:
Igor Monadical
2025-09-11 17:52:34 -04:00
committed by GitHub
parent 369ecdff13
commit b3a8e9739d
3 changed files with 63 additions and 13 deletions

View File

@@ -1,6 +1,8 @@
from pydantic.types import PositiveInt from pydantic.types import PositiveInt
from pydantic_settings import BaseSettings, SettingsConfigDict from pydantic_settings import BaseSettings, SettingsConfigDict
from reflector.utils.string import NonEmptyString
class Settings(BaseSettings): class Settings(BaseSettings):
model_config = SettingsConfigDict( model_config = SettingsConfigDict(
@@ -120,7 +122,7 @@ class Settings(BaseSettings):
# Whereby integration # Whereby integration
WHEREBY_API_URL: str = "https://api.whereby.dev/v1" WHEREBY_API_URL: str = "https://api.whereby.dev/v1"
WHEREBY_API_KEY: str | None = None WHEREBY_API_KEY: NonEmptyString | None = None
WHEREBY_WEBHOOK_SECRET: str | None = None WHEREBY_WEBHOOK_SECRET: str | None = None
AWS_WHEREBY_ACCESS_KEY_ID: str | None = None AWS_WHEREBY_ACCESS_KEY_ID: str | None = None
AWS_WHEREBY_ACCESS_KEY_SECRET: str | None = None AWS_WHEREBY_ACCESS_KEY_SECRET: str | None = None

View File

@@ -10,8 +10,11 @@ NonEmptyString = Annotated[
non_empty_string_adapter = TypeAdapter(NonEmptyString) non_empty_string_adapter = TypeAdapter(NonEmptyString)
def parse_non_empty_string(s: str) -> NonEmptyString: def parse_non_empty_string(s: str, error: str | None = None) -> NonEmptyString:
return non_empty_string_adapter.validate_python(s) try:
return non_empty_string_adapter.validate_python(s)
except Exception as e:
raise ValueError(f"{e}: {error}" if error else e) from e
def try_parse_non_empty_string(s: str) -> NonEmptyString | None: def try_parse_non_empty_string(s: str) -> NonEmptyString | None:

View File

@@ -1,18 +1,60 @@
import logging
from datetime import datetime from datetime import datetime
import httpx import httpx
from reflector.db.rooms import Room from reflector.db.rooms import Room
from reflector.settings import settings from reflector.settings import settings
from reflector.utils.string import parse_non_empty_string
logger = logging.getLogger(__name__)
def _get_headers():
api_key = parse_non_empty_string(
settings.WHEREBY_API_KEY, "WHEREBY_API_KEY value is required."
)
return {
"Content-Type": "application/json; charset=utf-8",
"Authorization": f"Bearer {api_key}",
}
HEADERS = {
"Content-Type": "application/json; charset=utf-8",
"Authorization": f"Bearer {settings.WHEREBY_API_KEY}",
}
TIMEOUT = 10 # seconds TIMEOUT = 10 # seconds
def _get_whereby_s3_auth():
errors = []
try:
bucket_name = parse_non_empty_string(
settings.RECORDING_STORAGE_AWS_BUCKET_NAME,
"RECORDING_STORAGE_AWS_BUCKET_NAME value is required.",
)
except Exception as e:
errors.append(e)
try:
key_id = parse_non_empty_string(
settings.AWS_WHEREBY_ACCESS_KEY_ID,
"AWS_WHEREBY_ACCESS_KEY_ID value is required.",
)
except Exception as e:
errors.append(e)
try:
key_secret = parse_non_empty_string(
settings.AWS_WHEREBY_ACCESS_KEY_SECRET,
"AWS_WHEREBY_ACCESS_KEY_SECRET value is required.",
)
except Exception as e:
errors.append(e)
if len(errors) > 0:
raise Exception(
f"Failed to get Whereby auth settings: {', '.join(str(e) for e in errors)}"
)
return bucket_name, key_id, key_secret
async def create_meeting(room_name_prefix: str, end_date: datetime, room: Room): async def create_meeting(room_name_prefix: str, end_date: datetime, room: Room):
s3_bucket_name, s3_key_id, s3_key_secret = _get_whereby_s3_auth()
data = { data = {
"isLocked": room.is_locked, "isLocked": room.is_locked,
"roomNamePrefix": room_name_prefix, "roomNamePrefix": room_name_prefix,
@@ -23,23 +65,26 @@ async def create_meeting(room_name_prefix: str, end_date: datetime, room: Room):
"type": room.recording_type, "type": room.recording_type,
"destination": { "destination": {
"provider": "s3", "provider": "s3",
"bucket": settings.RECORDING_STORAGE_AWS_BUCKET_NAME, "bucket": s3_bucket_name,
"accessKeyId": settings.AWS_WHEREBY_ACCESS_KEY_ID, "accessKeyId": s3_key_id,
"accessKeySecret": settings.AWS_WHEREBY_ACCESS_KEY_SECRET, "accessKeySecret": s3_key_secret,
"fileFormat": "mp4", "fileFormat": "mp4",
}, },
"startTrigger": room.recording_trigger, "startTrigger": room.recording_trigger,
}, },
"fields": ["hostRoomUrl"], "fields": ["hostRoomUrl"],
} }
async with httpx.AsyncClient() as client: async with httpx.AsyncClient() as client:
response = await client.post( response = await client.post(
f"{settings.WHEREBY_API_URL}/meetings", f"{settings.WHEREBY_API_URL}/meetings",
headers=HEADERS, headers=_get_headers(),
json=data, json=data,
timeout=TIMEOUT, timeout=TIMEOUT,
) )
if response.status_code == 403:
logger.warning(
f"Failed to create meeting: access denied on Whereby: {response.text}"
)
response.raise_for_status() response.raise_for_status()
return response.json() return response.json()
@@ -48,7 +93,7 @@ async def get_room_sessions(room_name: str):
async with httpx.AsyncClient() as client: async with httpx.AsyncClient() as client:
response = await client.get( response = await client.get(
f"{settings.WHEREBY_API_URL}/insights/room-sessions?roomName={room_name}", f"{settings.WHEREBY_API_URL}/insights/room-sessions?roomName={room_name}",
headers=HEADERS, headers=_get_headers(),
timeout=TIMEOUT, timeout=TIMEOUT,
) )
response.raise_for_status() response.raise_for_status()