gandi_v5 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +24 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +20 -0
  5. data/.travis.yml +23 -0
  6. data/CHANGELOG.md +3 -0
  7. data/Gemfile +6 -0
  8. data/Guardfile +40 -0
  9. data/LICENSE.md +32 -0
  10. data/README.md +94 -0
  11. data/Rakefile +3 -0
  12. data/TODO.md +29 -0
  13. data/bin/console +13 -0
  14. data/gandi_v5.gemspec +41 -0
  15. data/lib/gandi_v5/billing/info/prepaid.rb +33 -0
  16. data/lib/gandi_v5/billing/info.rb +26 -0
  17. data/lib/gandi_v5/billing.rb +28 -0
  18. data/lib/gandi_v5/data/converter/array_of.rb +35 -0
  19. data/lib/gandi_v5/data/converter/symbol.rb +26 -0
  20. data/lib/gandi_v5/data/converter/time.rb +28 -0
  21. data/lib/gandi_v5/data/converter.rb +41 -0
  22. data/lib/gandi_v5/data.rb +244 -0
  23. data/lib/gandi_v5/domain/auto_renew.rb +64 -0
  24. data/lib/gandi_v5/domain/contact.rb +102 -0
  25. data/lib/gandi_v5/domain/contract.rb +22 -0
  26. data/lib/gandi_v5/domain/dates.rb +44 -0
  27. data/lib/gandi_v5/domain/renewal_information.rb +41 -0
  28. data/lib/gandi_v5/domain/restore_information.rb +18 -0
  29. data/lib/gandi_v5/domain/sharing_space.rb +21 -0
  30. data/lib/gandi_v5/domain.rb +431 -0
  31. data/lib/gandi_v5/email/mailbox/responder.rb +36 -0
  32. data/lib/gandi_v5/email/mailbox.rb +236 -0
  33. data/lib/gandi_v5/email/offer.rb +27 -0
  34. data/lib/gandi_v5/email/slot.rb +134 -0
  35. data/lib/gandi_v5/email.rb +11 -0
  36. data/lib/gandi_v5/error/gandi_error.rb +21 -0
  37. data/lib/gandi_v5/error.rb +9 -0
  38. data/lib/gandi_v5/live_dns/domain.rb +211 -0
  39. data/lib/gandi_v5/live_dns/record_set.rb +79 -0
  40. data/lib/gandi_v5/live_dns/zone/snapshot.rb +62 -0
  41. data/lib/gandi_v5/live_dns/zone.rb +301 -0
  42. data/lib/gandi_v5/live_dns.rb +30 -0
  43. data/lib/gandi_v5/organization.rb +66 -0
  44. data/lib/gandi_v5/version.rb +5 -0
  45. data/lib/gandi_v5.rb +178 -0
  46. data/spec/.rubocop.yml +4 -0
  47. data/spec/features/domain_spec.rb +45 -0
  48. data/spec/features/livedns_domain_spec.rb +8 -0
  49. data/spec/features/livedns_zone_spec.rb +45 -0
  50. data/spec/features/mailbox_spec.rb +18 -0
  51. data/spec/fixtures/bodies/GandiV5_Billing/info.yaml +10 -0
  52. data/spec/fixtures/bodies/GandiV5_Domain/availability.yaml +15 -0
  53. data/spec/fixtures/bodies/GandiV5_Domain/fetch_contacts.yaml +8 -0
  54. data/spec/fixtures/bodies/GandiV5_Domain/get.yaml +37 -0
  55. data/spec/fixtures/bodies/GandiV5_Domain/list.yaml +20 -0
  56. data/spec/fixtures/bodies/GandiV5_Domain/renewal_info.yaml +12 -0
  57. data/spec/fixtures/bodies/GandiV5_Domain/restore_info.yaml +5 -0
  58. data/spec/fixtures/bodies/GandiV5_Domain/tld.yaml +10 -0
  59. data/spec/fixtures/bodies/GandiV5_Domain/tlds.yaml +7 -0
  60. data/spec/fixtures/bodies/GandiV5_Email_Mailbox/get.yaml +16 -0
  61. data/spec/fixtures/bodies/GandiV5_Email_Mailbox/list.yaml +8 -0
  62. data/spec/fixtures/bodies/GandiV5_Email_Slot/get.yaml +10 -0
  63. data/spec/fixtures/bodies/GandiV5_Email_Slot/list.yaml +8 -0
  64. data/spec/fixtures/bodies/GandiV5_LiveDNS_Domain/get.yaml +4 -0
  65. data/spec/fixtures/bodies/GandiV5_LiveDNS_Domain/list.yaml +2 -0
  66. data/spec/fixtures/bodies/GandiV5_LiveDNS_Zone/get.yaml +11 -0
  67. data/spec/fixtures/bodies/GandiV5_LiveDNS_Zone/list.yaml +11 -0
  68. data/spec/fixtures/bodies/GandiV5_LiveDNS_Zone_Snapshot/get.yaml +9 -0
  69. data/spec/fixtures/bodies/GandiV5_Organization/get.yaml +17 -0
  70. data/spec/fixtures/vcr/Domain_features/List_domains.yml +54 -0
  71. data/spec/fixtures/vcr/Domain_features/Renew_domain.yml +133 -0
  72. data/spec/fixtures/vcr/LiveDNS_Domain_features/List_domains.yml +32 -0
  73. data/spec/fixtures/vcr/LiveDNS_Zone_features/List_zones.yml +42 -0
  74. data/spec/fixtures/vcr/LiveDNS_Zone_features/Make_and_save_snapshot.yml +72 -0
  75. data/spec/fixtures/vcr/LiveDNS_Zone_features/Save_zone_to_file.yml +28 -0
  76. data/spec/fixtures/vcr/Mailbox_features/List_mailboxes.yml +39 -0
  77. data/spec/spec_helper.rb +60 -0
  78. data/spec/test.env +1 -0
  79. data/spec/units/gandi_v5/billing/info/prepaid_spec.rb +20 -0
  80. data/spec/units/gandi_v5/billing/info_spec.rb +4 -0
  81. data/spec/units/gandi_v5/billing_spec.rb +41 -0
  82. data/spec/units/gandi_v5/data/converter/array_of_spec.rb +18 -0
  83. data/spec/units/gandi_v5/data/converter/symbol_spec.rb +16 -0
  84. data/spec/units/gandi_v5/data/converter/time_spec.rb +16 -0
  85. data/spec/units/gandi_v5/data/converter_spec.rb +31 -0
  86. data/spec/units/gandi_v5/data_spec.rb +340 -0
  87. data/spec/units/gandi_v5/domain/auto_renew_spec.rb +70 -0
  88. data/spec/units/gandi_v5/domain/contact_spec.rb +36 -0
  89. data/spec/units/gandi_v5/domain/contract_spec.rb +4 -0
  90. data/spec/units/gandi_v5/domain/dates_spec.rb +4 -0
  91. data/spec/units/gandi_v5/domain/renewal_information_spec.rb +81 -0
  92. data/spec/units/gandi_v5/domain/restore_information_spec.rb +4 -0
  93. data/spec/units/gandi_v5/domain/sharing_space_spec.rb +4 -0
  94. data/spec/units/gandi_v5/domain_spec.rb +451 -0
  95. data/spec/units/gandi_v5/email/mailbox/responder_spec.rb +131 -0
  96. data/spec/units/gandi_v5/email/mailbox_spec.rb +384 -0
  97. data/spec/units/gandi_v5/email/offer_spec.rb +17 -0
  98. data/spec/units/gandi_v5/email/slot_spec.rb +102 -0
  99. data/spec/units/gandi_v5/error/gandi_error_spec.rb +30 -0
  100. data/spec/units/gandi_v5/error_spec.rb +4 -0
  101. data/spec/units/gandi_v5/live_dns/domain_spec.rb +247 -0
  102. data/spec/units/gandi_v5/live_dns/record_set_spec.rb +74 -0
  103. data/spec/units/gandi_v5/live_dns/zone/snapshot_spec.rb +37 -0
  104. data/spec/units/gandi_v5/live_dns/zone_spec.rb +329 -0
  105. data/spec/units/gandi_v5/live_dns_spec.rb +17 -0
  106. data/spec/units/gandi_v5/organization_spec.rb +30 -0
  107. data/spec/units/gandi_v5_spec.rb +204 -0
  108. metadata +406 -0
