packetdial: add onboard-domain wrapper (GUI Add-a-Domain -> 3-call API flow)

onboard-domain runs POST /domains -> addresses/validate (gen E911 pidflo) -> addresses/create
from one JSON body (domain fields + optional `emergency` block), gated --confirm. Reverse-
engineered from the OITVOIP wizard screenshots; live-created the real client domain
vwp.91912.service (Valley Wide Plastering) + E911 address, and proved the wrapper with a
throwaway create->delete (no leftovers, vwp intact). Documented GUI->API mapping + the two
manual gaps (voicemail user-defaults, email-send-from-address pending the packetdial.com mailbox)
+ the domain-type "no"-on-create quirk.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-22 09:34:48 -07:00
parent 4157fc6f1d
commit d1d1302d55
3 changed files with 89 additions and 0 deletions

View File

@@ -565,3 +565,36 @@ class NetSapiensClient:
return self.request(
"GET", f"domains/{urllib.parse.quote(domain)}/transcriptions", params=filters or None
)
# ======================================================================
# ORCHESTRATION — onboard a new client domain (mirrors the GUI "Add a Domain"
# wizard: Basic + Defaults + Limitations -> POST /domains; Emergency tab ->
# validate (generates the pidflo) -> create E911 address). Live-proven against
# vwp.91912.service 2026-06-22.
# ======================================================================
def onboard_domain(self, body: dict) -> dict:
"""Create a domain and (optionally) its E911 address in one flow.
body = the POST /domains fields PLUS an optional "emergency" sub-object
(the address fields). Returns {"domain":..., "address_validate":...,
"address":...}. Requires body["domain"] (full, e.g. vwp.91912.service).
"""
body = dict(body)
emergency = body.pop("emergency", None)
body.setdefault("synchronous", "yes")
domain = body.get("domain")
if not domain:
raise PacketDialError("onboard_domain: body must include 'domain' (e.g. acme.91912.service)")
result = {"domain": self.create_domain(body)}
if emergency:
d = urllib.parse.quote(domain)
v = self.request("POST", f"domains/{d}/addresses/validate", json_body=dict(emergency))
vobj = v[0] if isinstance(v, list) and v else v
addr = dict(emergency)
addr["address-formatted-pidflo"] = vobj.get("address-formatted-pidflo")
addr["emergency-address-id"] = vobj.get("emergency-address-id")
result["address_validate"] = {"status": (vobj or {}).get("ValidationStatus")
or (vobj.get("address-formatted-pidflo") or {}).get("ValidationStatus")
if isinstance(vobj, dict) else None}
result["address"] = self.create_address(domain, addr)
return result