From ccac0ff7fafdbceb57a1d6241222e8ef3d2d0c57 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Thu, 27 Jul 2023 20:55:32 -0300 Subject: [PATCH] refactor(digital-ocean-dns): Implement basic DTO for Digital Ocean DNS to avoid dynamic objects --- .../digital_ocean_dns_api.dart | 52 +++++++-------- .../models/json/digital_ocean_dns_info.dart | 66 +++++++++++++++++++ .../models/json/digital_ocean_dns_info.g.dart | 41 ++++++++++++ .../dns_providers/digital_ocean_dns.dart | 49 ++++++++++---- 4 files changed, 167 insertions(+), 41 deletions(-) create mode 100644 lib/logic/models/json/digital_ocean_dns_info.dart create mode 100644 lib/logic/models/json/digital_ocean_dns_info.g.dart diff --git a/lib/logic/api_maps/rest_maps/dns_providers/digital_ocean_dns/digital_ocean_dns_api.dart b/lib/logic/api_maps/rest_maps/dns_providers/digital_ocean_dns/digital_ocean_dns_api.dart index 70a70b7c..e1b5c601 100644 --- a/lib/logic/api_maps/rest_maps/dns_providers/digital_ocean_dns/digital_ocean_dns_api.dart +++ b/lib/logic/api_maps/rest_maps/dns_providers/digital_ocean_dns/digital_ocean_dns_api.dart @@ -4,8 +4,7 @@ import 'package:dio/dio.dart'; import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/api_maps/generic_result.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/rest_api_map.dart'; -import 'package:selfprivacy/logic/models/hive/server_domain.dart'; -import 'package:selfprivacy/logic/models/json/dns_records.dart'; +import 'package:selfprivacy/logic/models/json/digital_ocean_dns_info.dart'; class DigitalOceanDnsApi extends RestApiMap { DigitalOceanDnsApi({ @@ -92,13 +91,17 @@ class DigitalOceanDnsApi extends RestApiMap { ); } - Future> getDomains() async { - List domains = []; + Future>> getDomains() async { + List domains = []; final Dio client = await getClient(); try { final Response response = await client.get('/domains'); - domains = response.data['domains']; + domains = response.data['domains']! + .map( + (final e) => DigitalOceanDomain.fromJson(e), + ) + .toList(); } catch (e) { print(e); return GenericResult( @@ -114,25 +117,18 @@ class DigitalOceanDnsApi extends RestApiMap { } Future> createMultipleDnsRecords({ - required final ServerDomain domain, - required final List records, + required final String domainName, + required final List records, }) async { - final String domainName = domain.domainName; final List allCreateFutures = []; final Dio client = await getClient(); try { - for (final DnsRecord record in records) { + for (final DigitalOceanDnsRecord record in records) { allCreateFutures.add( client.post( '/domains/$domainName/records', - data: { - 'type': record.type, - 'name': record.name, - 'data': record.content, - 'ttl': record.ttl, - 'priority': record.priority, - }, + data: record.toJson(), ), ); } @@ -155,17 +151,15 @@ class DigitalOceanDnsApi extends RestApiMap { } Future> removeSimilarRecords({ - required final ServerDomain domain, - required final List records, + required final String domainName, + required final List records, }) async { - final String domainName = domain.domainName; - final Dio client = await getClient(); try { final List allDeleteFutures = []; for (final record in records) { allDeleteFutures.add( - client.delete("/domains/$domainName/records/${record['id']}"), + client.delete('/domains/$domainName/records/${record.id}'), ); } await Future.wait(allDeleteFutures); @@ -183,12 +177,11 @@ class DigitalOceanDnsApi extends RestApiMap { return GenericResult(success: true, data: null); } - Future> getDnsRecords({ - required final ServerDomain domain, - }) async { + Future>> getDnsRecords( + final String domainName, + ) async { Response response; - final String domainName = domain.domainName; - List allRecords = []; + List allRecords = []; /// Default amount is 20, but we will eventually overflow it, /// so I hardcode it to the maximum available amount in advance just in case @@ -200,7 +193,12 @@ class DigitalOceanDnsApi extends RestApiMap { final Dio client = await getClient(); try { response = await client.get(url); - allRecords = response.data['domain_records'] ?? []; + allRecords = response.data['domain_records'] + .map( + (final e) => DigitalOceanDnsRecord.fromJson(e), + ) + .toList() ?? + []; } catch (e) { print(e); GenericResult( diff --git a/lib/logic/models/json/digital_ocean_dns_info.dart b/lib/logic/models/json/digital_ocean_dns_info.dart new file mode 100644 index 00000000..0881b214 --- /dev/null +++ b/lib/logic/models/json/digital_ocean_dns_info.dart @@ -0,0 +1,66 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'digital_ocean_dns_info.g.dart'; + +/// https://docs.digitalocean.com/reference/api/api-reference/#tag/Domains +@JsonSerializable() +class DigitalOceanDomain { + DigitalOceanDomain({ + required this.name, + this.ttl, + }); + + /// The name of the domain itself. + /// This should follow the standard domain format of domain.TLD. + /// + /// For instance, example.com is a valid domain name. + final String name; + + /// This value is the time to live for the records on this domain, in seconds. + /// + /// This defines the time frame that clients can cache queried information before a refresh should be requested. + final int? ttl; + + static DigitalOceanDomain fromJson(final Map json) => + _$DigitalOceanDomainFromJson(json); +} + +/// https://docs.digitalocean.com/reference/api/api-reference/#tag/Domain-Records +@JsonSerializable() +class DigitalOceanDnsRecord { + DigitalOceanDnsRecord({ + required this.id, + required this.name, + required this.type, + required this.ttl, + required this.data, + this.priority, + }); + + /// A unique identifier for each domain record. + final int? id; + + /// The host name, alias, or service being defined by the record. + final String name; + + /// The type of the DNS record. For example: A, CNAME, TXT, ... + final String type; + + /// This value is the time to live for the record, in seconds. + /// + /// This defines the time frame that clients can cache queried information before a refresh should be requested. + final int ttl; + + /// Variable data depending on record type. + /// + /// For example, the "data" value for an A record would be the IPv4 address to which the domain will be mapped. + /// For a CAA record, it would contain the domain name of the CA being granted permission to issue certificates. + final String data; + + /// The priority for SRV and MX records. + final int? priority; + + static DigitalOceanDnsRecord fromJson(final Map json) => + _$DigitalOceanDnsRecordFromJson(json); + Map toJson() => _$DigitalOceanDnsRecordToJson(this); +} diff --git a/lib/logic/models/json/digital_ocean_dns_info.g.dart b/lib/logic/models/json/digital_ocean_dns_info.g.dart new file mode 100644 index 00000000..d66c0352 --- /dev/null +++ b/lib/logic/models/json/digital_ocean_dns_info.g.dart @@ -0,0 +1,41 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'digital_ocean_dns_info.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +DigitalOceanDomain _$DigitalOceanDomainFromJson(Map json) => + DigitalOceanDomain( + name: json['name'] as String, + ttl: json['ttl'] as int?, + ); + +Map _$DigitalOceanDomainToJson(DigitalOceanDomain instance) => + { + 'name': instance.name, + 'ttl': instance.ttl, + }; + +DigitalOceanDnsRecord _$DigitalOceanDnsRecordFromJson( + Map json) => + DigitalOceanDnsRecord( + id: json['id'] as int?, + name: json['name'] as String, + type: json['type'] as String, + ttl: json['ttl'] as int, + data: json['data'] as String, + priority: json['priority'] as int?, + ); + +Map _$DigitalOceanDnsRecordToJson( + DigitalOceanDnsRecord instance) => + { + 'id': instance.id, + 'name': instance.name, + 'type': instance.type, + 'ttl': instance.ttl, + 'data': instance.data, + 'priority': instance.priority, + }; diff --git a/lib/logic/providers/dns_providers/digital_ocean_dns.dart b/lib/logic/providers/dns_providers/digital_ocean_dns.dart index 3056f94d..7397f41c 100644 --- a/lib/logic/providers/dns_providers/digital_ocean_dns.dart +++ b/lib/logic/providers/dns_providers/digital_ocean_dns.dart @@ -1,6 +1,7 @@ import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/desired_dns_record.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/digital_ocean_dns/digital_ocean_dns_api.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart'; +import 'package:selfprivacy/logic/models/json/digital_ocean_dns_info.dart'; import 'package:selfprivacy/logic/models/json/dns_records.dart'; import 'package:selfprivacy/logic/providers/dns_providers/dns_provider.dart'; @@ -59,7 +60,7 @@ class DigitalOceanDnsProvider extends DnsProvider { domains = result.data .map( - (final el) => el['name'] as String, + (final el) => el.name, ) .toList(); @@ -75,11 +76,22 @@ class DigitalOceanDnsProvider extends DnsProvider { final String? ip4, }) async => _adapter.api().createMultipleDnsRecords( - domain: domain, + domainName: domain.domainName, records: getProjectDnsRecords( domain.domainName, ip4, - ), + ) + .map( + (final e) => DigitalOceanDnsRecord( + name: e.name ?? '', + id: null, + data: e.content ?? '', + ttl: e.ttl, + type: e.type, + priority: e.priority, + ), + ) + .toList(), ); @override @@ -87,7 +99,7 @@ class DigitalOceanDnsProvider extends DnsProvider { required final ServerDomain domain, final String? ip4, }) async { - final result = await _adapter.api().getDnsRecords(domain: domain); + final result = await _adapter.api().getDnsRecords(domain.domainName); if (result.data.isEmpty || !result.success) { return GenericResult( success: result.success, @@ -98,15 +110,15 @@ class DigitalOceanDnsProvider extends DnsProvider { } const ignoreType = 'SOA'; - final filteredRecords = []; + final List filteredRecords = []; for (final record in result.data) { - if (record['type'] != ignoreType) { + if (record.type != ignoreType) { filteredRecords.add(record); } } return _adapter.api().removeSimilarRecords( - domain: domain, + domainName: domain.domainName, records: filteredRecords, ); } @@ -116,7 +128,7 @@ class DigitalOceanDnsProvider extends DnsProvider { required final ServerDomain domain, }) async { final List records = []; - final result = await _adapter.api().getDnsRecords(domain: domain); + final result = await _adapter.api().getDnsRecords(domain.domainName); if (result.data.isEmpty || !result.success) { return GenericResult( success: result.success, @@ -129,10 +141,10 @@ class DigitalOceanDnsProvider extends DnsProvider { for (final rawRecord in result.data) { records.add( DnsRecord( - name: rawRecord['name'], - type: rawRecord['type'], - content: rawRecord['data'], - ttl: rawRecord['ttl'], + name: rawRecord.name, + type: rawRecord.type, + content: rawRecord.data, + ttl: rawRecord.ttl, proxied: false, ), ); @@ -147,8 +159,17 @@ class DigitalOceanDnsProvider extends DnsProvider { final ServerDomain domain, ) async => _adapter.api().createMultipleDnsRecords( - domain: domain, - records: [record], + domainName: domain.domainName, + records: [ + DigitalOceanDnsRecord( + data: record.content ?? '', + id: null, + name: record.name ?? '', + ttl: record.ttl, + type: record.type, + priority: record.priority, + ), + ], ); @override