Validate domain resource and extend DNS record typed fields

This commit is contained in:
beatz174-bit committed 2026-05-04 00:48:54 +10:00
1 parent 6badcf35f4
commit e59ad5a65b
9 files changed
+599 -20

No files matched your search

+78 -10
View File
@@ -106,6 +106,12 @@ type DNSRecord struct {
UpdatedOn string `json:"updatedOn"`
Group string `json:"group"`
Host string `json:"host"`
Priority int64 `json:"priority"`
Weight int64 `json:"weight"`
Port int64 `json:"port"`
Flags int64 `json:"flags"`
Tag string `json:"tag"`
Value string `json:"value"`
}
type CreateDNSRecordRequest struct {
@@ -116,6 +122,12 @@ type CreateDNSRecordRequest struct {
State *bool `json:"state,omitempty"`
Group string `json:"group,omitempty"`
Host string `json:"host,omitempty"`
Priority int64 `json:"priority,omitempty"`
Weight int64 `json:"weight,omitempty"`
Port int64 `json:"port,omitempty"`
Flags int64 `json:"flags,omitempty"`
Tag string `json:"tag,omitempty"`
Value string `json:"value,omitempty"`
}
type UpdateDNSRecordRequest struct {
@@ -126,8 +138,24 @@ type UpdateDNSRecordRequest struct {
State *bool `json:"state,omitempty"`
Group string `json:"group,omitempty"`
Host string `json:"host,omitempty"`
Priority int64 `json:"priority,omitempty"`
Weight int64 `json:"weight,omitempty"`
Port int64 `json:"port,omitempty"`
Flags int64 `json:"flags,omitempty"`
Tag string `json:"tag,omitempty"`
Value string `json:"value,omitempty"`
}
type CreateDomainRequest struct {
Name string `json:"name"`
IPv4Address string `json:"ipv4Address,omitempty"`
IPv6Address string `json:"ipv6Address,omitempty"`
TTL int64 `json:"ttl,omitempty"`
Group string `json:"group,omitempty"`
}
type UpdateDomainRequest = CreateDomainRequest
type listDomainsResponse struct {
apiResponse
Domains []Domain `json:"domains"`
@@ -172,6 +200,26 @@ func (c *Client) GetDomainByID(ctx context.Context, domainID int64) (*Domain, er
return &resp.Domain, nil
}
func (c *Client) CreateDomain(ctx context.Context, req CreateDomainRequest) (*Domain, error) {
var resp getDomainResponse
if err := c.doRequest(ctx, http.MethodPost, "/dns", req, &resp); err != nil {
return nil, err
}
return &resp.Domain, nil
}
func (c *Client) UpdateDomain(ctx context.Context, domainID int64, req UpdateDomainRequest) (*Domain, error) {
var resp getDomainResponse
if err := c.doRequest(ctx, http.MethodPost, fmt.Sprintf("/dns/%d", domainID), req, &resp); err != nil {
return nil, err
}
return &resp.Domain, nil
}
func (c *Client) DeleteDomain(ctx context.Context, domainID int64) error {
return c.doRequest(ctx, http.MethodDelete, fmt.Sprintf("/dns/%d", domainID), nil, nil)
}
func (c *Client) GetRootDomain(ctx context.Context, hostname string) (int64, string, error) {
var resp getRootResponse
if err := c.doRequest(ctx, http.MethodGet, fmt.Sprintf("/dns/getroot/%s", url.PathEscape(hostname)), nil, &resp); err != nil {
@@ -207,7 +255,7 @@ func (c *Client) GetDNSRecord(ctx context.Context, domainID int64, recordID int6
func (c *Client) CreateDNSRecord(ctx context.Context, domainID int64, req CreateDNSRecordRequest) (*DNSRecord, error) {
var resp getDNSRecordResponse
if err := c.doRequest(ctx, http.MethodPost, fmt.Sprintf("/dns/%d/record", domainID), buildDNSRecordUpsertPayload(req.RecordType, req.NodeName, req.Content, req.TTL, req.State, req.Group, req.Host), &resp); err != nil {
if err := c.doRequest(ctx, http.MethodPost, fmt.Sprintf("/dns/%d/record", domainID), buildDNSRecordUpsertPayload(req), &resp); err != nil {
return nil, err
}
normalizeDNSRecord(&resp.DNSRecord)
@@ -216,7 +264,7 @@ func (c *Client) CreateDNSRecord(ctx context.Context, domainID int64, req Create
func (c *Client) UpdateDNSRecord(ctx context.Context, domainID int64, recordID int64, req UpdateDNSRecordRequest) (*DNSRecord, error) {
var resp getDNSRecordResponse
if err := c.doRequest(ctx, http.MethodPost, fmt.Sprintf("/dns/%d/record/%d", domainID, recordID), buildDNSRecordUpsertPayload(req.RecordType, req.NodeName, req.Content, req.TTL, req.State, req.Group, req.Host), &resp); err != nil {
if err := c.doRequest(ctx, http.MethodPost, fmt.Sprintf("/dns/%d/record/%d", domainID, recordID), buildDNSRecordUpsertPayload(CreateDNSRecordRequest(req)), &resp); err != nil {
return nil, err
}
normalizeDNSRecord(&resp.DNSRecord)
@@ -319,18 +367,30 @@ type dnsRecordUpsertPayload struct {
State *bool `json:"state,omitempty"`
Group string `json:"group,omitempty"`
Host string `json:"host,omitempty"`
Priority int64 `json:"priority,omitempty"`
Weight int64 `json:"weight,omitempty"`
Port int64 `json:"port,omitempty"`
Flags int64 `json:"flags,omitempty"`
Tag string `json:"tag,omitempty"`
Value string `json:"value,omitempty"`
}
func buildDNSRecordUpsertPayload(recordType string, nodeName string, content *string, ttl int64, state *bool, group string, host string) dnsRecordUpsertPayload {
normalizedType := strings.ToUpper(strings.TrimSpace(recordType))
normalizedContent := normalizeOptionalContent(content)
func buildDNSRecordUpsertPayload(req CreateDNSRecordRequest) dnsRecordUpsertPayload {
normalizedType := strings.ToUpper(strings.TrimSpace(req.RecordType))
normalizedContent := normalizeOptionalContent(req.Content)
payload := dnsRecordUpsertPayload{
NodeName: nodeName,
NodeName: req.NodeName,
RecordType: normalizedType,
TTL: ttl,
State: state,
Group: group,
Host: host,
TTL: req.TTL,
State: req.State,
Group: req.Group,
Host: req.Host,
Priority: req.Priority,
Weight: req.Weight,
Port: req.Port,
Flags: req.Flags,
Tag: req.Tag,
Value: req.Value,
}
switch normalizedType {
@@ -346,6 +406,14 @@ func buildDNSRecordUpsertPayload(recordType string, nodeName string, content *st
if normalizedContent != nil {
payload.Host = *normalizedContent
}
case "MX", "SRV", "NS", "PTR":
if normalizedContent != nil {
payload.Host = *normalizedContent
}
case "CAA":
if normalizedContent != nil && payload.Value == "" {
payload.Value = *normalizedContent
}
default:
payload.Content = normalizedContent
}
+88
View File
@@ -457,3 +457,91 @@ func TestClientCreateDNSRecordOmitsContentWhenUnset(t *testing.T) {
func stringPointer(value string) *string {
return &value
}
func TestClientDomainCRUD(t *testing.T) {
fake := fakedynu.NewServer()
defer fake.Close()
client := dynuclient.New("test-key", dynuclient.WithBaseURL(fake.BaseURL()), dynuclient.WithHTTPClient(fake.Client()))
created, err := client.CreateDomain(context.Background(), dynuclient.CreateDomainRequest{Name: "new.example.com", TTL: 300, IPv4Address: "203.0.113.10"})
if err != nil {
t.Fatalf("CreateDomain() error = %v", err)
}
if created.ID == 0 || created.Name != "new.example.com" {
t.Fatalf("unexpected created domain: %#v", created)
}
updated, err := client.UpdateDomain(context.Background(), created.ID, dynuclient.UpdateDomainRequest{Name: "new.example.com", TTL: 600})
if err != nil {
t.Fatalf("UpdateDomain() error = %v", err)
}
if updated.TTL != 600 {
t.Fatalf("expected ttl 600, got %d", updated.TTL)
}
if err := client.DeleteDomain(context.Background(), created.ID); err != nil {
t.Fatalf("DeleteDomain() error = %v", err)
}
_, err = client.GetDomainByID(context.Background(), created.ID)
if err == nil {
t.Fatalf("expected error reading deleted domain")
}
}
func TestClientCreateDNSRecordSendsMXFields(t *testing.T) {
fake := fakedynu.NewServer()
defer fake.Close()
client := dynuclient.New("test-key", dynuclient.WithBaseURL(fake.BaseURL()), dynuclient.WithHTTPClient(fake.Client()))
host := "mail.example.com"
_, err := client.CreateDNSRecord(context.Background(), 1001, dynuclient.CreateDNSRecordRequest{NodeName: "@", RecordType: "MX", Content: &host, Priority: 10})
if err != nil {
t.Fatalf("CreateDNSRecord() error = %v", err)
}
rec, _ := client.GetDNSRecord(context.Background(), 1001, 21)
if rec.Priority != 10 || rec.Host != host {
t.Fatalf("unexpected MX fields: %#v", rec)
}
}
func TestClientCreateDNSRecordSendsSRVFields(t *testing.T) {
fake := fakedynu.NewServer()
defer fake.Close()
client := dynuclient.New("test-key", dynuclient.WithBaseURL(fake.BaseURL()), dynuclient.WithHTTPClient(fake.Client()))
target := "sip.example.com"
_, err := client.CreateDNSRecord(context.Background(), 1001, dynuclient.CreateDNSRecordRequest{NodeName: "_sip._tcp", RecordType: "SRV", Content: &target, Priority: 10, Weight: 5, Port: 5060})
if err != nil {
t.Fatalf("CreateDNSRecord() error = %v", err)
}
rec, _ := client.GetDNSRecord(context.Background(), 1001, 21)
if rec.Port != 5060 || rec.Weight != 5 || rec.Host != target {
t.Fatalf("unexpected SRV fields: %#v", rec)
}
}
func TestClientCreateDNSRecordPreservesTXTContent(t *testing.T) {
fake := fakedynu.NewServer()
defer fake.Close()
client := dynuclient.New("test-key", dynuclient.WithBaseURL(fake.BaseURL()), dynuclient.WithHTTPClient(fake.Client()))
txt := "v=spf1 include:_spf.example.com ~all"
rec, err := client.CreateDNSRecord(context.Background(), 1001, dynuclient.CreateDNSRecordRequest{NodeName: "@", RecordType: "TXT", Content: &txt})
if err != nil {
t.Fatalf("CreateDNSRecord() error = %v", err)
}
if rec.Content != txt {
t.Fatalf("expected TXT content preserved, got %q", rec.Content)
}
}
func TestClientCreateDNSRecordSendsCAAFields(t *testing.T) {
fake := fakedynu.NewServer()
defer fake.Close()
client := dynuclient.New("test-key", dynuclient.WithBaseURL(fake.BaseURL()), dynuclient.WithHTTPClient(fake.Client()))
value := "letsencrypt.org"
rec, err := client.CreateDNSRecord(context.Background(), 1001, dynuclient.CreateDNSRecordRequest{NodeName: "@", RecordType: "CAA", Content: &value, Flags: 0, Tag: "issue"})
if err != nil {
t.Fatalf("CreateDNSRecord() error = %v", err)
}
if rec.Tag != "issue" || rec.Value != value {
t.Fatalf("unexpected CAA fields: %#v", rec)
}
}