gandi_v5 0.1.0 → 0.2.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 (107) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/.travis.yml +1 -0
  4. data/CHANGELOG.md +44 -2
  5. data/Guardfile +5 -6
  6. data/README.md +5 -5
  7. data/doc/GandiV5/Billing/Info/Prepaid.html +817 -0
  8. data/doc/GandiV5/Billing/Info.html +641 -0
  9. data/doc/GandiV5/Billing.html +293 -0
  10. data/doc/GandiV5/Data/ClassMethods.html +223 -0
  11. data/doc/GandiV5/Data/Converter/ArrayOf.html +413 -0
  12. data/doc/GandiV5/Data/Converter/Symbol.html +322 -0
  13. data/doc/GandiV5/Data/Converter/Time.html +330 -0
  14. data/doc/GandiV5/Data/Converter.html +433 -0
  15. data/doc/GandiV5/Data.html +785 -0
  16. data/doc/GandiV5/Domain/AutoRenew.html +1237 -0
  17. data/doc/GandiV5/Domain/Availability/Product/Period.html +220 -0
  18. data/doc/GandiV5/Domain/Availability/Product/Price.html +1031 -0
  19. data/doc/GandiV5/Domain/Availability/Product.html +988 -0
  20. data/doc/GandiV5/Domain/Availability/Tax.html +440 -0
  21. data/doc/GandiV5/Domain/Availability.html +1020 -0
  22. data/doc/GandiV5/Domain/Contact.html +4459 -0
  23. data/doc/GandiV5/Domain/Contract.html +520 -0
  24. data/doc/GandiV5/Domain/Dates.html +1313 -0
  25. data/doc/GandiV5/Domain/RenewalInformation.html +1147 -0
  26. data/doc/GandiV5/Domain/RestoreInformation.html +339 -0
  27. data/doc/GandiV5/Domain/SharingSpace.html +437 -0
  28. data/doc/GandiV5/Domain/TLD.html +1565 -0
  29. data/doc/GandiV5/Domain.html +16847 -0
  30. data/doc/GandiV5/Email/Mailbox/Responder.html +1560 -0
  31. data/doc/GandiV5/Email/Mailbox.html +6307 -0
  32. data/doc/GandiV5/Email/Offer.html +514 -0
  33. data/doc/GandiV5/Email/Slot.html +4244 -0
  34. data/doc/GandiV5/Email.html +144 -0
  35. data/doc/GandiV5/Error/GandiError.html +270 -0
  36. data/doc/GandiV5/Error.html +151 -0
  37. data/doc/GandiV5/LiveDNS/Domain.html +2984 -0
  38. data/doc/GandiV5/LiveDNS/RecordSet.html +1593 -0
  39. data/doc/GandiV5/LiveDNS/Zone/Snapshot.html +1556 -0
  40. data/doc/GandiV5/LiveDNS/Zone.html +8891 -0
  41. data/doc/GandiV5/LiveDNS.html +300 -0
  42. data/doc/GandiV5/Organization.html +2341 -0
  43. data/doc/GandiV5.html +1183 -0
  44. data/doc/_index.html +474 -0
  45. data/doc/class_list.html +51 -0
  46. data/doc/css/common.css +1 -0
  47. data/doc/css/full_list.css +58 -0
  48. data/doc/css/style.css +496 -0
  49. data/doc/file.README.html +175 -0
  50. data/doc/file_list.html +56 -0
  51. data/doc/frames.html +17 -0
  52. data/doc/index.html +175 -0
  53. data/doc/js/app.js +303 -0
  54. data/doc/js/full_list.js +216 -0
  55. data/doc/js/jquery.js +4 -0
  56. data/doc/method_list.html +2427 -0
  57. data/doc/top-level-namespace.html +110 -0
  58. data/gandi_v5.gemspec +1 -1
  59. data/lib/gandi_v5/billing.rb +2 -2
  60. data/lib/gandi_v5/data.rb +2 -0
  61. data/lib/gandi_v5/domain/auto_renew.rb +4 -4
  62. data/lib/gandi_v5/domain/availability/product/period.rb +24 -0
  63. data/lib/gandi_v5/domain/availability/product/price.rb +36 -0
  64. data/lib/gandi_v5/domain/availability/product.rb +52 -0
  65. data/lib/gandi_v5/domain/availability/tax.rb +20 -0
  66. data/lib/gandi_v5/domain/availability.rb +49 -0
  67. data/lib/gandi_v5/domain/tld.rb +57 -0
  68. data/lib/gandi_v5/domain.rb +41 -86
  69. data/lib/gandi_v5/email/mailbox/responder.rb +43 -2
  70. data/lib/gandi_v5/email/mailbox.rb +32 -21
  71. data/lib/gandi_v5/email/offer.rb +2 -2
  72. data/lib/gandi_v5/email/slot.rb +42 -16
  73. data/lib/gandi_v5/error/gandi_error.rb +2 -2
  74. data/lib/gandi_v5/live_dns/domain.rb +59 -45
  75. data/lib/gandi_v5/live_dns/zone/snapshot.rb +27 -8
  76. data/lib/gandi_v5/live_dns/zone.rb +63 -59
  77. data/lib/gandi_v5/live_dns.rb +20 -0
  78. data/lib/gandi_v5/organization.rb +2 -2
  79. data/lib/gandi_v5/version.rb +1 -1
  80. data/lib/gandi_v5.rb +25 -5
  81. data/spec/features/domain_spec.rb +1 -1
  82. data/spec/fixtures/bodies/{GandiV5_Domain/availability.yaml → GandiV5_Domain_Availability/fetch.yaml} +0 -0
  83. data/spec/fixtures/bodies/{GandiV5_Domain/tld.yaml → GandiV5_Domain_TLD/fetch.yaml} +0 -0
  84. data/spec/fixtures/bodies/{GandiV5_Domain/tlds.yaml → GandiV5_Domain_TLD/list.yaml} +0 -0
  85. data/spec/fixtures/bodies/GandiV5_LiveDNS_Zone_Snapshot/{get.yaml → fetch.yaml} +0 -0
  86. data/spec/fixtures/bodies/GandiV5_LiveDNS_Zone_Snapshot/list.yaml +3 -0
  87. data/spec/units/gandi_v5/billing_spec.rb +2 -2
  88. data/spec/units/gandi_v5/domain/auto_renew_spec.rb +5 -5
  89. data/spec/units/gandi_v5/domain/availability/product/period_spec.rb +4 -0
  90. data/spec/units/gandi_v5/domain/availability/product/price_spec.rb +4 -0
  91. data/spec/units/gandi_v5/domain/availability/product_spec.rb +4 -0
  92. data/spec/units/gandi_v5/domain/availability/tax_spec.rb +4 -0
  93. data/spec/units/gandi_v5/domain/availability_spec.rb +43 -0
  94. data/spec/units/gandi_v5/domain/tld_spec.rb +29 -0
  95. data/spec/units/gandi_v5/domain_spec.rb +89 -81
  96. data/spec/units/gandi_v5/email/mailbox/responder_spec.rb +52 -0
  97. data/spec/units/gandi_v5/email/mailbox_spec.rb +56 -27
  98. data/spec/units/gandi_v5/email/offer_spec.rb +1 -1
  99. data/spec/units/gandi_v5/email/slot_spec.rb +101 -13
  100. data/spec/units/gandi_v5/live_dns/domain_spec.rb +70 -40
  101. data/spec/units/gandi_v5/live_dns/zone/snapshot_spec.rb +32 -3
  102. data/spec/units/gandi_v5/live_dns/zone_spec.rb +68 -50
  103. data/spec/units/gandi_v5/live_dns_spec.rb +24 -0
  104. data/spec/units/gandi_v5/organization_spec.rb +1 -1
  105. data/spec/units/gandi_v5_spec.rb +37 -12
  106. metadata +72 -9
  107. data/TODO.md +0 -29
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # TODO: Allow enable/disable from here too ???
4
-
5
3
  class GandiV5
