gandi_v5 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ef84fa55ae8d79f2d6fa178db059e4130ff8ae9ba2eb5f5886a57b9f965d8b48
4
- data.tar.gz: b5bc9448754bf08ab28c1e3235d01929a6edf3b82f00dbdf935b58d863035de1
3
+ metadata.gz: 02621cbb8405cbec872ddcf3d549770535377dc1e6b158e9488f6bddcf65a771
4
+ data.tar.gz: 73901bcd9e8cb66d5c56c1802915dc849bd66d17d8c013a2895f472ef93f9543
5
5
  SHA512:
6
- metadata.gz: 90083b929123d60702880dc1fbaa7500c972f38ce1134dbb031d1e5585cad63fbe7265ee38075e5329e2e125f4b441ef0bc02da79137444df4ea27c999c1fa26
7
- data.tar.gz: 536d1dae15301728cfda7df6c556913e75025d90f801c5748da598716b887bd0e8c4d25c2f0226ea25d9bab02fa9a3f185d64a5cf9a31694cb21a1ea5f8ac264
6
+ metadata.gz: e3caf30dc92addf609acb1e5e7f69841a5f896579f55c839813790200170222ede6df0e2c6448af2b3fa5848de5acb1a1cd50dbd1a19d538afc5c24e2d84e217
7
+ data.tar.gz: e1efdd6ed8ab463d7379b8040aeff61c62be58485464ed09e00bba3d28a4af9e27ac74f9333a74500f98e094b48832ed00439c01909fd9a1e5fb1a9677a7da76
data/.gitignore CHANGED
@@ -5,6 +5,7 @@ Gemfile.lock
5
5
  # And because this is Ruby, ignore the following
6
6
  # (source: https://github.com/github/gitignore/blob/master/Ruby.gitignore):
7
7
 
8
+ .ruby-version
8
9
  notes.txt
9
10
  todo.txt
10
11
  *.gem
data/.rubocop.yml CHANGED
@@ -8,13 +8,23 @@ Metrics/AbcSize:
8
8
  Max: 20
9
9
  Metrics/ClassLength:
10
10
  Max: 250
11
- Metrics/LineLength:
12
- Max: 100
11
+ Metrics/CyclomaticComplexity:
12
+ Max: 8
13
13
  Metrics/MethodLength:
14
14
  Max: 15
15
15
  Metrics/ModuleLength:
16
16
  Max: 200
17
+ Metrics/ParameterLists:
18
+ Max: 6
17
19
  Style/DocumentationMethod:
18
20
  Enabled: true
21
+ Style/HashEachMethods:
22
+ Enabled: true
23
+ Style/HashTransformKeys:
24
+ Enabled: true
25
+ Style/HashTransformValues:
26
+ Enabled: true
19
27
  Style/SignalException:
20
28
  Enabled: false
29
+ Layout/LineLength:
30
+ Max: 100
data/.travis.yml CHANGED
@@ -5,6 +5,10 @@ rvm:
5
5
  - 2.6.2
6
6
  - 2.6.3
7
7
  - 2.6.4
8
+ - 2.6.5
9
+ - 2.6.6
10
+ - 2.7.0
11
+ - 2.7.1
8
12
  gemfile:
9
13
  - Gemfile
10
14
  branches:
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Gandi V5 API Gem Changelog
2
2
 
