Class: Artifactory::Client

Inherits:
Object
  • Object
show all
Includes:
Configurable
Defined in:
lib/artifactory/client.rb

Overview

Client for the Artifactory API.

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Configurable

#configure, keys, #reset!

Constructor Details

#initialize(options = {}) ⇒ Artifactory::Client

Create a new Artifactory Client with the given options. Any options given take precedence over the default options.



68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/artifactory/client.rb', line 68

def initialize(options = {})
  # Use any options given, but fall back to the defaults set on the module
  Artifactory::Configurable.keys.each do |key|
    value = if options[key].nil?
      Artifactory.instance_variable_get(:"@#{key}")
    else
      options[key]
    end

    instance_variable_set(:"@#{key}", value)
  end
end

Class Method Details

.proxy(klass) ⇒ Object



33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/artifactory/client.rb', line 33

def proxy(klass)
  namespace = klass.name.split('::').last.downcase
  klass.singleton_methods(false).each do |name|
    define_method("#{namespace}_#{name}") do |*args|
      if args.last.is_a?(Hash)
        args.last[:client] = self
      else
        args << { client: self }
      end

      klass.send(name, *args)
    end
  end
end

Instance Method Details

#build_uri(verb, path, params = {}) ⇒ URI

Construct a URL from the given verb and path. If the request is a GET or DELETE request, the params are assumed to be query params are are converted as such using #to_query_string.

If the path is relative, it is merged with the Defaults.endpoint attribute. If the path is absolute, it is converted to a URI object and returned.

Parameters:

  • verb (Symbol)

    the lowercase HTTP verb (e.g. :get)

  • path (String)

    the absolute or relative HTTP path (url) to get

  • params (Hash) (defaults to: {})

    the list of params to build the URI with (for GET and DELETE requests)

Returns:

  • (URI)


294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
# File 'lib/artifactory/client.rb', line 294

def build_uri(verb, path, params = {})
  # Add any query string parameters
  if [:delete, :get].include?(verb)
    path = [path, to_query_string(params)].compact.join('?')
  end

  # Parse the URI
  uri = URI.parse(path)

  # Don't merge absolute URLs
  uri = URI.parse(File.join(endpoint, path)) unless uri.absolute?

  # Return the URI object
  uri
end

#class_for_request(verb) ⇒ Class

Helper method to get the corresponding Net::HTTP class from the given HTTP verb.

Parameters:

  • verb (#to_s)

    the HTTP verb to create a class from

Returns:

  • (Class)


319
320
321
# File 'lib/artifactory/client.rb', line 319

def class_for_request(verb)
  Net::HTTP.const_get(verb.to_s.capitalize)
end

#default_headersHash

The list of default headers (such as Keep-Alive and User-Agent) for the client object.

Returns:

  • (Hash)


268
269
270
271
272
273
274
# File 'lib/artifactory/client.rb', line 268

def default_headers
  {
    'Connection' => 'keep-alive',
    'Keep-Alive' => '30',
    'User-Agent' => user_agent,
  }
end

#delete(path, params = {}, headers = {}) ⇒ String, Hash

Make a HTTP DELETE request

Parameters:

  • path (String)

    the absolute or relative path from Defaults.endpoint to make the request against

  • params (Hash) (defaults to: {})

    the list of query params

  • headers (Hash) (defaults to: {})

    the list of headers to use

Returns:

  • (String, Hash)

    the response body

Raises:



158
159
160
# File 'lib/artifactory/client.rb', line 158

def delete(path, params = {}, headers = {})
  request(:delete, path, params, headers)
end

#error(response) ⇒ Object

Raise a response error, extracting as much information from the server’s response as possible.

Parameters:

  • response (HTTP::Message)

    the response object from the request

Raises:



367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
# File 'lib/artifactory/client.rb', line 367

def error(response)
  if (response.content_type || '').include?('json')
    # Attempt to parse the error as JSON
    begin
      json = JSON.parse(response.body)

      if json['errors'] && json['errors'].first
        raise Error::HTTPError.new(json['errors'].first)
      end
    rescue JSON::ParserError; end
  end

  raise Error::HTTPError.new(
    'status'  => response.code,
    'message' => response.body,
  )
end

#get(path, params = {}, headers = {}) ⇒ String, Hash

Make a HTTP GET request

Parameters:

  • params (Hash) (defaults to: {})

    the list of query params

  • path (String)

    the absolute or relative path from Defaults.endpoint to make the request against

  • headers (Hash) (defaults to: {})

    the list of headers to use

Returns:

  • (String, Hash)

    the response body

Raises:



101
102
103
# File 'lib/artifactory/client.rb', line 101

def get(path, params = {}, headers = {})
  request(:get, path, params, headers)
end

#patch(path, data, headers = {}) ⇒ String, Hash

Make a HTTP PATCH request

Parameters:

  • path (String)

    the absolute or relative path from Defaults.endpoint to make the request against

  • data (String, #read)

    the body to use for the request

  • headers (Hash) (defaults to: {})

    the list of headers to use

Returns:

  • (String, Hash)

    the response body

Raises:



144
145
146
# File 'lib/artifactory/client.rb', line 144

def patch(path, data, headers = {})
  request(:patch, path, data, headers)
end

#post(path, data, headers = {}) ⇒ String, Hash

Make a HTTP POST request

Parameters:

  • data (String, #read)

    the body to use for the request

  • path (String)

    the absolute or relative path from Defaults.endpoint to make the request against

  • headers (Hash) (defaults to: {})

    the list of headers to use

Returns:

  • (String, Hash)

    the response body

Raises:



116
117
118
# File 'lib/artifactory/client.rb', line 116

def post(path, data, headers = {})
  request(:post, path, data, headers)
end

#put(path, data, headers = {}) ⇒ String, Hash

Make a HTTP PUT request

Parameters:

  • path (String)

    the absolute or relative path from Defaults.endpoint to make the request against

  • data (String, #read)

    the body to use for the request

  • headers (Hash) (defaults to: {})

    the list of headers to use

Returns:

  • (String, Hash)

    the response body

Raises:



130
131
132
# File 'lib/artifactory/client.rb', line 130

def put(path, data, headers = {})
  request(:put, path, data, headers)
end

#request(verb, path, data = {}, headers = {}) ⇒ String, Hash

Make an HTTP request with the given verb, data, params, and headers. If the response has a return type of JSON, the JSON is automatically parsed and returned as a hash; otherwise it is returned as a string.

Parameters:

  • verb (Symbol)

    the lowercase symbol of the HTTP verb (e.g. :get, :delete)

  • path (String)

    the absolute or relative path from Defaults.endpoint to make the request against

  • data (#read, Hash, nil) (defaults to: {})

    the data to use (varies based on the verb)

  • headers (Hash) (defaults to: {})

    the list of headers to use

Returns:

  • (String, Hash)

    the response body

Raises:



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/artifactory/client.rb', line 183

def request(verb, path, data = {}, headers = {})
  # Build the URI and request object from the given information
  uri = build_uri(verb, path, data)
  request = class_for_request(verb).new(uri.request_uri)

  # Add headers
  default_headers.merge(headers).each do |key, value|
    request.add_field(key, value)
  end

  # Add basic authentication
  if username && password
    request.basic_auth(username, password)
  end

  # Setup PATCH/POST/PUT
  if [:patch, :post, :put].include?(verb)
    if data.respond_to?(:read)
      request.content_length = data.size
      request.body_stream = data
    elsif data.is_a?(Hash)
      request.form_data = data
    else
      request.body = data
    end
  end

  # Create the HTTP connection object - since the proxy information defaults
  # to +nil+, we can just pass it to the initializer method instead of doing
  # crazy strange conditionals.
  connection = Net::HTTP.new(uri.host, uri.port,
    proxy_address, proxy_port, proxy_username, proxy_password)

  # The artifacts being uploaded might be large, so there’s a good chance
  # we'll need to bump this higher than the `Net::HTTP` default of 60
  # seconds.
  connection.read_timeout = read_timeout

  # Apply SSL, if applicable
  if uri.scheme == 'https'
    require 'net/https' unless defined?(Net::HTTPS)

    # Turn on SSL
    connection.use_ssl = true

    # Custom pem files, no problem!
    if ssl_pem_file
      pem = File.read(ssl_pem_file)
      connection.cert = OpenSSL::X509::Certificate.new(pem)
      connection.key = OpenSSL::PKey::RSA.new(pem)
      connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
    end

    # Naughty, naughty, naughty! Don't blame when when someone hops in
    # and executes a MITM attack!
    unless ssl_verify
      connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
    end
  end

  # Create a connection using the block form, which will ensure the socket
  # is properly closed in the event of an error.
  connection.start do |http|
    response = http.request(request)

    case response
    when Net::HTTPRedirection
      redirect = URI.parse(response['location'])
      request(verb, redirect, data, headers)
    when Net::HTTPSuccess
      success(response)
    else
      error(response)
    end
  end
rescue SocketError, Errno::ECONNREFUSED, EOFError
  raise Error::ConnectionError.new(endpoint)
end

#same_options?(opts) ⇒ Boolean

Determine if the given options are the same as ours.

Returns:

  • (Boolean)


86
87
88
# File 'lib/artifactory/client.rb', line 86

def same_options?(opts)
  opts.hash == options.hash
end

#success(response) ⇒ String, Hash

Parse the response object and manipulate the result based on the given Content-Type header. For now, this method only parses JSON, but it could be expanded in the future to accept other content types.

Parameters:

  • response (HTTP::Message)

    the response object from the request

Returns:

  • (String, Hash)

    the parsed response, as an object



350
351
352
353
354
355
356
# File 'lib/artifactory/client.rb', line 350

def success(response)
  if (response.content_type || '').include?('json')
    JSON.parse(response.body)
  else
    response.body
  end
end

#to_query_string(hash) ⇒ String?

Convert the given hash to a list of query string parameters. Each key and value in the hash is URI-escaped for safety.

Parameters:

  • hash (Hash)

    the hash to create the query string from

Returns:

  • (String, nil)

    the query string as a string, or nil if there are no params



333
334
335
336
337
# File 'lib/artifactory/client.rb', line 333

def to_query_string(hash)
  hash.map do |key, value|
    "#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}"
  end.join('&')[/.+/]
end