class Bicho::Client

Client to query bugzilla

Attributes

api_url[R]

@return [URI] XML-RPC API end-point

This URL is automatically inferred from the Client#site_url

Plugins can modify the inferred value by providing a transform_api_url_hook(url, logger) method returning the modified value.

site_url[R]

@return [URI] Bugzilla installation website

This value is provided at construction time

userid[R]

@return [String] user id, available after login

Public Class Methods

new(site_url) click to toggle source

@param [String] site_url Bugzilla installation site url

# File lib/bicho/client.rb, line 96
def initialize(site_url)
  @plugins = []
  load_plugins!
  instantiate_plugins!

  if site_url.nil?
    @plugins.each do |pl_instance|
      if pl_instance.respond_to?(:default_site_url_hook)
        default = pl_instance.default_site_url_hook(logger)
        site_url = default unless default.nil?
      end
    end
  end

  # If the default url is still null, we can't continue
  raise ArgumentError, 'missing bugzilla site' if site_url.nil?

  @plugins.each do |pl_instance|
    if pl_instance.respond_to?(:transform_site_url_hook)
      site_url = pl_instance.transform_site_url_hook(site_url, logger)
    end
  end

  # Don't modify the original url
  @site_url = site_url.is_a?(URI) ? site_url.clone : URI.parse(site_url)

  api_url = @site_url.clone
  api_url.path = '/xmlrpc.cgi'

  @plugins.each do |pl_instance|
    # Modify API url
    if pl_instance.respond_to?(:transform_api_url_hook)
      api_url = pl_instance.transform_api_url_hook(api_url, logger)
    end
  end

  @api_url = api_url.is_a?(URI) ? api_url.clone : URI.parse(api_url)

  @client = XMLRPC::Client.new_from_uri(@api_url.to_s, nil, 900)
  @client.set_debug
  @plugins.each do |pl_instance|
    # Modify API url
    if pl_instance.respond_to?(:transform_xmlrpc_client_hook)
      pl_instance.transform_xmlrpc_client_hook(@client, logger)
    end
  end

  # User.login sets the credentials cookie for subsequent calls
  if @client.user && @client.password
    ret = @client.call('User.login', 'login' => @client.user, 'password' => @client.password, 'remember' => 0)
    handle_faults(ret)
    @userid = ret['id']
  end
end

Public Instance Methods

add_attachment(summary, file, *ids, **kwargs) click to toggle source

Add an attachment to bugs with given ids

Params: @param summary - a short string describing the attachment @param file - [File] object to attach @param *ids - a list of bug ids to which the attachment will be added @param **kwargs - optional keyword-args that may contain:

- content_type - content type of the attachment (if ommited,
'application/octet-stream' will be used)
- file_name - name of the file (if ommited, the base name of the
provided file will be used)
- patch? - flag saying that the attachment is a patch
- private? - flag saying that the attachment is private
- comment

@return [Array<ID>] a list of the attachment id(s) created.

# File lib/bicho/client.rb, line 382
def add_attachment(summary, file, *ids, **kwargs)
  params = {}
  params[:ids] = ids
  params[:summary] = summary
  params[:content_type] = kwargs.fetch(:content_type, 'application/octet-stream')
  params[:file_name] = kwargs.fetch(:file_name, File.basename(file))
  params[:is_patch] = kwargs[:patch?] if kwargs[:patch?]
  params[:is_private] = kwargs[:private?] if kwargs[:private?]
  params[:comment] = kwargs[:comment] if kwargs[:comment]
  params[:data] = XMLRPC::Base64.new(file.read)
  ret = @client.call('Bug.add_attachment', params)
  handle_faults(ret)
  ret['ids']
end
create_bug(product, component, summary, version, **kwargs) click to toggle source

Create a bug

@param product - the name of the product the bug is being filed against @param component - the name of a component in the product above. @param summary - a brief description of the bug being filed. @param version - version of the product above; the version the bug was found in. @param **kwargs - keyword-args containing optional/defaulted params

Return the new bug ID

# File lib/bicho/client.rb, line 200
def create_bug(product, component, summary, version, **kwargs)
  params = {}
  params = params.merge(kwargs)
  params[:product] = product
  params[:component] = component
  params[:summary] = summary
  params[:version] = version
  ret = @client.call('Bug.create', params)
  handle_faults(ret)
  ret['id']
end
expand_named_query(what) click to toggle source

Given a named query's name, runs it on the server @returns [Array<String>] list of bugs

# File lib/bicho/client.rb, line 251
def expand_named_query(what)
  url = @api_url.clone
  url.path = '/buglist.cgi'
  url.query = "cmdtype=runnamed&namedcmd=#{URI.escape(what)}&ctype=atom"
  logger.info("Expanding named query: '#{what}' to #{url.request_uri}")
  fetch_named_query_url(url, 5)
end
fetch_named_query_url(url, redirects_left) click to toggle source

Fetches a named query by its full url @private @returns [Array<String>] list of bugs