3
+ ## Version 0.4.0
4
+
5
+ * Fix exception when delete returns no content-type
6
+ * Add support for ruby 2.7.0
7
+ * Add up/downgrading mailbox offer
8
+ * Add dry run option to creating a mailbox
9
+ * Add sharing_id & dry run option for renewing domain
10
+ * Add listing customers under a reseller organization (GandiV5::Organization::Customer.list and GandiV5::Organization#customers)
11
+ * Add creating customer under a reseller organization (GandiV5::Organization::Customer.create and GandiV5::Organization#create_customer)
12
+
3
13
  ## Version 0.3.0
4
14
 
5
15
  * Additions to GandiV5::Domain
data/README.md CHANGED
@@ -8,7 +8,8 @@
8
8
  This gem supports the following versions of ruby, it may work on other versions but is not tested against them so don't rely on it.
9
9
 
10
10
  * ruby:
11
- * 2.6.0 - 2.6.4
11
+ * 2.6.0 - 2.6.6
12
+ * 2.7.0 - 2.7.1
12
13
  * jruby, once it's reached parity with ruby 2.6.x
13
14
  * truffleruby, once it's reached parity with ruby 2.6.x
14
15
  * rubinius, once it's reached parity with ruby 2.6.x
@@ -76,6 +77,7 @@ We follow the [Semantic Versioning](http://semver.org/) concept.
76
77
 
77
78
  | Gem Version | Gandi API Release Date |
78
79
  | --------------- | ---------------------- |
80
+ | 0.4.0 | 2019-10-01 |
79
81
  | 0.3.0 | 2019-08-22 |
80
82
  | 0.2.0 | 2019-05-16 |
81
83
  | 0.1.0 | 2019-05-16 |
data/lib/gandi_v5/data.rb CHANGED
@@ -179,9 +179,8 @@ class GandiV5
179
179
  define_method "#{name}=" do |value|
180
180
  instance_variable_set("@#{name}", value)
181
181
  end
182
- # rubocop:disable Style/AccessModifierDeclarations
182
+
183
183
  private "#{name}="
184
- # rubocop:enable Style/AccessModifierDeclarations
185
184
  end
186
185
 
187
186
  # @api private
@@ -211,10 +211,13 @@ class GandiV5
211
211
  # @param duration [Integer, #to_s] how long to renew for (in years).
212
212
  # @return [String] confirmation message from Gandi.
213
213
  # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
214
- def renew_for(duration = 1)
214
+ def renew_for(duration = 1, sharing_id: nil, dry_run: false)
215
215
  body = { duration: duration }.to_json
216
- _response, data = GandiV5.post url('renew'), body
217
- data['message']
216
+ url_ = url('renew')
217
+ url_ = sharing_id ? "#{url_}?sharing_id=#{sharing_id}" : url_
218
+
219
+ _response, data = GandiV5.post(url_, body, 'Dry-Run': dry_run ? 1 : 0)
220
+ dry_run ? data : data['message']
218
221
  end
219
222
 
220
223
  # Restoration information for the domain.
@@ -105,6 +105,39 @@ class GandiV5
105
105
  data['message']
106
106
  end
107
107
 
108
+ # Upgrade a standard mailbox to premium.
109
+ # If the current slot is a free one, a new premium slot is created and
110
+ # used for the mailbox. Otherwise, the slot is upgraded to premium.
111
+ # @see https://api.gandi.net/docs/email#patch-v5-email-mailboxes-domain-mailbox_id-type
112
+ # @param sharing_id [String, #to_s, nil] (optional)
113
+ # the organisation ID to bill for the mailbox.
114
+ # @param dry_run [Boolean] whether the details should be checked instead
115
+ # of actually upgrading the mailbox.
116
+ # @return [true] if the mailbox was upgraded
117
+ # @return [false] if the mailbox was not upgraded (it's already premium)
118
+ # @return [Hash] if doing a dry run, you get what Gandi returns
119
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error
120
+ def upgrade(sharing_id: nil, dry_run: false)
121
+ patch_type :premium, sharing_id, dry_run
122
+ end
123
+
124
+ # Downgrade a premium mailbox to standard.
125
+ # If a free slot is available, the premium slot is destroyed
126
+ # (and refunded) and the free one is used for the mailbox.
127
+ # Otherwise, the slot is downgraded to standard.
128
+ # @see https://api.gandi.net/docs/email#patch-v5-email-mailboxes-domain-mailbox_id-type
129
+ # @param sharing_id [String, #to_s, nil] (optional)
130
+ # the organisation ID to bill for the mailbox.
131
+ # @param dry_run [Boolean] whether the details should be checked instead
132
+ # of actually downgrading the mailbox.
133
+ # @return [true] if the mailbox was downgraded
134
+ # @return [false] if the mailbox was not downgraded (it's already standard)
135
+ # @return [Hash] if doing a dry run, you get what Gandi returns
136
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error
137
+ def downgrade(sharing_id: nil, dry_run: false)
138
+ patch_type :standard, sharing_id, dry_run
139
+ end
140
+
108
141
  # Create a new mailbox.
109
142
  # Note that before you can create a mailbox, you must have a slot available.
110
143
  # @see https://api.gandi.net/docs/email#post-v5-email-mailboxes-domain
@@ -113,10 +146,13 @@ class GandiV5
113
146
  # @param password [String, #to_s] the password to use.
114
147
  # @param aliases [Array<String, #to_s>] any alternative email address to be used.
115
148
  # @param type [:standard, :premium] the type of mailbox slot to use.
149
+ # @param dry_run [Boolean] whether the details should be checked instead
150
+ # of actually creating the mailbox.
116
151
  # @return [GandiV5::Email::Mailbox] The created mailbox.
117
152
  # @raise [GandiV5::Error] if no slots are available.
118
153
  # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
119
- def self.create(fqdn, login, password, aliases: [], type: :standard)
154
+ # rubocop:disable Metrics/AbcSize
155
+ def self.create(fqdn, login, password, aliases: [], type: :standard, dry_run: false)
120
156
  fail ArgumentError, "#{type.inspect} is not a valid type" unless TYPES.include?(type)
121
157
  if GandiV5::Email::Slot.list.none? { |slot| slot.mailbox_type == type && slot.inactive? }
122
158
  fail GandiV5::Error, "no available #{type} slots"
@@ -131,9 +167,11 @@ class GandiV5
131
167
  aliases: aliases.push
132
168
  }.to_json
133
169
 
134
- response, _data = GandiV5.post url(fqdn), body
135
- fetch fqdn, response.headers[:location].split('/').last
170
+ response, data = GandiV5.post(url(fqdn), body, 'Dry-Run': dry_run ? 1 : 0)
171
+
172
+ dry_run ? data : fetch(fqdn, response.headers[:location].split('/').last)
136
173
  end
174
+ # rubocop:enable Metrics/AbcSize
137
175
 
138
176
  # Get information for a mailbox.
139
177
  # @see https://api.gandi.net/docs/email#get-v5-email-mailboxes-domain-mailbox_id
@@ -240,6 +278,20 @@ class GandiV5
240
278
  def crypt_password(password)
241
279
  self.class.send :crypt_password, password
242
280
  end
281
+
282
+ def patch_type(new_type, sharing_id, dry_run)
283
+ fail ArgumentError unless TYPES.include?(new_type)
284
+ return false if type == new_type
285
+
286
+ url_ = "#{url}/type"
287
+ url_ = sharing_id ? "#{url_}?sharing_id=#{sharing_id}" : url_
288
+ body = { mailbox_type: new_type }
289
+
290
+ _response, data = GandiV5.patch(url_, body.to_json, 'Dry-Run': dry_run ? 1 : 0)
291
+
292
+ @type = new_type unless dry_run
293
+ dry_run ? data : true
294
+ end
243
295
  end
244
296
  end
245
297
  end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ class GandiV5
4
+ class Organization
5
+ # A customer of a reseller organization
6
+ # @!attribute [r] id
7
+ # @return [String] The main identifier of the customer.
8
+ # Also known as sharing_id in many routes.
9
+ # @!attribute [r] email
10
+ # @return [String] Email of the customer.
11
+ # @!attribute [r] first_name
12
+ # @return [String] First name of the customer.
13
+ # @!attribute [r] last_name
14
+ # @return [String] Last name of the customer.
15
+ # @!attribute [r] name
16
+ # @return [String] Name of the customer.
17
+ # @!attribute [r] type
18
+ # @return [:individual, :company, :association, :publicbody]
19
+ # @!attribute [r] org_name
20
+ # @return [nil, String] Organization legal name of the customer..
21
+ class Customer
22
+ include GandiV5::Data
23
+
24
+ members :email, :name
25
+ member :uuid, gandi_key: 'id'
26
+ member :first_name, gandi_key: 'firstname'
27
+ member :last_name, gandi_key: 'lastname'
28
+ member :org_name, gandi_key: 'orgname'
29
+ member :type, converter: GandiV5::Data::Converter::Symbol
30
+
31
+ # Create a new customer for this organization.
32
+ # @see https://api.gandi.net/docs/organization/#post-v5-organization-organizations-id-customers
33
+ # @param org_uuid [String] UUID of the organization to create the customer for.
34
+ # @param firstname [String, #to_s] (required) Customer's first name.
35
+ # @param lastname [String, #to_s] (required) Customer's last name.
36
+ # @param type [String, #to_s] (required) Type of customer
37
+ # ("individual", "company", "association" or "publicbody").
38
+ # @param streetaddr [String, #to_s] (required) Customer's street address.
39
+ # @param city [String, #to_s] (required) Customer's city.
40
+ # @param country [String, #to_s] (required) Customer's country.
41
+ # @param email [String, #to_s] (required) Customer's email address.
42
+ # @param phone [String, #to_s] (required) Customer's phone number.
43
+ # @param fax [String, #to_s] (optional) Customer's fax number.
44
+ # @param streetaddr2 [String, #to_s] (optional) Customer's street address (2nd line).
45
+ # @param state [String, #to_s] (optional) Customer's state/province/region.
46
+ # @param zip [String, #to_s] (optional) Customer's postal/zip code.
47
+ # @param reference [String, #to_s] (optional)
48
+ # Optional text to display on the invoice, such as your own customer reference info.
49
+ # @param orgname [String, #to_s] (optional) Customer Organization's legal name.
50
+ # @return [nil]
51
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error
52
+ def self.create(org_uuid, **params)
53
+ %i[city country email firstname lastname phone streetaddr type].each do |attr|
54
+ fail ArgumentError, "missing keyword: #{attr}" unless params.key?(attr)
55
+ end
56
+ unless %w[individual company association publicbody].include?(params[:type].to_s)
57
+ fail ArgumentError, "invalid type: #{params[:type].inspect}"
58
+ end
59
+
60
+ _response, _data = GandiV5.post(url(org_uuid), params.to_json)
61
+ nil
62
+ end
63
+
64
+ # List organisation's customers.
65
+ # @see https://api.gandi.net/docs/organization/#get-v5-organization-organizations-id-customers
66
+ # @param org_uuid [String] UUID of the organization to fetch customers for.
67
+ # @param name [String, #to_s] (optional)
68
+ # filters the list by name, with optional patterns.
69
+ # e.g. "alice", "ali*", "*ice"
70
+ # @param permission [String, #to_s] (optional)
71
+ # filters the list by the permission the authenticated user has on
72
+ # that organization and products in it.
73
+ # @param sort_by [String, #to_s] (optional default "name") how to sort the list.
74
+ # @return [Array<GandiV5::Organization>]
75
+ # @raise [GandiV5::Error::GandiError] if Gandi returns an error.
76
+ def self.list(org_uuid, **params)
77
+ params['~name'] = params.delete(:name) if params.key?(:name)
78
+ _resp, data = GandiV5.get url(org_uuid), params: params
79
+ data.map { |organisation| from_gandi organisation }
80
+ end
81
+
82
+ private
83
+
84
+ def self.url(org_uuid)
85
+ "#{BASE}organization/organizations/#{org_uuid}/customers"
86
+ end
87
+ private_class_method :url
88
+ end
89
+ end
90
+ end
@@ -50,6 +50,16 @@ class GandiV5
50
50
 
51
51
  alias organization_uuid uuid
52
52
 
53
+ # @see GandiV5::Organization::Customer.list
54
+ def customers(org_uuid, **params)
55
+ GandiV5::Organization::Customer.list(org_uuid, **params)
56
+ end
57
+
58
+ # @see GandiV5::Organization::Customer.create
59
+ def create_customer(org_uuid, **params)
60
+ GandiV5::Organization::Customer.create(org_uuid, **params)
61
+ end
62
+
53
63
  # Get information about the current authenticated user.
54
64
  # @see https://api.gandi.net/docs/organization#get-v5-organization-user-info
55
65
  # @return [GandiV5::Organization]
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class GandiV5
4
- VERSION = '0.3.0'
4
+ VERSION = '0.4.0'
5
5
  end
data/lib/gandi_v5.rb CHANGED
@@ -93,7 +93,10 @@ class GandiV5
93
93
  def delete(url, **headers)
94
94
  prepare_headers headers, url
95
95
  response = RestClient.delete url, **headers
96
- [response, parse_response(response)]
96
+ [
97
+ response,
98
+ response.headers.key?(:content_type) ? parse_response(response) : nil
99
+ ]
97
100
  rescue RestClient::BadRequest => e
98
101
  handle_bad_request(e)
99
102
  end
data/spec/.rubocop.yml CHANGED
@@ -1,4 +1,11 @@
1
1
  Metrics/BlockLength:
2
2
  Max: 750
3
- Metrics/LineLength:
3
+ Layout/LineLength:
4
4
  Max: 125
5
+ Style/HashEachMethods:
6
+ Enabled: true
7
+ Style/HashTransformKeys:
8
+ Enabled: true
9
+ Style/HashTransformValues:
10
+ Enabled: true
11
+
@@ -0,0 +1,8 @@
1
+ ---
2
+ - id: customer-uuid
3
+ name: FirstLast
4
+ firstname: First
5
+ lastname: Last
6
+ email: first.last@example.com
7
+ type: individual
8
+ orgname: Org
@@ -373,10 +373,46 @@ describe GandiV5::Domain do
373
373
  its('durations') { should match_array [1, 2] }
374
374
  end
375
375
 
376
- it '#renew_for' do
377
- expect(GandiV5).to receive(:post).with('https://api.gandi.net/v5/domain/domains/example.com/renew', '{"duration":2}')
378
- .and_return([nil, { 'message' => 'Confirmation message.' }])
379
- expect(subject.renew_for(2)).to eq 'Confirmation message.'
376
+ describe '#renew_for' do
377
+ it 'Defaults to 1 year and current user' do
378
+ expect(GandiV5).to receive(:post).with(
379
+ 'https://api.gandi.net/v5/domain/domains/example.com/renew',
380
+ '{"duration":1}',
381
+ 'Dry-Run': 0
382
+ )
383
+ .and_return([nil, { 'message' => 'Confirmation message.' }])
384
+ expect(subject.renew_for).to eq 'Confirmation message.'
385
+ end
386
+
387
+ it 'With provided duration' do
388
+ expect(GandiV5).to receive(:post).with(
389
+ 'https://api.gandi.net/v5/domain/domains/example.com/renew',
390
+ '{"duration":2}',
391
+ 'Dry-Run': 0
392
+ )
393
+ .and_return([nil, { 'message' => 'Confirmation message.' }])
394
+ expect(subject.renew_for(2)).to eq 'Confirmation message.'
395
+ end
396
+
397
+ it 'With provided sharing_id' do
398
+ expect(GandiV5).to receive(:post).with(
399
+ 'https://api.gandi.net/v5/domain/domains/example.com/renew?sharing_id=def',
400
+ '{"duration":1}',
401
+ 'Dry-Run': 0
402
+ )
403
+ .and_return([nil, { 'message' => 'Confirmation message.' }])
404
+ expect(subject.renew_for(sharing_id: 'def')).to eq 'Confirmation message.'
405
+ end
406
+
407
+ it 'Does a dry run' do
408
+ expect(GandiV5).to receive(:post).with(
409
+ 'https://api.gandi.net/v5/domain/domains/example.com/renew',
410
+ '{"duration":1}',
411
+ 'Dry-Run': 1
412
+ )
413
+ .and_return([nil, { 'status' => 'success' }])
414
+ expect(subject.renew_for(dry_run: true)).to eq('status' => 'success')
415
+ end
380
416
  end
381
417
 
382
418
  describe '#renewal_price' do
@@ -87,6 +87,108 @@ describe GandiV5::Email::Mailbox do
87
87
  end
88
88
  end
89
89
 
90
+ describe '#upgrade' do
91
+ let(:url) { 'https://api.gandi.net/v5/email/mailboxes/example.com/mailbox-uuid/type' }
92
+
93
+ context 'No sharing_id' do
94
+ it 'Is upgraded' do
95
+ expect(GandiV5).to receive(:patch).with(url, '{"mailbox_type":"premium"}', 'Dry-Run': 0)
96
+ .and_return([nil, { 'message' => 'Confirmation message.' }])
97
+ expect(subject.upgrade).to be true
98
+ expect(subject.type).to be :premium
99
+ end
100
+
101
+ it 'Is already premium' do
102
+ subject.instance_exec { @type = :premium }
103
+ expect(GandiV5).to_not receive(:patch)
104
+ expect(subject.upgrade).to be false
105
+ end
106
+ end
107
+
108
+ context 'With sharing_id' do
109
+ let(:url) { 'https://api.gandi.net/v5/email/mailboxes/example.com/mailbox-uuid/type?sharing_id=abc' }
110
+
111
+ it 'Is upgraded' do
112
+ expect(GandiV5).to receive(:patch).with(url, '{"mailbox_type":"premium"}', 'Dry-Run': 0)
113
+ .and_return([nil, { 'message' => 'Confirmation message.' }])
114
+ expect(subject.upgrade(sharing_id: 'abc')).to be true
115
+ expect(subject.type).to be :premium
116
+ end
117
+
118
+ it 'Is already premium' do
119
+ subject.instance_exec { @type = :premium }
120
+ expect(GandiV5).to_not receive(:patch)
121
+ expect(subject.upgrade(sharing_id: 'abc')).to be false
122
+ end
123
+ end
124
+
125
+ context 'Dry run' do
126
+ it 'Is upgraded' do
127
+ expect(GandiV5).to receive(:patch).with(url, '{"mailbox_type":"premium"}', 'Dry-Run': 1)
128
+ .and_return([nil, { 'status' => 'success' }])
129
+ expect(subject.upgrade(dry_run: true)).to eq('status' => 'success')
130
+ expect(subject.type).to be :standard
131
+ end
132
+
133
+ it 'Is already premium' do
134
+ subject.instance_exec { @type = :premium }
135
+ expect(GandiV5).to_not receive(:patch)
136
+ expect(subject.upgrade(dry_run: true)).to be false
137
+ end
138
+ end
139
+ end
140
+
141
+ describe '#downgrade' do
142
+ let(:url) { 'https://api.gandi.net/v5/email/mailboxes/example.com/mailbox-uuid/type' }
143
+
144
+ context 'No sharing_id' do
145
+ it 'Is downgraded' do
146
+ subject.instance_exec { @type = :premium }
147
+ expect(GandiV5).to receive(:patch).with(url, '{"mailbox_type":"standard"}', 'Dry-Run': 0)
148
+ .and_return([nil, { 'message' => 'Confirmation message.' }])
149
+ expect(subject.downgrade).to be true
150
+ expect(subject.type).to be :standard
151
+ end
152
+
153
+ it 'Is already premium' do
154
+ expect(GandiV5).to_not receive(:patch)
155
+ expect(subject.downgrade).to be false
156
+ end
157
+ end
158
+
159
+ context 'With sharing_id' do
160
+ let(:url) { 'https://api.gandi.net/v5/email/mailboxes/example.com/mailbox-uuid/type?sharing_id=abc' }
161
+
162
+ it 'Is downgraded' do
163
+ subject.instance_exec { @type = :premium }
164
+ expect(GandiV5).to receive(:patch).with(url, '{"mailbox_type":"standard"}', 'Dry-Run': 0)
165
+ .and_return([nil, { 'message' => 'Confirmation message.' }])
166
+ expect(subject.downgrade(sharing_id: 'abc')).to be true
167
+ expect(subject.type).to be :standard
168
+ end
169
+
170
+ it 'Is already premium' do
171
+ expect(GandiV5).to_not receive(:patch)
172
+ expect(subject.downgrade(sharing_id: 'abc')).to be false
173
+ end
174
+ end
175
+
176
+ context 'Dry run' do
177
+ it 'Is downgraded' do
178
+ subject.instance_exec { @type = :premium }
179
+ expect(GandiV5).to receive(:patch).with(url, '{"mailbox_type":"standard"}', 'Dry-Run': 1)
180
+ .and_return([nil, { 'status' => 'success' }])
181
+ expect(subject.downgrade(dry_run: true)).to eq('status' => 'success')
182
+ expect(subject.type).to be :premium
183
+ end
184
+
185
+ it 'Is already premium' do
186
+ expect(GandiV5).to_not receive(:patch)
187
+ expect(subject.downgrade(dry_run: true)).to be false
188
+ end
189
+ end
190
+ end
191
+
90
192
  it '#delete' do
91
193
  url = 'https://api.gandi.net/v5/email/mailboxes/example.com/mailbox-uuid'
92
194
  expect(GandiV5).to receive(:delete).with(url)
@@ -204,7 +306,7 @@ describe GandiV5::Email::Mailbox do
204
306
 
205
307
  it 'No aliases and :standard type' do
206
308
  body = '{"mailbox_type":"standard","login":"login","password":"crypted_password","aliases":[]}'
207
- expect(GandiV5).to receive(:post).with(url, body)
309
+ expect(GandiV5).to receive(:post).with(url, body, 'Dry-Run': 0)
208
310
  .and_return([created_response, { 'message' => 'Confirmation message.' }])
209
311
  expect(described_class).to receive(:crypt_password).with(good_password).and_return('crypted_password')
210
312
  expect(described_class).to receive(:fetch).with('example.com', 'created-mailbox-uuid').and_return(created_mailbox)
@@ -214,7 +316,7 @@ describe GandiV5::Email::Mailbox do
214
316
 
215
317
  it 'With aliases' do
216
318
  body = '{"mailbox_type":"standard","login":"login","password":"crypted_password","aliases":["alias-1"]}'
217
- expect(GandiV5).to receive(:post).with(url, body)
319
+ expect(GandiV5).to receive(:post).with(url, body, 'Dry-Run': 0)
218
320
  .and_return([created_response, { 'message' => 'Confirmation message.' }])
219
321
  expect(described_class).to receive(:crypt_password).with(good_password).and_return('crypted_password')
220
322
  expect(described_class).to receive(:fetch).with('example.com', 'created-mailbox-uuid').and_return(created_mailbox)
@@ -224,7 +326,7 @@ describe GandiV5::Email::Mailbox do
224
326
 
225
327
  it 'With different type' do
226
328
  body = '{"mailbox_type":"premium","login":"login","password":"crypted_password","aliases":[]}'
227
- expect(GandiV5).to receive(:post).with(url, body)
329
+ expect(GandiV5).to receive(:post).with(url, body, 'Dry-Run': 0)
228
330
  .and_return([created_response, { 'message' => 'Confirmation message.' }])
229
331
  expect(described_class).to receive(:crypt_password).with(good_password).and_return('crypted_password')
230
332
  expect(described_class).to receive(:fetch).with('example.com', 'created-mailbox-uuid').and_return(created_mailbox)
@@ -254,6 +356,16 @@ describe GandiV5::Email::Mailbox do
254
356
  'no available standard slots'
255
357
  )
256
358
  end
359
+
360
+ it 'Doing a dry run' do
361
+ body = '{"mailbox_type":"standard","login":"login","password":"crypted_password","aliases":[]}'
362
+ expect(GandiV5).to receive(:post).with(url, body, 'Dry-Run': 1)
363
+ .and_return([nil, { 'status' => 'success' }])
364
+ expect(described_class).to receive(:crypt_password).with(good_password).and_return('crypted_password')
365
+ expect(described_class).to_not receive(:fetch)
366
+
367
+ expect(described_class.create('example.com', 'login', good_password, dry_run: true)).to eq('status' => 'success')
368
+ end
257
369
  end
258
370
 
259
371
  describe '.fetch' do
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe GandiV5::Organization::Customer do
4
+ let(:body_fixtures) { File.expand_path(File.join('spec', 'fixtures', 'bodies', 'GandiV5_Organization_Customer')) }
5
+
6
+ describe '.create' do
7
+ let(:url) { 'https://api.gandi.net/v5/organization/organizations/uuid/customers' }
8
+ let(:attrs) do
9
+ {
10
+ city: 'Ci',
11
+ country: 'Co',
12
+ email: 'a@e',
13
+ firstname: 'f',
14
+ lastname: 'l',
15
+ phone: '0',
16
+ streetaddr: 'sa',
17
+ type: :individual
18
+ }
19
+ end
20
+
21
+ it 'Success' do
22
+ response = double RestClient::Response, headers: { location: '' }
23
+ expect(GandiV5).to receive(:post).with(url, attrs.to_json).and_return([response, nil])
24
+ expect(described_class.create('uuid', **attrs)).to be nil
25
+ end
26
+
27
+ describe 'Checks for required attributes' do
28
+ %i[city country email firstname lastname phone streetaddr type].each do |attr|
29
+ it attr do
30
+ attrs.delete attr
31
+ expect { described_class.new('org_uuid', **attrs) }.to raise_exception ArgumentError
32
+ end
33
+ end
34
+ end
35
+
36
+ it 'Invalid type' do
37
+ attrs[:type] = :invalid
38
+ expect { described_class.new('org_uuid', **attrs) }.to raise_exception ArgumentError
39
+ end
40
+ end
41
+
42
+ describe '.list' do
43
+ describe 'With default values' do
44
+ subject { described_class.list('uuid') }
45
+
46
+ before :each do
47
+ url = 'https://api.gandi.net/v5/organization/organizations/uuid/customers'
48
+ expect(GandiV5).to receive(:get).with(url, params: {})
49
+ .and_return([nil, YAML.load_file(File.join(body_fixtures, 'list.yml'))])
50
+ end
51
+
52
+ its('count') { should eq 1 }
53
+ its('first.uuid') { should eq 'customer-uuid' }
54
+ its('first.name') { should eq 'FirstLast' }
55
+ its('first.first_name') { should eq 'First' }
56
+ its('first.last_name') { should eq 'Last' }
57
+ its('first.email') { should eq 'first.last@example.com' }
58
+ its('first.type') { should eq :individual }
59
+ its('first.org_name') { should eq 'Org' }
60
+ end
61
+
62
+ describe 'Passes optional query params' do
63
+ let(:url) { 'https://api.gandi.net/v5/organization/organizations/org_uuid/customers' }
64
+
65
+ it 'name' do
66
+ expect(GandiV5).to receive(:get).with(url, params: { '~name' => '5' })
67
+ .and_return([nil, []])
68
+ expect(described_class.list('org_uuid', name: '5')).to eq []
69
+ end
70
+
71
+ %i[permission sort_by].each do |param|
72
+ it param.to_s do
73
+ headers = { param => 5 }
74
+ expect(GandiV5).to receive(:get).with(url, params: headers)
75
+ .and_return([nil, []])
76
+ expect(described_class.list('org_uuid', **headers)).to eq []
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -64,4 +64,18 @@ describe GandiV5::Organization do
64
64
  its('security_email_validated') { should eq true }
65
65
  its('security_email_validation_deadline') { should eq Time.new(2017, 11, 22, 17, 13, 33) }
66
66
  end
67
+
68
+ it '#customers' do
69
+ subject = described_class.new uuid: 'org_uuid'
70
+ returns = double Array
71
+ expect(GandiV5::Organization::Customer).to receive(:list).with('org_uuid', param: :value).and_return(returns)
72
+ expect(subject.customers('org_uuid', param: :value)).to be returns
73
+ end
74
+
75
+ it '#create_customer' do
76
+ subject = described_class.new uuid: 'org_uuid'
77
+ returns = double Object
78
+ expect(GandiV5::Organization::Customer).to receive(:create).with('org_uuid', param: :value).and_return(returns)
79
+ expect(subject.create_customer('org_uuid', param: :value)).to be returns
80
+ end
67
81
  end
@@ -47,6 +47,10 @@ describe GandiV5 do
47
47
  end
48
48
 
49
49
  describe 'Uses RestClient' do
50
+ let(:response) do
51
+ double RestClient::Response, body: 'Hello world!', headers: { content_type: 'text/plain' }
52
+ end
53
+
50
54
  %i[get delete].each do |method|
51
55
  describe ":#{method}" do
52
56
  it 'As JSON' do
@@ -58,30 +62,30 @@ describe GandiV5 do
58
62
  end
59
63
 
60
64
  it 'As text' do
61
- response_data = 'Hello world!'
62
- response = double RestClient::Response, body: response_data, headers: { content_type: 'text/plain' }
63
65
  expect(described_class).to receive(:prepare_headers)
64
66
  expect(RestClient).to receive(method).with('url', hash_including(accept: 'text/plain')).and_return(response)
65
- expect(described_class.send(method, 'url', accept: 'text/plain')).to match_array [response, response_data]
67
+ expect(described_class.send(method, 'url', accept: 'text/plain')).to match_array [response, 'Hello world!']
66
68
  end
67
69
 
68
70
  it 'Passes request headers' do
69
71
  expect(described_class).to receive(:prepare_headers)
70
72
  expect(described_class).to receive(:parse_response)
71
- expect(RestClient).to receive(method).with(anything, header: 'value')
72
- expect(described_class.send(method, 'url', header: 'value')).to match_array [nil, nil]
73
+ expect(RestClient).to receive(method).with(anything, header: 'value').and_return(response)
74
+ expect(described_class.send(method, 'url', header: 'value')).to match_array [response, nil]
73
75
  end
74
76
 
75
77
  it 'Adds authentication header' do
76
78
  expect(RestClient).to receive(method).with(anything, hash_including(Authorization: 'Apikey abdce12345'))
79
+ .and_return(response)
77
80
  expect(described_class).to receive(:parse_response)
78
- expect(described_class.send(method, 'https://api.gandi.net/v5/')).to match_array [nil, nil]
81
+ expect(described_class.send(method, 'https://api.gandi.net/v5/')).to match_array [response, nil]
79
82
  end
80
83
 
81
84
  it 'Default accept header' do
82
85
  expect(RestClient).to receive(method).with(any_args, hash_including(accept: 'application/json'))
86
+ .and_return(response)
83
87
  expect(described_class).to receive(:parse_response)
84
- expect(described_class.send(method, 'https://api.gandi.net/v5/')).to match_array [nil, nil]
88
+ expect(described_class.send(method, 'https://api.gandi.net/v5/')).to match_array [response, nil]
85
89
  end
86
90
 
87
91
  it 'Converts a 406 (bad request) exception' do
@@ -92,6 +96,13 @@ describe GandiV5 do
92
96
  end
93
97
  end
94
98
 
99
+ it ':delete handles no content-type' do
100
+ response = double RestClient::Response, headers: {}
101
+ expect(described_class).to receive(:prepare_headers)
102
+ expect(RestClient).to receive(:delete).with('url', hash_including(accept: 'text/plain')).and_return(response)
103
+ expect(described_class.delete('url', accept: 'text/plain')).to match_array [response, nil]
104
+ end
105
+
95
106
  %i[patch post put].each do |method|
96
107
  describe ":#{method}" do
97
108
  let(:payload) { '{"say":"hello world"}' }
@@ -107,26 +118,25 @@ describe GandiV5 do
107
118
  end
108
119
 
109
120
  it 'As text' do
110
- response_data = 'hello world'
111
- response = double RestClient::Response, body: response_data, headers: { content_type: 'text/plain' }
112
121
  expect(described_class).to receive(:prepare_headers)
113
122
  expect(RestClient).to receive(method).with('url', payload, hash_including(accept: 'text/plain'))
114
123
  .and_return(response)
115
- expect(described_class.send(method, 'url', payload, accept: 'text/plain')).to match_array [response, response_data]
124
+ array = [response, 'Hello world!']
125
+ expect(described_class.send(method, 'url', payload, accept: 'text/plain')).to match_array array
116
126
  end
117
127
 
118
128
  it 'Passes payload' do
119
129
  expect(described_class).to receive(:prepare_headers)
120
130
  expect(described_class).to receive(:parse_response)
121
- expect(RestClient).to receive(method).with(anything, payload, any_args)
122
- expect(described_class.send(method, 'url', payload)).to match_array [nil, nil]
131
+ expect(RestClient).to receive(method).with(anything, payload, any_args).and_return(response)
132
+ expect(described_class.send(method, 'url', payload)).to match_array [response, nil]
123
133
  end
124
134
 
125
135
  it 'Passes request headers' do
126
136
  expect(described_class).to receive(:prepare_headers)
127
137
  expect(described_class).to receive(:parse_response)
128
- expect(RestClient).to receive(method).with(any_args, hash_including(header: 'value'))
129
- expect(described_class.send(method, 'url', payload, header: 'value')).to match_array [nil, nil]
138
+ expect(RestClient).to receive(method).with(any_args, hash_including(header: 'value')).and_return(response)
139
+ expect(described_class.send(method, 'url', payload, header: 'value')).to match_array [response, nil]
130
140
  end
131
141
 
132
142
  it 'Adds content type header' do
@@ -137,14 +147,16 @@ describe GandiV5 do
137
147
 
138
148
  it 'Adds authentication header' do
139
149
  expect(RestClient).to receive(method).with(any_args, hash_including(Authorization: 'Apikey abdce12345'))
150
+ .and_return(response)
140
151
  expect(described_class).to receive(:parse_response)
141
- expect(described_class.send(method, 'https://api.gandi.net/v5/', payload)).to match_array [nil, nil]
152
+ expect(described_class.send(method, 'https://api.gandi.net/v5/', payload)).to match_array [response, nil]
142
153
  end
143
154
 
144
155
  it 'Default accept header' do
145
156
  expect(RestClient).to receive(method).with(any_args, hash_including(accept: 'application/json'))
157
+ .and_return(response)
146
158
  expect(described_class).to receive(:parse_response)
147
- expect(described_class.send(method, 'https://api.gandi.net/v5/', payload)).to match_array [nil, nil]
159
+ expect(described_class.send(method, 'https://api.gandi.net/v5/', payload)).to match_array [response, nil]
148
160
  end
149
161
 
150
162
  it 'Converts a 406 (bad request) exception' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gandi_v5
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Gauld
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-09-03 00:00:00.000000000 Z
11
+ date: 2020-05-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dotenv
@@ -340,6 +340,7 @@ files:
340
340
  - lib/gandi_v5/live_dns/zone.rb
341
341
  - lib/gandi_v5/live_dns/zone/snapshot.rb
342
342
  - lib/gandi_v5/organization.rb
343
+ - lib/gandi_v5/organization/customer.rb
343
344
  - lib/gandi_v5/version.rb
344
345
  - spec/.rubocop.yml
345
346
  - spec/features/domain_spec.rb
@@ -371,6 +372,7 @@ files:
371
372
  - spec/fixtures/bodies/GandiV5_LiveDNS_Zone_Snapshot/list.yml
372
373
  - spec/fixtures/bodies/GandiV5_Organization/fetch.yml
373
374
  - spec/fixtures/bodies/GandiV5_Organization/list.yml
375
+ - spec/fixtures/bodies/GandiV5_Organization_Customer/list.yml
374
376
  - spec/fixtures/vcr/Domain_features/List_domains.yml
375
377
  - spec/fixtures/vcr/Domain_features/Renew_domain.yml
376
378
  - spec/fixtures/vcr/LiveDNS_Domain_features/List_domains.yml
@@ -415,6 +417,7 @@ files:
415
417
  - spec/units/gandi_v5/live_dns/zone/snapshot_spec.rb
416
418
  - spec/units/gandi_v5/live_dns/zone_spec.rb
417
419
  - spec/units/gandi_v5/live_dns_spec.rb
420
+ - spec/units/gandi_v5/organization/customer_spec.rb
418
421
  - spec/units/gandi_v5/organization_spec.rb
419
422
  - spec/units/gandi_v5_spec.rb
420
423
  homepage: https://github.com/robertgauld/gandi_v5
@@ -436,7 +439,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
436
439
  - !ruby/object:Gem::Version
437
440
  version: 2.6.14
438
441
  requirements: []
439
- rubygems_version: 3.0.3
442
+ rubygems_version: 3.1.2
440
443
  signing_key:
441
444
  specification_version: 4
442
445
  summary: Make use of Gandi's V5 API.