6
4
  class Email
7
5
  class Mailbox
@@ -15,13 +13,26 @@ class GandiV5
15
13
  # @return [nil, Time]
16
14
  # @!attribute [r] message
17
15
  # @return [nil, String]
16
+ # @!attribute [r] mailbox
17
+ # @return [GandiV5::Email::Mailbox] the mailbox this repsonder belongs to.
18
18
  class Responder
19
19
  include GandiV5::Data
20
20
 
21
+ attr_reader :mailbox
22
+
21
23
  members :enabled, :message
22
24
  member :starts_at, converter: GandiV5::Data::Converter::Time
23
25
  member :ends_at, converter: GandiV5::Data::Converter::Time
24
26
 
27
+ # Create a new GandiV5::Email::Mailbox::Responder
28
+ # @param mailbox [GandiV5::Email::Mailbox] the mailbox this responder belongs to.
29
+ # @param members [Hash<Symbol => Object>]
30
+ # @return [GandiV5::Email::Slot]
31
+ def initialize(mailbox: nil, **members)
32
+ super(**members)
33
+ @mailbox = mailbox
34
+ end
35
+
25
36
  # Check if this responder is currently active.
26
37
  # @return [Boolean] whether the responder is enabled,
27
38
  # started in the past and ends in the future.
@@ -30,6 +41,36 @@ class GandiV5
30
41
  (starts_at.nil? || starts_at < Time.now) &&
