class Artifactory::Client

Client for the Artifactory API.

@see www.jfrog.com/confluence/display/RTF/Artifactory+REST+API

Public Class Methods

new(options = {}) click to toggle source

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

@return [Artifactory::Client]

# File lib/artifactory/client.rb, line 69
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
proxy(klass) click to toggle source

@private

# 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

Public Instance Methods

build_uri(verb, path, params = {}) click to toggle source

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 {Client#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.

@param [Symbol] verb

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

@param [String] path

the absolute or relative HTTP path (url) to get

@param [Hash] params

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

@return [URI]

# File lib/artifactory/client.rb, line 323
def build_uri(verb, path, params = {})
  # Add any query string parameters
  if %i{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) click to toggle source

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

@param [#to_s] verb

the HTTP verb to create a class from

@return [Class]

# File lib/artifactory/client.rb, line 348
def class_for_request(verb)
  Net::HTTP.const_get(verb.to_s.capitalize)
end
default_headers() click to toggle source

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

@return [Hash]

# File lib/artifactory/client.rb, line 297
def default_headers
  {
    "Connection" => "keep-alive",
    "Keep-Alive" => "30",
    "User-Agent" => user_agent,
  }
end
delete(path, params = {}, headers = {}) click to toggle source

Make a HTTP DELETE request

@param path (see Client#request) @param params (see Client#get) @param headers (see Client#request)

@raise (see Client#request) @return (see Client#request)

# File lib/artifactory/client.rb, line 164
def delete(path, params = {}, headers = {})
  request(:delete, path, params, headers)
end
error(response) click to toggle source

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

@raise [Error::HTTPError]

@param [HTTP::Message] response

the response object from the request
# File lib/artifactory/client.rb, line 396
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 = {}, &block) click to toggle source

Make a HTTP GET request

If a block is provided the response body is yielded in chunks/fragments as it is read from the undelrying socket.

@param path (see Client#request) @param [Hash] params

the list of query params

@param headers (see Client#request)

@yield [chunk] Partial piece of response body

@raise (see Client#request) @return (see Client#request)

# File lib/artifactory/client.rb, line 107
def get(path, params = {}, headers = {}, &block)
  request(:get, path, params, headers, &block)
end
patch(path, data, headers = {}) click to toggle source

Make a HTTP PATCH request

@param path (see Client#request) @param data (see Client#post) @param headers (see Client#request)

@raise (see Client#request) @return (see Client#request)

# File lib/artifactory/client.rb, line 150
def patch(path, data, headers = {})
  request(:patch, path, data, headers)
end
post(path, data, headers = {}) click to toggle source

Make a HTTP POST request

@param path (see Client#request) @param [String, read] data

the body to use for the request

@param headers (see Client#request)

@raise (see Client#request) @return (see Client#request)

# File lib/artifactory/client.rb, line 122
def post(path, data, headers = {})
  request(:post, path, data, headers)
end
put(path, data, headers = {}) click to toggle source

Make a HTTP PUT request

@param path (see Client#request) @param data (see Client#post) @param headers (see Client#request)

@raise (see Client#request) @return (see Client#request)

# File lib/artifactory/client.rb, line 136
def put(path, data, headers = {})
  request(:put, path, data, headers)
end
request(verb, path, data = {}, headers = {}) { |chunk| ... } click to toggle source

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. If a block is provided the response body is yielded in chunks/fragments as it is read from the undelrying socket.

@raise [Error::HTTPError]

if the request is not an HTTP 200 OK

@param [Symbol] verb

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

@param [String] path

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

@param [#read, Hash, nil] data

the data to use (varies based on the +verb+)

@param [Hash] headers

the list of headers to use

@yield [chunk] Partial piece of response body

@return [String, Hash]

the response body
# File lib/artifactory/client.rb, line 193
def request(verb, path, data = {}, headers = {}, &block)
  # 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)
  elsif api_key
    request.add_field("X-JFrog-Art-Api", api_key)
  end

  # Setup PATCH/POST/PUT
  if %i{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|

    if block_given?
      http.request(request) do |response|
        case response
        when Net::HTTPRedirection
          redirect = response["location"]
          request(verb, redirect, data, headers, &block)
        when Net::HTTPSuccess
          response.read_body do |chunk|
            yield chunk
          end
        else
          error(response)
        end
      end
    else
      response = http.request(request)

      case response
      when Net::HTTPRedirection
        redirect = response["location"]
        request(verb, redirect, data, headers)
      when Net::HTTPSuccess
        success(response)
      else
        error(response)
      end
    end
  end
rescue SocketError, Errno::ECONNREFUSED, EOFError
  raise Error::ConnectionError.new(endpoint)
end
same_options?(opts) click to toggle source

Determine if the given options are the same as ours.

@return [Boolean]

# File lib/artifactory/client.rb, line 87
def same_options?(opts)
  opts.hash == options.hash
end
success(response) click to toggle source

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.

@param [HTTP::Message] response

the response object from the request

@return [String, Hash]

the parsed response, as an object
# File lib/artifactory/client.rb, line 379
def success(response)
  if (response.content_type || "").include?("json")
    JSON.parse(response.body || "{}")
  else
    response.body || ""
  end
end
to_query_string(hash) click to toggle source

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

@param [Hash] hash

the hash to create the query string from

@return [String, nil]

the query string as a string, or +nil+ if there are no params
# File lib/artifactory/client.rb, line 362
def to_query_string(hash)
  hash.map do |key, value|
    "#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}"
  end.join("&")[/.+/]
end