droplet_kit 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -4
  3. data/CHANGELOG.md +6 -0
  4. data/README.md +70 -1
  5. data/droplet_kit.gemspec +1 -1
  6. data/lib/droplet_kit.rb +11 -0
  7. data/lib/droplet_kit/client.rb +1 -0
  8. data/lib/droplet_kit/mappings/domain_record_mapping.rb +1 -0
  9. data/lib/droplet_kit/mappings/firewall_inbound_rule_mapping.rb +16 -0
  10. data/lib/droplet_kit/mappings/firewall_mapping.rb +25 -0
  11. data/lib/droplet_kit/mappings/firewall_outbound_rule_mapping.rb +16 -0
  12. data/lib/droplet_kit/mappings/firewall_pending_change_mapping.rb +16 -0
  13. data/lib/droplet_kit/mappings/firewall_rule_mapping.rb +14 -0
  14. data/lib/droplet_kit/mappings/tag_mapping.rb +1 -1
  15. data/lib/droplet_kit/models/domain_record.rb +1 -0
  16. data/lib/droplet_kit/models/firewall.rb +13 -0
  17. data/lib/droplet_kit/models/firewall_inbound_rule.rb +7 -0
  18. data/lib/droplet_kit/models/firewall_outbound_rule.rb +7 -0
  19. data/lib/droplet_kit/models/firewall_pending_change.rb +7 -0
  20. data/lib/droplet_kit/models/firewall_rule.rb +6 -0
  21. data/lib/droplet_kit/resources/firewall_resource.rb +85 -0
  22. data/lib/droplet_kit/resources/tag_resource.rb +0 -5
  23. data/lib/droplet_kit/version.rb +1 -1
  24. data/spec/fixtures/domain_records/all.json +5 -0
  25. data/spec/fixtures/domain_records/create.json +1 -0
  26. data/spec/fixtures/domain_records/find.json +1 -0
  27. data/spec/fixtures/domain_records/update.json +1 -0
  28. data/spec/fixtures/firewalls/all.json +67 -0
  29. data/spec/fixtures/firewalls/find.json +61 -0
  30. data/spec/lib/droplet_kit/resources/domain_record_resource_spec.rb +5 -1
  31. data/spec/lib/droplet_kit/resources/firewall_resource_spec.rb +265 -0
  32. data/spec/lib/droplet_kit/resources/tag_resource_spec.rb +0 -14
  33. data/spec/support/shared_examples/paginated_endpoint.rb +4 -2
  34. data/spec/support/shared_examples/unprocessable_entity.rb +11 -0
  35. metadata +24 -7
  36. data/spec/support/shared_examples/unsuccessful_create.rb +0 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 025077918c4a61f739d58486166607209439b129
4
- data.tar.gz: c66125a6ac2909c842b79242e3c818c0e5228287
3
+ metadata.gz: 9321808dc6ae64cb77eb86c4411a41a3eb6592f9
4
+ data.tar.gz: 83ad4999326aa114fb3a05832ae08ed31c4bd86a
5
5
  SHA512:
6
- metadata.gz: 6198f8fc7be8f537cbbbd8996253c877748428ada7078c680f4f95598d3e2d7dbcd8aebb99da5623927c7d8dbaec30ff2dc6f6a2eb7c814c2bd2d6a2029c4e57
7
- data.tar.gz: f84a6955aae3e753dcba901b31527bde4048e887779be627b77a68b6a0e3b49801e71b4b3624d3d7043c4c59640af27cae4cf512d2a516ff5a7c38025ae736c8
6
+ metadata.gz: 036ef5cb5972947986806e9e194ac470d03684e32597702d3ac866b65b51dd39e7761fbc0b1b93862fbf795b3b3c46fa2b5dc1d2622461731c7e639a57db50a9
7
+ data.tar.gz: 0add3525b382329e513187b96e3ed6b1c3c7bad1485c24c327553a126de270781ec4e40c513b12e8c34d68d7dc903d6d477a31cb585279fd8620235584354909
@@ -6,15 +6,16 @@ before_install:
6
6
 
7
7
  rvm:
8
8
  - "2.0.0"
9
- - "2.1.5"
10
- - "2.2.2"
11
- - "2.3.0"
9
+ - "2.1.10"
10
+ - "2.2.7"
11
+ - "2.3.4"
12
+ - "2.4.1"
12
13
 
13
14
  matrix:
14
15
  exclude:
15
16
  - rvm: "2.0.0"
16
17
  env: ACTIVESUPPORT_VERSION=5
17
- - rvm: "2.1.5"
18
+ - rvm: "2.1.10"
18
19
  env: ACTIVESUPPORT_VERSION=5
19
20
 
20
21
  env:
@@ -1,3 +1,9 @@
1
+ ### Version 2.2.0
2
+ * Added Firewall resource.
3
+ * Added support for updating TTLs for DomainRecord resource.
4
+ * Added support of all Rails 5 releases.
5
+ * Added depreciation for Tag resource rename.
6
+
1
7
  ### Version 2.1.0
2
8
  * Added monitoring to the Droplet resource.
3
9
  * Added LoadBalancer resource.
data/README.md CHANGED
@@ -162,7 +162,8 @@ client.domain_records #=> DropletKit::DomainRecordResource
162
162
  domain_record = DropletKit::DomainRecord.new(
163
163
  type: 'CNAME',
164
164
  name: 'www',
165
- data: '@'
165
+ data: '@',
166
+ ttl: 1800
166
167
  )