31
42
  (ends_at.nil? || ends_at > Time.now)
32
43
  end
44
+
45
+ # Enable the auto responder in Gandi.
46
+ # @param message [String]
47
+ # @param starts_at [Time]
48
+ # @param ends_at [Time]
49
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
50
+ def enable(message:, ends_at:, starts_at: Time.now)
51
+ mailbox.update responder: {
52
+ message: message,
53
+ starts_at: GandiV5::Data::Converter::Time.to_gandi(starts_at),
54
+ ends_at: GandiV5::Data::Converter::Time.to_gandi(ends_at),
55
+ enabled: true
56
+ }
57
+
58
+ self.starts_at = starts_at
59
+ self.ends_at = ends_at
60
+ self.message = message
61
+ self.enabled = true
62
+ end
63
+
64
+ # Disable the auto responder in Gandi.
65
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
66
+ def disable
67
+ mailbox.update responder: { enabled: false }
68
+
69
+ self.starts_at = nil
70
+ self.ends_at = nil
71
+ self.message = nil
72
+ self.enabled = false
73
+ end
33
74
  end
34
75
  end
35
76
  end
@@ -44,33 +44,43 @@ class GandiV5
44
44
 
45
45
  alias mailbox_uuid uuid
46
46
 
47
+ # Create a new GandiV5::Email::Mailbox
48
+ # @param members [Hash<Symbol => Object>]
49
+ # @return [GandiV5::Email::Slot]
50
+ def initialize(**members)
51
+ super(**members)
52
+ responder.instance_exec(self) { |mb| @mailbox = mb } if responder?
53
+ end
54
+
47
55
  # Delete the mailbox and it's contents.
48
56
  # If you delete a mailbox for which you have purchased a slot,
49
57
  # this action frees the slot so it once again becomes available
50
58
  # for use with a new mailbox, or for deletion.
51
59
  # @see https://api.gandi.net/docs/email#delete-v5-email-mailboxes-domain-mailbox_id
52
60
  # @return [String] The confirmation message from Gandi.
53
- # @raise [GandiV5::Error::GandiError::GandiError] if Gandi returns an error.
61
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
54
62
  def delete
55
- data = GandiV5.delete url
63
+ _response, data = GandiV5.delete url
56
64
  data['message']
57
65
  end
58
66
 
59
67
  # Purge the contents of the mailbox.
60
68
  # @see https://api.gandi.net/docs/email#delete-v5-email-mailboxes-domain-mailbox_id-contents
61
69
  # @return [String] The confirmation message from Gandi.
62
- # @raise [GandiV5::Error::GandiError::GandiError] if Gandi returns an error.
70
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
63
71
  def purge
64
- data = GandiV5.delete "#{url}/contents"
72
+ _response, data = GandiV5.delete "#{url}/contents"
65
73
  data['message']
66
74
  end
67
75
 
68
76
  # Requery Gandi fo this mailbox's information.
69
77
  # @return [GandiV5::Email::Mailbox]
70
- # @raise [GandiV5::Error::GandiError::GandiError] if Gandi returns an error.
78
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
71
79
  def refresh
72
- data = GandiV5.get url
80
+ _response, data = GandiV5.get url
73
81
  from_gandi data
82
+ responder.instance_exec(self) { |mb| @mailbox = mb } if responder?
83
+ self
74
84
  end
75
85
 
76
86
  # Update the mailbox's settings.
@@ -81,8 +91,7 @@ class GandiV5
81
91
  # @param responder [Hash, GandiV5::Mailbox::Responder, #to_gandi, #to_h]
82
92
  # auto responder settings.
83
93
  # @return [String] The confirmation message from Gandi.
84
- # @raise [GandiV5::Error::GandiError::GandiError] if Gandi returns an error.
85
- # rubocop:disable Metrics/AbcSize
94
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
86
95
  def update(**body)
87
96
  return 'Nothing to update.' if body.empty?
88
97
 
@@ -93,11 +102,10 @@ class GandiV5
93
102
  body[:responder] = responder.respond_to?(:to_gandi) ? responder.to_gandi : responder.to_h
94
103
  end
95
104
 
96
- data = GandiV5.patch url, body.to_json
105
+ _response, data = GandiV5.patch url, body.to_json
97
106
  refresh
98
107
  data['message']
99
108
  end