# File lib/bicho/client.rb, line 277
def fetch_named_query_url(url, redirects_left)
  raise 'You need to be authenticated to use named queries' unless @userid
  http = Net::HTTP.new(@api_url.host, @api_url.port)
  http.set_debug_output(Bicho::LoggerIODevice.new)
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE
  http.use_ssl = (@api_url.scheme == 'https')
  # request = Net::HTTP::Get.new(url.request_uri, {'Cookie' => self.cookie})
  request = Net::HTTP::Get.new(url.request_uri)
  request.basic_auth @api_url.user, @api_url.password
  response = http.request(request)
  case response
  when Net::HTTPSuccess
    bugs = []
    begin
      xml = Nokogiri::XML.parse(response.body)
      xml.root.xpath('//xmlns:entry/xmlns:link/@href', xml.root.namespace).each do |attr|
        uri = URI.parse attr.value
        bugs << uri.query.split('=')[1]
      end
      return bugs
    rescue Nokogiri::XML::XPath::SyntaxError
      raise "Named query '#{url.request_uri}' not found"
    end
  when Net::HTTPRedirection
    location = response['location']
    if redirects_left.zero?
      raise "Maximum redirects exceeded (redirected to #{location})"
    end
    new_location_uri = URI.parse(location)
    logger.debug("Moved to #{new_location_uri}")
    fetch_named_query_url(new_location_uri, redirects_left - 1)
  else
    raise "Error when expanding named query '#{url.request_uri}': #{response}"
  end
end
get_attachments(*ids) click to toggle source

@return [Array<Attachment>] a list of attachments for the given bugs.

Payload is lazy-loaded

# File lib/bicho/client.rb, line 352
def get_attachments(*ids)
  params = {}
  params[:ids] = normalize_ids ids

  ret = @client.call('Bug.attachments',
                     params.merge(exclude_fields: ['data']))
  handle_faults(ret)
  ret['bugs'].map do |_, attachments_data|
    attachments_data.map do |attachment_data|
      Attachment.new(self, @client, attachment_data)
    end
  end.flatten
end
get_bug(id) click to toggle source

Gets a single bug @return [Bug] a single bug by id

# File lib/bicho/client.rb, line 315
def get_bug(id)
  get_bugs(id).first
end
get_bugs(*ids) click to toggle source

Retrieves one or more bugs by id @return [Array<Bug>] a list of bugs

# File lib/bicho/client.rb, line 321
def get_bugs(*ids)
  params = {}
  params[:ids] = normalize_ids ids

  bugs = []
  ret = @client.call('Bug.get', params)
  handle_faults(ret)
  ret['bugs'].each do |bug_data|
    bugs << Bug.new(self, bug_data)
  end
  bugs
end
get_history(*ids) click to toggle source

@return [Array<History>] the history of the given bugs

# File lib/bicho/client.rb, line 335
def get_history(*ids)
  params = {}
  params[:ids] = normalize_ids ids

  histories = []
  ret = @client.call('Bug.history', params)
  handle_faults(ret)
  ret['bugs'].each do |history_data|
    histories << History.new(self, history_data)
  end
  histories
end
handle_faults(ret) click to toggle source
# File lib/bicho/client.rb, line 176
def handle_faults(ret)
  if ret.key?('faults')
    ret['faults'].each do |fault|
      logger.error fault
    end
  end
end
instantiate_plugins!() click to toggle source

instantiate all plugin classes in the Bicho::Plugins module and add them to the list of known plugins

# File lib/bicho/client.rb, line 163
def instantiate_plugins!
  ::Bicho::Plugins.constants.each do |cnt|
    pl_class = ::Bicho::Plugins.const_get(cnt)
    pl_instance = pl_class.new
    logger.debug("Loaded: #{pl_instance}")
    @plugins << pl_instance
  end
end
load_plugins!() click to toggle source

ruby-load all the files in the plugins directory

# File lib/bicho/client.rb, line 152
def load_plugins!
  # Scan plugins
  plugin_glob = File.join(File.dirname(__FILE__), 'plugins', '*.rb')
  Dir.glob(plugin_glob).each do |plugin|
    logger.debug("Loading file: #{plugin}")
    require plugin
  end
end
normalize_ids(ids) click to toggle source

normalize bug ids @param ids - array of bug numbers (Integer) or bug aliases (String) @returns Array of bug numbers (Integer)

@private

# File lib/bicho/client.rb, line 264
def normalize_ids(ids)
  ids.collect(&:to_s).map do |what|
    if what =~ /^[0-9]+$/
      next what.to_i
    else
      next expand_named_query(what)
    end
  end.flatten
end
search_bugs(query) click to toggle source

Search for a bug

query has to be either a Query object or a String that will be searched in the summary of the bugs.

# File lib/bicho/client.rb, line 235
def search_bugs(query)
  # allow plain strings to be passed, interpretting them
  query = Query.new.summary(query) if query.is_a?(String)

  ret = @client.call('Bug.search', query.query_map)
  handle_faults(ret)
  bugs = []
  ret['bugs'].each do |bug_data|
    bugs << Bug.new(self, bug_data)
  end
  bugs
end
update_bug(id, **kwargs) click to toggle source

Update a bug

@param id - bug number (Integer) or alias (String) of bug to be updated @param **kwargs - keyword-args containing optional/defaulted params

@returns id of updated bug

# File lib/bicho/client.rb, line 219
def update_bug(id, **kwargs)
  params = {}
  params = params.merge(kwargs)
  params[:ids] = normalize_ids [id]
  ret = @client.call('Bug.update', params)
  logger.info "Bug.update returned #{ret.inspect}"
  handle_faults(ret)
  ret.dig('bugs', 0, 'id')
end
url() click to toggle source

@visibility private Implemented only to warn users about the replacement APIs

# File lib/bicho/client.rb, line 90
def url
  warn 'url is deprecated. Use site_url or api_url'
  raise NoMethodError
end
version() click to toggle source

Return Bugzilla API version

# File lib/bicho/client.rb, line 185
def version
  ret = @client.call('Bugzilla.version')
  handle_faults(ret)
  ret['version']
end