167
168
  ```
168
169
 
@@ -174,6 +175,58 @@ Actions supported:
174
175
  * `client.domain_records.delete(for_domain: 'for_domain', id: 'id')`
175
176
  * `client.domain_records.update(domain_record, for_domain: 'for_domain', id: 'id')`
176
177
 
178
+ ## Firewall resource
179
+
180
+ ```ruby
181
+ client = DropletKit::Client.new(access_token: 'TOKEN')
182
+ client.firewalls #=> DropletKit::FirewallResource
183
+
184
+ inbound_rule = DropletKit::FirewallInboundRule.new(
185
+ protocol: 'icmp',
186
+ ports: '0',
187
+ sources: {
188
+ tags: ['frontend', 'backend'],
189
+ load_balancer_uids: ['d2d3920a-9d45-41b0-b018-d15e18ec60a4']
190
+ }
191
+ )
192
+
193
+ outbound_rule = DropletKit::FirewallOutboundRule.new(
194
+ protocol: 'icmp',
195
+ ports: '0',
196
+ destinations: {
197
+ addresses: ["127.0.0.0"],
198
+ droplet_ids: [456, 789]
199
+ }
200
+ )
201
+
202
+ firewall = DropletKit::Firewall.new(
203
+ name: 'firewall',
204
+ inbound_rules: [
205
+ inbound_rule
206
+ ],
207
+ outbound_rules: [
208
+ outbound_rule
209
+ ],
210
+ droplet_ids: [123],
211
+ tags: ['backend']
212
+ )
213
+ ```
214
+
215
+ Actions supported:
216
+
217
+ * `client.firewalls.find(id: 'id')`
218
+ * `client.firewalls.create(firewall)`
219
+ * `client.firewalls.update(firewall, id: 'id')`
220
+ * `client.firewalls.all()`
221
+ * `client.firewalls.all_by_droplet(droplet_id: 'id')`
222
+ * `client.firewalls.delete(id: 'id')`
223
+ * `client.firewalls.add_droplets([droplet.id], id: 'id')`
224
+ * `client.firewalls.remove_droplets([droplet.id], id: 'id')`
225
+ * `client.firewalls.add_tags([tag.name], id: 'id')`
226
+ * `client.firewalls.remove_tags([tag.name], id: 'id')`
227
+ * `client.firewalls.add_rules(inbound_rules: [inbound_rule], outbound_rules: [outbound_rule], id: 'id')`
228
+ * `client.firewalls.remove_rules(inbound_rules: [inbound_rule], outbound_rules: [outbound_rule], id: 'id')`
229
+
177
230
 
178
231
  ## Image resource
179
232
 
@@ -272,6 +325,22 @@ Actions supported:
272
325
  * `client.ssh_keys.delete(id: 'id')`
273
326
  * `client.ssh_keys.update(ssh_key, id: 'id')`
274
327
 
328
+ ## Tag resource
329
+
330
+ ```ruby
331
+ client = DropletKit::Client.new(access_token: 'TOKEN')
332
+ client.tags #=> DropletKit::TagResource
333
+ ```
334
+
335
+ Actions supported:
336
+
337
+ * `client.tags.all()`
338
+ * `client.tags.find(name: 'name')`
339
+ * `client.tags.create(tag)
340
+ * `client.tags.delete(name: 'name')`
341
+ * `client.tags.tag_resources(name: 'name', resources: [{ resource_id => 'droplet_id', resource_type: 'droplet' }])`
342
+ * `client.tags.untag_resources(name 'name', resources: [{ resource_id => 'droplet_id', resource_type: 'droplet' }])`
343
+
275
344
  ## Account resource
276
345
 