100
- # rubocop:enable Metrics/AbcSize
101
109
 
102
110
  # Create a new mailbox.
103
111
  # Note that before you can create a mailbox, you must have a slot available.
@@ -107,13 +115,16 @@ class GandiV5
107
115
  # @param password [String, #to_s] the password to use.
108
116
  # @param aliases [Array<String, #to_s>] any alternative email address to be used.
109
117
  # @param type [:standard, :premium] the type of mailbox slot to use.
110
- # @return [String] The confirmation message from Gandi.
111
- # @raise [GandiV5::Error::GandiError::GandiError] if Gandi returns an error.
112
- # TODO: Fetch created mailbox
118
+ # @return [GandiV5::Email::Mailbox] The created mailbox.
119
+ # @raise [GandiV5::Error] if no slots are available.
120
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
113
121
  def self.create(fqdn, login, password, aliases: [], type: :standard)
114
- # TODO: Check type is valid
122
+ fail ArgumentError, "#{type.inspect} is not a valid type" unless TYPES.include?(type)
123
+ if GandiV5::Email::Slot.list.none? { |slot| slot.mailbox_type == type && slot.inactive? }
124
+ fail GandiV5::Error, "no available #{type} slots"
125
+ end
126
+
115
127
  check_password password
116
- # TODO: Check if a slot is available
117
128
 
118
129
  body = {
119
130
  mailbox_type: type,
@@ -122,8 +133,8 @@ class GandiV5
122
133
  aliases: aliases.push
123
134
  }.to_json
124
135
 
125
- data = GandiV5.post url(fqdn), body
126
- data['message']
136
+ response, _data = GandiV5.post url(fqdn), body
137
+ fetch fqdn, response.headers[:location].split('/').last
127
138
  end
128
139
 
129
140
  # Get information for a mailbox.
@@ -131,9 +142,9 @@ class GandiV5
131
142
  # @param fqdn [String, #to_s] the fully qualified domain name for the mailbox.
132
143
  # @param uuid [String, #to_s] unique identifier of the mailbox.
133
144
  # @return [GandiV5::Email::Mailbox]
134
- # @raise [GandiV5::Error::GandiError::GandiError] if Gandi returns an error.
145
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
135
146
  def self.fetch(fqdn, uuid)
136
- data = GandiV5.get url(fqdn, uuid)
147
+ _response, data = GandiV5.get url(fqdn, uuid)
137
148
  from_gandi data
138
149
  end
139
150
 
@@ -149,7 +160,7 @@ class GandiV5
149
160
  # @param login [String] (optional) filter the list by login (pattern)
150
161
  # e.g. ("alice" "*lice", "alic*").
151
162
  # @return [Array<GandiV5::Email::Mailbox>]
152
- # @raise [GandiV5::Error::GandiError::GandiError] if Gandi returns an error.
163
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
153
164
  def self.list(fqdn, page: (1..), **params)
154
165
  page = [page.to_i] unless page.respond_to?(:each)
155
166
 
@@ -158,7 +169,7 @@ class GandiV5
158
169
 
159
170
  mailboxes = []
160
171
  page.each do |page_number|
161
- data = GandiV5.get url(fqdn), params: params.merge(page: page_number)
172
+ _response, data = GandiV5.get url(fqdn), params: params.merge(page: page_number)
162
173
  break if data.empty?
163
174
 
164
175
  mailboxes += data.map { |mailbox| from_gandi mailbox }
@@ -17,9 +17,9 @@ class GandiV5
17
17
  # @see https://api.gandi.net/docs/email#get-v5-email-offers-domain
18
18
  # @param fqdn [String, #to_s] the fully qualified domain name to get the offer for.
19
19
  # @return [GandiV5::Email::Offer]
20
- # @raise [GandiV5::Error::GandiError::GandiError] if Gandi returns an error.
20
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
21
21
  def self.fetch(fqdn)
22
- data = GandiV5.get "#{BASE}email/offers/#{CGI.escape fqdn}"
22
+ _response, data = GandiV5.get "#{BASE}email/offers/#{CGI.escape fqdn}"
23
23
  from_gandi data
24
24
  end
25
25
  end
@@ -39,12 +39,12 @@ class GandiV5
39
39
  alias slot_id id
40
40
 
41
41
  # Create a new GandiV5::Email::Slot
42
- # @param string [fqdn] the fully qualified domain this slot belongs to.
42
+ # @param fqdn [String] the fully qualified domain this slot belongs to.
43
43
  # @param members [Hash<Symbol => Object>]
44
44
  # @return [GandiV5::Email::Slot]
