Skip to content

fix(hoverclient): read NS from /api/domains (not PUT-only control_panel GET) — v0.5.4#36

Merged
intel352 merged 1 commit into
mainfrom
fix/hover-delegation-read-endpoint
Jun 5, 2026
Merged

fix(hoverclient): read NS from /api/domains (not PUT-only control_panel GET) — v0.5.4#36
intel352 merged 1 commit into
mainfrom
fix/hover-delegation-read-endpoint

Conversation

@intel352
Copy link
Copy Markdown
Contributor

@intel352 intel352 commented Jun 5, 2026

The delegation import 404'd on every domain: GetDomainDelegation did GET /api/control_panel/domains/domain-<name>, but that path is PUT-only (field updates). Root-caused live against Hover's API.

Fix: nameservers are already in the GET /api/domains list response (one call, all domains). ListDomains now parses + caches them; GetDomainDelegation reads from the cache (so the delegation import is ~1 Hover call, not 30 — which also avoids the Imperva 429 fan-out), with GET /api/domains/<name> as the per-domain fallback. SetNameservers (PUT to control_panel) unchanged — it was correct. plugin.json → 0.5.4. go test green.

Last piece for the both-layers catalog: release v0.5.4 → re-pin → re-import.

🤖 Generated with Claude Code

…y control_panel GET + v0.5.4

getDomainDelegationHTTP was hitting GET /api/control_panel/domains/domain-<name>
which is PUT-only on live Hover (returns 404). Fix: populate a domainNS cache in
listDomainsHTTP from the nameservers field already present in GET /api/domains;
getDomainDelegationHTTP checks the cache first, falls back to GET /api/domains/<name>
(wrapped envelope). Eliminates per-domain fan-out and the 429s it caused.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 5, 2026 06:14
@intel352 intel352 merged commit 454842b into main Jun 5, 2026
5 checks passed
@intel352 intel352 deleted the fix/hover-delegation-read-endpoint branch June 5, 2026 06:16
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes Hover delegation reads by sourcing nameservers from the correct read endpoints and reducing API fan-out, addressing 404s (PUT-only control_panel path) and helping avoid rate limiting during imports.

Changes:

  • Add nameservers parsing to GET /api/domains, cache them, and have GetDomainDelegation read from the cache first.
  • Add per-domain fallback read via GET /api/domains/<name> and update JSON decoding accordingly.
  • Bump plugin version to v0.5.4 and extend tests to cover parsing/caching/fallback behavior.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
plugin.json Bumps plugin version to 0.5.4.
cmd/workflow-plugin-hover/plugin.json Bumps embedded plugin version to 0.5.4.
pkg/hoverclient/client.go Implements NS caching from /api/domains and updates delegation reads to use cache + per-domain fallback.
pkg/hoverclient/client_test.go Updates existing delegation tests and adds coverage for NS parsing + cache behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pkg/hoverclient/client.go
Comment on lines +436 to +447
c.mu.Lock()
cached, ok := c.domainNS[domainName]
if ok {
ns := make([]string, len(cached))
copy(ns, cached)
c.mu.Unlock()
if len(ns) == 0 {
return nil, fmt.Errorf("hover: GetDomainDelegation %q: %w", domainName, ErrEmptyNameservers)
}
return &DomainDelegation{Name: domainName, Nameservers: ns}, nil
}
c.mu.Unlock()
Comment thread pkg/hoverclient/client.go
Comment on lines +463 to 472
// The per-domain endpoint wraps the domain in {"succeeded":...,"domain":{...}}.
var wrap struct {
Succeeded bool `json:"succeeded"`
Domain Domain `json:"domain"`
}
if err := json.NewDecoder(resp.Body).Decode(&wrap); err != nil {
return nil, fmt.Errorf("hover: GetDomainDelegation %q: decode: %w", domainName, err)
}
if len(d.Nameservers) == 0 {
if len(wrap.Domain.Nameservers) == 0 {
return nil, fmt.Errorf("hover: GetDomainDelegation %q: %w", domainName, ErrEmptyNameservers)
Comment thread pkg/hoverclient/client.go
Comment on lines +597 to +609
c.mu.Lock()
if c.domainNS == nil {
c.domainNS = make(map[string][]string, len(body.Domains))
}
for _, d := range body.Domains {
if d.Name == "" {
continue
}
ns := make([]string, len(d.Nameservers))
copy(ns, d.Nameservers)
c.domainNS[d.Name] = ns
}
c.mu.Unlock()
Comment on lines +878 to +893
mux := http.NewServeMux()
mux.HandleFunc("/signin/auth.json", func(w http.ResponseWriter, _ *http.Request) {
_ = json.NewEncoder(w).Encode(map[string]any{"status": "completed"})
})
mux.HandleFunc("/api/domains", func(w http.ResponseWriter, r *http.Request) {
// Return a.com with ns1.x — only exact /api/domains (not /api/domains/*)
if r.URL.Path != "/api/domains" {
// This is a per-domain fallback hit — count it.
perDomainHits++
http.Error(w, "should not be called", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
_, _ = io.WriteString(w, `{"succeeded":true,"domains":[{"id":"dom1","domain_name":"a.com","nameservers":["ns1.x"]}]}`)
})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants