hcloud 0.1.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +0 -1
- data/.rubocop.yml +24 -9
- data/.rubocop_todo.yml +18 -121
- data/.travis.yml +3 -3
- data/CHANGELOG.md +13 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +143 -0
- data/README.md +34 -1
- data/Rakefile +2 -0
- data/bin/console +1 -0
- data/hcloud.gemspec +5 -3
- data/lib/hcloud.rb +31 -5
- data/lib/hcloud/abstract_resource.rb +162 -55
- data/lib/hcloud/action.rb +8 -10
- data/lib/hcloud/action_resource.rb +3 -29
- data/lib/hcloud/client.rb +66 -28
- data/lib/hcloud/datacenter.rb +7 -7
- data/lib/hcloud/datacenter_resource.rb +6 -31
- data/lib/hcloud/entry_loader.rb +181 -20
- data/lib/hcloud/errors.rb +2 -0
- data/lib/hcloud/floating_ip.rb +18 -29
- data/lib/hcloud/floating_ip_resource.rb +15 -30
- data/lib/hcloud/image.rb +12 -32
- data/lib/hcloud/image_resource.rb +7 -38
- data/lib/hcloud/iso.rb +4 -1
- data/lib/hcloud/iso_resource.rb +7 -28
- data/lib/hcloud/location.rb +3 -9
- data/lib/hcloud/location_resource.rb +6 -27
- data/lib/hcloud/network.rb +33 -0
- data/lib/hcloud/network_resource.rb +25 -0
- data/lib/hcloud/pagination.rb +2 -9
- data/lib/hcloud/server.rb +37 -70
- data/lib/hcloud/server_resource.rb +16 -36
- data/lib/hcloud/server_type.rb +3 -10
- data/lib/hcloud/server_type_resource.rb +6 -28
- data/lib/hcloud/ssh_key.rb +6 -17
- data/lib/hcloud/ssh_key_resource.rb +13 -32
- data/lib/hcloud/typhoeus_ext.rb +112 -0
- data/lib/hcloud/version.rb +3 -1
- data/lib/hcloud/volume.rb +32 -0
- data/lib/hcloud/volume_resource.rb +29 -0
- metadata +31 -13
- data/lib/hcloud/multi_reply.rb +0 -21
data/Rakefile
CHANGED
data/bin/console
CHANGED
data/hcloud.gemspec
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
|
-
lib = File.expand_path('
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
5
|
require 'hcloud/version'
|
5
6
|
|
@@ -19,10 +20,11 @@ Gem::Specification.new do |spec|
|
|
19
20
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
21
|
spec.require_paths = ['lib']
|
21
22
|
|
23
|
+
spec.add_development_dependency 'activemodel'
|
22
24
|
spec.add_development_dependency 'activesupport'
|
23
|
-
spec.add_development_dependency 'bundler'
|
25
|
+
spec.add_development_dependency 'bundler'
|
24
26
|
spec.add_development_dependency 'grape'
|
25
|
-
spec.add_development_dependency 'rake'
|
27
|
+
spec.add_development_dependency 'rake'
|
26
28
|
spec.add_development_dependency 'rspec'
|
27
29
|
spec.add_development_dependency 'webmock'
|
28
30
|
spec.add_runtime_dependency 'activesupport'
|
data/lib/hcloud.rb
CHANGED
@@ -1,29 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'hcloud/version'
|
2
4
|
require 'active_support/core_ext/object/to_query'
|
5
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
3
6
|
|
4
7
|
module Hcloud
|
5
8
|
autoload :Error, 'hcloud/errors'
|
6
9
|
autoload :Client, 'hcloud/client'
|
10
|
+
autoload :TyphoeusExt, 'hcloud/typhoeus_ext'
|
7
11
|
autoload :AbstractResource, 'hcloud/abstract_resource'
|
8
|
-
autoload :MultiReply, 'hcloud/multi_reply'
|
9
|
-
autoload :ServerResource, 'hcloud/server_resource'
|
10
12
|
autoload :EntryLoader, 'hcloud/entry_loader'
|
13
|
+
|
14
|
+
autoload :Server, 'hcloud/server'
|
15
|
+
autoload :ServerResource, 'hcloud/server_resource'
|
16
|
+
|
17
|
+
autoload :ServerType, 'hcloud/server_type'
|
18
|
+
autoload :ServerTypeResource, 'hcloud/server_type_resource'
|
19
|
+
|
11
20
|
autoload :FloatingIP, 'hcloud/floating_ip'
|
12
21
|
autoload :FloatingIPResource, 'hcloud/floating_ip_resource'
|
22
|
+
|
13
23
|
autoload :SSHKey, 'hcloud/ssh_key'
|
14
24
|
autoload :SSHKeyResource, 'hcloud/ssh_key_resource'
|
15
|
-
|
16
|
-
autoload :ServerType, 'hcloud/server_type'
|
17
|
-
autoload :ServerTypeResource, 'hcloud/server_type_resource'
|
25
|
+
|
18
26
|
autoload :Datacenter, 'hcloud/datacenter'
|
19
27
|
autoload :DatacenterResource, 'hcloud/datacenter_resource'
|
28
|
+
|
20
29
|
autoload :Location, 'hcloud/location'
|
21
30
|
autoload :LocationResource, 'hcloud/location_resource'
|
31
|
+
|
22
32
|
autoload :Image, 'hcloud/image'
|
23
33
|
autoload :ImageResource, 'hcloud/image_resource'
|
34
|
+
|
35
|
+
autoload :Network, 'hcloud/network'
|
36
|
+
autoload :NetworkResource, 'hcloud/network_resource'
|
37
|
+
|
38
|
+
autoload :Volume, 'hcloud/volume'
|
39
|
+
autoload :VolumeResource, 'hcloud/volume_resource'
|
40
|
+
|
24
41
|
autoload :Action, 'hcloud/action'
|
25
42
|
autoload :ActionResource, 'hcloud/action_resource'
|
43
|
+
|
26
44
|
autoload :Iso, 'hcloud/iso'
|
27
45
|
autoload :IsoResource, 'hcloud/iso_resource'
|
46
|
+
|
28
47
|
autoload :Pagination, 'hcloud/pagination'
|
48
|
+
|
49
|
+
COLLECT_ARGS = proc do |method_name, bind|
|
50
|
+
query = bind.receiver.method(method_name).parameters.inject({}) do |hash, (_type, name)|
|
51
|
+
hash.merge(name => bind.local_variable_get(name))
|
52
|
+
end
|
53
|
+
query.delete_if { |_, v| v.nil? }
|
54
|
+
end
|
29
55
|
end
|
@@ -1,75 +1,185 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/core_ext/string'
|
4
|
+
|
1
5
|
module Hcloud
|
2
6
|
class AbstractResource
|
3
7
|
include Enumerable
|
4
8
|
|
5
|
-
|
9
|
+
delegate :request, :prepare_request, to: :client
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def bind_to(klass)
|
13
|
+
resource = self
|
14
|
+
%w[find find_by where all [] page limit per_page order
|
15
|
+
to_a count pagnation each].each do |method|
|
16
|
+
klass.define_singleton_method(method) do |*args, &block|
|
17
|
+
resource.new(client: Client.connection).public_send(method, *args, &block)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def filter_attributes(*keys)
|
23
|
+
return @filter_attributes if keys.to_a.empty?
|
24
|
+
|
25
|
+
@filter_attributes = keys
|
26
|
+
end
|
27
|
+
|
28
|
+
def resource_class
|
29
|
+
ancestors[ancestors.index(Hcloud::AbstractResource) - 1]
|
30
|
+
end
|
31
|
+
|
32
|
+
def resource_url(url = nil)
|
33
|
+
return (@resource_url = url) if url
|
34
|
+
|
35
|
+
@resource_url || resource_class.name.demodulize.gsub('Resource', '').tableize
|
36
|
+
end
|
37
|
+
|
38
|
+
def resource_path(path = nil)
|
39
|
+
return (@resource_path = path) if path
|
40
|
+
|
41
|
+
@resource_path || resource_url
|
42
|
+
end
|
6
43
|
|
7
|
-
|
44
|
+
def resource(res = nil)
|
45
|
+
return (@resource = res) if res
|
46
|
+
return @resource if @resource
|
47
|
+
|
48
|
+
auto_const = resource_class.name.demodulize.gsub('Resource', '').to_sym
|
49
|
+
return Hcloud.const_get(auto_const) if Hcloud.constants.include?(auto_const)
|
50
|
+
|
51
|
+
raise Error, "unable to lookup resource class for #{name}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
attr_reader :client
|
56
|
+
|
57
|
+
def initialize(client:, base_path: '')
|
8
58
|
@client = client
|
9
|
-
@parent = parent
|
10
59
|
@page = 1
|
11
60
|
@per_page = 25
|
12
61
|
@order = []
|
13
62
|
@base_path = base_path
|
14
63
|
end
|
15
64
|
|
65
|
+
def all
|
66
|
+
where
|
67
|
+
end
|
68
|
+
|
69
|
+
def where(**kwargs)
|
70
|
+
kwargs.keys.each do |key|
|
71
|
+
keys = self.class.filter_attributes.map(&:to_s)
|
72
|
+
next if keys.include?(key.to_s)
|
73
|
+
|
74
|
+
raise ArgumentError, "unknown filter #{key}, allowed keys are #{keys}"
|
75
|
+
end
|
76
|
+
|
77
|
+
_dup :@query, @query.to_h.merge(kwargs)
|
78
|
+
end
|
79
|
+
|
80
|
+
def find(id)
|
81
|
+
prepare_request(
|
82
|
+
[self.class.resource_url, id].join('/'),
|
83
|
+
resource_path: resource_path.to_s.singularize,
|
84
|
+
resource_class: self.class.resource
|
85
|
+
)
|
86
|
+
end
|
87
|
+
|
88
|
+
def [](arg)
|
89
|
+
find_by(id: arg)
|
90
|
+
end
|
91
|
+
|
92
|
+
def find_by(**kwargs)
|
93
|
+
if id = kwargs.delete(:id)
|
94
|
+
return find(id)
|
95
|
+
end
|
96
|
+
|
97
|
+
per_page(1).where(**kwargs).first
|
98
|
+
rescue Error::NotFound
|
99
|
+
end
|
100
|
+
|
101
|
+
# def count
|
102
|
+
# per_page(1).first&.response&.pagination&.total_entries.to_i
|
103
|
+
# end
|
104
|
+
|
16
105
|
def page(page)
|
17
|
-
|
18
|
-
self
|
106
|
+
_dup :@page, page
|
19
107
|
end
|
20
108
|
|
21
109
|
def per_page(per_page)
|
22
|
-
|
23
|
-
self
|
110
|
+
_dup :@per_page, per_page
|
24
111
|
end
|
25
112
|
|
26
113
|
def limit(limit)
|
27
|
-
|
28
|
-
self
|
114
|
+
_dup :@limit, limit
|
29
115
|
end
|
30
116
|
|
31
117
|
def order(*sort)
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
118
|
+
_dup :@order,
|
119
|
+
begin
|
120
|
+
sort.flat_map do |s|
|
121
|
+
case s
|
122
|
+
when Symbol, String then s.to_s
|
123
|
+
when Hash then s.map { |k, v| "#{k}:#{v}" }
|
124
|
+
else
|
125
|
+
raise ArgumentError,
|
126
|
+
"Unable to resolve type for given #{s.inspect} from #{sort}"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
42
130
|
end
|
43
131
|
|
44
|
-
def
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
end
|
52
|
-
client.hydra.run
|
53
|
-
j = requests.map do |x|
|
54
|
-
Oj.load(x.response.body)
|
55
|
-
end
|
56
|
-
m = MultiReply.new(j: j, pagination: :auto)
|
57
|
-
m.cb = block
|
58
|
-
return m
|
59
|
-
end
|
60
|
-
m = MultiReply.new(j: [Oj.load(request(path, o.merge(ep: ep)).run.body)])
|
61
|
-
m.cb = block
|
62
|
-
m
|
132
|
+
def run
|
133
|
+
@run ||= multi_query(
|
134
|
+
resource_url,
|
135
|
+
q: @query,
|
136
|
+
resource_path: resource_path,
|
137
|
+
resource_class: self.class.resource
|
138
|
+
)
|
63
139
|
end
|
64
140
|
|
65
141
|
def each
|
66
|
-
|
142
|
+
run.each do |member|
|
67
143
|
yield(member)
|
68
144
|
end
|
69
145
|
end
|
70
146
|
|
147
|
+
# this is just to keep the actual bevahior
|
148
|
+
def pagination
|
149
|
+
return :auto if client.auto_pagination
|
150
|
+
|
151
|
+
run.response.pagination
|
152
|
+
end
|
153
|
+
|
71
154
|
protected
|
72
155
|
|
156
|
+
def _dup(var, value)
|
157
|
+
dup.tap do |res|
|
158
|
+
res.instance_variable_set(var, value)
|
159
|
+
res.instance_variable_set(:@run, nil)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def resource_path
|
164
|
+
self.class.resource_path || self.class.resource_url
|
165
|
+
end
|
166
|
+
|
167
|
+
def resource_url
|
168
|
+
[@base_path.to_s, self.class.resource_url.to_s].reject(&:empty?).join('/')
|
169
|
+
end
|
170
|
+
|
171
|
+
def multi_query(path, **o)
|
172
|
+
return prepare_request(path, o.merge(ep: ep)) unless client&.auto_pagination
|
173
|
+
|
174
|
+
raise Error, 'unable to run auto paginate within concurrent excecution' if @concurrent
|
175
|
+
|
176
|
+
requests = __entries__(path, **o)
|
177
|
+
return requests.flat_map(&:resource) if requests.all? { |req| req.respond_to? :resource }
|
178
|
+
|
179
|
+
client.hydra.run
|
180
|
+
requests.flat_map { |req| req.response.resource }
|
181
|
+
end
|
182
|
+
|
73
183
|
def page_params(per_page: nil, page: nil)
|
74
184
|
{ per_page: per_page || @per_page, page: page || @page }.to_param
|
75
185
|
end
|
@@ -85,27 +195,24 @@ module Hcloud
|
|
85
195
|
r.compact.join('&')
|
86
196
|
end
|
87
197
|
|
88
|
-
def request(*args)
|
89
|
-
client.request(*args)
|
90
|
-
end
|
91
|
-
|
92
198
|
def __entries__(path, **o)
|
93
|
-
|
94
|
-
|
95
|
-
return [
|
199
|
+
first_page = request(path, o.merge(ep: ep(per_page: 1, page: 1))).run
|
200
|
+
total_entries = first_page.pagination.total_entries
|
201
|
+
return [first_page] if total_entries <= 1 || @limit == 1
|
202
|
+
|
96
203
|
unless @limit.nil?
|
97
|
-
|
204
|
+
total_entries = @limit if total_entries > @limit
|
98
205
|
end
|
99
|
-
|
100
|
-
|
101
|
-
|
206
|
+
pages = total_entries / Client::MAX_ENTRIES_PER_PAGE
|
207
|
+
pages += 1 if (total_entries % Client::MAX_ENTRIES_PER_PAGE).positive?
|
208
|
+
pages.times.map do |page|
|
102
209
|
per_page = Client::MAX_ENTRIES_PER_PAGE
|
103
|
-
if !@limit.nil? && (
|
104
|
-
per_page =
|
210
|
+
if !@limit.nil? && (pages == (page + 1)) && (total_entries % per_page != 0)
|
211
|
+
per_page = total_entries % per_page
|
212
|
+
end
|
213
|
+
request(path, o.merge(ep: ep(per_page: per_page, page: page + 1))).tap do |req|
|
214
|
+
client.hydra.queue req
|
105
215
|
end
|
106
|
-
req = request(path, o.merge(ep: ep(per_page: per_page, page: i + 1)))
|
107
|
-
client.hydra.queue req
|
108
|
-
req
|
109
216
|
end
|
110
217
|
end
|
111
218
|
end
|
data/lib/hcloud/action.rb
CHANGED
@@ -1,16 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Hcloud
|
2
4
|
class Action
|
3
|
-
|
4
|
-
id: nil,
|
5
|
-
command: nil,
|
6
|
-
status: nil,
|
7
|
-
progress: nil,
|
8
|
-
started: :time,
|
9
|
-
finished: :time,
|
10
|
-
resources: nil,
|
11
|
-
error: nil
|
12
|
-
}.freeze
|
5
|
+
require 'hcloud/action_resource'
|
13
6
|
|
14
7
|
include EntryLoader
|
8
|
+
|
9
|
+
schema(
|
10
|
+
started: :time,
|
11
|
+
finished: :time
|
12
|
+
)
|
15
13
|
end
|
16
14
|
end
|
@@ -1,35 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
3
|
module Hcloud
|
3
4
|
class ActionResource < AbstractResource
|
4
|
-
|
5
|
-
mj(base_path('actions')) do |j|
|
6
|
-
j.flat_map { |x| x['actions'].map { |x| Action.new(x, self, client) } }
|
7
|
-
end
|
8
|
-
end
|
5
|
+
filter_attributes :status
|
9
6
|
|
10
|
-
|
11
|
-
Action.new(
|
12
|
-
Oj.load(request(base_path("actions/#{id.to_i}")).run.body)['action'],
|
13
|
-
self,
|
14
|
-
client
|
15
|
-
)
|
16
|
-
end
|
17
|
-
|
18
|
-
def [](arg)
|
19
|
-
find(arg)
|
20
|
-
rescue Error::NotFound
|
21
|
-
end
|
22
|
-
|
23
|
-
def where(status: nil)
|
24
|
-
mj(base_path('actions'), q: { status: status }) do |j|
|
25
|
-
j.flat_map { |x| x['actions'].map { |x| Action.new(x, self, client) } }
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
def base_path(ext)
|
32
|
-
[@base_path, ext].reject(&:empty?).join('/')
|
33
|
-
end
|
7
|
+
bind_to Action
|
34
8
|
end
|
35
9
|
end
|
data/lib/hcloud/client.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
autoload :Typhoeus, 'typhoeus'
|
2
4
|
autoload :Oj, 'oj'
|
3
5
|
|
@@ -5,14 +7,43 @@ module Hcloud
|
|
5
7
|
class Client
|
6
8
|
MAX_ENTRIES_PER_PAGE = 50
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
+
class << self
|
11
|
+
attr_writer :connection
|
12
|
+
|
13
|
+
def connection
|
14
|
+
return @connection if @connection.is_a? Hcloud::Client
|
15
|
+
|
16
|
+
raise ArgumentError, "client not correctly initialized, actually #{@client.inspect}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :token, :auto_pagination, :hydra, :user_agent
|
21
|
+
def initialize(token:, auto_pagination: false, concurrency: 20, user_agent: nil)
|
10
22
|
@token = token
|
23
|
+
@user_agent = user_agent || "hcloud-ruby v#{VERSION}"
|
11
24
|
@auto_pagination = auto_pagination
|
12
25
|
@concurrency = concurrency
|
13
26
|
@hydra = Typhoeus::Hydra.new(max_concurrency: concurrency)
|
14
27
|
end
|
15
28
|
|
29
|
+
def concurrent
|
30
|
+
@concurrent = true
|
31
|
+
ret = yield
|
32
|
+
ret.each do |element|
|
33
|
+
next unless element.is_a?(AbstractResource)
|
34
|
+
|
35
|
+
element.run
|
36
|
+
end
|
37
|
+
hydra.run
|
38
|
+
ret
|
39
|
+
ensure
|
40
|
+
@concurrent = nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def concurrent?
|
44
|
+
!@concurrent.nil?
|
45
|
+
end
|
46
|
+
|
16
47
|
def authorized?
|
17
48
|
request('server_types').run
|
18
49
|
true
|
@@ -56,8 +87,34 @@ module Hcloud
|
|
56
87
|
FloatingIPResource.new(client: self)
|
57
88
|
end
|
58
89
|
|
59
|
-
def
|
60
|
-
|
90
|
+
def networks
|
91
|
+
NetworkResource.new(client: self)
|
92
|
+
end
|
93
|
+
|
94
|
+
def volumes
|
95
|
+
VolumeResource.new(client: self)
|
96
|
+
end
|
97
|
+
|
98
|
+
class ResourceFuture < Delegator
|
99
|
+
def initialize(request)
|
100
|
+
@request = request
|
101
|
+
end
|
102
|
+
|
103
|
+
def __getobj__
|
104
|
+
@__getobj__ ||= @request&.response&.resource
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def prepare_request(url, **args, &block)
|
109
|
+
req = request(url, **args.merge(block: block))
|
110
|
+
return req.run.resource unless concurrent?
|
111
|
+
|
112
|
+
hydra.queue req
|
113
|
+
ResourceFuture.new(req)
|
114
|
+
end
|
115
|
+
|
116
|
+
def request(path, **options) # rubocop:disable Metrics/MethodLength
|
117
|
+
hcloud_attributes = TyphoeusExt.collect_attributes(options)
|
61
118
|
if x = options.delete(:j)
|
62
119
|
options[:body] = Oj.dump(x, mode: :compat)
|
63
120
|
options[:method] ||= :post
|
@@ -74,35 +131,16 @@ module Hcloud
|
|
74
131
|
{
|
75
132
|
headers: {
|
76
133
|
'Authorization' => "Bearer #{token}",
|
134
|
+
'User-Agent' => user_agent,
|
77
135
|
'Content-Type' => 'application/json'
|
78
136
|
}
|
79
137
|
}.merge(options)
|
80
138
|
)
|
81
139
|
r.on_complete do |response|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
raise Error::Unauthorized
|
87
|
-
when 0
|
88
|
-
raise Error::ServerError, "Connection error: #{response.return_code}"
|
89
|
-
when 400...600
|
90
|
-
j = Oj.load(response.body)
|
91
|
-
code = j.dig('error', 'code')
|
92
|
-
error_class = case code
|
93
|
-
when 'invalid_input' then Error::InvalidInput
|
94
|
-
when 'forbidden' then Error::Forbidden
|
95
|
-
when 'locked' then Error::Locked
|
96
|
-
when 'not_found' then Error::NotFound
|
97
|
-
when 'rate_limit_exceeded' then Error::RateLimitExceeded
|
98
|
-
when 'resource_unavailable' then Error::ResourceUnavilable
|
99
|
-
when 'service_error' then Error::ServiceError
|
100
|
-
when 'uniqueness_error' then Error::UniquenessError
|
101
|
-
else
|
102
|
-
Error::ServerError
|
103
|
-
end
|
104
|
-
raise error_class, j.dig('error', 'message')
|
105
|
-
end
|
140
|
+
response.extend(TyphoeusExt)
|
141
|
+
response.attributes = hcloud_attributes
|
142
|
+
response.context.client = self
|
143
|
+
response.check_for_error unless response.request.hydra
|
106
144
|
end
|
107
145
|
r
|
108
146
|
end
|