45
45
  def initialize(fqdn: nil, **members)
46
46
  super(**members)
47
- @fqdn = fqdn if fqdn
47
+ @fqdn = fqdn
48
48
  end
49
49
 
50
50
  # Delete this slot if it is inactive and refundable.
@@ -53,20 +53,23 @@ class GandiV5
53
53
  # @see GandiV5::Email::Mailbox#delete
54
54
  # @see https://api.gandi.net/docs/email#delete-v5-email-slots-domain-slot_id
55
55
  # @return [String] The confirmation message from Gandi.
56
- # @raise [GandiV5::Error::GandiError::GandiError] if Gandi returns an error.
57
- # TODO: check for inactiveness
58
- # TODO: check for refundableness
56
+ # @raise [GandiV5::Error] if slot is active.
57
+ # @raise [GandiV5::Error] if slot is not refundable.
58
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
59
59
  def delete
60
- data = GandiV5.delete url
60
+ fail GandiV5::Error, 'slot can\'t be deleted whilst active' if active?
61
+ fail GandiV5::Error, 'slot can\'t be deleted if it\'s not refundable' unless refundable
62
+
63
+ _response, data = GandiV5.delete url
61
64
  data['message']
62
65
  end
63
66
 
64
67
  # Requery Gandi for this slot's information.
65
68
  # @see https://api.gandi.net/docs/email#get-v5-email-slots-domain-slot_id
66
69
  # @return [GandiV5::Email::Slot]
67
- # @raise [GandiV5::Error::GandiError::GandiError] if Gandi returns an error.
70
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
68
71
  def refresh
69
- data = GandiV5.get url
72
+ _response, data = GandiV5.get url
70
73
  from_gandi data
71
74
  end
72
75
 
@@ -77,15 +80,14 @@ class GandiV5
77
80
  # @param fqdn [String, #to_s] the fully qualified domain name to add the slot to.
78
81
  # @param type [:standard, :premium] Tyhe type of slot to add.
79
82
  # @return [String] The confirmation message from Gandi.
80
- # @raise [GandiV5::Error::GandiError::GandiError] if Gandi returns an error.
81
- # TODO: Fetch created slot
83
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
82
84
  def self.create(fqdn, type = :standard)
83
85
  body = {
84
86
  mailbox_type: type
85
87
  }.to_json
86
88
 
87
- data = GandiV5.post url(fqdn), body
88
- data['message']
89
+ response, _data = GandiV5.post url(fqdn), body
90
+ fetch fqdn, response.headers[:location].split('/').last
89
91
  end
90
92
 
91
93
  # Get information for a slot.
@@ -93,9 +95,9 @@ class GandiV5
93
95
  # @param fqdn [String, #to_s] the fully qualified domain name the slot is on.
94
96
  # @param id [String, #to_s] the ID of the slot to fetch.
95
97
  # @return [GandiV5::Email::Slot]
96
- # @raise [GandiV5::Error::GandiError::GandiError] if Gandi returns an error.
98
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
97
99
  def self.fetch(fqdn, id)
98
- data = GandiV5.get url(fqdn, id)
100
+ _response, data = GandiV5.get url(fqdn, id)
99
101
  slot = from_gandi data
100
102
  slot.instance_eval { @fqdn = fqdn }
101
103
  slot
@@ -105,9 +107,9 @@ class GandiV5
105
107
  # @see https://api.gandi.net/docs/email#
106
108
  # @param fqdn [String, #to_s] the fully qualified domain name to list slots for.
107
109
  # @return [Array<GandiV5::Email::Slot>]
108
- # @raise [GandiV5::Error::GandiError::GandiError] if Gandi returns an error.
110
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
109
111
  def self.list(fqdn)
110
- data = GandiV5.get url(fqdn)
112
+ _response, data = GandiV5.get url(fqdn)
111
113
  data.map { |item| from_gandi item }
112
114
  .each { |item| item.instance_eval { @fqdn = fqdn } }
113
115
  end
@@ -118,6 +120,30 @@ class GandiV5
118
120
  status.eql?(:active)
119
121
  end
120
122
 
123
+ # Check if the slot is inactive (not in use)
124
+ # @return [Boolean]
125
+ def inactive?
126
+ status.eql?(:inactive)
127
+ end
128
+
129
+ # Check if the slot's mailbox_type is :free
130
+ # @return [Boolean]
131
+ def free?
132
+ mailbox_type.eql?(:free)
133
+ end
134
+
135
+ # Check if the slot's mailbox_type is :standard
136
+ # @return [Boolean]
137
+ def standard?
138
+ mailbox_type.eql?(:standard)
139
+ end
140
+
141
+ # Check if the slot's mailbox_type is :premium
142
+ # @return [Boolean]
143
+ def premium?
144
+ mailbox_type.eql?(:premium)
145
+ end
146
+
121
147
  private
