🚧 WIP
This commit is contained in:
+8
-5
@@ -109,7 +109,7 @@ def main() -> None:
|
||||
topic = message.topic
|
||||
with imei_lock:
|
||||
current_map = imei_map
|
||||
result = process_message(
|
||||
results = process_message(
|
||||
topic,
|
||||
bytes(message.payload),
|
||||
current_map,
|
||||
@@ -117,15 +117,18 @@ def main() -> None:
|
||||
device_key=device_key,
|
||||
time_keys=time_keys,
|
||||
)
|
||||
if result is None:
|
||||
if not results:
|
||||
logger.warning(
|
||||
"skipping message on %r: payload is not a JSON object or last topic "
|
||||
"skipping message on %r: no JSON object(s) in payload or last topic "
|
||||
"segment is not a 15-digit IMEI",
|
||||
topic,
|
||||
)
|
||||
return
|
||||
inserter.insert(topic, result.imei, result.device, result.payload, result.payload_time)
|
||||
logger.info("stored %s/%s -> %s", result.imei, result.device, topic)
|
||||
for result in results:
|
||||
inserter.insert(
|
||||
topic, result.imei, result.device, result.payload, result.payload_time
|
||||
)
|
||||
logger.info("stored %s/%s -> %s", result.imei, result.device, topic)
|
||||
except Exception:
|
||||
logger.exception("message handling failed for topic %s", getattr(message, "topic", "?"))
|
||||
|
||||
|
||||
+43
-26
@@ -97,16 +97,20 @@ def process_message(
|
||||
imei_key: str = "imei",
|
||||
device_key: str = "device",
|
||||
time_keys: tuple[str, ...] = DEFAULT_TIME_KEYS,
|
||||
) -> InjectionResult | None:
|
||||
) -> list[InjectionResult]:
|
||||
"""Parse a payload and inject IMEI + device into it, extracting the
|
||||
device timestamp.
|
||||
|
||||
Returns None when the payload is not valid JSON or not a JSON object —
|
||||
those messages are skipped (logged by the caller). The original payload's
|
||||
own imei/device fields are overwritten by the values derived from the
|
||||
topic, so the topic is always authoritative.
|
||||
The payload may be a single JSON object or a JSON array of objects:
|
||||
every object yields one result (the caller stores one database row per
|
||||
result). Returns an empty list when the payload is not valid JSON,
|
||||
contains no JSON object at all (a scalar, or an array without objects)
|
||||
or the last topic segment is not a 15-digit IMEI — those messages are
|
||||
skipped (logged by the caller). The payload's own imei/device fields
|
||||
are overwritten by the values derived from the topic, so the topic is
|
||||
always authoritative.
|
||||
|
||||
A missing or unparseable timestamp does NOT skip the message: the
|
||||
A missing or unparseable timestamp does NOT skip the object: its
|
||||
payload_time is simply None and only received_at (server receipt time)
|
||||
exists for the row.
|
||||
"""
|
||||
@@ -116,29 +120,42 @@ def process_message(
|
||||
|
||||
normalized = normalize_imei(raw_imei)
|
||||
if normalized is None:
|
||||
return None
|
||||
return []
|
||||
|
||||
try:
|
||||
payload = json.loads(payload_bytes.decode("utf-8"))
|
||||
parsed = json.loads(payload_bytes.decode("utf-8"))
|
||||
except (UnicodeDecodeError, json.JSONDecodeError):
|
||||
return None
|
||||
if not isinstance(payload, dict):
|
||||
return None
|
||||
return []
|
||||
|
||||
payload = dict(payload) # do not mutate shared objects
|
||||
payload[imei_key] = normalized
|
||||
payload[device_key] = imei_map.get(normalized, normalized)
|
||||
if isinstance(parsed, dict):
|
||||
items: list[dict] = [parsed]
|
||||
elif isinstance(parsed, list):
|
||||
# Arrays may mix objects with non-objects; only the objects are
|
||||
# messages. Nested arrays are NOT flattened.
|
||||
items = [item for item in parsed if isinstance(item, dict)]
|
||||
else:
|
||||
return []
|
||||
|
||||
payload_time = None
|
||||
for key in time_keys:
|
||||
if key in payload:
|
||||
payload_time = parse_payload_time(payload[key])
|
||||
if payload_time is not None:
|
||||
break
|
||||
device = imei_map.get(normalized, normalized)
|
||||
results: list[InjectionResult] = []
|
||||
for item in items:
|
||||
payload = dict(item) # do not mutate shared objects
|
||||
payload[imei_key] = normalized
|
||||
payload[device_key] = device
|
||||
|
||||
return InjectionResult(
|
||||
imei=normalized,
|
||||
device=payload[device_key],
|
||||
payload=payload,
|
||||
payload_time=payload_time,
|
||||
)
|
||||
payload_time = None
|
||||
for key in time_keys:
|
||||
if key in payload:
|
||||
payload_time = parse_payload_time(payload[key])
|
||||
if payload_time is not None:
|
||||
break
|
||||
|
||||
results.append(
|
||||
InjectionResult(
|
||||
imei=normalized,
|
||||
device=payload[device_key],
|
||||
payload=payload,
|
||||
payload_time=payload_time,
|
||||
)
|
||||
)
|
||||
return results
|
||||
Reference in New Issue
Block a user