277
346
  ```ruby
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
23
23
  spec.add_dependency 'virtus', '~> 1.0.3'
24
24
  spec.add_dependency "resource_kit", '~> 0.1.5'
25
25
  spec.add_dependency "kartograph", '~> 0.2.3'
26
- spec.add_dependency "activesupport", '> 3.0', '< 5.1'
26
+ spec.add_dependency "activesupport", '> 3.0', '< 6'
27
27
  spec.add_dependency "faraday", '~> 0.9.1'
28
28
 
29
29
  spec.add_development_dependency "bundler", ">= 1.11.0"
@@ -33,6 +33,11 @@ module DropletKit
33
33
  autoload :HealthCheck, 'droplet_kit/models/health_check'
34
34
  autoload :ForwardingRule, 'droplet_kit/models/forwarding_rule'
35
35
  autoload :Certificate, 'droplet_kit/models/certificate'
36
+ autoload :Firewall, 'droplet_kit/models/firewall'
37
+ autoload :FirewallRule, 'droplet_kit/models/firewall_rule'
38
+ autoload :FirewallInboundRule, 'droplet_kit/models/firewall_inbound_rule'
39
+ autoload :FirewallOutboundRule, 'droplet_kit/models/firewall_outbound_rule'
40
+ autoload :FirewallPendingChange, 'droplet_kit/models/firewall_pending_change'
36
41
 
37
42
  # Resources
38
43
  autoload :DropletResource, 'droplet_kit/resources/droplet_resource'
@@ -55,6 +60,7 @@ module DropletKit
55
60
  autoload :SnapshotResource, 'droplet_kit/resources/snapshot_resource'
56
61
  autoload :LoadBalancerResource, 'droplet_kit/resources/load_balancer_resource'
57
62
  autoload :CertificateResource, 'droplet_kit/resources/certificate_resource'
63
+ autoload :FirewallResource, 'droplet_kit/resources/firewall_resource'
58
64
 
59
65
  # JSON Maps
60
66
  autoload :DropletMapping, 'droplet_kit/mappings/droplet_mapping'
@@ -83,6 +89,11 @@ module DropletKit
83
89
  autoload :HealthCheckMapping, 'droplet_kit/mappings/health_check_mapping'
84
90
  autoload :ForwardingRuleMapping, 'droplet_kit/mappings/forwarding_rule_mapping'
85
91
  autoload :CertificateMapping, 'droplet_kit/mappings/certificate_mapping'
92
+ autoload :FirewallMapping, 'droplet_kit/mappings/firewall_mapping'
93
+ autoload :FirewallRuleMapping, 'droplet_kit/mappings/firewall_rule_mapping'
94
+ autoload :FirewallInboundRuleMapping, 'droplet_kit/mappings/firewall_inbound_rule_mapping'
95
+ autoload :FirewallOutboundRuleMapping, 'droplet_kit/mappings/firewall_outbound_rule_mapping'
96
+ autoload :FirewallPendingChangeMapping, 'droplet_kit/mappings/firewall_pending_change_mapping'
86
97
 
87
98
  # Utils
88
99
  autoload :PaginatedResource, 'droplet_kit/paginated_resource'
@@ -24,6 +24,7 @@ module DropletKit
24
24
  domains: DomainResource,
25
25
  domain_records: DomainRecordResource,
26
26
  droplet_actions: DropletActionResource,
27
+ firewalls: FirewallResource,
27
28
  images: ImageResource,
28
29
  image_actions: ImageActionResource,
29
30
  load_balancers: LoadBalancerResource,
@@ -12,6 +12,7 @@ module DropletKit
12
12
  property :data, scopes: [:read, :create, :update]
13
13
  property :priority, scopes: [:read, :create, :update]
14
14
  property :port, scopes: [:read, :create, :update]
15
+ property :ttl, scopes: [:read, :create, :update]
15
16
  property :weight, scopes: [:read, :create, :update]
16
17
  end
17
18
  end
@@ -0,0 +1,16 @@
1
+ module DropletKit
2
+ class FirewallInboundRuleMapping
3
+ include Kartograph::DSL
4
+
5
+ kartograph do
6
+ mapping FirewallInboundRule
7
+ root_key plural: 'inbound_rules', scopes: [:read, :create, :update]
8
+
9
+ scoped :read, :create, :update do
10
+ property :protocol
11
+ property :ports
12
+ property :sources
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,25 @@
1
+ module DropletKit
2
+ class FirewallMapping
3
+ include Kartograph::DSL
4
+
5
+ kartograph do
6
+ mapping Firewall
7
+ root_key singular: 'firewall', plural: 'firewalls', scopes: [:read]
8
+
9
+ scoped :read do
10
+ property :id
11
+ property :status
12
+ property :created_at
13
+ property :pending_changes, plural: true, include: FirewallPendingChangeMapping
14
+ end
15
+
16
+ scoped :read, :create, :update do
17
+ property :name
18
+ property :inbound_rules, plural: true, include: FirewallInboundRuleMapping
19
+ property :outbound_rules, plural: true, include: FirewallOutboundRuleMapping
20
+ property :droplet_ids
21
+ property :tags
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,16 @@
1
+ module DropletKit
2
+ class FirewallOutboundRuleMapping
3
+ include Kartograph::DSL
4
+
5
+ kartograph do
6
+ mapping FirewallOutboundRule
7
+ root_key plural: 'outbound_rules', scopes: [:read, :create, :update]
8
+
9
+ scoped :read, :create, :update do
10
+ property :protocol
11
+ property :ports
12
+ property :destinations
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module DropletKit
2
+ class FirewallPendingChangeMapping
3
+ include Kartograph::DSL
4
+
5
+ kartograph do
6
+ mapping FirewallPendingChange
7
+ root_key plural: 'pending_changes', scopes: [:read]
8
+
9
+ scoped :read do
10
+ property :droplet_id
11
+ property :removing
12
+ property :status
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ module DropletKit
2
+ class FirewallRuleMapping
3
+ include Kartograph::DSL
4
+
5
+ kartograph do
6
+ mapping FirewallRule
7
+
8
+ scoped :create, :update do
9
+ property :inbound_rules, plural: true, include: FirewallInboundRuleMapping
10
+ property :outbound_rules, plural: true, include: FirewallOutboundRuleMapping
11
+ end
12
+ end
13
+ end
14
+ end
@@ -6,7 +6,7 @@ module DropletKit
6
6
  mapping Tag
7
7
  root_key plural: 'tags', singular: 'tag', scopes: [:read]
8
8
 
9
- scoped :read, :create, :update do
9
+ scoped :read, :create do
10
10
  property :name
11
11
  end
12
12
 
@@ -6,6 +6,7 @@ module DropletKit
6
6
  attribute :data
7
7
  attribute :priority
8
8
  attribute :port
9
+ attribute :ttl
9
10
  attribute :weight
10
11
  end
11
12
  end
@@ -0,0 +1,13 @@
1
+ module DropletKit
2
+ class Firewall < BaseModel
3
+ attribute :id
4
+ attribute :name
5
+ attribute :status
6
+ attribute :created_at
7
+ attribute :inbound_rules
8
+ attribute :outbound_rules
9
+ attribute :droplet_ids
10
+ attribute :tags
11
+ attribute :pending_changes
12
+ end
13
+ end
@@ -0,0 +1,7 @@
1
+ module DropletKit
2
+ class FirewallInboundRule < BaseModel
3
+ attribute :protocol
4
+ attribute :ports
5
+ attribute :sources
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module DropletKit
2
+ class FirewallOutboundRule < BaseModel
3
+ attribute :protocol
4
+ attribute :ports
5
+ attribute :destinations
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module DropletKit
2
+ class FirewallPendingChange < BaseModel
3
+ attribute :droplet_id
4
+ attribute :removing
5
+ attribute :status
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ module DropletKit
2
+ class FirewallRule < BaseModel
3
+ attribute :inbound_rules
4
+ attribute :outbound_rules
5
+ end
6
+ end
@@ -0,0 +1,85 @@
1
+ module DropletKit
2
+ class FirewallResource < ResourceKit::Resource
3
+ include ErrorHandlingResourcable
4
+
5
+ resources do
6
+ action :find, 'GET /v2/firewalls/:id' do
7
+ handler(200) { |response| FirewallMapping.extract_single(response.body, :read) }
8
+ end
9
+
10
+ action :create, 'POST /v2/firewalls' do
11
+ body { |firewall| FirewallMapping.representation_for(:create, firewall) }
12
+ handler(202) { |response| FirewallMapping.extract_single(response.body, :read) }
13
+ handler(422) { |response| ErrorMapping.fail_with(FailedCreate, response.body) }
14
+ end
15
+
16
+ action :update, 'PUT /v2/firewalls/:id' do
17
+ body { |firewall| FirewallMapping.representation_for(:update, firewall) }
18
+ handler(200) { |response| FirewallMapping.extract_single(response.body, :read) }
19
+ handler(422) { |response| ErrorMapping.fail_with(FailedUpdate, response.body) }
20
+ end
21
+
22
+ action :all, 'GET /v2/firewalls' do
23
+ query_keys :per_page, :page
24
+ handler(200) { |response| FirewallMapping.extract_collection(response.body, :read) }
25
+ end
26
+
27
+ action :all_by_droplet, 'GET /v2/droplets/:droplet_id/firewalls' do
28
+ query_keys :per_page, :page
29
+ handler(200) { |response| FirewallMapping.extract_collection(response.body, :read) }
30
+ end
31
+
32
+ action :delete, 'DELETE /v2/firewalls/:id' do
33
+ handler(204) { |_| true }
34
+ end
35
+
36
+ action :add_droplets, 'POST /v2/firewalls/:id/droplets' do
37
+ body { |droplet_ids| { droplet_ids: droplet_ids }.to_json }
38
+ handler(204) { |_| true }
39
+ end
40
+
41
+ action :remove_droplets, 'DELETE /v2/firewalls/:id/droplets' do
42
+ body { |droplet_ids| { droplet_ids: droplet_ids }.to_json }
43
+ handler(204) { |_| true }
44
+ end
45
+
46
+ action :add_tags, 'POST /v2/firewalls/:id/tags' do
47
+ body { |tags| { tags: tags}.to_json }
48
+ handler(204) { |_| true }
49
+ end
50
+
51
+ action :remove_tags, 'DELETE /v2/firewalls/:id/tags' do
52
+ body { |tags| { tags: tags}.to_json }
53
+ handler(204) { |_| true }
54
+ end
55
+
56
+ action :add_rules, 'POST /v2/firewalls/:id/rules' do
57
+ body do |rules|
58
+ DropletKit::FirewallRuleMapping
59
+ .representation_for(:create,
60
+ FirewallRule.new(inbound_rules: rules[:inbound_rules],
61
+ outbound_rules: rules[:outbound_rules]))
62
+ end
63
+ handler(204) { |_| true }
64
+ end
65
+
66
+ action :remove_rules, 'DELETE /v2/firewalls/:id/rules' do
67
+ body do |rules|
68
+ DropletKit::FirewallRuleMapping
69
+ .representation_for(:update,
70
+ FirewallRule.new(inbound_rules: rules[:inbound_rules],
71
+ outbound_rules: rules[:outbound_rules]))
72
+ end
73
+ handler(204) { |_| true }
74
+ end
75
+ end
76
+
77
+ def all(*args)
78
+ PaginatedResource.new(action(:all), self, *args)
79
+ end
80
+
81
+ def all_by_droplet(*args)
82
+ PaginatedResource.new(action(:all_by_droplet), self, *args)
83
+ end
84
+ end
85
+ end
@@ -18,11 +18,6 @@ module DropletKit
18
18
  handler(422) { |response| ErrorMapping.fail_with(FailedCreate, response.body) }
19
19
  end
20
20
 
21
- action :update, 'PUT /v2/tags/:name' do
22
- body { |object| TagMapping.representation_for(:update, object) }
23
- handler(200) { |response| TagMapping.extract_single(response.body, :read) }
24
- end
25
-
26
21
  action :delete, 'DELETE /v2/tags/:name' do
27
22
  handler(204) { |_| true }
28
23
  handler(422) { |response| ErrorMapping.fail_with(FailedCreate, response.body) }
@@ -1,3 +1,3 @@
1
1
  module DropletKit
2
- VERSION = "2.1.0"
2
+ VERSION = "2.2.0"
3
3
  end
@@ -7,6 +7,7 @@
7
7
  "data": "8.8.8.8",
8
8
  "priority": null,
9
9
  "port": null,
10
+ "ttl": 1800,
10
11
  "weight": null
11
12
  },
12
13
  {
@@ -16,6 +17,7 @@
16
17
  "data": "NS1.DIGITALOCEAN.COM.",
17
18
  "priority": null,
18
19
  "port": null,
20
+ "ttl": 1800,
19
21
  "weight": null
20
22
  },
21
23
  {
@@ -25,6 +27,7 @@
25
27
  "data": "NS2.DIGITALOCEAN.COM.",
26
28
  "priority": null,
27
29
  "port": null,
30
+ "ttl": 1800,
28
31
  "weight": null
29
32
  },
30
33
  {
@@ -34,6 +37,7 @@
34
37
  "data": "NS3.DIGITALOCEAN.COM.",
35
38
  "priority": null,
36
39
  "port": null,
40
+ "ttl": 1800,
37
41
  "weight": null
38
42
  },
39
43
  {
@@ -43,6 +47,7 @@
43
47
  "data": "@",
44
48
  "priority": null,
45
49
  "port": null,
50
+ "ttl": 1800,
46
51
  "weight": null
47
52
  }
48
53
  ],
@@ -6,6 +6,7 @@
6
6
  "data": "@",
7
7
  "priority": null,
8
8
  "port": null,
9
+ "ttl": 90,
9
10
  "weight": null
10
11
  }
11
12
  }
@@ -6,6 +6,7 @@
6
6
  "data": "@",
7
7
  "priority": null,
8
8
  "port": null,
9
+ "ttl": 1800,
9
10
  "weight": null
10
11
  }
11
12
  }
@@ -6,6 +6,7 @@
6
6
  "data": "@",
7
7
  "priority": null,
8
8
  "port": null,
9
+ "ttl": 1800,
9
10
  "weight": null
10
11
  }
11
12
  }
@@ -0,0 +1,67 @@
1
+ {
2
+ "firewalls": [
3
+ {
4
+ "id": "11d87802-df17-4f8f-a691-58e408570c12",
5
+ "name": "firewall",
6
+ "status": "succeeded",
7
+ "inbound_rules": [
8
+ {
9
+ "protocol": "icmp",
10
+ "ports": "0",
11
+ "sources": {
12
+ "load_balancer_uids": [
13
+ "d2d3920a-9d45-41b0-b018-d15e18ec60a4"
14
+ ],
15
+ "tags": [
16
+ "backend"
17
+ ]
18
+ }
19
+ },
20
+ {
21
+ "protocol": "tcp",
22
+ "ports": "8000-9000",
23
+ "sources": {
24
+ "addresses": [
25
+ "0.0.0.0/0"
26
+ ]
27
+ }
28
+ }
29
+ ],
30
+ "outbound_rules": [
31
+ {
32
+ "protocol": "icmp",
33
+ "ports": "0",
34
+ "destinations": {
35
+ "tags": [
36
+ "backend"
37
+ ]
38
+ }
39
+ },
40
+ {
41
+ "protocol": "tcp",
42
+ "ports": "8000-9000",
43
+ "destinations": {
44
+ "addresses": [
45
+ "127.0.0.0"
46
+ ],
47
+ "droplet_ids": [
48
+ 345
49
+ ]
50
+ }
51
+ }
52
+ ],
53
+ "created_at": "2017-05-30T16:25:21Z",
54
+ "droplet_ids": [
55
+ 123
56
+ ],
57
+ "tags": [
58
+ "backend"
59
+ ],
60
+ "pending_changes": []
61
+ }
62
+ ],
63
+ "links": {},
64
+ "meta": {
65
+ "total": 1
66
+ }
67
+ }
@@ -0,0 +1,61 @@
1
+ {
2
+ "firewall": {
3
+ "id": "11d87802-df17-4f8f-a691-58e408570c12",
4
+ "name": "firewall",
5
+ "status": "succeeded",
6
+ "inbound_rules": [
7
+ {
8
+ "protocol": "icmp",
9
+ "ports": "0",
10
+ "sources": {
11
+ "load_balancer_uids": [
12
+ "d2d3920a-9d45-41b0-b018-d15e18ec60a4"
13
+ ],
14
+ "tags": [
15
+ "backend"
16
+ ]
17
+ }
18
+ },
19
+ {
20
+ "protocol": "tcp",
21
+ "ports": "8000-9000",
22
+ "sources": {
23
+ "addresses": [
24
+ "0.0.0.0/0"
25
+ ]
26
+ }
27
+ }
28
+ ],
29
+ "outbound_rules": [
30
+ {
31
+ "protocol": "icmp",
32
+ "ports": "0",
33
+ "destinations": {
34
+ "tags": [
35
+ "backend"
36
+ ]
37
+ }
38
+ },
39
+ {
40
+ "protocol": "tcp",
41
+ "ports": "8000-9000",
42
+ "destinations": {
43
+ "addresses": [
44
+ "127.0.0.0"
45
+ ],
46
+ "droplet_ids": [
47
+ 345
48
+ ]
49
+ }
50
+ }
51
+ ],
52
+ "created_at": "2017-05-30T16:25:21Z",
53
+ "droplet_ids": [
54
+ 123
55
+ ],
56
+ "tags": [
57
+ "backend"
58
+ ],
59
+ "pending_changes": []
60
+ }
61
+ }
@@ -29,12 +29,14 @@ RSpec.describe DropletKit::DomainRecordResource do
29
29
  domain_record = DropletKit::DomainRecord.new(
30
30
  type: 'CNAME',
31
31
  name: 'www',
32
- data: '@'
32
+ data: '@',
33
+ ttl: 90
33
34
  )
34
35
  as_hash = DropletKit::DomainRecordMapping.hash_for(:create, domain_record)
35
36
  expect(as_hash['type']).to eq('CNAME')
36
37
  expect(as_hash['name']).to eq('www')
37
38
  expect(as_hash['data']).to eq('@')
39
+ expect(as_hash['ttl']).to eq(90)
38
40
 
39
41
  as_json = DropletKit::DomainRecordMapping.representation_for(:create, domain_record)
40
42
  stub_do_api('/v2/domains/example.com/records', :post).with(body: as_json).to_return(body: response, status: 201)
@@ -44,6 +46,7 @@ RSpec.describe DropletKit::DomainRecordResource do
44
46
  expect(created_domain_record.name).to eq('www')
45
47
  expect(created_domain_record.type).to eq('CNAME')
46
48
  expect(created_domain_record.data).to eq('@')
49
+ expect(created_domain_record.ttl).to eq(90)
47
50
  end
48
51
  end
49
52
 
@@ -90,6 +93,7 @@ RSpec.describe DropletKit::DomainRecordResource do
90
93
  expect(updated_domain_record.name).to eq('lol')
91
94
  expect(updated_domain_record.type).to eq('CNAME')
92
95
  expect(updated_domain_record.data).to eq('@')
96
+ expect(updated_domain_record.ttl).to eq(1800)
93
97
  end
94
98
  end
95
99
  end
@@ -0,0 +1,265 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe DropletKit::FirewallResource do
4
+ include_context 'resources'
5
+
6
+ let(:firewall_fixture_path) { 'firewalls/find' }
7
+ let(:base_path) { '/v2/firewalls' }
8
+ let(:firewall_id) { '11d87802-df17-4f8f-a691-58e408570c12' }
9
+ let(:firewall) do
10
+ DropletKit::Firewall.new(
11
+ name: 'firewall',
12
+ inbound_rules: [
13
+ DropletKit::FirewallInboundRule.new(
14
+ protocol: 'icmp',
15
+ ports: '0',
16
+ sources: {
17
+ tags: ['backend'],
18
+ load_balancer_uids: ['d2d3920a-9d45-41b0-b018-d15e18ec60a4']
19
+ }
20
+ ),
21
+ DropletKit::FirewallInboundRule.new(
22
+ protocol: 'tcp',
23
+ ports: '8000-9000',
24
+ sources: {
25
+ addresses: ['0.0.0.0/0']
26
+ }
27
+ )
28
+ ],
29
+ outbound_rules: [
30
+ DropletKit::FirewallOutboundRule.new(
31
+ protocol: 'icmp',
32
+ ports: '0',
33
+ destinations: {
34
+ tags: ['backend']
35
+ }
36
+ ),
37
+ DropletKit::FirewallOutboundRule.new(
38
+ protocol: 'tcp',
39
+ ports: '8000-9000',
40
+ destinations: {
41
+ addresses: ['127.0.0.0']
42
+ }
43
+ )
44
+ ],
45
+ droplet_ids: [123],
46
+ tags: ['backend'])
47
+ end
48
+
49
+ subject(:resource) { described_class.new(connection: connection) }
50
+
51
+ RSpec::Matchers.define :match_firewall_fixture do
52
+ match do |firewall|
53
+ expect(firewall.id).to eq('11d87802-df17-4f8f-a691-58e408570c12')
54
+ expect(firewall.name).to eq('firewall')
55
+ expect(firewall.status).to eq('succeeded')
56
+ expect(firewall.created_at).to eq('2017-05-30T16:25:21Z')
57
+ expect(firewall.inbound_rules.count).to eq(2)
58
+ expect(firewall.inbound_rules.first.attributes)
59
+ .to match(a_hash_including(protocol: 'icmp', ports: '0',
60
+ sources: { 'load_balancer_uids' => ['d2d3920a-9d45-41b0-b018-d15e18ec60a4'],
61
+ 'tags' => ['backend'] }))
62
+ expect(firewall.inbound_rules.last.attributes)
63
+ .to match(a_hash_including(protocol: 'tcp', ports: '8000-9000',
64
+ sources: { 'addresses' => ['0.0.0.0/0'] }))
65
+ expect(firewall.outbound_rules.count).to eq(2)
66
+ expect(firewall.outbound_rules.first.attributes)
67
+ .to match(a_hash_including(protocol: 'icmp', ports: '0',
68
+ destinations: { 'tags' => ['backend'] }))
69
+ expect(firewall.outbound_rules.last.attributes)
70
+ .to match(a_hash_including(protocol: 'tcp', ports: '8000-9000',
71
+ destinations: { 'addresses' => ['127.0.0.0'],
72
+ 'droplet_ids' => [345] }))
73
+ expect(firewall.droplet_ids).to match_array([123])
74
+ expect(firewall.tags).to match_array(['backend'])
75
+ expect(firewall.pending_changes).to be_empty
76
+ end
77
+ end
78
+
79
+ describe '#find' do
80
+ it 'returns firewall' do
81
+ stub_do_api(File.join(base_path, firewall_id), :get).to_return(body: api_fixture(firewall_fixture_path))
82
+ firewall = resource.find(id: firewall_id)
83
+
84
+ expect(firewall).to match_firewall_fixture
85
+ end
86
+ end
87
+
88
+ describe '#create' do
89
+ let(:path) { base_path }
90
+
91
+ it 'returns created firewall' do
92
+ json_body = DropletKit::FirewallMapping.representation_for(:create, firewall)
93
+ stub_do_api(path, :post).with(body: json_body).to_return(body: api_fixture(firewall_fixture_path), status: 202)
94
+
95
+ expect(resource.create(firewall)).to match_firewall_fixture
96
+ end
97
+
98
+ it_behaves_like 'an action that handles invalid parameters' do
99
+ let(:action) { 'create' }
100
+ let(:arguments) { DropletKit::Firewall.new }
101
+ end
102
+ end
103
+
104
+ describe '#update' do
105
+ let(:path) { base_path }
106
+
107
+ it 'returns updated firewall' do
108
+ json_body = DropletKit::FirewallMapping.representation_for(:update, firewall)
109
+ stub_do_api(File.join(base_path, firewall_id), :put).with(body: json_body).to_return(body: api_fixture(firewall_fixture_path), status: 200)
110
+
111
+ expect(resource.update(firewall, id: firewall_id)).to match_firewall_fixture
112
+ end
113
+
114
+ it_behaves_like 'an action that handles invalid parameters' do
115
+ let(:verb) { :put }
116
+ let(:exception) { DropletKit::FailedUpdate }
117
+ let(:action) { 'update' }
118
+ let(:arguments) { DropletKit::Firewall.new }
119
+ end
120
+ end
121
+
122
+ describe '#all' do
123
+ let(:firewalls_fixture_path) { 'firewalls/all' }
124
+
125
+ it 'returns all firewalls' do
126
+ stub_do_api(base_path, :get).to_return(body: api_fixture(firewalls_fixture_path))
127
+ firewalls = resource.all
128
+
129
+ expect(firewalls).to all(be_kind_of(DropletKit::Firewall))
130
+ expect(firewalls.first).to match_firewall_fixture
131
+ end
132
+
133
+ it_behaves_like 'a paginated index' do
134
+ let(:fixture_path) { firewalls_fixture_path }
135
+ let(:api_path) { base_path }
136
+ end
137
+ end
138
+
139
+ describe '#all_by_droplet' do
140
+ let(:droplet_id) { 123 }
141
+ let(:all_by_droplet_path) { "/v2/droplets/#{droplet_id}/firewalls" }
142
+ let(:firewalls_fixture_path) { 'firewalls/all' }
143
+
144
+ it 'returns all firewalls for a provided droplet' do
145
+ stub_do_api(all_by_droplet_path, :get).to_return(body: api_fixture(firewalls_fixture_path))
146
+ firewalls = resource.all_by_droplet(droplet_id: droplet_id)
147
+
148
+ expect(firewalls).to all(be_kind_of(DropletKit::Firewall))
149
+ expect(firewalls.first).to match_firewall_fixture
150
+ end
151
+
152
+ it_behaves_like 'a paginated index' do
153
+ let(:fixture_path) { firewalls_fixture_path }
154
+ let(:action) { :all_by_droplet }
155
+ let(:parameters) { { droplet_id: droplet_id } }
156
+ let(:api_path) { all_by_droplet_path }
157
+ end
158
+ end
159
+
160
+ describe '#delete' do
161
+ it 'sends request to delete given firewall' do
162
+ request = stub_do_api(File.join(base_path, firewall_id), :delete)
163
+ resource.delete(id: firewall_id)
164
+
165
+ expect(request).to have_been_made
166
+ end
167
+ end
168
+
169
+ context 'droplets' do
170
+ let(:droplet_id_1) { 1 }
171
+ let(:droplet_id_2) { 2 }
172
+
173
+ describe '#add_droplets' do
174
+ it 'sends request to add droplets for a given firewall' do
175
+ request = stub_do_api(File.join(base_path, firewall_id, 'droplets'), :post).with(body: { droplet_ids: [droplet_id_1, droplet_id_2] }.to_json)
176
+ resource.add_droplets([droplet_id_1, droplet_id_2], id: firewall_id)
177
+
178
+ expect(request).to have_been_made
179
+ end
180
+ end
181
+
182
+ describe '#remove_droplets' do
183
+ it 'sends request to remove droplets from a given firewall' do
184
+ request = stub_do_api(File.join(base_path, firewall_id, 'droplets'), :delete).with(body: { droplet_ids: [droplet_id_1, droplet_id_2]}.to_json)
185
+ resource.remove_droplets([droplet_id_1, droplet_id_2], id: firewall_id)
186
+
187
+ expect(request).to have_been_made
188
+ end
189
+ end
190
+ end
191
+
192
+ context 'tags' do
193
+ let(:frontend_tag) { 'frontend' }
194
+ let(:backend_tag) { 'backend' }
195
+
196
+ describe '#add_tags' do
197
+ it 'sends request to add tags for a given firewall' do
198
+ request = stub_do_api(File.join(base_path, firewall_id, 'tags'), :post).with(body: { tags: [frontend_tag, backend_tag] }.to_json)
199
+ resource.add_tags([frontend_tag, backend_tag], id: firewall_id)
200
+
201
+ expect(request).to have_been_made
202
+ end
203
+ end
204
+
205
+ describe '#remove_tags' do
206
+ it 'sends request to remove tags from a given firewall' do
207
+ request = stub_do_api(File.join(base_path, firewall_id, 'tags'), :delete).with(body: { tags: [frontend_tag, backend_tag]}.to_json)
208
+ resource.remove_tags([frontend_tag, backend_tag], id: firewall_id)
209
+
210
+ expect(request).to have_been_made
211
+ end
212
+ end
213
+ end
214
+
215
+ context 'rules' do
216
+ let(:inbound_rule) do
217
+ DropletKit::FirewallInboundRule.new(
218
+ protocol: 'tcp',
219
+ ports: '22',
220
+ sources: {
221
+ addresses: ['127.0.0.0'],
222
+ tags: ['frontend', 'backend']
223
+ }
224
+ )
225
+ end
226
+
227
+ let(:outbound_rule) do
228
+ DropletKit::FirewallOutboundRule.new(
229
+ protocol: 'tcp',
230
+ ports: '8080',
231
+ destinations: {
232
+ droplet_ids: [123, 456],
233
+ load_balancer_uids: ['lb-uuid']
234
+ }
235
+ )
236
+ end
237
+
238
+ let(:rules) do
239
+ DropletKit::FirewallRule.new(
240
+ inbound_rules: [inbound_rule],
241
+ outbound_rules: [outbound_rule]
242
+ )
243
+ end
244
+
245
+ describe '#add_rules' do
246
+ it 'sends request to add rules for a given firewall' do
247
+ json_body = DropletKit::FirewallRuleMapping.representation_for(:create, rules)
248
+ request = stub_do_api(File.join(base_path, firewall_id, 'rules'), :post).with(body: json_body)
249
+ resource.add_rules(inbound_rules: [inbound_rule], outbound_rules: [outbound_rule], id: firewall_id)
250
+
251
+ expect(request).to have_been_made
252
+ end
253
+ end
254
+
255
+ describe '#remove_rules' do
256
+ it 'sends request to remove rules from a given firewall' do
257
+ json_body = DropletKit::FirewallRuleMapping.representation_for(:update, rules)
258
+ request = stub_do_api(File.join(base_path, firewall_id, 'rules'), :delete).with(body: json_body)
259
+ resource.remove_rules(inbound_rules: [inbound_rule], outbound_rules: [outbound_rule], id: firewall_id)
260
+
261
+ expect(request).to have_been_made
262
+ end
263
+ end
264
+ end
265
+ end
@@ -67,20 +67,6 @@ describe DropletKit::TagResource do
67
67
  end
68
68
  end
69
69
 
70
- describe '#update' do
71
- it 'updateds a tag' do
72
- tag = DropletKit::Tag.new(name: 'old-testing-1')
73
- tag.name = 'testing-1'
74
-
75
- request = stub_do_api('/v2/tags/old-testing-1', :put)
76
- .with(body: DropletKit::TagMapping.representation_for(:update, tag))
77
- .to_return(body: api_fixture('tags/find'))
78
-
79
- tag = resource.update(tag, name: 'old-testing-1')
80
- expect(tag.name).to eq('testing-1')
81
- end
82
- end
83
-
84
70
  describe '#delete' do
85
71
  it 'deletes a tag' do
86
72
  request = stub_do_api('/v2/tags/testing-1', :delete)
@@ -4,11 +4,13 @@ shared_examples_for 'a paginated index' do
4
4
  let(:fixture_path) { }
5
5
  let(:api_path) { }
6
6
  let(:parameters) { {} }
7
+ let(:action) { :all }
7
8
 
8
9
  it 'returns a paginated resource' do
9
10
  fixture = api_fixture(fixture_path)
10
11
  stub_do_api(api_path, :get).to_return(body: fixture)
11
- response = resource.all({page: 1, per_page: 1}.merge(parameters))
12
+ response = resource.send(action, {page: 1, per_page: 1}.merge(parameters))
13
+
12
14
  expect(response).to be_kind_of(DropletKit::PaginatedResource)
13
15
  end
14
- end
16
+ end
@@ -0,0 +1,11 @@
1
+ shared_examples_for 'an action that handles invalid parameters' do
2
+ let(:verb) { :post }
3
+ let(:exception) { DropletKit::FailedCreate }
4
+
5
+ it 'raises an exception with the message attached' do
6
+ response_body = { id: :unprocessable_entity, message: 'Something is not right' }
7
+ stub_do_api(path, verb).to_return(body: response_body.to_json, status: 422)
8
+
9
+ expect { resource.send(action, arguments) }.to raise_exception(exception).with_message(response_body[:message])
10
+ end
11
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: droplet_kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Ross
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-03-13 00:00:00.000000000 Z
11
+ date: 2017-06-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: virtus
@@ -61,7 +61,7 @@ dependencies:
61
61
  version: '3.0'
62
62
  - - "<"
63
63
  - !ruby/object:Gem::Version
64
- version: '5.1'
64
+ version: '6'
65
65
  type: :runtime
66
66
  prerelease: false
67
67
  version_requirements: !ruby/object:Gem::Requirement
@@ -71,7 +71,7 @@ dependencies:
71
71
  version: '3.0'
72
72
  - - "<"
73
73
  - !ruby/object:Gem::Version
74
- version: '5.1'
74
+ version: '6'
75
75
  - !ruby/object:Gem::Dependency
76
76
  name: faraday
77
77
  requirement: !ruby/object:Gem::Requirement
@@ -211,6 +211,11 @@ files:
211
211
  - lib/droplet_kit/mappings/droplet_mapping.rb
212
212
  - lib/droplet_kit/mappings/droplet_upgrade_mapping.rb
213
213
  - lib/droplet_kit/mappings/error_mapping.rb
214
+ - lib/droplet_kit/mappings/firewall_inbound_rule_mapping.rb
215
+ - lib/droplet_kit/mappings/firewall_mapping.rb
216
+ - lib/droplet_kit/mappings/firewall_outbound_rule_mapping.rb
217
+ - lib/droplet_kit/mappings/firewall_pending_change_mapping.rb
218
+ - lib/droplet_kit/mappings/firewall_rule_mapping.rb
214
219
  - lib/droplet_kit/mappings/floating_ip_mapping.rb
215
220
  - lib/droplet_kit/mappings/forwarding_rule_mapping.rb
216
221
  - lib/droplet_kit/mappings/health_check_mapping.rb
@@ -236,6 +241,11 @@ files:
236
241
  - lib/droplet_kit/models/domain_record.rb
237
242
  - lib/droplet_kit/models/droplet.rb
238
243
  - lib/droplet_kit/models/droplet_upgrade.rb
244
+ - lib/droplet_kit/models/firewall.rb
245
+ - lib/droplet_kit/models/firewall_inbound_rule.rb
246
+ - lib/droplet_kit/models/firewall_outbound_rule.rb
247
+ - lib/droplet_kit/models/firewall_pending_change.rb
248
+ - lib/droplet_kit/models/firewall_rule.rb
239
249
  - lib/droplet_kit/models/floating_ip.rb
240
250
  - lib/droplet_kit/models/forwarding_rule.rb
241
251
  - lib/droplet_kit/models/health_check.rb
@@ -264,6 +274,7 @@ files:
264
274
  - lib/droplet_kit/resources/droplet_action_resource.rb
265
275
  - lib/droplet_kit/resources/droplet_resource.rb
266
276
  - lib/droplet_kit/resources/droplet_upgrade_resource.rb
277
+ - lib/droplet_kit/resources/firewall_resource.rb
267
278
  - lib/droplet_kit/resources/floating_ip_action_resource.rb
268
279
  - lib/droplet_kit/resources/floating_ip_resource.rb
269
280
  - lib/droplet_kit/resources/image_action_resource.rb
@@ -318,6 +329,8 @@ files:
318
329
  - spec/fixtures/droplets/list_backups.json
319
330
  - spec/fixtures/droplets/list_kernels.json
320
331
  - spec/fixtures/droplets/list_snapshots.json
332
+ - spec/fixtures/firewalls/all.json
333
+ - spec/fixtures/firewalls/find.json
321
334
  - spec/fixtures/floating_ip_actions/all.json
322
335
  - spec/fixtures/floating_ip_actions/assign.json
323
336
  - spec/fixtures/floating_ip_actions/find.json
@@ -371,6 +384,7 @@ files:
371
384
  - spec/lib/droplet_kit/resources/droplet_action_resource_spec.rb
372
385
  - spec/lib/droplet_kit/resources/droplet_resource_spec.rb
373
386
  - spec/lib/droplet_kit/resources/droplet_upgrade_resource_spec.rb
387
+ - spec/lib/droplet_kit/resources/firewall_resource_spec.rb
374
388
  - spec/lib/droplet_kit/resources/floating_ip_action_resource_spec.rb
375
389
  - spec/lib/droplet_kit/resources/floating_ip_resource_spec.rb
376
390
  - spec/lib/droplet_kit/resources/image_action_resource_spec.rb
@@ -389,7 +403,7 @@ files:
389
403
  - spec/support/resource_context.rb
390
404
  - spec/support/shared_examples/common_errors.rb
391
405
  - spec/support/shared_examples/paginated_endpoint.rb
392
- - spec/support/shared_examples/unsuccessful_create.rb
406
+ - spec/support/shared_examples/unprocessable_entity.rb
393
407
  homepage: https://github.com/digitalocean/droplet_kit
394
408
  licenses:
395
409
  - MIT
@@ -410,7 +424,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
410
424
  version: '0'
411
425
  requirements: []
412
426
  rubyforge_project:
413
- rubygems_version: 2.5.1
427
+ rubygems_version: 2.6.8
414
428
  signing_key:
415
429
  specification_version: 4
416
430
  summary: Droplet Kit is the official Ruby library for DigitalOcean's API
@@ -455,6 +469,8 @@ test_files:
455
469
  - spec/fixtures/droplets/list_backups.json
456
470
  - spec/fixtures/droplets/list_kernels.json
457
471
  - spec/fixtures/droplets/list_snapshots.json
472
+ - spec/fixtures/firewalls/all.json
473
+ - spec/fixtures/firewalls/find.json
458
474
  - spec/fixtures/floating_ip_actions/all.json
459
475
  - spec/fixtures/floating_ip_actions/assign.json
460
476
  - spec/fixtures/floating_ip_actions/find.json
@@ -508,6 +524,7 @@ test_files:
508
524
  - spec/lib/droplet_kit/resources/droplet_action_resource_spec.rb
509
525
  - spec/lib/droplet_kit/resources/droplet_resource_spec.rb
510
526
  - spec/lib/droplet_kit/resources/droplet_upgrade_resource_spec.rb
527
+ - spec/lib/droplet_kit/resources/firewall_resource_spec.rb
511
528
  - spec/lib/droplet_kit/resources/floating_ip_action_resource_spec.rb
512
529
  - spec/lib/droplet_kit/resources/floating_ip_resource_spec.rb
513
530
  - spec/lib/droplet_kit/resources/image_action_resource_spec.rb
@@ -526,4 +543,4 @@ test_files:
526
543
  - spec/support/resource_context.rb
527
544
  - spec/support/shared_examples/common_errors.rb
528
545
  - spec/support/shared_examples/paginated_endpoint.rb
529
- - spec/support/shared_examples/unsuccessful_create.rb
546
+ - spec/support/shared_examples/unprocessable_entity.rb
@@ -1,8 +0,0 @@
1
- shared_examples_for 'an action that handles invalid parameters' do
2
- it 'raises a FailedCreate exception with the message attached' do
3
- response_body = { id: :unprocessable_entity, message: 'Something is not right' }
4
- stub_do_api(path, :post).to_return(body: response_body.to_json, status: 422)
5
-
6
- expect { resource.send(action, arguments) }.to raise_exception(DropletKit::FailedCreate).with_message(response_body[:message])
7
- end
8
- end