122
148
 
123
149
  def url
@@ -4,9 +4,9 @@ class GandiV5
4
4
  class Error < RuntimeError
5
5
  # Generic error class for errors returned by Gandi.
6
6
  class GandiError < GandiV5::Error
7
- # Generate a new GandiV5::Error::GandiError::GandiError from the hash returned by Gandi.
7
+ # Generate a new GandiV5::Error::GandiError from the hash returned by Gandi.
8
8
  # @param hash [Hash] the hash returned by Gandi.
9
- # @return [GandiV5::Error::GandiError::GandiError]
9
+ # @return [GandiV5::Error::GandiError]
10
10
  def self.from_hash(hash)
11
11
  hash['errors'] ||= []
12
12
 
@@ -20,9 +20,9 @@ class GandiV5
20
20
 
21
21
  # Refetch the information for this domain from Gandi.
22
22
  # @return [GandiV5::LiveDNS::Domain]
23
- # @raise [GandiV5::Error::GandiError::GandiError] if Gandi returns an error.
23
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
24
24
  def refresh
25
- data = GandiV5.get url
25
+ _response, data = GandiV5.get url
26
26
  from_gandi data
27
27
  end
28
28
 
@@ -36,7 +36,7 @@ class GandiV5
36
36
  # @param name [String] the name to fetch records for.
37
37
  # @param type [String] the record type to fetch.
38
38
  # @return [Array<GandiV5::LiveDNS::RecordSet>]
39
- # @raise [GandiV5::Error::GandiError::GandiError] if Gandi returns an error.
39
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
40
40
  def fetch_records(name = nil, type = nil)
41
41
  GandiV5::LiveDNS.require_valid_record_type type if type
42
42
 
@@ -44,7 +44,7 @@ class GandiV5
44
44
  url_ += "/#{CGI.escape name}" if name
45
45
  url_ += "/#{CGI.escape type}" if type
46
46
 
47
- data = GandiV5.get url_
47
+ _response, data = GandiV5.get url_
48
48
  data = [data] unless data.is_a?(Array)
49
49
  data.map { |item| GandiV5::LiveDNS::RecordSet.from_gandi item }
50
50
  end
@@ -59,7 +59,7 @@ class GandiV5
59
59
  # @param name [String] the name to fetch records for.
60
60
  # @param type [String] the record type to fetch.
61
61
  # @return [String]
62
- # @raise [GandiV5::Error::GandiError::GandiError] if Gandi returns an error.
62
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
63
63
  def fetch_zone_lines(name = nil, type = nil)
64
64
  GandiV5::LiveDNS.require_valid_record_type type if type
65
65
 
@@ -67,7 +67,7 @@ class GandiV5
67
67
  url_ += "/#{CGI.escape name}" if name
68
68
  url_ += "/#{CGI.escape type}" if type
69
69
 
70
- GandiV5.get url_, accept: 'text/plain'
70
+ GandiV5.get(url_, accept: 'text/plain').last
71
71
  end
72
72
 
73
73
  # Add record to this domain.
@@ -76,7 +76,7 @@ class GandiV5
76
76
  # @param ttl [Integer]
77
77
  # @param values [Array<String>]
78
78
  # @return [String] The confirmation message from Gandi.
79
- # @raise [GandiV5::Error::GandiError::GandiError] if Gandi returns an error.
79
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
80
80
  def add_record(name, type, ttl, *values)
81
81
  GandiV5::LiveDNS.require_valid_record_type type
82
82
  fail ArgumentError, 'ttl must be positive and non-zero' unless ttl.positive?
@@ -88,7 +88,7 @@ class GandiV5
88
88
  rrset_ttl: ttl,
89
89
  rrset_values: values
90
90
  }.to_json
91
- data = GandiV5.post "#{url}/records", body
91
+ _response, data = GandiV5.post "#{url}/records", body
92
92
  data['message']
93
93
  end
94
94
 
@@ -102,14 +102,14 @@ class GandiV5
102
102
  # @param name [String] the name to delete records for.
103
103
  # @param type [String] the record type to delete.
104
104
  # @return [nil]
105
- # @raise [GandiV5::Error::GandiError::GandiError] if Gandi returns an error.
105
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
106
106
  def delete_records(name = nil, type = nil)
107
107
  GandiV5::LiveDNS.require_valid_record_type(type) if type
108
108
 
