dm-bugzilla-adapter 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ *~
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ extra_docs = ['README*', 'TODO*', 'CHANGELOG*']
5
+
6
+ task :default => [:test]
7
+
8
+ Rake::TestTask.new do |t|
9
+ t.libs << File.expand_path('../test', __FILE__)
10
+ t.libs << File.expand_path('../', __FILE__)
11
+ t.test_files = FileList['test/test*.rb']
12
+ t.verbose = true
13
+ end
14
+
15
+ begin
16
+ require 'yard'
17
+ YARD::Rake::YardocTask.new(:doc) do |t|
18
+ t.files = ['lib/**/*.rb', *extra_docs]
19
+ t.options = ['--no-private']
20
+ end
21
+ rescue LoadError
22
+ STDERR.puts "Install yard if you want prettier docs"
23
+ require 'rdoc/task'
24
+ Rake::RDocTask.new(:doc) do |rdoc|
25
+ rdoc.rdoc_dir = "doc"
26
+ rdoc.title = "dm-bugzilla-adapter #{DataMapper::Adapters::BugzillaAdapter::VERSION}"
27
+ extra_docs.each { |ex| rdoc.rdoc_files.include ex }
28
+ end
29
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "dm-bugzilla-adapter/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "dm-bugzilla-adapter"
7
+ s.version = DataMapper::Adapters::BugzillaAdapter::VERSION
8
+
9
+ s.platform = Gem::Platform::RUBY
10
+ s.authors = ["Klaus Kämpf"]
11
+ s.email = ["kkaempf@suse.de"]
12
+ s.homepage = ""
13
+ s.summary = %q{A datamapper adapter for Bugzilla (aka bugzilla.novell.com)}
14
+ s.description = %q{Use it in Ruby applications to access Bugzilla}
15
+
16
+ s.add_dependency("bicho", ["~> 0.0.2"])
17
+ s.add_dependency("nokogiri", ["~> 1.4"])
18
+
19
+ s.rubyforge_project = "dm-bugzilla-adapter"
20
+
21
+ s.files = `git ls-files`.split("\n")
22
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
23
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
+ s.require_paths = ["lib"]
25
+ end
@@ -0,0 +1,29 @@
1
+ require 'rubygems'
2
+ require 'dm-core'
3
+ class Bug
4
+ include DataMapper::Resource
5
+
6
+ belongs_to :named_query, :required => false
7
+
8
+ property :id, Integer, :key => true # id [Integer] The numeric id of the bug.
9
+ property :status, String # status [String] The current status of a bug (not including its resolution, if it has one, which is a separate field above).
10
+ property :severity, String # severity [String] The Severity field on a bug.
11
+ property :priority, String # priority [String] The Priority field on a bug.
12
+ property :product, String # product [String] The name of the Product that the bug is in.
13
+ property :component, String # component [String] The name of the Component that the bug is in.
14
+ property :assigned_to, String # assigned_to [String] The login name of a user that a bug is assigned to.
15
+ property :version, String # version [String] The Version field of a bug.
16
+ property :target_milestone, String # target_milestone [String] The Target Milestone field of a bug. Note that even if this Bugzilla does not have the Target Milestone field enabled, you can still search for bugs by Target Milestone. However, it is likely that in that case, most bugs will not have a Target Milestone set (it defaults to "---" when the field isn't enabled).
17
+ property :summary, String # summary [String] Searches for substrings in the single-line Summary field on bugs. If you specify an array, then bugs whose summaries match any of the passed substrings will be returned.
18
+ property :creation_time, DateTime # Searches for bugs that were created at this time or later. May not be an array.
19
+ property :creator, String # The login name of the user who created the bug.
20
+ property :last_change_time, DateTime # Searches for bugs that were modified at this time or later. May not be an array.
21
+ property :op_sys, String # The "Operating System" field of a bug.
22
+ property :platform, String # The Platform (sometimes called "Hardware") field of a bug.
23
+ property :creator, String # The login name of the user who reported the bug.
24
+ property :resolution, String # The current resolution--only set if a bug is closed. You can find open bugs by searching for bugs with an empty resolution.
25
+ property :qa_contact, String # The login name of the bug's QA Contact. Note that even if this Bugzilla does not have the QA Contact field enabled, you can still search for bugs by QA Contact (though it is likely that no bug will have a QA Contact set, if the field is disabled).
26
+ property :url, String # The "URL" field of a bug.
27
+ property :whiteboard, String # Search the "Status Whiteboard" field on bugs for a substring. Works the same as the summary field described above, but searches the Status Whiteboard field.
28
+
29
+ end
@@ -0,0 +1,13 @@
1
+ #
2
+ # named_query.rb
3
+ #
4
+ # A named query in Bugzilla
5
+ #
6
+ require 'rubygems'
7
+ require 'dm-core'
8
+
9
+ class NamedQuery
10
+ include DataMapper::Resource
11
+ property :name, String, :key => true
12
+ has n, :bugs
13
+ end
@@ -0,0 +1,71 @@
1
+ #
2
+ # dm-bugzilla-adapter.rb
3
+ #
4
+ require 'rubygems'
5
+
6
+ # DataMapper
7
+ require 'dm-core'
8
+ require 'dm-core/adapters/abstract_adapter'
9
+
10
+ # bugzilla xmlrpc
11
+ require "bicho"
12
+
13
+ # bugzilla https
14
+ require 'net/https'
15
+ require 'nokogiri'
16
+
17
+ # dm-bugzilla-adapter modules
18
+ require "dm-bugzilla-adapter/create"
19
+ require "dm-bugzilla-adapter/read"
20
+ require "dm-bugzilla-adapter/update"
21
+ require "dm-bugzilla-adapter/delete"
22
+ require "dm-bugzilla-adapter/misc"
23
+
24
+ module DataMapper
25
+ class Property
26
+ autoload :XML, "property/xml"
27
+ end
28
+ end
29
+
30
+
31
+ module DataMapper::Adapters
32
+
33
+ class BugzillaAdapter < AbstractAdapter
34
+
35
+ # @api public
36
+ def initialize(name, options)
37
+ super
38
+ require 'uri'
39
+ @uri = URI.parse(options[:url])
40
+ @client = Bicho::Client.new(@uri)
41
+ end
42
+
43
+ # get Bugs by ids
44
+ def get_bugs(ids)
45
+ bugs = @client.get_bugs(*ids)
46
+ end
47
+
48
+ # run named query, return Array of bug ids
49
+ def named_query(name)
50
+ http = Net::HTTP.new(@uri.host, @uri.port)
51
+ http.use_ssl = true
52
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
53
+ request = Net::HTTP::Get.new("/buglist.cgi?cmdtype=runnamed&namedcmd=#{name}&ctype=atom", { "Cookie" => @client.cookie } )
54
+ response = http.request(request)
55
+ case response
56
+ when Net::HTTPSuccess
57
+ bugs = []
58
+ xml = Nokogiri::XML.parse(response.body)
59
+ xml.root.xpath("//xmlns:entry/xmlns:link/@href", xml.root.namespace).each do |attr|
60
+ uri = URI.parse attr.value
61
+ bugs << uri.query.split("=")[1]
62
+ end
63
+ get_bugs(bugs)
64
+ when Net::HTTPRedirect
65
+ raise "HTTP redirect not supported in named_query"
66
+ else
67
+ response.error!
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,22 @@
1
+ # create.rb
2
+ module DataMapper::Adapters
3
+ class BugzillaAdapter < AbstractAdapter
4
+ def create(resources)
5
+ STDERR.puts "BugzillaAdapter::create"
6
+ table_groups = group_resources_by_table(resources)
7
+ table_groups.each do |table, resources|
8
+ # make
9
+ # class User
10
+ # property :id, Serial
11
+ # end
12
+ # work
13
+ resources.each do |resource|
14
+ initialize_serial(resource,
15
+ worksheet_record_cound(table)+1)
16
+ post_resource_to_worksheet(resource,table)
17
+ end
18
+ end
19
+ resources.size
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # delete.rb
2
+ module DataMapper::Adapters
3
+ class BugzillaAdapter < AbstractAdapter
4
+ # Constructs and executes DELETE statement for given query
5
+ #
6
+ # @param [Collection] collection
7
+ # collection of records to be deleted
8
+ #
9
+ # @return [Integer]
10
+ # the number of records deleted
11
+ #
12
+ # @api semipublic
13
+
14
+ def delete(collection)
15
+ each_resource_with_edit_url(collection) do |resource, edit_url|
16
+ connection.delete(edit_url, 'If-Match' => "*")
17
+ end
18
+ # return count
19
+ collection.size
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,9 @@
1
+ # misc.rb
2
+ module DataMapper::Adapters
3
+ class BugzillaAdapter < AbstractAdapter
4
+ # Auto-migration
5
+ # DataMapper.auto_migrate!
6
+ # or
7
+ # DataMapper.auto_update!
8
+ end
9
+ end
@@ -0,0 +1,176 @@
1
+ # read.rb
2
+ module DataMapper::Adapters
3
+ class BugzillaAdapter < AbstractAdapter
4
+ require 'bugzilla/bug'
5
+ require 'bugzilla/named_query'
6
+ ##
7
+ # Retrieves records for a particular model.
8
+ #
9
+ # @param [DataMapper::Query] query
10
+ # The query used to locate the resources
11
+ #
12
+ # @return [Array]
13
+ # An array of hashes of all of the records for a particular model
14
+ #
15
+ # @api semipublic
16
+ def read(query)
17
+ # STDERR.puts "BugzillaAdapter::read"
18
+ # fields = query.fields
19
+ # STDERR.puts "fields #{fields.inspect}"
20
+ # types = fields.map { |property| property.primitive }
21
+ # STDERR.puts "types #{types.inspect}"
22
+ records = records_for(query)
23
+ # query.filter_records(records)
24
+ end
25
+
26
+ private
27
+ # taken from https://github.com/whoahbot/dm-redis-adapter/
28
+
29
+ def records_for(query)
30
+ # STDERR.puts "records_for(#{query})"
31
+ # STDERR.puts "records_for(#{query.inspect})"
32
+ records = []
33
+ if query.conditions.nil?
34
+ raise "query.conditions.nil?" # FIXME
35
+ else
36
+ query.conditions.operands.each do |operand|
37
+ if operand.is_a?(DataMapper::Query::Conditions::OrOperation)
38
+ operand.each do |op|
39
+ records = records + perform_query(query, op)
40
+ end
41
+ else
42
+ records = perform_query(query, operand)
43
+ end
44
+ end
45
+ end
46
+ records
47
+ end #def
48
+
49
+ ##
50
+ # Find records that match have a matching value
51
+ #
52
+ # @param [DataMapper::Query] query
53
+ # The query used to locate the resources
54
+ #
55
+ # @param [DataMapper::Operation] the operation for the query
56
+ #
57
+ # @return [Array]
58
+ # An array of hashes of all of the records for a particular model
59
+ #
60
+ # @api private
61
+ def perform_query(query, operand)
62
+ # STDERR.puts "perform_query(q '#{query}', op '#{operand}')"
63
+ records = []
64
+
65
+ if operand.is_a?(DataMapper::Query::Conditions::NotOperation)
66
+ # STDERR.puts "operand is a NOT operation"
67
+ subject = operand.first.subject
68
+ value = operand.first.value
69
+ elsif operand.subject.is_a?(DataMapper::Associations::ManyToOne::Relationship)
70
+ # STDERR.puts "operand subject is a many-to-one relation: '#{operand.subject.inspect}'"
71
+ if operand.subject.parent_model == NamedQuery && operand.subject.child_model == Bug
72
+ name = operand.value[operand.subject.parent_key.first.name]
73
+ bugs = named_query(name)
74
+ bugs.each do |bug|
75
+ records << bug_to_record(query.model, bug)
76
+ end
77
+ return records
78
+ else
79
+ raise "Unknown parent (#{operand.subject.parent_model}) child (#{operand.subject.child_model}) relation"
80
+ end
81
+ else
82
+ # STDERR.puts "operand is normal"
83
+ subject = operand.subject
84
+ value = operand.value
85
+ end
86
+
87
+ if subject.is_a?(DataMapper::Associations::ManyToOne::Relationship)
88
+ # STDERR.puts "subject is a many-to-one relation"
89
+ subject = subject.child_key.first
90
+ end
91
+
92
+ # typical queries
93
+ #
94
+
95
+ # STDERR.puts "perform_query(subject '#{subject.inspect}', value '#{value.inspect}')"
96
+ case query.model.name
97
+ when "Bug"
98
+ if query.model.key.include?(subject)
99
+ # get single <bug>
100
+ bugs = get_bugs(value)
101
+ records << bug_to_record(query.model, bugs.first)
102
+ else
103
+ raise "Unknown query for bug"
104
+ end
105
+ when "NamedQuery"
106
+ records << { subject.name.to_s => value }
107
+ # be lazy: do the actual named query when NamedQuery#bugs gets accessed
108
+ else
109
+ raise "Unsupported model '#{query.model.name}'"
110
+ end
111
+
112
+ records
113
+ end # def
114
+
115
+ ##
116
+ # Convert Bicho::Bug into record (as hash of key/value pairs)
117
+ #
118
+ # @return [Hash]
119
+ # A hash of all of the properties for a particular record
120
+ #
121
+ # assigned_to [String] The login name of a user that a bug is assigned to.
122
+ # component [String] The name of the Component that the bug is in.
123
+ # creation_time [DateTime] Searches for bugs that were created at this time or later. May not be an array.
124
+ # creator [String] The login name of the user who created the bug.
125
+ # id [Integer] The numeric id of the bug.
126
+ # last_change_time [DateTime] Searches for bugs that were modified at this time or later. May not be an array.
127
+ # limit [Integer] Limit the number of results returned to int records.
128
+ # offset [Integer] Used in conjunction with the limit argument, offset defines the starting position for the search. For example, given a search that would return 100 bugs, setting limit to 10 and offset to 10 would return bugs 11 through 20 from the set of 100.
129
+ # op_sys [String] The "Operating System" field of a bug.
130
+ # platform [String] The Platform (sometimes called "Hardware") field of a bug.
131
+ # priority [String] The Priority field on a bug.
132
+ # product [String] The name of the Product that the bug is in.
133
+ # creator [String] The login name of the user who reported the bug.
134
+ # resolution [String] The current resolution--only set if a bug is closed. You can find open bugs by searching for bugs with an empty resolution.
135
+ # severity [String] The Severity field on a bug.
136
+ # status [String] The current status of a bug (not including its resolution, if it has one, which is a separate field above).
137
+ # summary [String] Searches for substrings in the single-line Summary field on bugs. If you specify an array, then bugs whose summaries match any of the passed substrings will be returned.
138
+ # target_milestone [String] The Target Milestone field of a bug. Note that even if this Bugzilla does not have the Target Milestone field enabled, you can still search for bugs by Target Milestone. However, it is likely that in that case, most bugs will not have a Target Milestone set (it defaults to "---" when the field isn't enabled).
139
+ # qa_contact [String] The login name of the bug's QA Contact. Note that even if this Bugzilla does not have the QA Contact field enabled, you can still search for bugs by QA Contact (though it is likely that no bug will have a QA Contact set, if the field is disabled).
140
+ # url [String] The "URL" field of a bug.
141
+ # version [String] The Version field of a bug.
142
+ # whiteboard [String] Search the "Status Whiteboard" field on bugs for a substring. Works the same as the summary field described above, but searches the Status Whiteboard field.
143
+ #
144
+ # @api private
145
+ def bug_to_record(model, bug)
146
+ # STDERR.puts "bug_to_record #{bug.inspect}"
147
+ record = { }
148
+ model.properties.each do |p|
149
+ v = case p.name
150
+ when :version, :target_milestone, :op_sys, :qa_contact : bug['internals', p.name]
151
+ when :platform : bug['internals', 'rep_platform']
152
+ when :creator : bug['internals', 'reporter_id']
153
+ when :whiteboard : bug['internals', 'status_whiteboard']
154
+ else
155
+ bug[p.name]
156
+ end
157
+ # STDERR.puts "#{v.inspect} -> #{p.inspect}"
158
+ if v
159
+ case p
160
+ when DataMapper::Property::String: v = v.to_s
161
+ when DataMapper::Property::Integer: v = v.to_i
162
+ when DataMapper::Property::DateTime
163
+ # STDERR.puts "DateTime #{v.to_a.inspect}"
164
+ v = DateTime.civil(*v.to_a)
165
+ # STDERR.puts "DateTime #{v}"
166
+ else
167
+ raise "*** Unsupported property type #{p.inspect}"
168
+ end
169
+ end
170
+ record[p.name.to_s] = v
171
+ end
172
+ record
173
+ end
174
+
175
+ end # class
176
+ end # module
@@ -0,0 +1,24 @@
1
+ # update.rb
2
+ module DataMapper::Adapters
3
+ class BugzillaAdapter < AbstractAdapter
4
+ # Constructs and executes UPDATE statement for given
5
+ # attributes and a query
6
+ #
7
+ # @param [Hash(Property => Object)] attributes
8
+ # hash of attribute values to set, keyed by Property
9
+ # @param [Collection] collection
10
+ # collection of records to be updated
11
+ #
12
+ # @return [Integer]
13
+ # the number of records updated
14
+ #
15
+ # @api semipublic
16
+ def update(attributes, collection)
17
+ each_resource_with_edit_url(collection) do |resource, edit_url|
18
+ put_updated_resource(edit_url, resource)
19
+ end
20
+ # return count
21
+ collection.size
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,8 @@
1
+ require 'dm-core'
2
+ require 'dm-core/adapters/abstract_adapter'
3
+
4
+ module DataMapper::Adapters
5
+ class BugzillaAdapter < AbstractAdapter
6
+ VERSION = "0.0.1"
7
+ end
8
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,8 @@
1
+ $: << File.join(File.dirname(__FILE__), "..", "lib")
2
+ require 'test/unit'
3
+ require 'dm-bugzilla-adapter'
4
+
5
+ if ENV["DEBUG"]
6
+ Bicho::Logging.logger = Logger.new(STDERR)
7
+ Bicho::Logging.logger.level = Logger::DEBUG
8
+ end
@@ -0,0 +1,19 @@
1
+ require File.join(File.dirname(__FILE__), 'helper')
2
+
3
+ class Get_bug_test < Test::Unit::TestCase
4
+
5
+ def test_get_bug
6
+ DataMapper::Logger.new(STDOUT, :debug)
7
+ keeper = DataMapper.setup(:default,
8
+ :adapter => 'bugzilla',
9
+ :url => 'https://bugzilla.novell.com')
10
+
11
+ require 'bugzilla/bug'
12
+ DataMapper.finalize
13
+
14
+ bug = Bug.get(424242)
15
+ assert bug
16
+ puts "Bug #{bug.inspect}"
17
+ end
18
+
19
+ end
@@ -0,0 +1,21 @@
1
+ require File.join(File.dirname(__FILE__), 'helper')
2
+
3
+ class Run_named_query_test < Test::Unit::TestCase
4
+
5
+ def test_run_named_query
6
+ DataMapper::Logger.new(STDOUT, :debug)
7
+ keeper = DataMapper.setup(:default,
8
+ :adapter => 'bugzilla',
9
+ :url => 'https://bugzilla.novell.com')
10
+
11
+ require 'bugzilla/named_query'
12
+ DataMapper.finalize
13
+
14
+ named_query = NamedQuery.get("Manager")
15
+ assert named_query
16
+ bugs = named_query.bugs
17
+ assert bugs
18
+ puts "Query '#{named_query.name}' results in #{bugs.size} bugs"
19
+ end
20
+
21
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dm-bugzilla-adapter
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - "Klaus K\xC3\xA4mpf"
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-09-27 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: bicho
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 27
30
+ segments:
31
+ - 0
32
+ - 0
33
+ - 2
34
+ version: 0.0.2
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: nokogiri
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ hash: 7
46
+ segments:
47
+ - 1
48
+ - 4
49
+ version: "1.4"
50
+ type: :runtime
51
+ version_requirements: *id002
52
+ description: Use it in Ruby applications to access Bugzilla
53
+ email:
54
+ - kkaempf@suse.de
55
+ executables: []
56
+
57
+ extensions: []
58
+
59
+ extra_rdoc_files: []
60
+
61
+ files:
62
+ - .gitignore
63
+ - Rakefile
64
+ - dm-bugzilla-adapter.gemspec
65
+ - lib/bugzilla/bug.rb
66
+ - lib/bugzilla/named_query.rb
67
+ - lib/dm-bugzilla-adapter.rb
68
+ - lib/dm-bugzilla-adapter/create.rb
69
+ - lib/dm-bugzilla-adapter/delete.rb
70
+ - lib/dm-bugzilla-adapter/misc.rb
71
+ - lib/dm-bugzilla-adapter/read.rb
72
+ - lib/dm-bugzilla-adapter/update.rb
73
+ - lib/dm-bugzilla-adapter/version.rb
74
+ - test/helper.rb
75
+ - test/test_get_bug.rb
76
+ - test/test_run_named_query.rb
77
+ has_rdoc: true
78
+ homepage: ""
79
+ licenses: []
80
+
81
+ post_install_message:
82
+ rdoc_options: []
83
+
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ hash: 3
92
+ segments:
93
+ - 0
94
+ version: "0"
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ hash: 3
101
+ segments:
102
+ - 0
103
+ version: "0"
104
+ requirements: []
105
+
106
+ rubyforge_project: dm-bugzilla-adapter
107
+ rubygems_version: 1.5.2
108
+ signing_key:
109
+ specification_version: 3
110
+ summary: A datamapper adapter for Bugzilla (aka bugzilla.novell.com)
111
+ test_files:
112
+ - test/helper.rb
113
+ - test/test_get_bug.rb
114
+ - test/test_run_named_query.rb