From 14343b230b2b7f7ccd3c2404f23db415d07eec2b Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 7 Feb 2024 21:49:43 +0100 Subject: [PATCH] added dns zone management --- src/Api/DnsZonesApi.php | 225 ++++++++++++++++++ src/Domain/DnsZone.php | 87 +++++++ src/Domain/DnsZoneCollection.php | 24 ++ src/Domain/Enum/ZoneServiceEnum.php | 9 + src/IsProxy.php | 2 +- src/RealtimeRegister.php | 4 + tests/Clients/DnsZonesApiCreateTest.php | 80 +++++++ tests/Clients/DnsZonesApiDeleteTest.php | 19 ++ tests/Clients/DnsZonesApiGetTest.php | 21 ++ tests/Clients/DnsZonesApiListTest.php | 92 +++++++ tests/Clients/DnsZonesApiUpdateTest.php | 83 +++++++ tests/Domain/DnsZoneCollectionTest.php | 74 ++++++ .../data/dnszone_valid_with_records.php | 51 ++++ .../data/dnszone_valid_without_records.php | 37 +++ tests/Domain/data/dnszonecollection_valid.php | 6 + 15 files changed, 813 insertions(+), 1 deletion(-) create mode 100644 src/Api/DnsZonesApi.php create mode 100644 src/Domain/DnsZone.php create mode 100644 src/Domain/DnsZoneCollection.php create mode 100644 src/Domain/Enum/ZoneServiceEnum.php create mode 100644 tests/Clients/DnsZonesApiCreateTest.php create mode 100644 tests/Clients/DnsZonesApiDeleteTest.php create mode 100644 tests/Clients/DnsZonesApiGetTest.php create mode 100644 tests/Clients/DnsZonesApiListTest.php create mode 100644 tests/Clients/DnsZonesApiUpdateTest.php create mode 100644 tests/Domain/DnsZoneCollectionTest.php create mode 100644 tests/Domain/data/dnszone_valid_with_records.php create mode 100644 tests/Domain/data/dnszone_valid_without_records.php create mode 100644 tests/Domain/data/dnszonecollection_valid.php diff --git a/src/Api/DnsZonesApi.php b/src/Api/DnsZonesApi.php new file mode 100644 index 0000000..9eaf530 --- /dev/null +++ b/src/Api/DnsZonesApi.php @@ -0,0 +1,225 @@ +client->get('v2/dns/zones', $query); + return DnsZoneCollection::fromArray($response->json()); + } + + /** + * @see https://dm.realtimeregister.com/docs/api/dns/zones/get + * + * @param int $id + * + * @throws InvalidArgumentException + * + * @return DnsZone + */ + public function get(int $id): DnsZone + { + $response = $this->client->get( + sprintf('v2/dns/zones/%s', $id) + ); + return DnsZone::fromArray($response->json()); + } + + /** + * @see https://dm.realtimeregister.com/docs/api/dns/zones/create + * + * @throws InvalidArgumentException + */ + public function create( + string $name, + ?ZoneServiceEnum $service = null, + ?string $template = null, + ?bool $link = null, + ?string $master = null, + ?array $ns = null, + ?bool $dnssec = null, + ?string $hostMaster = null, + ?int $refresh = null, + ?int $retry = null, + ?int $expire = null, + ?int $ttl = null, + ?DomainZoneRecordCollection $records = null, + ): void { + $this->validateZoneName($name); + + $payload = [ + 'name' => $name, + ]; + if ($service !== null) { + $payload['service'] = $service->value; + } + if ($template !== null) { + $payload['template'] = $template; + } + if ($link !== null) { + $payload['link'] = $link; + } + if ($master !== null) { + $payload['master'] = $master; + } + if($ns !== null) { + $payload['ns'] = $ns; + } + if($dnssec !== null) { + $payload['dnssec'] = $dnssec; + } + if($hostMaster !== null) { + $payload['hostMaster'] = $hostMaster; + } + if($refresh !== null) { + $payload['refresh'] = $refresh; + } + if($retry !== null) { + $payload['retry'] = $retry; + } + if($expire !== null) { + $payload['expire'] = $expire; + } + if($ttl !== null) { + $payload['ttl'] = $ttl; + } + if($records !== null) { + $payload['records'] = $records->toArray(); + } + + $this->client->post('v2/dns/zones', $payload); + } + + /** + * @see https://dm.realtimeregister.com/docs/api/dns/zones/update + * + * @throws InvalidArgumentException + */ + public function update( + int $id, + ?string $name, + ?ZoneServiceEnum $service = null, + ?string $template = null, + ?bool $link = null, + ?string $master = null, + ?array $ns = null, + ?bool $dnssec = null, + ?string $hostMaster = null, + ?int $refresh = null, + ?int $retry = null, + ?int $expire = null, + ?int $ttl = null, + ?DomainZoneRecordCollection $records = null, + ): void { + $payload = []; + if ($name !== null) { + $this->validateZoneName($name); + $payload['name'] = $name; + } + if ($service !== null) { + $payload['service'] = $service->value; + } + if ($template !== null) { + $payload['template'] = $template; + } + if ($link !== null) { + $payload['link'] = $link; + } + if ($master !== null) { + $payload['master'] = $master; + } + if($ns !== null) { + $payload['ns'] = $ns; + } + if($dnssec !== null) { + $payload['dnssec'] = $dnssec; + } + if($hostMaster !== null) { + $payload['hostMaster'] = $hostMaster; + } + if($refresh !== null) { + $payload['refresh'] = $refresh; + } + if($retry !== null) { + $payload['retry'] = $retry; + } + if($expire !== null) { + $payload['expire'] = $expire; + } + if($ttl !== null) { + $payload['ttl'] = $ttl; + } + if($records !== null) { + $payload['records'] = $records->toArray(); + } + + $this->client->post( + sprintf('v2/dns/zones/%s/update', $id), + $payload, + ); + } + + /** + * @see https://dm.realtimeregister.com/docs/api/dns/zones/delete + * + * @param int $id The id of the zone to delete + */ + public function delete(int $id): void + { + $this->client->delete( + sprintf('v2/dns/zones/%s', $id) + ); + } + + /** + * Validate zone name input. + * + * @param string $name Zone name + * + * @throws InvalidArgumentException + */ + private function validateZoneName(string $name): void + { + Assert::lengthBetween($name, 3, 40, 'Zone name should be between 3 and 40 characters'); + Assert::regex($name, '/^[a-zA-Z0-9\-_@.]+$/', 'Invalid zone name, allowed characters: a-z A-Z 0-9 - _ @ .'); + } +} diff --git a/src/Domain/DnsZone.php b/src/Domain/DnsZone.php new file mode 100644 index 0000000..ddfbfbe --- /dev/null +++ b/src/Domain/DnsZone.php @@ -0,0 +1,87 @@ + $this->id, + 'customer' => $this->customer, + 'name' => $this->name, + 'createdDate' => $this->createdDate->format('Y-m-d\TH:i:s\Z'), + 'updatedDate' => $this->updatedDate?->format('Y-m-d\TH:i:s\Z'), + 'deletionDate' => $this->deletionDate?->format('Y-m-d\TH:i:s\Z'), + 'service' => $this->service->value, + 'dnssec' => $this->dnssec, + 'managed' => $this->managed, + 'template' => $this->template, + 'master' => $this->master, + 'ns' => $this->ns, + 'hostMaster' => $this->hostMaster, + 'refresh' => $this->refresh, + 'retry' => $this->retry, + 'expire' => $this->expire, + 'ttl' => $this->ttl, + 'defaultRecords' => $this->defaultRecords->toArray(), + 'records' => $this->records?->toArray(), + 'keyData' => $this->keyData?->toArray(), + ], function ($x) { + return $x !== null; + }); + } +} diff --git a/src/Domain/DnsZoneCollection.php b/src/Domain/DnsZoneCollection.php new file mode 100644 index 0000000..41d0374 --- /dev/null +++ b/src/Domain/DnsZoneCollection.php @@ -0,0 +1,24 @@ +entities[$offset] ?? null; + } + + public static function parseChild(array $json): DnsZone + { + return DnsZone::fromArray($json); + } +} diff --git a/src/Domain/Enum/ZoneServiceEnum.php b/src/Domain/Enum/ZoneServiceEnum.php new file mode 100644 index 0000000..76ff1da --- /dev/null +++ b/src/Domain/Enum/ZoneServiceEnum.php @@ -0,0 +1,9 @@ +processes = new ProcessesApi($client); $this->providers = new ProvidersApi($client); $this->dnstemplates = new DnsTemplatesApi($client); + $this->dnszones = new DnsZonesApi($client); $this->tlds = new TLDsApi($client); $this->financial = new FinancialApi($client); } diff --git a/tests/Clients/DnsZonesApiCreateTest.php b/tests/Clients/DnsZonesApiCreateTest.php new file mode 100644 index 0000000..ab594ed --- /dev/null +++ b/tests/Clients/DnsZonesApiCreateTest.php @@ -0,0 +1,80 @@ +dnszones->create( + 'test', + ZoneServiceEnum::BASIC, + 'magazine', + false, + ); + } + + public function test_create_with_records(): void + { + $sdk = MockedClientFactory::makeSdk( + 200, + '', + MockedClientFactory::assertRoute('POST', 'v2/dns/zones', $this) + ); + + $sdk->dnszones->create( + 'test', + ZoneServiceEnum::BASIC, + 'magazine', + false, + 'master', + ['ns1.donaldduck.nl', 'ns2.donaldduck.nl'], + false, + 'movies@ducktown.disney.com', + 129371293, + 123456, + 78123139, + 712312377, + DomainZoneRecordCollection::fromArray( + [ + [ + 'name' => '##DOMAIN##', + 'type' => 'URL', + 'content' => 'http://www.donaldduck.nl/', + 'ttl' => 300, + ], + [ + 'name' => 'www.##DOMAIN##', + 'type' => 'A', + 'content' => '1.1.1.1', + 'ttl' => 300, + ], + ] + ) + ); + } + + public function test_create_invalid(): void + { + $sdk = MockedClientFactory::makeSdk( + 200, + '' + ); + $this->expectException('Webmozart\Assert\InvalidArgumentException'); + $sdk->dnszones->create( + 'this is not possible', + ZoneServiceEnum::PREMIUM, + ); + } +} diff --git a/tests/Clients/DnsZonesApiDeleteTest.php b/tests/Clients/DnsZonesApiDeleteTest.php new file mode 100644 index 0000000..491a2eb --- /dev/null +++ b/tests/Clients/DnsZonesApiDeleteTest.php @@ -0,0 +1,19 @@ +dnszones->delete(1111111111); + } +} diff --git a/tests/Clients/DnsZonesApiGetTest.php b/tests/Clients/DnsZonesApiGetTest.php new file mode 100644 index 0000000..689b131 --- /dev/null +++ b/tests/Clients/DnsZonesApiGetTest.php @@ -0,0 +1,21 @@ +dnszones->get(1111111111); + $this->assertSame(2, $response->records->count()); + } +} diff --git a/tests/Clients/DnsZonesApiListTest.php b/tests/Clients/DnsZonesApiListTest.php new file mode 100644 index 0000000..564a658 --- /dev/null +++ b/tests/Clients/DnsZonesApiListTest.php @@ -0,0 +1,92 @@ + [ + include __DIR__ . '/../Domain/data/dnszone_valid_with_records.php', + include __DIR__ . '/../Domain/data/dnszone_valid_without_records.php', + include __DIR__ . '/../Domain/data/dnszone_valid_with_records.php', + ], + 'pagination' => [ + 'total' => 3, + 'offset' => 0, + 'limit' => 10, + ], + ]), + MockedClientFactory::assertRoute('GET', 'v2/dns/zones', $this) + ); + + $response = $sdk->dnszones->list(); + $this->assertSame(3, $response->count()); + } + + public function test_list_with_queries(): void + { + $sdk = MockedClientFactory::makeSdk( + 200, + json_encode([ + 'entities' => [ + include __DIR__ . '/../Domain/data/dnszone_valid_with_records.php', + include __DIR__ . '/../Domain/data/dnszone_valid_without_records.php', + include __DIR__ . '/../Domain/data/dnszone_valid_with_records.php', + ], + 'pagination' => [ + 'total' => 3, + 'offset' => 0, + 'limit' => 3, + ], + ]), + MockedClientFactory::assertRoute( + 'GET', + 'v2/dns/zones', + $this, + ['limit'=>'3', 'offset'=>'0', 'q'=>'john'] + ) + ); + + $response = $sdk->dnszones->list(3, 0, 'john'); + $this->assertSame(3, $response->count()); + } + + public function test_list_with_search_and_parameters(): void + { + $parameters = [ + 'name' => 'test', + ]; + + $sdk = MockedClientFactory::makeSdk( + 200, + json_encode([ + 'entities' => [ + include __DIR__ . '/../Domain/data/dnszone_valid_with_records.php', + include __DIR__ . '/../Domain/data/dnszone_valid_without_records.php', + include __DIR__ . '/../Domain/data/dnszone_valid_with_records.php', + ], + 'pagination' => [ + 'total' => 3, + 'offset' => 0, + 'limit' => 3, + ], + ]), + MockedClientFactory::assertRoute('GET', 'v2/dns/zones', $this, [ + 'name' => 'test', + 'limit' => '3', + 'offset' => '0', + 'q' => 'john', + ]) + ); + + $response = $sdk->dnszones->list(3, 0, 'john', $parameters); + $this->assertSame(3, $response->count()); + } +} diff --git a/tests/Clients/DnsZonesApiUpdateTest.php b/tests/Clients/DnsZonesApiUpdateTest.php new file mode 100644 index 0000000..5287806 --- /dev/null +++ b/tests/Clients/DnsZonesApiUpdateTest.php @@ -0,0 +1,83 @@ +dnszones->update( + 1111111111, + 'test', + ZoneServiceEnum::BASIC, + 'magazine', + false, + ); + } + + public function test_update_with_records(): void + { + $sdk = MockedClientFactory::makeSdk( + 200, + '', + MockedClientFactory::assertRoute('POST', 'v2/dns/zones/1111111111/update', $this) + ); + + $sdk->dnszones->update( + 1111111111, + 'test', + ZoneServiceEnum::BASIC, + 'magazine', + false, + 'master', + ['ns1.donaldduck.nl', 'ns2.donaldduck.nl'], + false, + 'movies@ducktown.disney.com', + 129371293, + 123456, + 78123139, + 712312377, + DomainZoneRecordCollection::fromArray( + [ + [ + 'name' => '##DOMAIN##', + 'type' => 'URL', + 'content' => 'http://www.donaldduck.nl/', + 'ttl' => 300, + ], + [ + 'name' => 'www.##DOMAIN##', + 'type' => 'A', + 'content' => '1.1.1.1', + 'ttl' => 300, + ], + ] + ) + ); + } + + public function test_update_invalid(): void + { + $sdk = MockedClientFactory::makeSdk( + 200, + '' + ); + $this->expectException('Webmozart\Assert\InvalidArgumentException'); + $sdk->dnszones->update( + 1111111111, + 'this is not possible', + ZoneServiceEnum::PREMIUM, + ); + } +} diff --git a/tests/Domain/DnsZoneCollectionTest.php b/tests/Domain/DnsZoneCollectionTest.php new file mode 100644 index 0000000..d98be9a --- /dev/null +++ b/tests/Domain/DnsZoneCollectionTest.php @@ -0,0 +1,74 @@ +offsetGet(1); + + assert($dnsTemplate !== null); + + Assert::assertInstanceOf(DnsZone::class, $dnsTemplate); + Assert::assertSame('john.doe@example.com', $dnsTemplate->hostMaster); + } + + public function test_from_and_to_array(): void + { + $dnsZoneCollectionData = include __DIR__ . '/data/dnszonecollection_valid.php'; + + $dnsZoneCollection = DnsZoneCollection::fromArray($dnsZoneCollectionData); + + Assert::assertSame($dnsZoneCollectionData, $dnsZoneCollection->toArray()); + } + + public function test_set_unset_exists(): void + { + $dnsZoneCollectionData = include __DIR__ . '/data/dnszonecollection_valid.php'; + + $dnsZoneCollection = DnsZoneCollection::fromArray($dnsZoneCollectionData); + $dnsZoneCollection->offsetSet( + '3', + DnsZone::fromArray([ + 'id' => 333333, + 'customer' => 'coca', + 'name' => 'cola', + 'hostMaster' => 'drink@fresh.com', + 'createdDate' => '2024-02-08T14:40:00Z', + 'service' => 'BASIC', + 'managed' => false, + 'dnssec' => false, + 'defaultRecords' => [], + 'refresh' => 123, + 'retry' => 456, + 'expire' => 789, + 'ttl' => 777, + ]) + ); + + Assert::assertTrue($dnsZoneCollection->offsetExists(3)); + + $dnsTemplate = $dnsZoneCollection->offsetGet(3); + + assert($dnsTemplate !== null); + + Assert::assertSame('cola', $dnsTemplate->name); + + $dnsZoneCollection->offsetUnset(3); + + Assert::assertNull($dnsZoneCollection->offsetGet(3)); + Assert::assertFalse($dnsZoneCollection->offsetExists(3)); + } +} diff --git a/tests/Domain/data/dnszone_valid_with_records.php b/tests/Domain/data/dnszone_valid_with_records.php new file mode 100644 index 0000000..3ec9a47 --- /dev/null +++ b/tests/Domain/data/dnszone_valid_with_records.php @@ -0,0 +1,51 @@ + 1111111111, + 'customer' => 'donaldduck', + 'name' => 'magazine', + 'createdDate' => '2023-05-08T05:10:20Z', + 'service' => 'PREMIUM', + 'dnssec' => false, + 'managed' => true, + 'ns' => ['ns1.yoursrs.com', 'ns2.yoursrs.com'], + 'hostMaster' => 'movies@ducktown.disney.com', + 'refresh' => 129371293, + 'retry' => 123456, + 'expire' => 78123139, + 'ttl' => 712312377, + 'defaultRecords' => [ + [ + 'name' => '##DOMAIN##', + 'type' => 'NS', + 'content' => 'ns2.yoursrs.com', + 'ttl' => 3600, + ], + [ + 'name' => '##DOMAIN##', + 'type' => 'NS', + 'content' => 'ns1.yoursrs.com', + 'ttl' => 3600, + ], + [ + 'name' => '##DOMAIN##', + 'type' => 'SOA', + 'content' => 'ns1.yoursrs.com dns.example.com 0 86400 10800 3600000 3600', + 'ttl' => 3600, + ], + ], + 'records' => [ + [ + 'name' => '##DOMAIN##', + 'type' => 'URL', + 'content' => 'http://www.donaldduck.nl/', + 'ttl' => 300, + ], + [ + 'name' => 'www.##DOMAIN##', + 'type' => 'A', + 'content' => '1.1.1.1', + 'ttl' => 300, + ], + ], +]; diff --git a/tests/Domain/data/dnszone_valid_without_records.php b/tests/Domain/data/dnszone_valid_without_records.php new file mode 100644 index 0000000..e1c3cb1 --- /dev/null +++ b/tests/Domain/data/dnszone_valid_without_records.php @@ -0,0 +1,37 @@ + 1111111112, + 'customer' => 'johndoe', + 'name' => 'test', + 'createdDate' => '2023-05-08T05:10:20Z', + 'service' => 'PREMIUM', + 'dnssec' => false, + 'managed' => true, + 'ns' => ['ns1.yoursrs.com', 'ns2.yoursrs.com'], + 'hostMaster' => 'john.doe@example.com', + 'refresh' => 129371293, + 'retry' => 123456, + 'expire' => 78123139, + 'ttl' => 712312377, + 'defaultRecords' => [ + [ + 'name' => '##DOMAIN##', + 'type' => 'NS', + 'content' => 'ns2.yoursrs.com', + 'ttl' => 3600, + ], + [ + 'name' => '##DOMAIN##', + 'type' => 'NS', + 'content' => 'ns1.yoursrs.com', + 'ttl' => 3600, + ], + [ + 'name' => '##DOMAIN##', + 'type' => 'SOA', + 'content' => 'ns1.yoursrs.com dns.example.com 0 86400 10800 3600000 3600', + 'ttl' => 3600, + ], + ], +]; diff --git a/tests/Domain/data/dnszonecollection_valid.php b/tests/Domain/data/dnszonecollection_valid.php new file mode 100644 index 0000000..70beb9e --- /dev/null +++ b/tests/Domain/data/dnszonecollection_valid.php @@ -0,0 +1,6 @@ +