109
109
  url_ = "#{url}/records"
110
110
  url_ += "/#{CGI.escape name}" if name
111
111
  url_ += "/#{CGI.escape type}" if type
112
- GandiV5.delete url_
112
+ GandiV5.delete(url_).last
113
113
  end
114
114
 
115
115
  # Replace all records for this domain.
@@ -119,7 +119,7 @@ class GandiV5
119
119
  # @param text [String] zone file lines to replace the records with.
120
120
  # @return [String] The confirmation message from Gandi.
121
121
  # @raise [ArgumentError] if neither/both of records & test are passed.
122
- # @raise [GandiV5::Error::GandiError::GandiError] if Gandi returns an error.
122
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
123
123
  def replace_records(records: nil, text: nil)
124
124
  unless [records, text].count(&:nil?).eql?(1)
125
125
  fail ArgumentError, 'you must pass ONE of records: or text:'
@@ -129,70 +129,84 @@ class GandiV5
129
129
  body = {
130
130
  items: records.map { |r| r.transform_keys { |k| "rrset_#{k}" } }
131
131
  }.to_json
132
- data = GandiV5.put "#{url}/records", body
132
+ _response, data = GandiV5.put "#{url}/records", body
133
133
  elsif text
134
- data = GandiV5.put "#{url}/records", text, 'content-type': 'text/plain'
134
+ _response, data = GandiV5.put "#{url}/records", text, 'content-type': 'text/plain'
135
135
  end
136
136
  data['message']
137
137
  end
138
138
 
139
- # Replace records for a name in this domain.
140
- # @param name [String]
141
- # @param records
142
- # [Array<Hash<type: String, ttl: Integer, values: Array<String>>>]
143
- # the records to add.
139
+ # @override replace_records_for(name, records)
140
+ # Replace records for a name in this domain.
141
+ # @param name [String]
142
+ # @param records
143
+ # [Array<Hash<type: String, ttl: Integer, values: Array<String>>>]
144
+ # the records to add.
145
+ # @override replace_records_for(name, values, type: nil, ttl: nil)
146
+ # Replace records for a name in this domain.
147
+ # @param name [String]
148
+ # @param type [String] the record type.
149
+ # @param ttl [Integer] the TTL to set for the record.
150
+ # @param values [Array<String>] the values to set for the record.
151
+ # @raise [ArgumentError] if ttl is present and type is absent.
144
152
  # @return [String] The confirmation message from Gandi.
145
- # @raise [GandiV5::Error::GandiError::GandiError] if Gandi returns an error.
146
- def replace_records_for(name, *records)
147
- body = {
148
- items: records.map { |r| r.transform_keys { |k| "rrset_#{k}" } }
149
- }.to_json
150
- data = GandiV5.put "#{url}/records/#{name}", body
151
- data['message']
152
- end
153
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
154
+ def replace_records_for(name, records, type: nil, ttl: nil)
155
+ fail ArgumentError, 'missing keyword: type' if ttl && type.nil?
156
+
157
+ if type
158
+ GandiV5::LiveDNS.require_valid_record_type type
159
+ body = { rrset_values: records, rrset_ttl: ttl }
160
+ # body[:rrset_ttl] = ttl if ttl
161
+ _response, data = GandiV5.put "#{url}/records/#{name}/#{type}", body.to_json
153
162
 
154
- GandiV5::LiveDNS::RECORD_TYPES.each do |type|
155
- # Replace records of a given type for a name in this domain.
156
- # TODO: @param name [Type] description.
157
- # TODO: @param ttl [Type] description.
158
- # TODO: documentation for *values
159
- # @return [String] The confirmation message from Gandi.
160
- # @raise [GandiV5::Error::GandiError::GandiError] if Gandi returns an error.
161
- define_method "replace_#{type.downcase}_records_for" do |name, ttl, *values|
163
+ else
162
164
  body = {
163
- rrset_ttl: ttl,
164
- rrset_values: values
165
- }.to_json
166
- data = GandiV5.put "#{url}/records/#{name}/#{type}", body
167
- data['message']
165
+ items: records.map { |r| r.transform_keys { |k| "rrset_#{k}" } }
166
+ }
167
+ _response, data = GandiV5.put "#{url}/records/#{name}", body.to_json
168
168
  end
169
+
170
+ data['message']
169
171
  end
170
172
 
171
173
  # Change the zone used by this domain.
172
174
  # @param uuid [String, #uuid, #to_s] the UUID of the zone this domain should now use.
173
175
  # @return [String] The confirmation message from Gandi.
174
- # @raise [GandiV5::Error::GandiError::GandiError] if Gandi returns an error.
176
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
175
177
  def change_zone(uuid)
