Skip to content

Handle contact information updates via OpenSRS API #21

@chicks-net

Description

@chicks-net

Background

We can sync domain contacts from OpenSRS into the local SQLite database, but there's no path to push changes back to the registry. This means macaw is currently read-only with respect to contact information — you can see what's registered but can't fix a typo, update a phone number, or change an address without going directly to the OpenSRS reseller console.

Current State

The existing implementation is half of a round-trip:

  • src/db/contacts.rs has find_or_create_contact() for deduplication, but no update_contact() path
  • src/opensrs/xml.rs deserialize_domain_all_info() initializes contact variables to None at lines 282–285 but never actually populates them from the XML
  • src/sync/mod.rs maps contacts one-way: OpenSRS → local DB

What Needs to Be Built

1. Fix the incomplete contact XML deserializer (src/opensrs/xml.rs)

deserialize_domain_all_info() declares four contact variables that are always None. The parser needs state machine entries to handle contact_set → owner/admin/billing/tech → first_name/last_name/org_name/email/phone/fax/address1/address2/city/state/country/postal_code.

2. Add SET_CONTACT request/response types (src/opensrs/types.rs)

New structs for the OpenSRS CONTACT object SET action. The outbound contact fields use OpenSRS naming (org_name, state, country) which differs from the DB schema (organization, state_province, country_code) — mapping needs to be explicit and documented.

3. XML serialization for contact update requests (src/opensrs/xml.rs)

A serialize_set_contact_request() function following the same string-building pattern used for domain requests. The contact set block nests four contact types each with ~12 fields.

4. Add update_contact() to the DB layer (src/db/contacts.rs)

Currently find_or_create_contact() is the only entry point. We need a function that updates an existing contact record in place. Important: contacts are reusable across multiple domains via domain_contacts. Updating a shared contact changes data for every domain linked to it — callers need to decide whether to update in place or create a new contact record and re-link the domain.

5. New OpenSrsClient::update_domain_contacts() method (src/opensrs/client.rs)

Handles authentication, XML serialization, HTTP dispatch, and response parsing for the SET_CONTACT operation. Should accept a domain name and a ContactSet and return a result.

6. CLI subcommand

A new subcommand (e.g., macaw contacts update <domain>) that reads updated contact info (from a file, flags, or interactive prompt — TBD), calls the OpenSRS client, updates the local DB, and writes an audit log entry.

7. Audit logging

Per the security considerations in CLAUDE.md, all contact changes must write to audit_log with the old and new values as JSON snapshots.

Tricky Bits

Field name mapping between OpenSRS and the local schema:

OpenSRS field DB column
org_name organization
state state_province
country country_code

Contact deduplication and shared contacts: The find_or_create_contact() function does exact-match deduplication, so the same contact record may be linked to multiple domains. An update workflow needs to handle this — probably by creating a new contact record and updating the domain_contacts link for the specific domain being changed, rather than mutating a shared record.

Acceptance Criteria

  • deserialize_domain_all_info() correctly populates contact fields from OpenSRS XML
  • SET_CONTACT XML request serialization produces valid OpenSRS XML
  • Successful round-trip test: read contacts from OpenSRS, modify a field, push back, re-read and verify
  • audit_log has entries for every contact change with old/new JSON snapshots
  • Contact deduplication behavior on update is documented and tested
  • Field name mapping between OpenSRS and DB schema is centralized (not scattered across call sites)

Related

  • src/db/contacts.rs — current read-only contact persistence
  • src/opensrs/xml.rs lines 282–285 — stub contact variables waiting to be wired up
  • src/sync/mod.rs — one-way sync that will need a complement for outbound changes
  • OpenSRS XCP API docs, CONTACT object, SET action

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions