gandi_v5 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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