domains.go raw
1 package desec
2
3 import (
4 "context"
5 "fmt"
6 "net/http"
7 "net/url"
8 "time"
9 )
10
11 // Domain a domain representation.
12 type Domain struct {
13 Name string `json:"name,omitempty"`
14 MinimumTTL int `json:"minimum_ttl,omitempty"`
15 Keys []DomainKey `json:"keys,omitempty"`
16 Created *time.Time `json:"created,omitempty"`
17 Published *time.Time `json:"published,omitempty"`
18 Touched *time.Time `json:"touched,omitempty"`
19 }
20
21 // DomainKey a domain key representation.
22 type DomainKey struct {
23 DNSKey string `json:"dnskey,omitempty"`
24 DS []string `json:"ds,omitempty"`
25 Flags int `json:"flags,omitempty"`
26 KeyType string `json:"keytype,omitempty"`
27 }
28
29 // DomainsService handles communication with the domain related methods of the deSEC API.
30 //
31 // https://desec.readthedocs.io/en/latest/dns/domains.html
32 type DomainsService struct {
33 client *Client
34 }
35
36 // Create creating a domain.
37 // https://desec.readthedocs.io/en/latest/dns/domains.html#creating-a-domain
38 func (s *DomainsService) Create(ctx context.Context, domainName string) (*Domain, error) {
39 endpoint, err := s.client.createEndpoint("domains")
40 if err != nil {
41 return nil, fmt.Errorf("failed to create endpoint: %w", err)
42 }
43
44 req, err := s.client.newRequest(ctx, http.MethodPost, endpoint, Domain{Name: domainName})
45 if err != nil {
46 return nil, err
47 }
48
49 resp, err := s.client.httpClient.Do(req)
50 if err != nil {
51 return nil, fmt.Errorf("failed to call API: %w", err)
52 }
53
54 defer func() { _ = resp.Body.Close() }()
55
56 if resp.StatusCode != http.StatusCreated {
57 return nil, handleError(resp)
58 }
59
60 var domain Domain
61
62 err = handleResponse(resp, &domain)
63 if err != nil {
64 return nil, err
65 }
66
67 return &domain, nil
68 }
69
70 // GetAll listing domains.
71 // https://desec.readthedocs.io/en/latest/dns/domains.html#listing-domains
72 func (s *DomainsService) GetAll(ctx context.Context) ([]Domain, error) {
73 domains, _, err := s.GetAllPaginated(ctx, "")
74 if err != nil {
75 return nil, err
76 }
77
78 return domains, nil
79 }
80
81 // GetAllPaginated listing domains.
82 // https://desec.readthedocs.io/en/latest/dns/domains.html#listing-domains
83 func (s *DomainsService) GetAllPaginated(ctx context.Context, cursor string) ([]Domain, *Cursors, error) {
84 queryValues := url.Values{}
85 queryValues.Set("cursor", cursor)
86
87 return s.getAll(ctx, queryValues)
88 }
89
90 // GetResponsible returns the responsible domain for a given DNS query name.
91 // https://desec.readthedocs.io/en/latest/dns/domains.html#identifying-the-responsible-domain-for-a-dns-name
92 func (s *DomainsService) GetResponsible(ctx context.Context, domainName string) (*Domain, error) {
93 queryValues := url.Values{}
94 queryValues.Set("owns_qname", domainName)
95
96 domains, _, err := s.getAll(ctx, queryValues)
97 if err != nil {
98 return nil, err
99 }
100
101 if len(domains) == 0 {
102 return nil, &NotFoundError{Detail: "no responsible domain found"}
103 }
104
105 return &domains[0], nil
106 }
107
108 // getAll listing domains.
109 // https://desec.readthedocs.io/en/latest/dns/domains.html#listing-domains
110 func (s *DomainsService) getAll(ctx context.Context, query url.Values) ([]Domain, *Cursors, error) {
111 endpoint, err := s.client.createEndpoint("domains")
112 if err != nil {
113 return nil, nil, fmt.Errorf("failed to create endpoint: %w", err)
114 }
115
116 req, err := s.client.newRequest(ctx, http.MethodGet, endpoint, nil)
117 if err != nil {
118 return nil, nil, err
119 }
120
121 if len(query) > 0 {
122 req.URL.RawQuery = query.Encode()
123 }
124
125 resp, err := s.client.httpClient.Do(req)
126 if err != nil {
127 return nil, nil, fmt.Errorf("failed to call API: %w", err)
128 }
129
130 defer func() { _ = resp.Body.Close() }()
131
132 if resp.StatusCode != http.StatusOK {
133 return nil, nil, handleError(resp)
134 }
135
136 cursors, err := parseCursor(resp.Header)
137 if err != nil {
138 return nil, nil, err
139 }
140
141 var domains []Domain
142
143 err = handleResponse(resp, &domains)
144 if err != nil {
145 return nil, nil, err
146 }
147
148 return domains, cursors, nil
149 }
150
151 // Get retrieving a specific domain.
152 // https://desec.readthedocs.io/en/latest/dns/domains.html#retrieving-a-specific-domain
153 func (s *DomainsService) Get(ctx context.Context, domainName string) (*Domain, error) {
154 endpoint, err := s.client.createEndpoint("domains", domainName)
155 if err != nil {
156 return nil, fmt.Errorf("failed to create endpoint: %w", err)
157 }
158
159 req, err := s.client.newRequest(ctx, http.MethodGet, endpoint, nil)
160 if err != nil {
161 return nil, err
162 }
163
164 resp, err := s.client.httpClient.Do(req)
165 if err != nil {
166 return nil, fmt.Errorf("failed to call API: %w", err)
167 }
168
169 defer func() { _ = resp.Body.Close() }()
170
171 if resp.StatusCode != http.StatusOK {
172 return nil, handleError(resp)
173 }
174
175 var domains Domain
176
177 err = handleResponse(resp, &domains)
178 if err != nil {
179 return nil, err
180 }
181
182 return &domains, nil
183 }
184
185 // Delete deleting a domain.
186 // https://desec.readthedocs.io/en/latest/dns/domains.html#deleting-a-domain
187 func (s *DomainsService) Delete(ctx context.Context, domainName string) error {
188 endpoint, err := s.client.createEndpoint("domains", domainName)
189 if err != nil {
190 return fmt.Errorf("failed to create endpoint: %w", err)
191 }
192
193 req, err := s.client.newRequest(ctx, http.MethodDelete, endpoint, nil)
194 if err != nil {
195 return err
196 }
197
198 resp, err := s.client.httpClient.Do(req)
199 if err != nil {
200 return fmt.Errorf("failed to call API: %w", err)
201 }
202
203 defer func() { _ = resp.Body.Close() }()
204
205 if resp.StatusCode != http.StatusNoContent {
206 return handleError(resp)
207 }
208
209 return nil
210 }
211