gandi_v5 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -0
  3. data/README.md +3 -2
  4. data/lib/gandi_v5.rb +34 -13
  5. data/lib/gandi_v5/domain.rb +2 -8
  6. data/lib/gandi_v5/email/forward.rb +2 -8
  7. data/lib/gandi_v5/email/mailbox.rb +2 -8
  8. data/lib/gandi_v5/live_dns.rb +0 -12
  9. data/lib/gandi_v5/live_dns/domain.rb +313 -29
  10. data/lib/gandi_v5/live_dns/domain/dnssec_key.rb +115 -0
  11. data/lib/gandi_v5/live_dns/domain/record.rb +81 -0
  12. data/lib/gandi_v5/live_dns/domain/snapshot.rb +107 -0
  13. data/lib/gandi_v5/live_dns/domain/tsig_key.rb +71 -0
  14. data/lib/gandi_v5/version.rb +1 -1
  15. data/spec/fixtures/bodies/GandiV5_LiveDNS_Domain/fetch.yml +1 -2
  16. data/spec/fixtures/bodies/GandiV5_LiveDNS_Domain/list_tsig.yml +3 -0
  17. data/spec/fixtures/bodies/GandiV5_LiveDNS_Domain/nameservers.yml +3 -0
  18. data/spec/fixtures/bodies/GandiV5_LiveDNS_Domain_DnssecKey/fetch.yml +12 -0
  19. data/spec/fixtures/bodies/GandiV5_LiveDNS_Domain_DnssecKey/list.yml +9 -0
  20. data/spec/fixtures/bodies/{GandiV5_LiveDNS_Zone_Snapshot → GandiV5_LiveDNS_Domain_Snapshot}/fetch.yml +4 -3
  21. data/spec/fixtures/bodies/GandiV5_LiveDNS_Domain_Snapshot/list.yml +5 -0
  22. data/spec/fixtures/bodies/GandiV5_LiveDNS_Domain_TsigKey/fetch.yml +9 -0
  23. data/spec/fixtures/bodies/GandiV5_LiveDNS_Domain_TsigKey/list.yml +4 -0
  24. data/spec/units/gandi_v5/domain_spec.rb +6 -37
  25. data/spec/units/gandi_v5/email/forward_spec.rb +5 -34
  26. data/spec/units/gandi_v5/email/mailbox_spec.rb +4 -34
  27. data/spec/units/gandi_v5/live_dns/domain/dnssec_key_spec.rb +128 -0
  28. data/spec/units/gandi_v5/live_dns/{record_set_spec.rb → domain/record_spec.rb} +1 -1
  29. data/spec/units/gandi_v5/live_dns/domain/snapshot_spec.rb +101 -0
  30. data/spec/units/gandi_v5/live_dns/domain/tsig_key_spec.rb +78 -0
  31. data/spec/units/gandi_v5/live_dns/domain_spec.rb +297 -118
  32. data/spec/units/gandi_v5/live_dns_spec.rb +0 -12
  33. data/spec/units/gandi_v5_spec.rb +111 -14
  34. metadata +18 -24
  35. data/lib/gandi_v5/live_dns/has_zone_records.rb +0 -153
  36. data/lib/gandi_v5/live_dns/record_set.rb +0 -79
  37. data/lib/gandi_v5/live_dns/zone.rb +0 -160
  38. data/lib/gandi_v5/live_dns/zone/snapshot.rb +0 -81
  39. data/spec/features/domain_spec.rb +0 -45
  40. data/spec/features/livedns_domain_spec.rb +0 -8
  41. data/spec/features/livedns_zone_spec.rb +0 -44
  42. data/spec/features/mailbox_spec.rb +0 -18
  43. data/spec/fixtures/bodies/GandiV5_LiveDNS_Zone/fetch.yml +0 -11
  44. data/spec/fixtures/bodies/GandiV5_LiveDNS_Zone/list.yml +0 -11
  45. data/spec/fixtures/bodies/GandiV5_LiveDNS_Zone_Snapshot/list.yml +0 -3
  46. data/spec/fixtures/vcr/Domain_features/List_domains.yml +0 -55
  47. data/spec/fixtures/vcr/Domain_features/Renew_domain.yml +0 -133
  48. data/spec/fixtures/vcr/LiveDNS_Domain_features/List_domains.yml +0 -32
  49. data/spec/fixtures/vcr/LiveDNS_Zone_features/List_zones.yml +0 -42
  50. data/spec/fixtures/vcr/LiveDNS_Zone_features/Make_and_save_snapshot.yml +0 -72
  51. data/spec/fixtures/vcr/LiveDNS_Zone_features/Save_zone_to_file.yml +0 -28
  52. data/spec/fixtures/vcr/Mailbox_features/List_mailboxes.yml +0 -39
  53. data/spec/units/gandi_v5/live_dns/zone/snapshot_spec.rb +0 -66
  54. data/spec/units/gandi_v5/live_dns/zone_spec.rb +0 -347
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 023623b5d81a2f3acc4a5fee812f0e8ca24dd8735f59a0f12b53ad35ddb7a397
4
- data.tar.gz: 3cf89099c45295da9b06a53ad64b601b3a77525a13ddade403d600c382be02d3
3
+ metadata.gz: 6873be5c700e1ef88075c90b71361bb9568b11661661dd0bc4f7d77cfdefd6f0
4
+ data.tar.gz: 13aac04c42357193607cff0f55c0502253fb6b527475e40ad5cb5d53011268ce
5
5
  SHA512:
6
- metadata.gz: 5e816b9de44330c832f6591ca6724b44f5b05c6fab53d8fda6d13a9820211292f29ee608953d8a562da97f4bcfee38f24f799b4a68184bba2b0ec751d21b3f61
7
- data.tar.gz: 622bded49d8bae3129ad74fae6fcaf1e8d7c3639b40348b7537e0ba46a3d5c668d2a134754dcf1ff31bcffe3cf0797f5c4368cbc849c386b4648f850d9e24117
6
+ metadata.gz: 46afb560fe574db5bf9d47e3e5bbce0843acef12edd132e26cba685608e4b1fa2dc1579dc30e287128ef6462924d56c14856168bcc2d277dededbf9360c45d0b
7
+ data.tar.gz: 9e35714cdb2ad68d57d11f4bdd689a6fc0103d155827682bb6a2f66340e8054af328761827f13e7af857c5c41263568daed84805e20b90e5b580917dea502eab
@@ -1,5 +1,32 @@
1
1
  # Gandi V5 API Gem Changelog
2
2
 
3
+ ## Version 0.7.0
4
+
5
+ * LiveDNS:
6
+ * Rename LiveDNS::RecordSet to LiveDNS::Domain::Record
7
+ * Domains:
8
+ * .list now returns an array of strings
9
+ * Can no longer change the zone used by a domain
10
+ * Added automatic_snapshots attribute for whether snapshots are automatically created when a modification is made to this domain's records
11
+ * #replace_records and #replace_records_for merged into #replace_records
12
+ * If replacing with a zone file use the new #replace_zone_lines
13
+ * Added:
14
+ * .create
15
+ * .record_types
16
+ * .generic_name_servers(fqdn)
17
+ * #name_servers and #fetch_name_servers
18
+ * #tsig_keys, #fetch_tsig_keys, #add_tsig_key, #remove_tsig_key
19
+ * #axfr_clients, #fetch_axfr_clients, #add_axfr_client, #remove_axfr_client
20
+ * ::DnssecKeys, #dnssec_keys, #fetch_dnssec_keys
21
+ * Snapshots:
22
+ * Moved to live under LiveDNS::Domain not LiveDNS::Zone
23
+ * Are now accessed via the fully qualified domain name NOT the zone's UUID
24
+ * Ability to access the zone from a snapshot is removed
25
+ * Taking a snapshot now allows for named snapshots
26
+ * Added automatic attribute for when a snapshot was taken due to a zone change
27
+ * .list now returns an array of snapshots (records are fetched in a seperate request when first needed)
28
+ * Zone removed.
29
+
3
30
  ## Version 0.6.0
4
31
 
5
32
  * GandiV5::Email::Slot.create now supports sharing_id
data/README.md CHANGED
@@ -36,12 +36,12 @@ Details of the API can be found at:
36
36
  If you're using bundler then add it to your Gemfile and run the bundle command.
37
37
 
38
38
  ```ruby
39
- gem 'gandi_v5', '~> 0.1'
39
+ gem 'gandi_v5', '~> 0.7'
40
40
  ```
41
41
 
42
42
  If you're not using bundler then install it from the command line.
43
43
  ```bash
44
- gem install gandi_v5 -v '~> 0.1'
44
+ gem install gandi_v5 -v '~> 0.7'
45
45
  ```
46
46
 
47
47
  ## Usage
@@ -77,6 +77,7 @@ We follow the [Semantic Versioning](http://semver.org/) concept.
77
77
 
78
78
  | Gem Version | Gandi API Release Date |
79
79
  | --------------- | ------------------------ |
80
+ | 0.7.0 | 2020-05-07 |
80
81
  | 0.6.0 | 2020-05-07 (not LiveDNS) |
81
82
  | 0.5.0 | 2019-10-01 |
82
83
  | 0.4.0 | 2019-10-01 |
@@ -82,6 +82,36 @@ class GandiV5
82
82
  handle_bad_request(e)
83
83
  end
84
84
 
85
+ # Might raise:
86
+ # * RestClient::NotFound
87
+ # * RestClient::Unauthorized
88
+ # Bad authentication attempt because of a wrong API Key.
89
+ # * RestClient::Forbidden
90
+ # Access to the resource is denied.
91
+ # Mainly due to a lack of permissions to access it.
92
+ # * GandiV5::Error
93
+ # * JSON::ParserError
94
+ def paginated_get(url, page = (1..), per_page = 100, **headers)
95
+ unless page.respond_to?(:each)
96
+ fail ArgumentError, 'page must be positive' unless page.positive?
97
+
98
+ page = [page]
99
+ end
100
+
101
+ headers[:params] ||= {}
102
+ headers[:params].transform_keys!(&:to_s)
103
+ headers[:params]['per_page'] = per_page
104
+
105
+ page.each do |page_number|
106
+ headers[:params]['page'] = page_number
107
+ _resp, this_data = get(url, **headers)
108
+ break if this_data.empty?
109
+
110
+ yield this_data
111
+ break if this_data.count < per_page
112
+ end
113
+ end
114
+
85
115
  # Might raise:
86
116
  # * RestClient::NotFound
87
117
  # * RestClient::Unauthorized
@@ -169,16 +199,6 @@ class GandiV5
169
199
  @api_key ||= ENV.fetch('GANDI_API_KEY')
170
200
  end
171
201
 
172
- def authorisation_header(url)
173
- if url.start_with?(BASE)
174
- { Authorization: "Apikey #{api_key}" }
175
- elsif url.start_with?(GandiV5::LiveDNS::BASE)
176
- { 'X-Api-Key': api_key }
177
- else
178
- fail ArgumentError, "Don't know how to authorise for url: #{url}"
179
- end
180
- end
181
-
182
202
  def parse_response(response)
183
203
  type = response.headers.fetch(:content_type).split(';').first.chomp
184
204
  case type
@@ -196,10 +216,11 @@ class GandiV5
196
216
  end
197
217
  end
198
218
 
199
- def prepare_headers(headers, url)
200
- headers.transform_keys!(&:to_sym)
219
+ def prepare_headers(headers, _url)
220
+ headers.transform_keys! { |key| key.to_s.downcase.to_sym }
201
221
  headers[:accept] ||= 'application/json'
202
- headers.merge!(authorisation_header(url))
222
+ headers[:authorization] = "Apikey #{api_key}"
223
+ headers
203
224
  end
204
225
 
205
226
  def handle_bad_request(exception)
@@ -492,7 +492,7 @@ class GandiV5
492
492
  # @param page [#each<Integer, #to_s>] the page(s) of results to retrieve.
493
493
  # If page is not provided keep querying until an empty list is returned.
494
494
  # If page responds to .each then iterate until an empty list is returned.
495
- # @param per_page [Integer, #to_s] (optional default 100) how many results ot get per page.
495
+ # @param per_page [Integer, #to_s] (optional default 100) how many results to get per page.
496
496
  # @param fqdn [String, #to_s] (optional)
497
497
  # filters the list by domain name, with optional patterns.
498
498
  # e.g. "example.net", "example.*", "*ample.com"
@@ -501,15 +501,9 @@ class GandiV5
501
501
  # @return [Array<GandiV5::Domain>]
502
502
  # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
503
503
  def self.list(page: (1..), per_page: 100, **params)
504
- page = [page.to_i] unless page.respond_to?(:each)
505
-
506
504
  domains = []
507
- page.each do |page_number|
508
- _resp, data = GandiV5.get url, params: params.merge(page: page_number, per_page: per_page)
509
- break if data.empty?
510
-
505
+ GandiV5.paginated_get(url, page, per_page, params: params) do |data|
511
506
  domains += data.map { |domain| from_gandi domain }
512
- break if data.count < per_page
513
507
  end
514
508
  domains
515
509
  end
@@ -76,18 +76,12 @@ class GandiV5
76
76
  # e.g. ("alice" "*lice", "alic*").
77
77
  # @return [Array<GandiV5::Email::Forward>]
78
78
  # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
79
- def self.list(fqdn, page: (1..), **params)
80
- page = [page.to_i] unless page.respond_to?(:each)
81
-
79
+ def self.list(fqdn, page: (1..), per_page: 100, **params)
82
80
  params.reject! { |_k, v| v.nil? }
83
81
 
84
82
  mailboxes = []
85
- page.each do |page_number|
86
- _response, data = GandiV5.get url(fqdn), params: params.merge(page: page_number)
87
- break if data.empty?
88
-
83
+ GandiV5.paginated_get(url(fqdn), page, per_page, params: params) do |data|
89
84
  mailboxes += data.map { |mailbox| from_gandi mailbox.merge(fqdn: fqdn) }
90
- break if data.count < params.fetch(:per_page, 100)
91
85
  end
92
86
  mailboxes
93
87
  end
@@ -197,19 +197,13 @@ class GandiV5
197
197
  # e.g. ("alice" "*lice", "alic*").
198
198
  # @return [Array<GandiV5::Email::Mailbox>]
199
199
  # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
200
- def self.list(fqdn, page: (1..), **params)
201
- page = [page.to_i] unless page.respond_to?(:each)
202
-
200
+ def self.list(fqdn, page: (1..), per_page: 100, **params)
203
201
  params['~login'] = params.delete(:login)
204
202
  params.reject! { |_k, v| v.nil? }
205
203
 
206
204
  mailboxes = []
207
- page.each do |page_number|
208
- _response, data = GandiV5.get url(fqdn), params: params.merge(page: page_number)
209
- break if data.empty?
210
-
205
+ GandiV5.paginated_get(url(fqdn), page, per_page, params: params) do |data|
211
206
  mailboxes += data.map { |mailbox| from_gandi mailbox }
212
- break if data.count < params.fetch(:per_page, 100)
213
207
  end
214
208
  mailboxes
215
209
  end
@@ -4,8 +4,6 @@
4
4
  class GandiV5
5
5
  # Gandi LiveDNS Management API.
6
6
  class LiveDNS
7
- BASE = 'https://dns.api.gandi.net/api/v5/'
8
-
9
7
  RECORD_TYPES = %w[
10
8
  A AAAA CNAME MX NS TXT ALIAS
11
9
  WKS SRV LOC SPF CAA DS SSHFP PTR KEY DNAME TLSA OPENPGPKEY CDS
@@ -21,16 +19,6 @@ class GandiV5
21
19
  GandiV5::LiveDNS::Domain.list
22
20
  end
23
21
 
24
- # @see GandiV5::LiveDNS::Zone.fetch
25
- def self.zone(uuid)
26
- GandiV5::LiveDNS::Zone.fetch(uuid)
27
- end
28
-
29
- # @see GandiV5::LiveDNS::Zone.list
30
- def self.zones
31
- GandiV5::LiveDNS::Zone.list
32
- end
33
-
34
22
  # Raise an error if passed type is invalid.
35
23
  # @param type [String] the record type to check.
36
24
  # @return [nil]
@@ -5,19 +5,12 @@ class GandiV5
5
5
  # A domain name within the LiveDNS system.
6
6
  # @!attribute [r] fqdn
7
7
  # @return [String]
8
- # @!attribute [r] zone_uuid
9
- # @return [String]
8
+ # @!attribute [r] automatic_snapshots
9
+ # @return [Boolean]
10
10
  class Domain
11
11
  include GandiV5::Data
12
- include GandiV5::LiveDNS::HasZoneRecords
13
-
14
- members :fqdn
15
12
 
16
- member(
17
- :zone_uuid,
18
- gandi_key: 'zone',
19
- converter: GandiV5::Data::Converter.new(from_gandi: ->(zone) { zone&.split('/')&.last })
20
- )
13
+ members :fqdn, :automatic_snapshots
21
14
 
22
15
  # Refetch the information for this domain from Gandi.
23
16
  # @return [GandiV5::LiveDNS::Domain]
@@ -27,35 +20,283 @@ class GandiV5
27
20
  from_gandi data
28
21
  end
29
22
 
30
- # Change the zone used by this domain.
31
- # @param uuid [String, #uuid, #to_s] the UUID of the zone this domain should now use.
23
+ # Update this domain's settings.
24
+ # @param automatic_snapshots [String, #to_s]
25
+ # Enable or disable the automatic creation of new snapshots when records are changed.
32
26
  # @return [String] The confirmation message from Gandi.
33
27
  # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
34
- def change_zone(uuid)
35
- uuid = uuid.uuid if uuid.respond_to?(:uuid)
36
- _response, data = GandiV5.patch url, { zone_uuid: uuid }.to_json
37
- self.zone_uuid = uuid
28
+ def update(automatic_snapshots:)
29
+ _response, data = GandiV5.patch url, { automatic_snapshots: automatic_snapshots }.to_json
30
+ self.automatic_snapshots = automatic_snapshots
38
31
  data['message']
39
32
  end
40
33
 
41
- # @see GandiV5::LiveDNS::Zone.fetch
42
- def fetch_zone
43
- GandiV5::LiveDNS::Zone.fetch zone_uuid
34
+ # @overload fetch_records()
35
+ # Fetch all records for this domain.
36
+ # @param page [Integer, #each<Integer>] which page(s) of results to get.
37
+ # If page is not provided keep querying until an empty list is returned.
38
+ # If page responds to .each then iterate until an empty list is returned.
39
+ # @param per_page [Integer, #to_s] (optional default 100) how many results ot get per page.
40
+ # @overload fetch_records(name)
41
+ # Fetch records for a name.
42
+ # @param name [String] the name to fetch records for.
43
+ # @param page [Integer, #each<Integer>] which page(s) of results to get.
44
+ # If page is not provided keep querying until an empty list is returned.
45
+ # If page responds to .each then iterate until an empty list is returned.
46
+ # @param per_page [Integer, #to_s] (optional default 100) how many results ot get per page.
47
+ # @overload fetch_records(name, type)
48
+ # Fetch records of a type for a name.
49
+ # @param name [String] the name to fetch records for.
50
+ # @param type [String] the record type to fetch.
51
+ # @param page [Integer, #each<Integer>] which page(s) of results to get.
52
+ # If page is not provided keep querying until an empty list is returned.
53
+ # If page responds to .each then iterate until an empty list is returned.
54
+ # @param per_page [Integer, #to_s] (optional default 100) how many results ot get per page.
55
+ # @return [Array<GandiV5::LiveDNS::Domain::Record>]
56
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
57
+ def fetch_records(name = nil, type = nil, page: (1..), per_page: 100)
58
+ GandiV5::LiveDNS.require_valid_record_type type if type
59
+
60
+ url_ = "#{url}/records"
61
+ url_ += "/#{CGI.escape name}" if name
62
+ url_ += "/#{CGI.escape type}" if type
63
+
64
+ all = []
65
+ GandiV5.paginated_get(url_, page, per_page) do |data|
66
+ all += [*data].map { |item| GandiV5::LiveDNS::Domain::Record.from_gandi item }
67
+ end
68
+ all
44
69
  end
45
70
 
46
- # The domain's zone (fetching from Gandi if required).
47
- # @return [GandiV5::LiveDNS::Zone]
71
+ # @overload fetch_zone_lines()
72
+ # Fetch all records for this domain.
73
+ # @overload fetch_zone_lines(name)
74
+ # Fetch records for a name.
75
+ # @param name [String] the name to fetch records for.
76
+ # @overload fetch_zone_lines(name, type)
77
+ # Fetch records of a type for a name.
78
+ # @param name [String] the name to fetch records for.
79
+ # @param type [String] the record type to fetch.
80
+ # @return [String]
48
81
  # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
49
- def zone
50
- @zone ||= fetch_zone
82
+ def fetch_zone_lines(name = nil, type = nil)
83
+ GandiV5::LiveDNS.require_valid_record_type type if type
84
+
85
+ url_ = "#{url}/records"
86
+ url_ += "/#{CGI.escape name}" if name
87
+ url_ += "/#{CGI.escape type}" if type
88
+
89
+ GandiV5.get(url_, accept: 'text/plain').last
90
+ end
91
+
92
+ # Add record to this domain.
93
+ # @param name [String]
94
+ # @param type [String]
95
+ # @param ttl [Integer]
96
+ # @param values [Array<String>]
97
+ # @return [String] The confirmation message from Gandi.
98
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
99
+ def add_record(name, type, ttl, *values)
100
+ GandiV5::LiveDNS.require_valid_record_type type
101
+ fail ArgumentError, 'ttl must be positive and non-zero' unless ttl.positive?
102
+ fail ArgumentError, 'there must be at least one value' if values.none?
103
+
104
+ body = {
105
+ rrset_name: name,
106
+ rrset_type: type,
107
+ rrset_ttl: ttl,
108
+ rrset_values: values
109
+ }.to_json
110
+ _response, data = GandiV5.post "#{url}/records", body
111
+ data['message']
112
+ end
113
+
114
+ # @overload delete_records()
115
+ # Delete all records for this domain.
116
+ # @overload delete_records(name)
117
+ # Delete records for a name.
118
+ # @param name [String] the name to delete records for.
119
+ # @overload delete_records(name, type)
120
+ # Delete records of a type for a name.
121
+ # @param name [String] the name to delete records for.
122
+ # @param type [String] the record type to delete.
123
+ # @return [nil]
124
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
125
+ def delete_records(name = nil, type = nil)
126
+ GandiV5::LiveDNS.require_valid_record_type(type) if type
127
+
128
+ url_ = "#{url}/records"
129
+ url_ += "/#{CGI.escape name}" if name
130
+ url_ += "/#{CGI.escape type}" if type
131
+ GandiV5.delete(url_).last
132
+ end
133
+
134
+ # Replace records for the domain.
135
+ # @param name [String, nil] only replaces records for this name.
136
+ # @param type [String, nil] only replaces record of this type (requires name).
137
+ # @param values [Array<String>] the values to set for the record.
138
+ # @raise [ArgumentError] if ttl is present and type is absent.
139
+ # @return [String] The confirmation message from Gandi.
140
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
141
+ # rubocop:disable Metrics/PerceivedComplexity
142
+ def replace_records(records, name: nil, type: nil)
143
+ if type
144
+ GandiV5::LiveDNS.require_valid_record_type(type) if type
145
+ fail ArgumentError, 'missing keyword: name' if name.nil?
146
+ end
147
+
148
+ url_ = "#{url}/records"
149
+ url_ += "/#{CGI.escape name}" if name
150
+ url_ += "/#{CGI.escape type}" if type
151
+
152
+ body = if type && name
153
+ { rrset_values: records }
154
+ else
155
+ { items: records.map { |r| r.transform_keys { |k| "rrset_#{k}" } } }
156
+ end
157
+
158
+ _response, data = GandiV5.put url_, body.to_json
159
+ data['message']
160
+ end
161
+ # rubocop:enable Metrics/PerceivedComplexity
162
+
163
+ # Replace all records for this domain.
164
+ # @param text [String] zone file lines to replace the records with.
165
+ # @return [String] The confirmation message from Gandi.
166
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
167
+ def replace_zone_lines(text)
168
+ _response, data = GandiV5.put "#{url}/records", text, 'content-type': 'text/plain'
169
+ data['message']
170
+ end
171
+
172
+ # The list of nameservers that this domain is using according to LiveDNS' systems.
173
+ # * Either there are no NS records on @ and the 3 hashed nameservers are returned
174
+ # (ns-{123}-{abc}.gandi.net)
175
+ # * Or some NS records exist on @ and it will return those
176
+ # @see https://api.gandi.net/docs/domains#get-v5-domain-domains-domain-name$
177
+ # @return [Array<String>]
178
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
179
+ def name_servers
180
+ @name_servers ||= fetch_name_servers
181
+ end
182
+
183
+ # Requery Gandi for the domain's name servers.
184
+ # @see https://api.gandi.net/docs/domains#get-v5-domain-domains-domain-name$
185
+ # @return [Array<String>]
186
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
187
+ def fetch_name_servers
188
+ _response, data = GandiV5.get "#{url}/nameservers"
189
+ @name_servers = data
190
+ end
191
+
192
+ # The list of DNSSEC keys for the domain.
193
+ # If you need the fingerprint, public_key or tag attributes you'll need
194
+ # use GandiV5::LiveDNS::Domain::DnssecKey.fetch on each item.
195
+ # @return [Array<GandiV5::LiveDNS::Domain::DnssecKey>]
196
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
197
+ def dnssec_keys
198
+ @dnssec_keys ||= fetch_dnssec_keys
199
+ end
200
+
201
+ # Requery Gandi for the domain's DNSSEC keys.
202
+ # @return [Array<GandiV5::LiveDNS::Domain::DnssecKey>]
203
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
204
+ def fetch_dnssec_keys
205
+ @dnssec_keys = GandiV5::LiveDNS::Domain::DnssecKey.list(fqdn)
206
+ end
207
+
208
+ # The list of TSIG keys for the domain.
209
+ # If you need the secret, fingerprint, public_key or tag attributes you'll need
210
+ # to use GandiV5::LiveDNS::Domain::DnssecKey.fetch on each item.
211
+ # @return [Array<GandiV5::LiveDNS::Domain::TsigKey>]
212
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
213
+ def tsig_keys
214
+ @tsig_keys ||= fetch_tsig_keys
215
+ end
216
+
217
+ # Requery Gandi for the domain's TSIG keys.
218
+ # @return [Array<GandiV5::LiveDNS::Domain::TsigKey>]
219
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
220
+ def fetch_tsig_keys
221
+ _response, data = GandiV5.get "#{url}/axfr/tsig"
222
+ data.map { |item| GandiV5::LiveDNS::Domain::TsigKey.from_gandi item }
223
+ end
224
+
225
+ # Add a Tsig key to this domain.
226
+ # @param key [GandiV5::LiveDNS::Domain::TsigKey, #uuid, String, #to_s]
227
+ # the key to add.
228
+ # @param sharing_id [nil, String, #to_s]
229
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
230
+ def add_tsig_key(key, sharing_id: nil)
231
+ key = key.uuid if key.respond_to?(:uuid)
232
+ url_ = "#{url}/axfr/tsig/#{key}"
233
+ url_ += "?sharing_id=#{CGI.escape sharing_id}" if sharing_id
234
+ _response, _data = GandiV5.put url_
235
+ end
236
+
237
+ # Remove a Tsig key from this domain.
238
+ # @param key [GandiV5::LiveDNS::Domain::TsigKey, #uuid, String, #to_s]
239
+ # the key to remove.
240
+ # @param sharing_id [nil, String, #to_s]
241
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
242
+ def remove_tsig_key(key, sharing_id: nil)
243
+ key = key.uuid if key.respond_to?(:uuid)
244
+ url_ = "#{url}/axfr/tsig/#{key}"
245
+ url_ += "?sharing_id=#{CGI.escape sharing_id}" if sharing_id
246
+ _response, _data = GandiV5.delete url_
247
+ end
248
+
249
+ # The list of AXFR clients for the domain.
250
+ # @return [Array<String>] list of IP addresses.
251
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
252
+ def axfr_clients
253
+ @axfr_clients ||= fetch_axfr_clients
254
+ end
255
+
256
+ # Requery Gandi for the domain's AXFR clients.
257
+ # @return [Array<String>] list of IP addresses.
258
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
259
+ def fetch_axfr_clients
260
+ _response, data = GandiV5.get "#{url}/axfr/slaves"
261
+ data
262
+ end
263
+
264
+ # Add an AXFR client to this domain.
265
+ # @param ip [String, #to_s] the IP address to add.
266
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
267
+ def add_axfr_client(ip)
268
+ _response, _data = GandiV5.put "#{url}/axfr/slaves/#{ip}"
269
+ end
270
+
271
+ # Remove and AXFR client from this domain.
272
+ # @param ip [String, #to_s] the IP address to remove.
273
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
274
+ def remove_axfr_client(ip)
275
+ _response, _data = GandiV5.delete "#{url}/axfr/slaves/#{ip}"
51
276
  end
52
277
 
53
278
  # List the domains.
54
- # @return [Array<GandiV5::LiveDNS::Domain>]
279
+ # @param page [#each<Integer, #to_s>] the page(s) of results to retrieve.
280
+ # If page is not provided keep querying until an empty list is returned.
281
+ # If page responds to .each then iterate until an empty list is returned.
282
+ # @param per_page [Integer, #to_s] (optional default 100) how many results to get per page.
283
+ # @param sharing_id [String, #to_s] (optional) filter by domains assigned to a given user.
284
+ # @return [Array<String>]
55
285
  # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
56
- def self.list
57
- _response, data = GandiV5.get url
58
- data.map { |item| from_gandi item }
286
+ def self.list(page: (1..), per_page: 100, sharing_id: nil)
287
+ page = [page.to_i] unless page.respond_to?(:each)
288
+ params = { per_page: per_page }
289
+ params[:sharing_id] = sharing_id unless sharing_id.nil?
290
+
291
+ domains = []
292
+ page.each do |page_number|
293
+ _resp, data = GandiV5.get url, params: params.merge(page: page_number)
294
+ break if data.empty?
295
+
296
+ domains += data.map { |item| item['fqdn'] }
297
+ break if data.count < per_page
298
+ end
299
+ domains
59
300
  end
60
301
 
61
302
  # Get a domain.
@@ -67,14 +308,57 @@ class GandiV5
67
308
  from_gandi data
68
309
  end
69
310
 
311
+ # Create a new domain in the LiveDNS system.
312
+ # You must have sufficent permission to manage the domain to do this.
313
+ # @param fqdn [String, #to_s] the fully qualified domain to add to LiveDNS.
314
+ # @param records [Array<Hash, GandiV5::LiveDNS::Domain::Record, #to_h, nil>]
315
+ # @param ttl [Integer, #to_s, nil] the TTL of the SOA record.
316
+ # Note that this is not a default TTL that will be used for the records in the zone.
317
+ # the records (if any) to add to the created zone.
318
+ # @param sharing_id [nil, String, #to_s]
319
+ # @return [GandiV5::LiveDNS::Domain]
320
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
321
+ def self.create(fqdn, records = nil, soa_ttl: nil, sharing_id: nil)
322
+ body = { fqdn: fqdn, zone: {} }
323
+ body[:zone][:ttl] = soa_ttl if soa_ttl
324
+ if records
325
+ body[:zone][:items] = records.map do |r|
326
+ r.to_h.transform_keys { |k| "rrset_#{k}" }
327
+ end
328
+ end
329
+
330
+ url_ = url
331
+ url_ += "?sharing_id=#{CGI.escape sharing_id}" if sharing_id
332
+
333
+ GandiV5.post url_, body.to_json
334
+ fetch(fqdn)
335
+ end
336
+
337
+ # Fetch the list of known record types (A, CNAME, etc.)
338
+ # @return [Array<String>]
339
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
340
+ def self.record_types
341
+ GandiV5.get("#{BASE}livedns/dns/rrtypes").last
342
+ end
343
+
344
+ # Get the LiveDNS servers to use for a domain.
345
+ # Does not take into account any NS records that exist in the zone.
346
+ # @param fqdn [String, #to_s] the fully qualified domain to hash in
347
+ # in order to get the LiveDNS servers to use.
348
+ # @return [Array<String>]
349
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
350
+ def self.generic_name_servers(fqdn)
351
+ GandiV5.get("#{BASE}livedns/nameservers/#{CGI.escape fqdn}").last
352
+ end
353
+
70
354
  private
71
355
 
72
356
  def url
73
- "#{BASE}domains/#{CGI.escape(fqdn)}"
357
+ "#{BASE}livedns/domains/#{CGI.escape fqdn}"
74
358
  end
75
359
 
76
360
  def self.url(fqdn = nil)
77
- "#{BASE}domains" + (fqdn ? "/#{CGI.escape(fqdn)}" : '')
361
+ "#{BASE}livedns/domains" + (fqdn ? "/#{CGI.escape fqdn}" : '')
78
362
  end
79
363
  private_class_method :url
80
364
  end