Passed
Push — master ( cf05d8...5fb5a4 )
by Teye
01:45 queued 16s
created

Client::getDomainList()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 5
c 1
b 0
f 1
nc 1
nop 0
dl 0
loc 11
ccs 0
cts 8
cp 0
crap 2
rs 10
1
<?php
2
3
namespace Level23\Dynadot;
4
5
use GuzzleHttp\Client as GuzzleClient;
6
use GuzzleHttp\Exception\RequestException;
7
use GuzzleHttp\Psr7\Request;
8
use InvalidArgumentException;
9
use Level23\Dynadot\Dto\AccountInfoResult;
10
use Level23\Dynadot\Dto\BulkSearchResult;
11
use Level23\Dynadot\Dto\Contact;
12
use Level23\Dynadot\Dto\ContactListResult;
13
use Level23\Dynadot\Dto\DomainListResult;
14
use Level23\Dynadot\Dto\DomainRegistrationRequest;
15
use Level23\Dynadot\Dto\DomainRegistrationResult;
16
use Level23\Dynadot\Dto\DtoInterface;
17
use Level23\Dynadot\Dto\NameserverUpdateResult;
18
use Level23\Dynadot\Dto\RenewOptionResult;
19
use Level23\Dynadot\Dto\SearchResult;
20
use Level23\Dynadot\Exception\ApiException;
21
use Level23\Dynadot\Exception\NetworkException;
22
use Ramsey\Uuid\Uuid;
23
24
class Client
25
{
26
    /** @var GuzzleClient */
27
    private GuzzleClient $http;
28
29
    /** @var string */
30
    private string $apiKey;
31
32
    /** @var string */
33
    private string $apiSecret;
34
35
    /** @var string */
36
    private string $apiVersion = 'v1';
37
38 85
    public function __construct(string $apiKey, string $apiSecret, bool $sandbox = false)
39
    {
40 85
        $this->apiKey    = $apiKey;
41 85
        $this->apiSecret = $apiSecret;
42 85
        $this->http      = new GuzzleClient([
43 85
            'base_uri' => 'https://api' . ($sandbox ? '-sandbox' : '') . '.dynadot.com/restful/' . $this->apiVersion . '/',
44 85
            'headers'  => [
45 85
                'Accept'       => 'application/json',
46 85
                'Content-Type' => 'application/json',
47 85
            ],
48 85
        ]);
49
    }
50
51
    /**
52
     * Retrieve detailed information about a single domain.
53
     *
54
     * @param string $domainName
55
     *
56
     * @return DomainListResult
57
     * @throws ApiException
58
     * @throws NetworkException
59
     */
60 7
    public function getDomainInfo(string $domainName): DomainListResult
61
    {
62
        /** @var DomainListResult $result */
63 7
        $result = $this->request(
64 7
            'GET',
65 7
            "domains/{$domainName}",
66 7
            [],
67 7
            DomainListResult::class
68 7
        );
69
70 4
        return $result;
71
    }
72
73
    /**
74
     * Set nameservers for a domain.
75
     *
76
     * @param string        $domainName
77
     * @param array<string> $nameservers
78
     *
79
     * @throws ApiException
80
     * @throws NetworkException
81
     */
82 10
    public function setNameservers(string $domainName, array $nameservers): NameserverUpdateResult
83
    {
84
        /** @var NameserverUpdateResult $result */
85 10
        $result = $this->request(
86 10
            'PUT',
87 10
            "domains/{$domainName}/nameservers",
88 10
            [
89 10
                'nameservers_list' => $nameservers,
90 10
            ],
91 10
            NameserverUpdateResult::class
92 10
        );
93
94 5
        return $result;
95
    }
96
97
    /**
98
     * Retrieve contact information for a given contact ID.
99
     *
100
     * @param int $contactId
101
     *
102
     * @return Contact
103
     * @throws ApiException
104
     * @throws NetworkException
105
     */
106 10
    public function getContactInfo(int $contactId): Contact
107
    {
108
        /** @var Contact $result */
109 10
        $result = $this->request(
110 10
            'GET',
111 10
            "contacts/{$contactId}",
112 10
            [],
113 10
            Contact::class
114 10
        );
115
116 6
        return $result;
117
    }
118
119
    /**
120
     * Retrieve a list of all contacts in your Dynadot account.
121
     *
122
     * @return ContactListResult
123
     * @throws ApiException
124
     * @throws NetworkException
125
     */
126 9
    public function getContactList(): ContactListResult
127
    {
128
        /** @var ContactListResult $result */
129 9
        $result = $this->request(
130 9
            'GET',
131 9
            "contacts",
132 9
            [],
133 9
            ContactListResult::class
134 9
        );
135
136 6
        return $result;
137
    }
138
139
    /**
140
     * Retrieve a list of all domains in your Dynadot account.
141
     *
142
     * This method returns detailed information about all domains including
143
     * domain names, expiration dates, registration dates, nameservers,
144
     * status information, and more.
145
     *
146
     * @return DomainListResult Contains an array of DomainInfo objects with detailed domain information
147
     * @throws ApiException When the API returns an error response
148
     * @throws NetworkException When there's a network communication error
149
     */
150
    public function getDomainList(): DomainListResult
151
    {
152
        /** @var DomainListResult $result */
153
        $result = $this->request(
154
            'GET',
155
            "domains",
156
            [],
157
            DomainListResult::class
158
        );
159
160
        return $result;
161
    }
162
163
    /**
164
     * Set the renew option for a domain.
165
     *
166
     * @param string $domain
167
     * @param string $renewOption
168
     *
169
     * @return RenewOptionResult
170
     * @throws ApiException
171
     * @throws NetworkException
172
     */
173 11
    public function setRenewOption(string $domain, string $renewOption): RenewOptionResult
174
    {
175
        /** @var RenewOptionResult $result */
176 11
        $result = $this->request(
177 11
            'PUT',
178 11
            "domains/{$domain}/renew_option",
179 11
            ['renew_option' => $renewOption],
180 11
            RenewOptionResult::class
181 11
        );
182
183 6
        return $result;
184
    }
185
186
    /**
187
     * Search for multiple domains at once.
188
     *
189
     * @param array<string> $domains
190
     *
191
     * @return BulkSearchResult
192
     * @throws ApiException
193
     * @throws NetworkException
194
     */
195 11
    public function bulkSearch(array $domains): BulkSearchResult
196
    {
197
        /** @var BulkSearchResult $result */
198 11
        $result = $this->request(
199 11
            'GET',
200 11
            "domains/bulk_search",
201 11
            ['domain_name_list' => implode(',', array_map('trim', $domains))],
202 11
            BulkSearchResult::class
203 11
        );
204
205 8
        return $result;
206
    }
207
208
    /**
209
     * Search for a domain.
210
     *
211
     * @param string $domain
212
     *
213
     * @return SearchResult
214
     * @throws ApiException
215
     * @throws NetworkException
216
     */
217 13
    public function search(string $domain, bool $showPrice = false, string $currency = 'USD'): SearchResult
218
    {
219
        /** @var SearchResult $result */
220 13
        $result = $this->request(
221 13
            'GET',
222 13
            "domains/{$domain}/search",
223 13
            [
224 13
                'show_price' => $showPrice ? 'true' : 'false',
225 13
                'currency'   => $currency,
226 13
            ],
227 13
            SearchResult::class
228 13
        );
229
230 9
        return $result;
231
    }
232
233
    /**
234
     * Register a new domain.
235
     *
236
     * @param string                    $domainName
237
     * @param DomainRegistrationRequest $registrationData
238
     *
239
     * @return DomainRegistrationResult
240
     * @throws ApiException
241
     * @throws NetworkException
242
     */
243 13
    public function registerDomain(
244
        string $domainName,
245
        DomainRegistrationRequest $registrationData
246
    ): DomainRegistrationResult {
247
        /** @var DomainRegistrationResult $result */
248 13
        $result = $this->request(
249 13
            'POST',
250 13
            "domains/{$domainName}/register",
251 13
            $registrationData->jsonSerialize(),
252 13
            DomainRegistrationResult::class
253 13
        );
254
255 8
        return $result;
256
    }
257
258
    /**
259
     * Retrieve account information for the authenticated user.
260
     *
261
     * @return AccountInfoResult
262
     * @throws ApiException
263
     * @throws NetworkException
264
     */
265 1
    public function getAccountInfo(): AccountInfoResult
266
    {
267
        /** @var AccountInfoResult $result */
268 1
        $result = $this->request(
269 1
            'GET',
270 1
            'accounts/info',
271 1
            [],
272 1
            AccountInfoResult::class
273 1
        );
274
275 1
        return $result;
276
    }
277
278
    /**
279
     * Generic request helper that wraps Guzzle exceptions and hydrates DTOs.
280
     *
281
     * @param string               $method
282
     * @param string               $path
283
     * @param array<string, mixed> $params
284
     * @param string               $dtoClass
285
     *
286
     * @return DtoInterface
287
     * @throws ApiException
288
     * @throws NetworkException
289
     */
290 85
    private function request(string $method, string $path, array $params, string $dtoClass): DtoInterface
291
    {
292 85
        $requestId = Uuid::uuid4()->toString();
293
294
        // Prepare request options
295 85
        $options = [
296 85
            'headers' => [
297 85
                'Authorization' => 'Bearer ' . $this->apiKey,
298 85
                'X-Request-Id'  => $requestId,
299 85
                'Accept'        => 'application/json',
300 85
                'Content-Type'  => 'application/json',
301 85
            ],
302 85
        ];
303
304
        // For GET requests, use params as query parameters
305 85
        if ($method === 'GET' && ! empty($params)) {
306 24
            $options['query'] = $params;
307 24
            $payloadJson      = '';
308
        } else {
309 61
            $payloadJson = json_encode($params, JSON_UNESCAPED_SLASHES);
310 61
            if ($payloadJson === false) {
311
                throw new InvalidArgumentException('Failed to encode request body as JSON');
312
            }
313 61
            $options['body'] = $payloadJson;
314
        }
315
316 85
        $stringToSign = implode("\n", [
317 85
            $this->apiKey,
318 85
            '/' . trim($path, '/'),
319 85
            $requestId,
320 85
            $payloadJson,
321 85
        ]);
322 85
        $signature                         = hash_hmac('sha256', $stringToSign, $this->apiSecret);
323 85
        $options['headers']['X-Signature'] = $signature;
324
325
        try {
326 85
            $response = $this->http->request($method, $path, $options);
327 32
        } catch (RequestException $e) {
328 32
            if ($e->getHandlerContext()['errno'] ?? null) {
329
                throw new NetworkException('Network error communicating with Dynadot API', 0, $e);
330
            }
331
332 32
            $response = $e->getResponse();
333 32
            if ($response === null) {
334 8
                throw new NetworkException('No response received from Dynadot API', 0, $e);
335
            }
336
337 24
            throw ApiException::fromResponse($e->getRequest(), $response, $e);
338
        }
339
340 53
        $data = json_decode($response->getBody()->getContents(), true);
341
342
        // check the status code in the json response and throw an exception if it's not 200
343 53
        if ($data['code'] >= 400) {
344
            // Create a request object for the exception
345
            $request = new Request($method, $path, $options['headers'], $options['body'] ?? null);
346
347
            throw ApiException::fromResponse($request, $response);
348
        }
349
350 53
        if (! is_a($dtoClass, DtoInterface::class, true)) {
351
            throw new InvalidArgumentException("$dtoClass must implement DtoInterface");
352
        }
353
354
        /** @var DtoInterface $dto */
355 53
        $dto = $dtoClass::fromArray($data['data'] ?? []);
356
357 53
        return $dto;
358
    }
359
}
360