hcloud 0.1.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.github/workflows/ruby.yml +32 -0
- data/.gitignore +0 -1
- data/.rubocop.yml +28 -10
- data/.rubocop_todo.yml +18 -121
- data/CHANGELOG.md +60 -2
- data/Gemfile +3 -0
- data/Gemfile.lock +149 -0
- data/README.md +34 -1
- data/Rakefile +2 -0
- data/bin/console +1 -0
- data/hcloud.gemspec +14 -9
- data/lib/hcloud/abstract_resource.rb +165 -60
- data/lib/hcloud/action.rb +8 -10
- data/lib/hcloud/action_resource.rb +3 -29
- data/lib/hcloud/client.rb +70 -29
- data/lib/hcloud/datacenter.rb +7 -7
- data/lib/hcloud/datacenter_resource.rb +6 -31
- data/lib/hcloud/entry_loader.rb +186 -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 +17 -38
- 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 +110 -0
- data/lib/hcloud/version.rb +3 -1
- data/lib/hcloud/volume.rb +32 -0
- data/lib/hcloud/volume_resource.rb +29 -0
- data/lib/hcloud.rb +31 -5
- metadata +56 -22
- data/.travis.yml +0 -9
- data/lib/hcloud/multi_reply.rb +0 -21
| @@ -1,75 +1,183 @@ | |
| 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
         | 
| 43 | 
            +
             | 
| 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
         | 
| 6 56 |  | 
| 7 | 
            -
                def initialize(client:,  | 
| 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.each_key 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 | 
            -
                def each
         | 
| 66 | 
            -
                   | 
| 67 | 
            -
             | 
| 68 | 
            -
             | 
| 141 | 
            +
                def each(&block)
         | 
| 142 | 
            +
                  run.each(&block)
         | 
| 143 | 
            +
                end
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                # this is just to keep the actual bevahior
         | 
| 146 | 
            +
                def pagination
         | 
| 147 | 
            +
                  return :auto if client.auto_pagination
         | 
| 148 | 
            +
             | 
| 149 | 
            +
                  run.response.pagination
         | 
| 69 150 | 
             
                end
         | 
| 70 151 |  | 
| 71 152 | 
             
                protected
         | 
| 72 153 |  | 
| 154 | 
            +
                def _dup(var, value)
         | 
| 155 | 
            +
                  dup.tap do |res|
         | 
| 156 | 
            +
                    res.instance_variable_set(var, value)
         | 
| 157 | 
            +
                    res.instance_variable_set(:@run, nil)
         | 
| 158 | 
            +
                  end
         | 
| 159 | 
            +
                end
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                def resource_path
         | 
| 162 | 
            +
                  self.class.resource_path || self.class.resource_url
         | 
| 163 | 
            +
                end
         | 
| 164 | 
            +
             | 
| 165 | 
            +
                def resource_url
         | 
| 166 | 
            +
                  [@base_path.to_s, self.class.resource_url.to_s].reject(&:empty?).join('/')
         | 
| 167 | 
            +
                end
         | 
| 168 | 
            +
             | 
| 169 | 
            +
                def multi_query(path, **o)
         | 
| 170 | 
            +
                  return prepare_request(path, o.merge(ep: ep)) unless client&.auto_pagination
         | 
| 171 | 
            +
             | 
| 172 | 
            +
                  raise Error, 'unable to run auto paginate within concurrent excecution' if @concurrent
         | 
| 173 | 
            +
             | 
| 174 | 
            +
                  requests = __entries__(path, **o)
         | 
| 175 | 
            +
                  return requests.flat_map(&:resource) if requests.all? { |req| req.respond_to? :resource }
         | 
| 176 | 
            +
             | 
| 177 | 
            +
                  client.hydra.run
         | 
| 178 | 
            +
                  requests.flat_map { |req| req.response.resource }
         | 
| 179 | 
            +
                end
         | 
| 180 | 
            +
             | 
| 73 181 | 
             
                def page_params(per_page: nil, page: nil)
         | 
| 74 182 | 
             
                  { per_page: per_page || @per_page, page: page || @page }.to_param
         | 
| 75 183 | 
             
                end
         | 
| @@ -85,28 +193,25 @@ module Hcloud | |
| 85 193 | 
             
                  r.compact.join('&')
         | 
| 86 194 | 
             
                end
         | 
| 87 195 |  | 
| 88 | 
            -
                 | 
| 89 | 
            -
                  client.request(*args)
         | 
| 90 | 
            -
                end
         | 
| 91 | 
            -
             | 
| 196 | 
            +
                # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
         | 
| 92 197 | 
             
                def __entries__(path, **o)
         | 
| 93 | 
            -
                   | 
| 94 | 
            -
                   | 
| 95 | 
            -
                  return [ | 
| 96 | 
            -
             | 
| 97 | 
            -
             | 
| 98 | 
            -
                   | 
| 99 | 
            -
                   | 
| 100 | 
            -
                   | 
| 101 | 
            -
                  requests = r.times.map do |i|
         | 
| 198 | 
            +
                  first_page = request(path, o.merge(ep: ep(per_page: 1, page: 1))).run
         | 
| 199 | 
            +
                  total_entries = first_page.pagination.total_entries
         | 
| 200 | 
            +
                  return [first_page] if total_entries <= 1 || @limit == 1
         | 
| 201 | 
            +
             | 
| 202 | 
            +
                  total_entries = @limit if !@limit.nil? && (total_entries > @limit)
         | 
| 203 | 
            +
                  pages = total_entries / Client::MAX_ENTRIES_PER_PAGE
         | 
| 204 | 
            +
                  pages += 1 if (total_entries % Client::MAX_ENTRIES_PER_PAGE).positive?
         | 
| 205 | 
            +
                  pages.times.map do |page|
         | 
| 102 206 | 
             
                    per_page = Client::MAX_ENTRIES_PER_PAGE
         | 
| 103 | 
            -
                    if !@limit.nil? && ( | 
| 104 | 
            -
                      per_page =  | 
| 207 | 
            +
                    if !@limit.nil? && (pages == (page + 1)) && (total_entries % per_page != 0)
         | 
| 208 | 
            +
                      per_page = total_entries % per_page
         | 
| 209 | 
            +
                    end
         | 
| 210 | 
            +
                    request(path, o.merge(ep: ep(per_page: per_page, page: page + 1))).tap do |req|
         | 
| 211 | 
            +
                      client.hydra.queue req
         | 
| 105 212 | 
             
                    end
         | 
| 106 | 
            -
                    req = request(path, o.merge(ep: ep(per_page: per_page, page: i + 1)))
         | 
| 107 | 
            -
                    client.hydra.queue req
         | 
| 108 | 
            -
                    req
         | 
| 109 213 | 
             
                  end
         | 
| 110 214 | 
             
                end
         | 
| 215 | 
            +
                # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
         | 
| 111 216 | 
             
              end
         | 
| 112 217 | 
             
            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,18 +1,52 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            autoload :Typhoeus, 'typhoeus'
         | 
| 2 4 | 
             
            autoload :Oj, 'oj'
         | 
| 3 5 |  | 
| 6 | 
            +
            require 'delegate'
         | 
| 7 | 
            +
             | 
| 4 8 | 
             
            module Hcloud
         | 
| 5 9 | 
             
              class Client
         | 
| 6 10 | 
             
                MAX_ENTRIES_PER_PAGE = 50
         | 
| 7 11 |  | 
| 8 | 
            -
                 | 
| 9 | 
            -
             | 
| 12 | 
            +
                class << self
         | 
| 13 | 
            +
                  attr_writer :connection
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  def connection
         | 
| 16 | 
            +
                    return @connection if @connection.is_a? Hcloud::Client
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    raise ArgumentError, "client not correctly initialized, actually #{@client.inspect}"
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                attr_reader :token, :auto_pagination, :hydra, :user_agent
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                def initialize(token:, auto_pagination: false, concurrency: 20, user_agent: nil)
         | 
| 10 25 | 
             
                  @token = token
         | 
| 26 | 
            +
                  @user_agent = user_agent || "hcloud-ruby v#{VERSION}"
         | 
| 11 27 | 
             
                  @auto_pagination = auto_pagination
         | 
| 12 28 | 
             
                  @concurrency = concurrency
         | 
| 13 29 | 
             
                  @hydra = Typhoeus::Hydra.new(max_concurrency: concurrency)
         | 
| 14 30 | 
             
                end
         | 
| 15 31 |  | 
| 32 | 
            +
                def concurrent
         | 
| 33 | 
            +
                  @concurrent = true
         | 
| 34 | 
            +
                  ret = yield
         | 
| 35 | 
            +
                  ret.each do |element|
         | 
| 36 | 
            +
                    next unless element.is_a?(AbstractResource)
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                    element.run
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
                  hydra.run
         | 
| 41 | 
            +
                  ret
         | 
| 42 | 
            +
                ensure
         | 
| 43 | 
            +
                  @concurrent = nil
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                def concurrent?
         | 
| 47 | 
            +
                  !@concurrent.nil?
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
             | 
| 16 50 | 
             
                def authorized?
         | 
| 17 51 | 
             
                  request('server_types').run
         | 
| 18 52 | 
             
                  true
         | 
| @@ -56,8 +90,34 @@ module Hcloud | |
| 56 90 | 
             
                  FloatingIPResource.new(client: self)
         | 
| 57 91 | 
             
                end
         | 
| 58 92 |  | 
| 59 | 
            -
                def  | 
| 60 | 
            -
                   | 
| 93 | 
            +
                def networks
         | 
| 94 | 
            +
                  NetworkResource.new(client: self)
         | 
| 95 | 
            +
                end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                def volumes
         | 
| 98 | 
            +
                  VolumeResource.new(client: self)
         | 
| 99 | 
            +
                end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                class ResourceFuture < Delegator
         | 
| 102 | 
            +
                  def initialize(request) # rubocop:disable Lint/MissingSuper
         | 
| 103 | 
            +
                    @request = request
         | 
| 104 | 
            +
                  end
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                  def __getobj__
         | 
| 107 | 
            +
                    @__getobj__ ||= @request&.response&.resource
         | 
| 108 | 
            +
                  end
         | 
| 109 | 
            +
                end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                def prepare_request(url, args = {}, &block)
         | 
| 112 | 
            +
                  req = request(url, **args.merge(block: block))
         | 
| 113 | 
            +
                  return req.run.resource unless concurrent?
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                  hydra.queue req
         | 
| 116 | 
            +
                  ResourceFuture.new(req)
         | 
| 117 | 
            +
                end
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                def request(path, options = {}) # rubocop:disable Metrics/MethodLength
         | 
| 120 | 
            +
                  hcloud_attributes = TyphoeusExt.collect_attributes(options)
         | 
| 61 121 | 
             
                  if x = options.delete(:j)
         | 
| 62 122 | 
             
                    options[:body] = Oj.dump(x, mode: :compat)
         | 
| 63 123 | 
             
                    options[:method] ||= :post
         | 
| @@ -68,41 +128,22 @@ module Hcloud | |
| 68 128 | 
             
                    q << x.to_param
         | 
| 69 129 | 
             
                  end
         | 
| 70 130 | 
             
                  path = path.dup
         | 
| 71 | 
            -
                  path <<  | 
| 131 | 
            +
                  path << "?#{q.join('&')}"
         | 
| 72 132 | 
             
                  r = Typhoeus::Request.new(
         | 
| 73 133 | 
             
                    "https://api.hetzner.cloud/v1/#{path}",
         | 
| 74 134 | 
             
                    {
         | 
| 75 135 | 
             
                      headers: {
         | 
| 76 136 | 
             
                        'Authorization' => "Bearer #{token}",
         | 
| 137 | 
            +
                        'User-Agent' => user_agent,
         | 
| 77 138 | 
             
                        'Content-Type' => 'application/json'
         | 
| 78 139 | 
             
                      }
         | 
| 79 140 | 
             
                    }.merge(options)
         | 
| 80 141 | 
             
                  )
         | 
| 81 142 | 
             
                  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
         | 
| 143 | 
            +
                    response.extend(TyphoeusExt)
         | 
| 144 | 
            +
                    response.attributes = hcloud_attributes
         | 
| 145 | 
            +
                    response.context.client = self
         | 
| 146 | 
            +
                    response.check_for_error unless response.request.hydra
         | 
| 106 147 | 
             
                  end
         | 
| 107 148 | 
             
                  r
         | 
| 108 149 | 
             
                end
         | 
    
        data/lib/hcloud/datacenter.rb
    CHANGED
    
    | @@ -1,13 +1,13 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module Hcloud
         | 
| 2 4 | 
             
              class Datacenter
         | 
| 3 | 
            -
                 | 
| 4 | 
            -
                  id: nil,
         | 
| 5 | 
            -
                  name: nil,
         | 
| 6 | 
            -
                  description: nil,
         | 
| 7 | 
            -
                  location: Location,
         | 
| 8 | 
            -
                  server_types: nil
         | 
| 9 | 
            -
                }.freeze
         | 
| 5 | 
            +
                require 'hcloud/datacenter_resource'
         | 
| 10 6 |  | 
| 11 7 | 
             
                include EntryLoader
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                schema(
         | 
| 10 | 
            +
                  location: Location
         | 
| 11 | 
            +
                )
         | 
| 12 12 | 
             
              end
         | 
| 13 13 | 
             
            end
         | 
| @@ -1,44 +1,19 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module Hcloud
         | 
| 2 4 | 
             
              class DatacenterResource < AbstractResource
         | 
| 3 | 
            -
                 | 
| 5 | 
            +
                filter_attributes :name
         | 
| 4 6 |  | 
| 5 | 
            -
                 | 
| 6 | 
            -
                  j = Oj.load(request('datacenters').run.body)
         | 
| 7 | 
            -
                  dc = j['datacenters'].map { |x| Datacenter.new(x, self, client) }
         | 
| 8 | 
            -
                  dc.reject { |x| x.id == j['recommendation'] }.unshift(
         | 
| 9 | 
            -
                    dc.find { |x| x.id == j['recommendation'] }
         | 
| 10 | 
            -
                  )
         | 
| 11 | 
            -
                end
         | 
| 7 | 
            +
                bind_to Datacenter
         | 
| 12 8 |  | 
| 13 9 | 
             
                def recommended
         | 
| 14 10 | 
             
                  all.first
         | 
| 15 11 | 
             
                end
         | 
| 16 12 |  | 
| 17 | 
            -
                def find(id)
         | 
| 18 | 
            -
                  Datacenter.new(
         | 
| 19 | 
            -
                    Oj.load(request("datacenters/#{id}").run.body)['datacenter'],
         | 
| 20 | 
            -
                    self,
         | 
| 21 | 
            -
                    client
         | 
| 22 | 
            -
                  )
         | 
| 23 | 
            -
                end
         | 
| 24 | 
            -
             | 
| 25 | 
            -
                def find_by(name:)
         | 
| 26 | 
            -
                  x = Oj.load(request('datacenters', q: { name: name }).run.body)['datacenters']
         | 
| 27 | 
            -
                  return nil if x.none?
         | 
| 28 | 
            -
                  x.each do |s|
         | 
| 29 | 
            -
                    return Datacenter.new(s, self, client)
         | 
| 30 | 
            -
                  end
         | 
| 31 | 
            -
                end
         | 
| 32 | 
            -
             | 
| 33 13 | 
             
                def [](arg)
         | 
| 34 14 | 
             
                  case arg
         | 
| 35 | 
            -
                  when Integer
         | 
| 36 | 
            -
             | 
| 37 | 
            -
                      find(arg)
         | 
| 38 | 
            -
                    rescue Error::NotFound
         | 
| 39 | 
            -
                    end
         | 
| 40 | 
            -
                  when String
         | 
| 41 | 
            -
                    find_by(name: arg)
         | 
| 15 | 
            +
                  when Integer then find_by(id: arg)
         | 
| 16 | 
            +
                  when String then find_by(name: arg)
         | 
| 42 17 | 
             
                  end
         | 
| 43 18 | 
             
                end
         | 
| 44 19 | 
             
              end
         |