93 lines
2.9 KiB
Python
93 lines
2.9 KiB
Python
"""Calendar upstream helpers — HTTP to EMAIL_URL/CALENDAR_URL.
|
|
|
|
Write paths (create / reschedule / cancel) follow the documented OpenAPI contract
|
|
but were not exercised live when this package was written; treat a non-2xx as an
|
|
upstream-contract finding before changing the request shape.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from urllib.parse import quote
|
|
|
|
import httpx
|
|
from dotenv import load_dotenv
|
|
|
|
load_dotenv()
|
|
|
|
CALENDAR_URL=os.getenv("CALENDAR_URL") or os.getenv("EMAIL_URL")
|
|
CALENDAR_API_TOKEN=os.getenv("CALENDAR_API_TOKEN") or os.getenv("EMAIL_API_TOKEN")
|
|
|
|
|
|
def _base_url():
|
|
if not CALENDAR_URL:
|
|
raise RuntimeError("CALENDAR_URL or EMAIL_URL must be set")
|
|
return CALENDAR_URL.rstrip("/")
|
|
|
|
|
|
def _auth_headers(token=None):
|
|
auth_token=token or CALENDAR_API_TOKEN
|
|
if not auth_token:
|
|
raise RuntimeError("CALENDAR_API_TOKEN or EMAIL_API_TOKEN must be set")
|
|
return {"Authorization":f"Bearer {auth_token}"}
|
|
|
|
|
|
async def create_event(payload, token=None):
|
|
"""POST {base}/calendar/events -> created event dict."""
|
|
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
response=await client.post(
|
|
f"{_base_url()}/calendar/events",
|
|
json=payload,
|
|
headers=_auth_headers(token),
|
|
)
|
|
if response.status_code>=400:
|
|
raise httpx.HTTPStatusError(
|
|
response.text,
|
|
request=response.request,
|
|
response=response,
|
|
)
|
|
if not response.content:
|
|
return {}
|
|
return response.json()
|
|
|
|
|
|
async def reschedule_event(event_id, payload, token=None):
|
|
"""PATCH {base}/calendar/events/{id}/reschedule -> updated event dict."""
|
|
encoded_id=quote(str(event_id),safe="")
|
|
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
response=await client.patch(
|
|
f"{_base_url()}/calendar/events/{encoded_id}/reschedule",
|
|
json=payload,
|
|
headers=_auth_headers(token),
|
|
)
|
|
if response.status_code>=400:
|
|
raise httpx.HTTPStatusError(
|
|
response.text,
|
|
request=response.request,
|
|
response=response,
|
|
)
|
|
if not response.content:
|
|
return {}
|
|
return response.json()
|
|
|
|
|
|
async def cancel_event(event_id, comment=None, token=None):
|
|
"""POST {base}/calendar/events/{id}/cancel -> response body or empty dict."""
|
|
encoded_id=quote(str(event_id),safe="")
|
|
body={"comment":comment} if comment is not None else {}
|
|
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
response=await client.post(
|
|
f"{_base_url()}/calendar/events/{encoded_id}/cancel",
|
|
json=body,
|
|
headers=_auth_headers(token),
|
|
)
|
|
if response.status_code>=400:
|
|
raise httpx.HTTPStatusError(
|
|
response.text,
|
|
request=response.request,
|
|
response=response,
|
|
)
|
|
if not response.content:
|
|
return {}
|
|
return response.json()
|