123 lines
2.9 KiB
Python
123 lines
2.9 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
from dataclasses import asdict, dataclass
|
|
from typing import Any, Callable
|
|
|
|
from .device_id import is_android_device_id, is_valid_egid
|
|
from .device_profile import DeviceProfile
|
|
from .dfp_forms import DfpRequestSpec
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class BootstrapIdentity:
|
|
did: str = ""
|
|
cdid_tag: int = 0
|
|
egid: str = ""
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
return asdict(self)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class DfpHttpResponse:
|
|
status_code: int
|
|
ok: bool
|
|
data: Any
|
|
text: str
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
return asdict(self)
|
|
|
|
|
|
def _as_mapping(value: Any) -> dict[str, Any]:
|
|
return value if isinstance(value, dict) else {}
|
|
|
|
|
|
def _int_tag(value: Any) -> int:
|
|
try:
|
|
return int(value)
|
|
except (TypeError, ValueError):
|
|
return 0
|
|
|
|
|
|
def extract_bootstrap_identity(
|
|
fetch_response: dict[str, Any] | None,
|
|
report_response: dict[str, Any] | None,
|
|
) -> BootstrapIdentity:
|
|
fetch = _as_mapping(fetch_response)
|
|
report = _as_mapping(report_response)
|
|
|
|
did = str(fetch.get("cloud_did") or fetch.get("did") or "")
|
|
if not is_android_device_id(did):
|
|
did = ""
|
|
|
|
cdid_tag = _int_tag(fetch.get("did_tag") or fetch.get("didTag") or fetch.get("cdid_tag"))
|
|
|
|
egid = str(report.get("egid") or fetch.get("egid") or "")
|
|
if egid and not is_valid_egid(egid):
|
|
egid = ""
|
|
|
|
return BootstrapIdentity(did=did, cdid_tag=cdid_tag, egid=egid)
|
|
|
|
|
|
def apply_bootstrap_identity(profile: DeviceProfile, identity: BootstrapIdentity) -> None:
|
|
if identity.did:
|
|
profile.apply_cloud_identity(identity.did, identity.cdid_tag, identity.egid)
|
|
return
|
|
if identity.egid:
|
|
profile.egid = identity.egid
|
|
profile.sync_egid_cache()
|
|
profile.validate()
|
|
|
|
|
|
def post_request(
|
|
request: DfpRequestSpec,
|
|
*,
|
|
timeout: int = 20,
|
|
post_func: Callable[..., Any] | None = None,
|
|
) -> DfpHttpResponse:
|
|
if post_func is None:
|
|
import requests
|
|
|
|
post_func = requests.post
|
|
|
|
try:
|
|
response = post_func(
|
|
request.url,
|
|
data=request.body.encode("utf-8"),
|
|
headers=request.headers,
|
|
timeout=timeout,
|
|
)
|
|
except Exception as exc:
|
|
text = str(exc)
|
|
return DfpHttpResponse(
|
|
status_code=0,
|
|
ok=False,
|
|
data={"error_type": exc.__class__.__name__, "error": text},
|
|
text=text,
|
|
)
|
|
text = getattr(response, "text", "")
|
|
try:
|
|
data = response.json()
|
|
except Exception:
|
|
try:
|
|
data = json.loads(text)
|
|
except Exception:
|
|
data = text
|
|
return DfpHttpResponse(
|
|
status_code=int(getattr(response, "status_code", 0)),
|
|
ok=bool(getattr(response, "ok", False)),
|
|
data=data,
|
|
text=text,
|
|
)
|
|
|
|
|
|
__all__ = [
|
|
"BootstrapIdentity",
|
|
"DfpHttpResponse",
|
|
"apply_bootstrap_identity",
|
|
"extract_bootstrap_identity",
|
|
"post_request",
|
|
]
|