@@ -0,0 +1,329 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe GandiV5::LiveDNS::Zone do
4
+ subject do
5
+ described_class.new(
6
+ name: 'Zone Name',
7
+ uuid: 'zone-uuid',
8
+ soa_retry: 1,
9
+ soa_minimum: 2,
10
+ soa_refresh: 3,
11
+ soa_expire: 4,
12
+ soa_serial: 5,
13
+ soa_email: 'admin.example.com',
14
+ soa_primary_ns: '192.168.0.1'
15
+ )
16
+ end
17
+
18
+ it '#to_s' do
19
+ expect(subject.to_s).to eq [
20
+ '@ IN SOA 192.168.0.1 admin.example.com (',
21
+ "\t5\t;Serial",
22
+ "\t3\t\t;Refresh",
23
+ "\t1\t\t;Retry",
24
+ "\t4\t\t;Expire",
25
+ "\t2\t\t;Minimum & Negative TTL",
26
+ ')'
27
+ ].join("\n")
28
+ end
29
+
30
+ describe '#fetch_records' do
31
+ it 'All of them' do
32
+ expect(GandiV5).to receive(:get).with('https://dns.api.gandi.net/api/v5/zones/zone-uuid/records')
33
+ .and_return([])
34
+
35
+ expect(subject.fetch_records).to eq []
36
+ end
37
+
38
+ it 'All for a name' do
39
+ expect(GandiV5).to receive(:get).with('https://dns.api.gandi.net/api/v5/zones/zone-uuid/records/name')
40
+ .and_return([])
41
+
42
+ expect(subject.fetch_records('name')).to eq []
43
+ end
44
+
45
+ it 'A type for a name' do
46
+ expect(GandiV5).to receive(:get).with('https://dns.api.gandi.net/api/v5/zones/zone-uuid/records/name/TXT')
47
+ .and_return(
48
+ [
49
+ {
50
+ 'rrset_type' => 'TXT',
51
+ 'rrset_name' => 'name',
52
+ 'rrset_ttl' => 600,
53
+ 'rrset_values' => %w[a b]
54
+ }
55
+ ]
56
+ )
57
+
58
+ records = subject.fetch_records('name', 'TXT')
59
+ expect(records.count).to eq 1
60
+ expect(records.first.type).to eq 'TXT'
61
+ expect(records.first.ttl).to eq 600
62
+ expect(records.first.name).to eq 'name'
63
+ expect(records.first.values).to match_array %w[a b]
64
+ end
65
+
66
+ it 'Invalid type' do
67
+ expect { subject.fetch_records 'name', 'INVALID-TYPE' }.to raise_error ArgumentError
68
+ end
69
+ end
70
+
71
+ describe '#fetch_zone_lines' do
72
+ it 'All of them' do
73
+ url = 'https://dns.api.gandi.net/api/v5/zones/zone-uuid/records'
74
+ expect(GandiV5).to receive(:get).with(url, accept: 'text/plain').and_return('returned')
75
+ expect(subject.fetch_zone_lines).to eq 'returned'
76
+ end
77
+
78
+ it 'All for a name' do
79
+ url = 'https://dns.api.gandi.net/api/v5/zones/zone-uuid/records/name'
80
+ expect(GandiV5).to receive(:get).with(url, accept: 'text/plain').and_return('returned')
81
+ expect(subject.fetch_zone_lines('name')).to eq 'returned'
82
+ end
83
+
84
+ it 'A type for a name' do
85
+ url = 'https://dns.api.gandi.net/api/v5/zones/zone-uuid/records/name/TXT'
86
+ expect(GandiV5).to receive(:get).with(url, accept: 'text/plain').and_return('returned')
87
+ expect(subject.fetch_zone_lines('name', 'TXT')).to eq 'returned'
88
+ end
89
+
90
+ it 'Invalid type' do
91
+ expect { subject.fetch_zone_lines 'name', 'INVALID-TYPE' }.to raise_error ArgumentError
92
+ end
93
+ end
94
+
95
+ describe '#add_record' do
96
+ it 'Success' do
97
+ url = 'https://dns.api.gandi.net/api/v5/zones/zone-uuid/records'
98
+ body = '{"rrset_name":"name","rrset_type":"TXT","rrset_ttl":900,"rrset_values":["a","b"]}'
99
+ expect(GandiV5).to receive(:post).with(url, body).and_return('message' => 'Confirmation message.')
100
+ expect(subject.add_record('name', 'TXT', 900, 'a', 'b')).to eq 'Confirmation message.'
101
+ end
102
+
103
+ it 'Bad type' do
104
+ expect { subject.add_record 'name', 'INVALID-TYPE', 900, 'a' }.to raise_error ArgumentError
105
+ end
106
+
107
+ it 'Bad TTL' do
108
+ expect { subject.add_record 'name', 'TXT', -900, 'a' }.to raise_error(
109
+ ArgumentError,
110
+ 'ttl must be positive and non-zero'
111
+ )
112
+ end
113
+
114
+ it 'No values' do
115
+ expect { subject.add_record 'name', 'TXT', 900 }.to raise_error(
116
+ ArgumentError,
117
+ 'there must be at least one value'
118
+ )
119
+ end
120
+ end
121
+
122
+ describe '#delete_records' do
123
+ it 'All of them' do
124
+ url = 'https://dns.api.gandi.net/api/v5/zones/zone-uuid/records'
125
+ expect(GandiV5).to receive(:delete).with(url)
126
+ subject.delete_records
127
+ end
128
+
129
+ it 'All for a name' do
130
+ url = 'https://dns.api.gandi.net/api/v5/zones/zone-uuid/records/name'
131
+ expect(GandiV5).to receive(:delete).with(url)
132
+ subject.delete_records 'name'
133
+ end
134
+
135
+ it 'A type for a name' do
136
+ url = 'https://dns.api.gandi.net/api/v5/zones/zone-uuid/records/name/TXT'
137
+ expect(GandiV5).to receive(:delete).with(url)
138
+ subject.delete_records 'name', 'TXT'
139
+ end
140
+
141
+ it 'Invalid type' do
142
+ expect { subject.delete_records 'name', 'INVALID-TYPE' }.to raise_error ArgumentError
143
+ end
144
+ end
145
+
146
+ describe '#replace_records' do
147
+ it 'Given array of GandiV5::LiveDNS::RecordSet' do
148
+ records = [
149
+ { name: 'name', ttl: 600, type: 'TXT', values: ['a'] }
150
+ ]
151
+
152
+ url = 'https://dns.api.gandi.net/api/v5/zones/zone-uuid/records'
153
+ body = '{"items":[{"rrset_name":"name","rrset_ttl":600,"rrset_type":"TXT","rrset_values":["a"]}]}'
154
+ expect(GandiV5).to receive(:put).with(url, body)
155
+ .and_return('message' => 'Confirmation message.')
156
+ expect(subject.replace_records(records: records)).to eq 'Confirmation message.'
157
+ end
158
+
159
+ it 'Given string' do
160
+ records = [
161
+ '@ 86400 IN A 192.168.0.1',
162
+ '* 86400 IN A 192.168.0.1'
163
+ ].join("\n")
164
+
165
+ url = 'https://dns.api.gandi.net/api/v5/zones/zone-uuid/records'
166
+ expect(GandiV5).to receive(:put).with(url, records, 'content-type': 'text/plain')
167
+ .and_return('message' => 'Confirmation message.')
168
+ expect(subject.replace_records(text: records)).to eq 'Confirmation message.'
169
+ end
170
+
171
+ it 'Given both' do
172
+ expect { subject.replace_records text: '', records: [] }.to raise_error(
173
+ ArgumentError,
174
+ 'you must pass ONE of records: or text:'
175
+ )
176
+ end
177
+
178
+ it 'Given nothing' do
179
+ expect { subject.replace_records }.to raise_error ArgumentError, 'you must pass ONE of records: or text:'
180
+ end
181
+ end
182
+
183
+ it '#replace_records_for' do
184
+ url = 'https://dns.api.gandi.net/api/v5/zones/zone-uuid/records/name'
185
+ body = '{"items":[{"rrset_type":"TXT","rrset_values":["a"]}]}'
186
+ expect(GandiV5).to receive(:put).with(url, body)
187
+ .and_return('message' => 'Confirmation message.')
188
+ records = { type: 'TXT', values: ['a'] }
189
+ expect(subject.replace_records_for('name', records)).to eq 'Confirmation message.'
190
+ end
191
+
192
+ describe '#replace_??_records_for' do
193
+ it '#replace_a_records_for' do
194
+ url = 'https://dns.api.gandi.net/api/v5/zones/zone-uuid/records/name/A'
195
+ body = '{"rrset_ttl":600,"rrset_values":["192.168.0.1","192.168.0.2"]}'
196
+ expect(GandiV5).to receive(:put).with(url, body)
197
+ .and_return('message' => 'Confirmation message.')
198
+ expect(subject.replace_a_records_for('name', 600, '192.168.0.1', '192.168.0.2')).to eq 'Confirmation message.'
199
+ end
200
+
201
+ it '#replace_txt_records_for' do
202
+ url = 'https://dns.api.gandi.net/api/v5/zones/zone-uuid/records/name/TXT'
203
+ body = '{"rrset_ttl":600,"rrset_values":["a","b"]}'
204
+ expect(GandiV5).to receive(:put).with(url, body)
205
+ .and_return('message' => 'Confirmation message.')
206
+ expect(subject.replace_txt_records_for('name', 600, 'a', 'b')).to eq 'Confirmation message.'
207
+ end
208
+ end
209
+
210
+ it '#list_domains' do
211
+ expect(GandiV5).to receive(:get).with('https://dns.api.gandi.net/api/v5/zones/zone-uuid/domains')
212
+ .and_return([{ 'fqdn' => 'example.com' }])
213
+ expect(subject.list_domains).to match_array ['example.com']
214
+ end
215
+
216
+ describe '#attach_domain' do
217
+ it 'Given a fqdn' do
218
+ url = 'https://dns.api.gandi.net/api/v5/domains/example.com'
219
+ body = '{"zone_uuid":"zone-uuid"}'
220
+ expect(GandiV5).to receive(:patch).with(url, body)
221
+ .and_return('message' => 'Confirmation message.')
222
+ expect(subject.attach_domain('example.com')).to eq 'Confirmation message.'
223
+ end
224
+
225
+ it 'Given something with a fqdn method' do
226
+ url = 'https://dns.api.gandi.net/api/v5/domains/example.com'
227
+ body = '{"zone_uuid":"zone-uuid"}'
228
+ expect(GandiV5).to receive(:patch).with(url, body)
229
+ .and_return('message' => 'Confirmation message.')
230
+ domain = double GandiV5::LiveDNS::Domain, fqdn: 'example.com'
231
+ expect(subject.attach_domain(domain)).to eq 'Confirmation message.'
232
+ end
233
+ end
234
+
235
+ it '#snapshots' do
236
+ returns = [{ 'uuid' => 'snapshot-uuid', 'date_created' => '2016-12-16T16:51:26Z' }]
237
+ expect(GandiV5).to receive(:get).with('https://dns.api.gandi.net/api/v5/zones/zone-uuid/snapshots')
238
+ .and_return(returns)
239
+ expect(subject.snapshots).to eq('snapshot-uuid' => Time.new(2016, 12, 16, 16, 51, 26, 0))
240
+ end
241
+
242
+ it '#snapshot' do
243
+ returns = double GandiV5::LiveDNS::Zone::Snapshot
244
+ expect(GandiV5::LiveDNS::Zone::Snapshot).to receive(:fetch).with('zone-uuid', 'snapshot-uuid')
245
+ .and_return(returns)
246
+ expect(subject.snapshot('snapshot-uuid')).to be returns
247
+ end
248
+
249
+ it '#take_snapshot' do
250
+ returns = double GandiV5::LiveDNS::Zone::Snapshot
251
+ expect(GandiV5).to receive(:post).with('https://dns.api.gandi.net/api/v5/zones/zone-uuid/snapshots')
252
+ .and_return('message' => 'Confirmation message.', 'uuid' => 'snapshot-uuid')
253
+ expect(GandiV5::LiveDNS::Zone::Snapshot).to receive(:fetch).with('zone-uuid', 'snapshot-uuid')
254
+ .and_return(returns)
255
+ expect(subject.take_snapshot).to be returns
256
+ end
257
+
258
+ it '#update' do
259
+ body = '{"name":"new-name"}'
260
+ expect(GandiV5).to receive(:patch).with('https://dns.api.gandi.net/api/v5/zones/zone-uuid', body)
261
+ .and_return('message' => 'Confirmation message.')
262
+ expect(subject.update(name: 'new-name')).to eq 'Confirmation message.'
263
+ end
264
+
265
+ it '#delete' do
266
+ expect(GandiV5).to receive(:delete).with('https://dns.api.gandi.net/api/v5/zones/zone-uuid')
267
+ subject.delete
268
+ end
269
+
270
+ describe '.create' do
271
+ it 'With sharing-id' do
272
+ body = '{"name":"Name"}'
273
+ params = { sharing_id: 'sharing-id' }
274
+ expect(GandiV5).to receive(:post).with('https://dns.api.gandi.net/api/v5/zones', body, params: params)
275
+ .and_return('message' => 'Confirmation message.')
276
+ expect(described_class.create('Name', sharing_id: 'sharing-id')).to eq 'Confirmation message.'
277
+ end
278
+
279
+ it 'Without sharing-id' do
280
+ body = '{"name":"Name"}'
281
+ expect(GandiV5).to receive(:post).with('https://dns.api.gandi.net/api/v5/zones', body, params: {})
282
+ .and_return('message' => 'Confirmation message.')
283
+ expect(described_class.create('Name')).to eq 'Confirmation message.'
284
+ end
285
+ end
286
+
287
+ describe '.list' do
288
+ subject { described_class.list }
289
+
290
+ before :each do
291
+ body_fixture = File.expand_path(File.join('spec', 'fixtures', 'bodies', 'GandiV5_LiveDNS_Zone', 'list.yaml'))
292
+ expect(GandiV5).to receive(:get).with('https://dns.api.gandi.net/api/v5/zones')
293
+ .and_return(YAML.load_file(body_fixture))
294
+ end
295
+
296
+ its('count') { should eq 1 }
297
+ its('first.uuid') { should eq 'zone-uuid' }
298
+ its('first.name') { should eq 'Name' }
299
+ its('first.sharing_uuid') { should be nil }
300
+ its('first.soa_retry') { should eq 3_600 }
301
+ its('first.soa_minimum') { should eq 900 }
302
+ its('first.soa_refresh') { should eq 10_800 }
303
+ its('first.soa_expire') { should eq 604_800 }
304
+ its('first.soa_serial') { should eq 1_432_798_405 }
305
+ its('first.soa_email') { should eq 'hostmaster.gandi.net.' }
306
+ its('first.soa_primary_ns') { should eq 'a.dns.gandi.net.' }
307
+ end
308
+
309
+ describe '.fetch' do
310
+ subject { described_class.fetch 'zone-uuid' }
311
+
312
+ before :each do
313
+ body_fixture = File.expand_path(File.join('spec', 'fixtures', 'bodies', 'GandiV5_LiveDNS_Zone', 'get.yaml'))
314
+ expect(GandiV5).to receive(:get).with('https://dns.api.gandi.net/api/v5/zones/zone-uuid')
315
+ .and_return(YAML.load_file(body_fixture))
316
+ end
317
+
318
+ its('uuid') { should eq 'zone-uuid' }
319
+ its('name') { should eq 'Name' }
320
+ its('sharing_uuid') { should be nil }
321
+ its('soa_retry') { should eq 3_600 }
322
+ its('soa_minimum') { should eq 900 }
323
+ its('soa_refresh') { should eq 10_800 }
324
+ its('soa_expire') { should eq 604_800 }
325
+ its('soa_serial') { should eq 1_432_798_405 }
326
+ its('soa_email') { should eq 'hostmaster.gandi.net.' }
327
+ its('soa_primary_ns') { should eq 'a.dns.gandi.net.' }
328
+ end
329
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe GandiV5::LiveDNS do
4
+ describe '.require_valid_record_type' do
5
+ it 'Does nothing for valid type' do
6
+ expect { described_class.require_valid_record_type 'A' }.to_not raise_error
7
+ end
8
+
9
+ it 'Errors on invalid type' do
10
+ expect { described_class.require_valid_record_type 'invalid' }.to raise_error(
11
+ ArgumentError,
12
+ 'type must be one of A, AAAA, CNAME, MX, NS, TXT, ALIAS, WKS, ' \
13
+ 'SRV, LOC, SPF, CAA, DS, SSHFP, PTR, KEY, DNAME, TLSA, OPENPGPKEY, CDS'
14
+ )
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe GandiV5::Organization do
4
+ let(:body_fixtures) { File.expand_path(File.join('spec', 'fixtures', 'bodies', 'GandiV5_Organization')) }
5
+
6
+ describe '.fetch' do
7
+ subject { described_class.fetch }
8
+ before :each do
9
+ expect(GandiV5).to receive(:get).with('https://api.gandi.net/v5/organization/user-info')
10
+ .and_return(YAML.load_file(File.join(body_fixtures, 'get.yaml')))
11
+ end
12
+
13
+ its('uuid') { should eq 'organization-uuid' }
14
+ its('username') { should eq 'UserName' }
15
+ its('name') { should eq 'FirstLast' }
16
+ its('first_name') { should eq 'First' }
17
+ its('last_name') { should eq 'Last' }
18
+ its('lang') { should eq 'en' }
19
+ its('street_address') { should eq 'Street Address' }
20
+ its('city') { should eq 'City' }
21
+ its('zip') { should eq 'Post Code' }
22
+ its('country') { should eq 'GB' }
23
+ its('email') { should eq 'username@example.com' }
24
+ its('phone') { should eq '+12.34567890' }
25
+ its('security_email') { should eq 'security@example.com' }
26
+ its('security_phone') { should eq '+23.45678901' }
27
+ its('security_email_validated') { should eq true }
28
+ its('security_email_validation_deadline') { should eq Time.new(2017, 11, 22, 17, 13, 33) }
29
+ end
30
+ end
@@ -0,0 +1,204 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe GandiV5 do
4
+ let(:api_key) { ENV['GANDI_API_KEY'] }
5
+
6
+ it 'Has a version' do
7
+ expect(defined?(GandiV5::VERSION)).to eq 'constant'
8
+ expect(GandiV5::VERSION).to_not be_empty
9
+ end
10
+
11
+ describe 'Attributes' do
12
+ describe 'api_key' do
13
+ it '.api_key=' do
14
+ described_class.api_key = '1a'
15
+ expect(described_class.send(:api_key)).to eq '1a'
16
+ described_class.api_key = api_key
17
+ end
18
+
19
+ it 'is write only' do
20
+ expect { described_class.api_key }.to raise_error NoMethodError
21
+ end
22
+ end
23
+ end
24
+
25
+ describe 'Uses RestClient' do
26
+ %i[get delete].each do |method|
27
+ describe ":#{method}" do
28
+ it 'As JSON' do
29
+ response_data = { 'hello' => 'world' }
30
+ response = double RestClient::Response, body: response_data.to_json, headers: { content_type: 'application/json' }
31
+ expect(described_class).to receive(:prepare_headers)
32
+ expect(RestClient).to receive(method).with('url', hash_including(accept: 'application/json')).and_return(response)
33
+ expect(described_class.send(method, 'url', accept: 'application/json')).to eq response_data
34
+ end
35
+
36
+ it 'As text' do
37
+ response_data = 'Hello world!'
38
+ response = double RestClient::Response, body: response_data, headers: { content_type: 'text/plain' }
39
+ expect(described_class).to receive(:prepare_headers)
40
+ expect(RestClient).to receive(method).with('url', hash_including(accept: 'text/plain')).and_return(response)
41
+ expect(described_class.send(method, 'url', accept: 'text/plain')).to eq response_data
42
+ end
43
+
44
+ it 'Passes request headers' do
45
+ expect(described_class).to receive(:prepare_headers)
46
+ expect(described_class).to receive(:parse_response)
47
+ expect(RestClient).to receive(method).with(anything, header: 'value')
48
+ expect(described_class.send(method, 'url', header: 'value')).to be_nil
49
+ end
50
+
51
+ it 'Adds authentication header' do
52
+ expect(RestClient).to receive(method).with(anything, hash_including(Authorization: 'Apikey abdce12345'))
53
+ expect(described_class).to receive(:parse_response)
54
+ expect(described_class.send(method, 'https://api.gandi.net/v5/')).to be_nil
55
+ end
56
+
57
+ it 'Default accept header' do
58
+ expect(RestClient).to receive(method).with(any_args, hash_including(accept: 'application/json'))
59
+ expect(described_class).to receive(:parse_response)
60
+ expect(described_class.send(method, 'https://api.gandi.net/v5/')).to be_nil
61
+ end
62
+
63
+ it 'Converts a 406 (bad request) exception' do
64
+ expect(RestClient).to receive(method).and_raise(RestClient::BadRequest)
65
+ expect(described_class).to receive(:handle_bad_request)
66
+ described_class.send(method, 'https://api.gandi.net/v5/')
67
+ end
68
+ end
69
+ end
70
+
71
+ %i[patch post put].each do |method|
72
+ describe ":#{method}" do
73
+ let(:payload) { '{"say":"hello world"}' }
74
+
75
+ it 'As JSON' do
76
+ response_data = { 'said' => 'hello world' }
77
+ response = double RestClient::Response, body: response_data.to_json, headers: { content_type: 'application/json' }
78
+ expect(described_class).to receive(:prepare_headers)
79
+ expect(RestClient).to receive(method).with('url', payload, hash_including(accept: 'application/json'))
80
+ .and_return(response)
81
+ expect(described_class.send(method, 'url', payload, accept: 'application/json')).to eq response_data
82
+ end
83
+
84
+ it 'As text' do
85
+ response_data = 'hello world'
86
+ response = double RestClient::Response, body: response_data, headers: { content_type: 'text/plain' }
87
+ expect(described_class).to receive(:prepare_headers)
88
+ expect(RestClient).to receive(method).with('url', payload, hash_including(accept: 'text/plain'))
89
+ .and_return(response)
90
+ expect(described_class.send(method, 'url', payload, accept: 'text/plain')).to eq response_data
91
+ end
92
+
93
+ it 'Passes payload' do
94
+ expect(described_class).to receive(:prepare_headers)
95
+ expect(described_class).to receive(:parse_response)
96
+ expect(RestClient).to receive(method).with(anything, payload, any_args)
97
+ expect(described_class.send(method, 'url', payload)).to be_nil
98
+ end
99
+
100
+ it 'Passes request headers' do
101
+ expect(described_class).to receive(:prepare_headers)
102
+ expect(described_class).to receive(:parse_response)
103
+ expect(RestClient).to receive(method).with(any_args, hash_including(header: 'value'))
104
+ expect(described_class.send(method, 'url', payload, header: 'value')).to be_nil
105
+ end
106
+
107
+ it 'Adds content type header' do
108
+ expect(RestClient).to receive(method).with(any_args, hash_including('content-type': 'application/json'))
109
+ expect(described_class).to receive(:parse_response)
110
+ expect(described_class.send(method, 'https://api.gandi.net/v5/', payload)).to be_nil
111
+ end
112
+
113
+ it 'Adds authentication header' do
114
+ expect(RestClient).to receive(method).with(any_args, hash_including(Authorization: 'Apikey abdce12345'))
115
+ expect(described_class).to receive(:parse_response)
116
+ expect(described_class.send(method, 'https://api.gandi.net/v5/', payload)).to be_nil
117
+ end
118
+
119
+ it 'Default accept header' do
120
+ expect(RestClient).to receive(method).with(any_args, hash_including(accept: 'application/json'))
121
+ expect(described_class).to receive(:parse_response)
122
+ expect(described_class.send(method, 'https://api.gandi.net/v5/', payload)).to be_nil
123
+ end
124
+
125
+ it 'Converts a 406 (bad request) exception' do
126
+ expect(RestClient).to receive(method).and_raise(RestClient::BadRequest)
127
+ expect(described_class).to receive(:handle_bad_request)
128
+ described_class.send(method, 'https://api.gandi.net/v5/', payload)
129
+ end
130
+ end
131
+ end
132
+ end
133
+
134
+ describe 'Generates correct authorisation header' do
135
+ it 'When requesting from main V5 API' do
136
+ expect(described_class.send(:authorisation_header, 'https://api.gandi.net/v5/example'))
137
+ .to eq(Authorization: "Apikey #{api_key}")
138
+ end
139
+
140
+ it 'When requesting from main LiveDNS V5 API' do
141
+ expect(described_class.send(:authorisation_header, 'https://dns.api.gandi.net/api/v5/example'))
142
+ .to eq('X-Api-Key': api_key)
143
+ end
144
+
145
+ it 'Raises ArgumentError when requesting an unknown url' do
146
+ expect { described_class.send(:authorisation_header, 'https://unknown.example.com') }
147
+ .to raise_error ArgumentError, 'Don\'t know how to authorise for url: https://unknown.example.com'
148
+ end
149
+ end
150
+
151
+ describe 'Parses response' do
152
+ it 'Text' do
153
+ response = double RestClient::Response, body: 'Hello World', headers: { content_type: 'text/plain' }
154
+ expect(described_class.send(:parse_response, response)).to eq 'Hello World'
155
+ end
156
+
157
+ describe 'JSON' do
158
+ it 'Response is parsable' do
159
+ response = double RestClient::Response, body: '{"hello":"world"}', headers: { content_type: 'application/json' }
160
+ expect(described_class.send(:parse_response, response)).to eq('hello' => 'world')
161
+ end
162
+
163
+ it 'Response is an error message' do
164
+ response = double RestClient::Response, body: '{"status":"error"}', headers: { content_type: 'application/json' }
165
+ expect { described_class.send(:parse_response, response) }.to raise_error GandiV5::Error::GandiError
166
+ end
167
+ end
168
+
169
+ it 'Raises ArgumentError when requesting an unknown parser' do
170
+ response = double RestClient::Response, headers: { content_type: 'unknown/unknown' }
171
+ expect { described_class.send(:parse_response, response) }
172
+ .to raise_error ArgumentError, 'Don\'t know how to parse a unknown/unknown response'
173
+ end
174
+ end
175
+
176
+ describe 'Handles HTTP error 406 (Bad Request)' do
177
+ it 'Passes on exception if body is not JSON' do
178
+ response = double RestClient::Response,
179
+ code: '406',
180
+ body: 'Something went wrong!'
181
+ exception = RestClient::BadRequest.new response
182
+ expect { described_class.send(:handle_bad_request, exception) }
183
+ .to raise_error exception
184
+ end
185
+
186
+ it 'Passes on exception if body is JSON but not an error hash' do
187
+ response = double RestClient::Response,
188
+ code: '406',
189
+ body: '{"status":"not error"}'
190
+ exception = RestClient::BadRequest.new response
191
+ expect { described_class.send(:handle_bad_request, exception) }
192
+ .to raise_error RestClient::BadRequest
193
+ end
194
+
195
+ it 'Raises GandiV5::Error::GandiError if response is an error hash' do
196
+ response = double RestClient::Response,
197
+ code: '406',
198
+ body: '{"status":"error","errors":[{"name":"field","description":"message"}]}'
199
+ exception = RestClient::BadRequest.new response
200
+ expect { described_class.send(:handle_bad_request, exception) }
201
+ .to raise_error GandiV5::Error::GandiError, 'field: message'
202
+ end
203
+ end
204
+ end