fix: parse real Jimi push format (msgType+data) #8

Merged
kianiadee merged 1 commit from quality-program-2026-04-12 into main 2026-04-21 09:10:21 +00:00
Showing only changes of commit 636dd2b8b0 - Show all commits

View file

@ -109,46 +109,36 @@ async def _parse_request(request: Request) -> tuple[str, list[dict]]:
Some older/configured endpoints may still use form-encoded. This helper
handles both so each endpoint doesn't need to know which format arrived.
"""
content_type = request.headers.get("content-type", "")
body = await request.body()
# TEMP DIAGNOSTIC: log every push so we can see what Jimi actually sends.
log.info("push %s: content-type=%r body=%.300s",
request.url.path, content_type,
body.decode("utf-8", errors="replace") if body else "<empty>")
if not body:
return "", []
# ── Try JSON body first (integration push format) ──────────────────────────
if "application/json" in content_type or body.lstrip()[:1] == b"{":
try:
payload = json.loads(body)
token = str(payload.get("token", ""))
raw_dl = payload.get("data_list", [])
if isinstance(raw_dl, list):
items = raw_dl[:MAX_ITEMS_PER_POST]
elif isinstance(raw_dl, str):
items = _parse_data_list(raw_dl)
else:
items = []
log.info("push: parsed JSON body — %d items", len(items))
return token, items
except (json.JSONDecodeError, TypeError):
log.warning("push: JSON body parse failed")
# ── Fall back to form-encoded ───────────────────────────────────────────────
# Jimi integration push format (observed live):
# Content-Type: application/x-www-form-urlencoded
# Body: msgType=<topic>&data=<URL-encoded JSON object or array>
# The `data` field holds a single JSON object per event, not an array.
try:
form = await request.form()
token = str(form.get("token", ""))
raw_dl = str(form.get("data_list", ""))
items = _parse_data_list(raw_dl) if raw_dl else []
log.info("push: parsed form body — %d items", len(items))
return token, items
except Exception:
log.warning("push: form body parse failed", exc_info=True)
log.warning("push: form parse failed", exc_info=True)
return "", []
return "", []
token = str(form.get("token", ""))
raw_data = form.get("data") or form.get("data_list") or ""
if not raw_data:
return token, []
try:
parsed = json.loads(raw_data)
except (json.JSONDecodeError, TypeError):
log.warning("push: data JSON parse failed — %.200s", raw_data)
return token, []
items = parsed if isinstance(parsed, list) else [parsed]
if len(items) > MAX_ITEMS_PER_POST:
log.warning("push: truncated %d%d items", len(items), MAX_ITEMS_PER_POST)
items = items[:MAX_ITEMS_PER_POST]
return token, items
def unix_to_ts(v) -> Optional[str]:
@ -351,9 +341,11 @@ async def push_alarm(request: Request):
for item in items:
try:
cur.execute("SAVEPOINT sp")
imei = clean(item.get("deviceImei"))
# Jimi integration push uses `imei` + `alarmTime`, NOT the
# `deviceImei` + `gateTime` fields shown in the API docs.
imei = clean(item.get("imei") or item.get("deviceImei"))
alarm_type = clean(item.get("alarmType"))
alarm_time = clean_ts(item.get("gateTime"))
alarm_time = clean_ts(item.get("alarmTime") or item.get("gateTime"))
# [BUG-02] Also guard alarm_type — NULL alarm_type violates NOT NULL constraint.
if not imei or not alarm_time or not alarm_type:
cur.execute("RELEASE SAVEPOINT sp")