176
178
  uuid = uuid.uuid if uuid.respond_to?(:uuid)
177
- data = GandiV5.patch url, { zone_uuid: uuid }.to_json
179
+ _response, data = GandiV5.patch url, { zone_uuid: uuid }.to_json
178
180
  self.zone_uuid = uuid
179
181
  data['message']
180
182
  end
181
183
 
184
+ # @see GandiV5::LiveDNS::Zone.fetch
185
+ def fetch_zone
186
+ GandiV5::LiveDNS::Zone.fetch zone_uuid
187
+ end
188
+
189
+ # The domain's zone (fetching from Gandi if required).
190
+ # @return [GandiV5::LiveDNS::Zone]
191
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
192
+ def zone
193
+ @zone ||= fetch_zone
194
+ end
195
+
182
196
  # List the domains.
183
197
  # @return [Array<GandiV5::LiveDNS::Domain>]
184
- # @raise [GandiV5::Error::GandiError::GandiError] if Gandi returns an error.
198
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
185
199
  def self.list
186
- data = GandiV5.get url
200
+ _response, data = GandiV5.get url
187
201
  data.map { |item| from_gandi item }
188
202
  end
189
203
 
190
204
  # Get a domain.
191
205
  # @param fqdn [String, #to_s] the fully qualified domain name to fetch.
192
206
  # @return [GandiV5::LiveDNS::Domain]
193
- # @raise [GandiV5::Error::GandiError::GandiError] if Gandi returns an error.
207
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
194
208
  def self.fetch(fqdn)
195
- data = GandiV5.get url(fqdn)
209
+ _response, data = GandiV5.get url(fqdn)
196
210
  from_gandi data
197
211
  end
198
212
 
@@ -28,32 +28,51 @@ class GandiV5
28
28
 
29
29
  # Delete this snapshot.
30
30
  # @return [String] The confirmation message from Gandi.
31
- # @raise [GandiV5::Error::GandiError::GandiError] if Gandi returns an error.
31
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
32
32
  def delete
33
- data = GandiV5.delete url
33
+ _response, data = GandiV5.delete url
34
34
  data['message']
35
35
  end
36
36
 
37
+ # @see GandiV5::LiveDNS::Zone.fetch
38
+ def fetch_zone
39
+ GandiV5::LiveDNS::Zone.fetch zone_uuid
40
+ end
41
+
42
+ # The snapshot's zone (fetching from Gandi if required).
43
+ # @return [GandiV5::LiveDNS::Zone]
44
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
45
+ def zone
46
+ @zone ||= fetch_zone
47
+ end
48
+
49
+ # Get snapshot UUIDs for this zone from Gandi.
50
+ # @return [Hash{String => Time}] Mapping UUID to time made.
51
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
52
+ def self.list(zone_uuid)
53
+ _response, data = GandiV5.get url(zone_uuid)
54
+ Hash[data.map { |snapshot| [snapshot['uuid'], Time.parse(snapshot['date_created'])] }]
55
+ end
56
+
37
57
  # Get snapshot from Gandi.
38
58
  # @param zone_uuid [String, #to_s] the UUID of the zone the snapshot was made of.
39
59
  # @param snapshot_uuid [String, #to_s] the UUID of the snapshot to fetch.
40
60
  # @return [GandiV5::LiveDNS::Zone::Snapshot]
41
- # @raise [GandiV5::Error::GandiError::GandiError] if Gandi returns an error.
61
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
42
62
  def self.fetch(zone_uuid, snapshot_uuid)
43
- data = GandiV5.get url(zone_uuid, snapshot_uuid)
63
+ _response, data = GandiV5.get url(zone_uuid, snapshot_uuid)
44
64
  from_gandi data
45
65
  end
46
66
 
47
- # TODO: Move listing snapshots to here
48
-
49
67
  private
50
68
 
51
69
  def url
52
70
  "#{BASE}zones/#{CGI.escape zone_uuid}/snapshots/#{CGI.escape uuid}"
53
71
  end
54
72
 
55
- def self.url(zone_uuid, snapshot_uuid)
56
- "#{BASE}zones/#{CGI.escape zone_uuid}/snapshots/#{CGI.escape snapshot_uuid}"
73
+ def self.url(zone_uuid, snapshot_uuid = nil)
74
+ "#{BASE}zones/#{CGI.escape zone_uuid}/snapshots" +
75
+ (snapshot_uuid ? "/#{CGI.escape snapshot_uuid}" : '')
57
76
  end
58
77
  private_class_method :url
59
78
  end