Class: Bicho::Client

Inherits:
Object
  • Object
show all
Includes:
Logging
Defined in:
lib/bicho/client.rb

Overview

Client to query bugzilla

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Logging

#logger

Constructor Details

#initialize(site_url) ⇒ Client

Returns a new instance of Client

Raises:

  • (ArgumentError)

94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/bicho/client.rb', line 94

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

Instance Attribute Details

#api_urlURI (readonly)

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.


75
76
77
# File 'lib/bicho/client.rb', line 75

def api_url
  @api_url
end

#site_urlURI (readonly)

This value is provided at construction time


80
81
82
# File 'lib/bicho/client.rb', line 80

def site_url
  @site_url
end

#useridString (readonly)


83
84
85
# File 'lib/bicho/client.rb', line 83

def userid
  @userid
end

Instance Method Details

#add_attachment(summary, file, *ids, **kwargs) ⇒ Array<ID>

Add an attachment to bugs with given ids

Params:


363
364
365
366
367
368
369
370
371
372
373
374
375
376
# File 'lib/bicho/client.rb', line 363

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

170
171
172
# File 'lib/bicho/client.rb', line 170

def cookie
  @client.cookie
end

#create_bug(product, component, summary, version, **kwargs) ⇒ Object

Create a bug

Return the new bug ID


198
199
200
201
202
203
204
205
206
207
208
# File 'lib/bicho/client.rb', line 198

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) ⇒ Object

Given a named query's name, runs it on the server


232
233
234
235
236
237
238
# File 'lib/bicho/client.rb', line 232

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) ⇒ Object

Fetches a named query by its full url


258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/bicho/client.rb', line 258

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) ⇒ Array<Attachment>

given bugs.

Payload is lazy-loaded


333
334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/bicho/client.rb', line 333

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) ⇒ Bug

Gets a single bug


296
297
298
# File 'lib/bicho/client.rb', line 296

def get_bug(id)
  get_bugs(id).first
end

#get_bugs(*ids) ⇒ Array<Bug>

Retrieves one or more bugs by id


302
303
304
305
306
307
308
309
310
311
312
313
# File 'lib/bicho/client.rb', line 302

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) ⇒ Array<History>


316
317
318
319
320
321
322
323
324
325
326
327
# File 'lib/bicho/client.rb', line 316

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) ⇒ Object


174
175
176
177
178
179
180
# File 'lib/bicho/client.rb', line 174

def handle_faults(ret)
  if ret.key?('faults')
    ret['faults'].each do |fault|
      logger.error fault
    end
  end
end

#instantiate_plugins!Object

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


161
162
163
164
165
166
167
168
# File 'lib/bicho/client.rb', line 161

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!Object

ruby-load all the files in the plugins directory


150
151
152
153
154
155
156
157
# File 'lib/bicho/client.rb', line 150

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) ⇒ Object

normalize bug ids


245
246
247
248
249
250
251
252
253
# File 'lib/bicho/client.rb', line 245

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) ⇒ Object

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.


216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/bicho/client.rb', line 216

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

#versionObject

Return Bugzilla API version


183
184
185
186
187
# File 'lib/bicho/client.rb', line 183

def version
  ret = @client.call('Bugzilla.version')
  handle_faults(ret)
  ret['version']
end