feta 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +8 -0
- data/Gemfile +4 -0
- data/README.rdoc +80 -0
- data/Rakefile +33 -0
- data/feta.gemspec +23 -0
- data/lib/feta.rb +33 -0
- data/lib/feta/client.rb +119 -0
- data/lib/feta/common_client.rb +56 -0
- data/lib/feta/feature.rb +101 -0
- data/lib/feta/logging.rb +43 -0
- data/lib/feta/plugins/suse.rb +71 -0
- data/lib/feta/query.rb +100 -0
- data/lib/feta/version.rb +3 -0
- data/test/helper.rb +8 -0
- data/test/test_query.rb +32 -0
- metadata +126 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.rdoc
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
|
|
2
|
+
= Feta
|
|
3
|
+
|
|
4
|
+
* http://github.com/dmacvicar/feta
|
|
5
|
+
|
|
6
|
+
== Introduction
|
|
7
|
+
|
|
8
|
+
Library to access FATE
|
|
9
|
+
|
|
10
|
+
Note: The API is still a draft. Be careful.
|
|
11
|
+
|
|
12
|
+
== Features
|
|
13
|
+
|
|
14
|
+
* Simple queries by actor, roles.
|
|
15
|
+
* For SUSE's FATE server, you can use the web url and it will use the
|
|
16
|
+
right keeper server and take your .oscrc credentials.
|
|
17
|
+
|
|
18
|
+
More will be coming, the focus is on providing an easy API. You can always
|
|
19
|
+
get anything else by just doing xpath queries.
|
|
20
|
+
|
|
21
|
+
== Example (API)
|
|
22
|
+
|
|
23
|
+
=== Client API
|
|
24
|
+
|
|
25
|
+
client = Feta::Client.new("https://user:password@keeper.host.com")
|
|
26
|
+
query = Feta::Query.new.only_actor("me@mail.com").only_product("openSUSE-11.4")
|
|
27
|
+
features = client.search(query)
|
|
28
|
+
|
|
29
|
+
=== ActiveRecord-like API
|
|
30
|
+
|
|
31
|
+
Feta.client = Feta::Client.new("https://user:password@keeper.host.com")
|
|
32
|
+
Feta::Feature.query.only_actor("me@mail.com").only_product("openSUSE-11.4").each do |feature|
|
|
33
|
+
# .. do something with feature
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
== Extending Feta
|
|
37
|
+
|
|
38
|
+
Feta will look in the following places for plugins:
|
|
39
|
+
|
|
40
|
+
* lib/feta/plugins in the gem path
|
|
41
|
+
* $HOME/.local/share/feta/plugins
|
|
42
|
+
|
|
43
|
+
A plugin is a class under Feta::Plugins, only one hook is available for now:
|
|
44
|
+
|
|
45
|
+
class Feta::Plugins::MyPlugin
|
|
46
|
+
|
|
47
|
+
def to_s
|
|
48
|
+
self.class.to_s
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def order
|
|
52
|
+
# load always last
|
|
53
|
+
(1.0/0)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def initialize_hook(url, logger, headers)
|
|
57
|
+
# modify the url, add headers or configure the logger
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
== SUSE plugin
|
|
62
|
+
|
|
63
|
+
One plugin is included which takes the credentials from .oscrc if using the
|
|
64
|
+
SUSE internal keeper server.
|
|
65
|
+
|
|
66
|
+
=== Roadmap
|
|
67
|
+
|
|
68
|
+
* Change query methods to make them more similar to the Bicho library
|
|
69
|
+
* Add more data to the Feature class
|
|
70
|
+
* Allow to search by an arbitrary xpath expression
|
|
71
|
+
|
|
72
|
+
== Authors
|
|
73
|
+
|
|
74
|
+
* Duncan Mac-Vicar P. <dmacvicar@suse.de>
|
|
75
|
+
|
|
76
|
+
== License
|
|
77
|
+
|
|
78
|
+
Copyright (c) 2011 SUSE LINUX Products GmbH.
|
|
79
|
+
|
|
80
|
+
Bicho is licensed under the MIT license. See MIT-LICENSE for details.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
$:.push(File.join(File.dirname(__FILE__), 'lib'))
|
|
2
|
+
require 'bundler'
|
|
3
|
+
Bundler::GemHelper.install_tasks
|
|
4
|
+
require 'feta/version'
|
|
5
|
+
require 'rake/testtask'
|
|
6
|
+
|
|
7
|
+
extra_docs = ['README*', 'TODO*', 'CHANGELOG*']
|
|
8
|
+
|
|
9
|
+
task :default => [:test]
|
|
10
|
+
|
|
11
|
+
Rake::TestTask.new do |t|
|
|
12
|
+
t.libs << File.expand_path('../test', __FILE__)
|
|
13
|
+
t.libs << File.expand_path('../', __FILE__)
|
|
14
|
+
t.test_files = FileList['test/test*.rb']
|
|
15
|
+
t.verbose = true
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
begin
|
|
19
|
+
require 'yard'
|
|
20
|
+
YARD::Rake::YardocTask.new(:doc) do |t|
|
|
21
|
+
t.files = ['lib/**/*.rb', *extra_docs]
|
|
22
|
+
t.options = ['--no-private']
|
|
23
|
+
end
|
|
24
|
+
rescue LoadError
|
|
25
|
+
STDERR.puts "Install yard if you want prettier docs"
|
|
26
|
+
require 'rdoc/task'
|
|
27
|
+
Rake::RDocTask.new(:doc) do |rdoc|
|
|
28
|
+
rdoc.rdoc_dir = "doc"
|
|
29
|
+
rdoc.title = "bicho #{Feta::VERSION}"
|
|
30
|
+
extra_docs.each { |ex| rdoc.rdoc_files.include ex }
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
data/feta.gemspec
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
|
3
|
+
require "feta/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |s|
|
|
6
|
+
s.name = "feta"
|
|
7
|
+
s.version = Feta::VERSION
|
|
8
|
+
s.platform = Gem::Platform::RUBY
|
|
9
|
+
s.authors = ["Duncan Mac-Vicar P."]
|
|
10
|
+
s.email = ["dmacvicar@suse.de"]
|
|
11
|
+
s.homepage = "http://github.com/dmacvicar/feta"
|
|
12
|
+
s.summary = %q{Library to access FATE features}
|
|
13
|
+
s.description = %q{Feta is a library to access FATE using the keeper API}
|
|
14
|
+
|
|
15
|
+
s.add_dependency("rest-client", ["~> 1.6"])
|
|
16
|
+
s.add_dependency("nokogiri", ["~> 1.5"])
|
|
17
|
+
s.add_dependency("inifile", ["~> 0.4.1"])
|
|
18
|
+
|
|
19
|
+
s.files = `git ls-files`.split("\n")
|
|
20
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
21
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
22
|
+
s.require_paths = ["lib"]
|
|
23
|
+
end
|
data/lib/feta.rb
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Copyright (c) 2011 SUSE LINUX Products GmbH
|
|
3
|
+
#
|
|
4
|
+
# Author: Duncan Mac-Vicar P. <dmacvicar@suse.de>
|
|
5
|
+
#
|
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
|
7
|
+
# a copy of this software and associated documentation files (the
|
|
8
|
+
# "Software"), to deal in the Software without restriction, including
|
|
9
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
|
10
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
11
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
|
12
|
+
# the following conditions:
|
|
13
|
+
#
|
|
14
|
+
# The above copyright notice and this permission notice shall be
|
|
15
|
+
# included in all copies or substantial portions of the Software.
|
|
16
|
+
#
|
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
18
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
19
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
20
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
21
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
22
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
23
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
24
|
+
#++
|
|
25
|
+
require 'rubygems'
|
|
26
|
+
require 'feta/version'
|
|
27
|
+
require 'feta/logging'
|
|
28
|
+
require 'feta/client'
|
|
29
|
+
require 'feta/feature'
|
|
30
|
+
|
|
31
|
+
module Feta
|
|
32
|
+
# Your code goes here...
|
|
33
|
+
end
|
data/lib/feta/client.rb
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Copyright (c) 2011 SUSE LINUX Products GmbH
|
|
3
|
+
#
|
|
4
|
+
# Author: Duncan Mac-Vicar P. <dmacvicar@suse.de>
|
|
5
|
+
#
|
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
|
7
|
+
# a copy of this software and associated documentation files (the
|
|
8
|
+
# "Software"), to deal in the Software without restriction, including
|
|
9
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
|
10
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
11
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
|
12
|
+
# the following conditions:
|
|
13
|
+
#
|
|
14
|
+
# The above copyright notice and this permission notice shall be
|
|
15
|
+
# included in all copies or substantial portions of the Software.
|
|
16
|
+
#
|
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
18
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
19
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
20
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
21
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
22
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
23
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
24
|
+
#++
|
|
25
|
+
require 'rest_client'
|
|
26
|
+
require 'nokogiri'
|
|
27
|
+
require 'feta/logging'
|
|
28
|
+
|
|
29
|
+
module Feta
|
|
30
|
+
|
|
31
|
+
module Plugins
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class Client
|
|
35
|
+
|
|
36
|
+
include ::Feta::Logging
|
|
37
|
+
|
|
38
|
+
KEEPER_XML_NS = "http://inttools.suse.de/sxkeeper/schema/keeper"
|
|
39
|
+
|
|
40
|
+
attr_accessor :url
|
|
41
|
+
|
|
42
|
+
# Construct a FATE client
|
|
43
|
+
#
|
|
44
|
+
# @param url [String, URI] URL of the FATE server
|
|
45
|
+
#
|
|
46
|
+
# @note Keeper behind iChain is not yet supported
|
|
47
|
+
#
|
|
48
|
+
def initialize(url)
|
|
49
|
+
RestClient.log = Feta::Logging.logger
|
|
50
|
+
url = URI.parse(url) if not url.is_a?(URI)
|
|
51
|
+
@url = url.clone
|
|
52
|
+
@headers = Hash.new
|
|
53
|
+
|
|
54
|
+
# Scan plugins
|
|
55
|
+
[ File.join(ENV['HOME'], '.local/share/feta/plugins/*.rb'),
|
|
56
|
+
File.join(File.dirname(__FILE__), 'plugins', '*.rb') ].each do |plugin_glob|
|
|
57
|
+
Dir.glob(plugin_glob).each do |plugin|
|
|
58
|
+
logger.debug("Loading file: #{plugin}")
|
|
59
|
+
load plugin
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
#instantiate plugins
|
|
64
|
+
pl_instances = []
|
|
65
|
+
::Feta::Plugins.constants.each do |cnt|
|
|
66
|
+
pl_class = ::Feta::Plugins.const_get(cnt)
|
|
67
|
+
pl_instances << pl_class.new
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
pl_instances.sort{|x,y| x.order <=> y.order}.each do |pl_instance|
|
|
71
|
+
logger.debug("Loaded: #{pl_instance}")
|
|
72
|
+
pl_instance.initialize_hook(url, logger, @headers)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
@keeper_url = url
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Search for features
|
|
79
|
+
#
|
|
80
|
+
# @param query [Query] Query specifying the search criteria
|
|
81
|
+
# @return [Array<Feature>] List of features
|
|
82
|
+
def search(query)
|
|
83
|
+
url_query = "?query=#{CGI.escape(query.to_xquery)}"
|
|
84
|
+
url = URI.parse("#{@keeper_url}/feature#{url_query}").to_s
|
|
85
|
+
|
|
86
|
+
xml = RestClient.get(url, @headers).body
|
|
87
|
+
features = []
|
|
88
|
+
doc = Nokogiri::XML(xml)
|
|
89
|
+
doc.xpath('//feature', 'k' => KEEPER_XML_NS).each do |feat_element|
|
|
90
|
+
feature = Feature.new(self)
|
|
91
|
+
feature.feature_id = feat_element.xpath('./@k:id', 'k' => KEEPER_XML_NS).first.value
|
|
92
|
+
feature.title = feat_element.xpath('./title', 'k' => KEEPER_XML_NS).first.content
|
|
93
|
+
feat_element.xpath('./actor', 'k' => KEEPER_XML_NS).each do |actor|
|
|
94
|
+
if actor.xpath('./role', 'k' => KEEPER_XML_NS).first.content == "infoprovider"
|
|
95
|
+
feature.infoprovider = actor.xpath('.//email', 'k' => KEEPER_XML_NS).first.content
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
feat_element.xpath('./productcontext', 'k' => KEEPER_XML_NS).each do |ctx_element|
|
|
99
|
+
ctx = Feature::ProductContext.new
|
|
100
|
+
product = ctx_element.xpath('./product/name').first.content
|
|
101
|
+
ctx.status = ctx_element.xpath('./status').children.select {|x| x.element?}.first.name.to_sym
|
|
102
|
+
feature.product_contexts[product] = ctx
|
|
103
|
+
|
|
104
|
+
# Priorities
|
|
105
|
+
ctx_element.xpath('./priority').each do |prio_element|
|
|
106
|
+
prio = prio_element.children.select(&:element?).first.name.to_sym
|
|
107
|
+
owner = prio_element.xpath('./owner/role').first.content.to_sym
|
|
108
|
+
ctx.priorities[owner] = prio
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
yield feature if block_given?
|
|
112
|
+
features << feature
|
|
113
|
+
end
|
|
114
|
+
features
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Copyright (c) 2011 SUSE LINUX Products GmbH
|
|
3
|
+
#
|
|
4
|
+
# Author: Duncan Mac-Vicar P. <dmacvicar@suse.de>
|
|
5
|
+
#
|
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
|
7
|
+
# a copy of this software and associated documentation files (the
|
|
8
|
+
# "Software"), to deal in the Software without restriction, including
|
|
9
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
|
10
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
11
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
|
12
|
+
# the following conditions:
|
|
13
|
+
#
|
|
14
|
+
# The above copyright notice and this permission notice shall be
|
|
15
|
+
# included in all copies or substantial portions of the Software.
|
|
16
|
+
#
|
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
18
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
19
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
20
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
21
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
22
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
23
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
24
|
+
#++
|
|
25
|
+
|
|
26
|
+
module Feta
|
|
27
|
+
# This module allows the [Feature] and other
|
|
28
|
+
# classes to offer an ActiveRecord like query
|
|
29
|
+
# API by allowing to set a default [Client]
|
|
30
|
+
# to make operations.
|
|
31
|
+
module CommonClient
|
|
32
|
+
def self.common_client=(client)
|
|
33
|
+
@common_client = client
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.common_client
|
|
37
|
+
@common_client || (raise "No common client set")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def common_client
|
|
41
|
+
CommonClient.common_client
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Sets the common client to be used by
|
|
46
|
+
# the library
|
|
47
|
+
def self.client=(client)
|
|
48
|
+
CommonClient.common_client = client
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Current client used by the library
|
|
52
|
+
def self.client
|
|
53
|
+
CommonClient.common_client
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
end
|
data/lib/feta/feature.rb
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
require 'cgi'
|
|
3
|
+
require 'rubygems'
|
|
4
|
+
require "nokogiri"
|
|
5
|
+
require 'open-uri'
|
|
6
|
+
require 'uri'
|
|
7
|
+
require 'feta/query'
|
|
8
|
+
|
|
9
|
+
# ?query=/feature[actor[person/email='dmacvicar@novell.com' and role='infoprovider']]
|
|
10
|
+
|
|
11
|
+
module Feta
|
|
12
|
+
#
|
|
13
|
+
#
|
|
14
|
+
# Small DSL into fate features
|
|
15
|
+
#
|
|
16
|
+
# Some examples:
|
|
17
|
+
# Feature.query.only_actor(x).with_role.each do {|feature| ... }
|
|
18
|
+
#
|
|
19
|
+
# Features responds to the following issue methods, shared with
|
|
20
|
+
# bugs:
|
|
21
|
+
# id, title, url
|
|
22
|
+
#
|
|
23
|
+
class Feature
|
|
24
|
+
|
|
25
|
+
attr_accessor :feature_id, :title, :products, :developers, :infoprovider
|
|
26
|
+
|
|
27
|
+
# A feature different statuses and other properties with regard
|
|
28
|
+
# to different products
|
|
29
|
+
#
|
|
30
|
+
# @return [Hash] key is the product, value is a {ProductContext}
|
|
31
|
+
attr_accessor :product_contexts
|
|
32
|
+
|
|
33
|
+
class ProductContext
|
|
34
|
+
|
|
35
|
+
def initialize
|
|
36
|
+
@priorities = Hash.new
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Status of the feature for a specific product
|
|
40
|
+
# (+:evaluation+, +:implementation+, etc)
|
|
41
|
+
attr_accessor :status
|
|
42
|
+
|
|
43
|
+
# If status is +:evaluation+ then the status has a owner
|
|
44
|
+
# (+:teamleader+, +:projectmanager+, etc)
|
|
45
|
+
attr_accessor :status_owner
|
|
46
|
+
|
|
47
|
+
# For an specific product, a feature has
|
|
48
|
+
# @return [Hash] key is the owner, value is the priority
|
|
49
|
+
attr_accessor :priorities
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def id
|
|
53
|
+
feature_id
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def initialize(client)
|
|
57
|
+
@client = client
|
|
58
|
+
@product_contexts = Hash.new
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def self.find(what=nil)
|
|
62
|
+
return Query.new.with_id(what).each.to_a.first if what
|
|
63
|
+
Query.new
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.query
|
|
67
|
+
Query.new
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def feature_url
|
|
71
|
+
"#{@client.url}/#{id}"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# @param product [String] Product name
|
|
75
|
+
# @return [Symbol] status for +product+ or nil
|
|
76
|
+
# if the product is not in the feature
|
|
77
|
+
def status_for_product(product)
|
|
78
|
+
product_contexts[product.to_s].status
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# @return [Symbol] the priority of this feature
|
|
82
|
+
# given by +role+ for +product+
|
|
83
|
+
def priority_for_product_and_role(product, role)
|
|
84
|
+
product_contexts[product.to_s].priorities[role.to_sym]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# @return [Fixnum] numeric priority for this feature
|
|
88
|
+
# use it for sorting
|
|
89
|
+
def numeric_priority_for_product_and_role(product, role)
|
|
90
|
+
case priority_for_product_and_role(product, role)
|
|
91
|
+
when :neutral then 0
|
|
92
|
+
when :desirable then 1
|
|
93
|
+
when :important then 2
|
|
94
|
+
when :mandatory then 3
|
|
95
|
+
else -1
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
end # module feta
|
data/lib/feta/logging.rb
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Copyright (c) 2011 SUSE LINUX Products GmbH
|
|
3
|
+
#
|
|
4
|
+
# Author: Duncan Mac-Vicar P. <dmacvicar@suse.de>
|
|
5
|
+
#
|
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
|
7
|
+
# a copy of this software and associated documentation files (the
|
|
8
|
+
# "Software"), to deal in the Software without restriction, including
|
|
9
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
|
10
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
11
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
|
12
|
+
# the following conditions:
|
|
13
|
+
#
|
|
14
|
+
# The above copyright notice and this permission notice shall be
|
|
15
|
+
# included in all copies or substantial portions of the Software.
|
|
16
|
+
#
|
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
18
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
19
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
20
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
21
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
22
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
23
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
24
|
+
#++
|
|
25
|
+
require 'logger'
|
|
26
|
+
|
|
27
|
+
module Feta
|
|
28
|
+
module Logging
|
|
29
|
+
|
|
30
|
+
def self.logger=(logger)
|
|
31
|
+
@logger = logger
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.logger
|
|
35
|
+
@logger ||= Logger.new('/dev/null')
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def logger
|
|
39
|
+
Logging.logger
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Copyright (c) 2011 SUSE LINUX Products GmbH
|
|
3
|
+
#
|
|
4
|
+
# Author: Duncan Mac-Vicar P. <dmacvicar@suse.de>
|
|
5
|
+
#
|
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
|
7
|
+
# a copy of this software and associated documentation files (the
|
|
8
|
+
# "Software"), to deal in the Software without restriction, including
|
|
9
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
|
10
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
11
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
|
12
|
+
# the following conditions:
|
|
13
|
+
#
|
|
14
|
+
# The above copyright notice and this permission notice shall be
|
|
15
|
+
# included in all copies or substantial portions of the Software.
|
|
16
|
+
#
|
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
18
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
19
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
20
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
21
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
22
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
23
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
24
|
+
#++
|
|
25
|
+
require 'inifile'
|
|
26
|
+
require 'uri'
|
|
27
|
+
|
|
28
|
+
module Feta
|
|
29
|
+
module Plugins
|
|
30
|
+
|
|
31
|
+
# Rewrites the fate.novell.com url to the
|
|
32
|
+
# right keeper server and inserts
|
|
33
|
+
# the osc credentials from .oscrc
|
|
34
|
+
#
|
|
35
|
+
class SUSE
|
|
36
|
+
|
|
37
|
+
OSCRC_CREDENTIALS = "https://api.opensuse.org"
|
|
38
|
+
|
|
39
|
+
def to_s
|
|
40
|
+
self.class.to_s
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.oscrc_credentials
|
|
44
|
+
oscrc = IniFile.new(File.join(ENV['HOME'], '.oscrc'))
|
|
45
|
+
if oscrc.has_section?(OSCRC_CREDENTIALS)
|
|
46
|
+
user = oscrc[OSCRC_CREDENTIALS]['user']
|
|
47
|
+
pass = oscrc[OSCRC_CREDENTIALS]['pass']
|
|
48
|
+
if user && pass
|
|
49
|
+
return {:user => user, :password => pass}
|
|
50
|
+
else
|
|
51
|
+
raise "No .oscrc credentials for bnc"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def order
|
|
57
|
+
1
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def initialize_hook(url, logger, headers)
|
|
61
|
+
return if not url.host.include?('fate.novell.com')
|
|
62
|
+
auth = SUSE.oscrc_credentials
|
|
63
|
+
url.host = url.host.gsub(/fate\.novell.com/, 'keeper.novell.com')
|
|
64
|
+
url.user = auth[:user]
|
|
65
|
+
url.password = auth[:password]
|
|
66
|
+
url.path = '/sxkeeper' if url.path.empty?
|
|
67
|
+
logger.debug("#{self} : Rewrote url to '#{url.to_s.gsub(/#{url.user}:#{url.password}/, "USER:PASS")}'")
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
data/lib/feta/query.rb
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Copyright (c) 2011 SUSE LINUX Products GmbH
|
|
3
|
+
#
|
|
4
|
+
# Author: Duncan Mac-Vicar P. <dmacvicar@suse.de>
|
|
5
|
+
#
|
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
|
7
|
+
# a copy of this software and associated documentation files (the
|
|
8
|
+
# "Software"), to deal in the Software without restriction, including
|
|
9
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
|
10
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
11
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
|
12
|
+
# the following conditions:
|
|
13
|
+
#
|
|
14
|
+
# The above copyright notice and this permission notice shall be
|
|
15
|
+
# included in all copies or substantial portions of the Software.
|
|
16
|
+
#
|
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
18
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
19
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
20
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
21
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
22
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
23
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
24
|
+
#++
|
|
25
|
+
require 'feta/common_client'
|
|
26
|
+
|
|
27
|
+
module Feta
|
|
28
|
+
|
|
29
|
+
# A query to the keeper server
|
|
30
|
+
class Query
|
|
31
|
+
|
|
32
|
+
include Enumerable
|
|
33
|
+
|
|
34
|
+
def initialize
|
|
35
|
+
@products = []
|
|
36
|
+
@actor = nil
|
|
37
|
+
@role = nil
|
|
38
|
+
@feature_id = nil
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Queries only features where this actor is present
|
|
42
|
+
# @param actor [String] Actor email
|
|
43
|
+
def only_actor(actor)
|
|
44
|
+
@actor = actor
|
|
45
|
+
self
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Restrict the actor's role to +role+
|
|
49
|
+
# @param role [String] Actor's role
|
|
50
|
+
def with_role(role)
|
|
51
|
+
@role = role
|
|
52
|
+
self
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Queries only features for these product
|
|
56
|
+
# @param product [String] Product name
|
|
57
|
+
def only_product(product)
|
|
58
|
+
only_products([product])
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Queries only features for these products
|
|
62
|
+
# @param products [Array<String>] Product name list
|
|
63
|
+
def only_products(products)
|
|
64
|
+
@products = []
|
|
65
|
+
@products = products.flatten
|
|
66
|
+
self
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def with_id(id)
|
|
70
|
+
@feature_id = id
|
|
71
|
+
self
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Converts the query to an xpath expression
|
|
75
|
+
# @return [String] xpath expression representing the query
|
|
76
|
+
# @private
|
|
77
|
+
def to_xquery
|
|
78
|
+
id_query = @feature_id ? "@k:id=#{@feature_id}" : ""
|
|
79
|
+
role_query = @role ? " and role='#{@role}'" : ""
|
|
80
|
+
actor_query = @actor ? "actor[person/email='#{@actor}'#{role_query}]" : ""
|
|
81
|
+
prods = @products.collect {|p| "product/name='#{p}'"}.join(" or ")
|
|
82
|
+
product_query = (!@products.empty?) ? "productcontext[#{prods}]" : ""
|
|
83
|
+
conditions = [id_query, actor_query, product_query].reject{ |x| x.empty? }.join(' and ')
|
|
84
|
+
query = "/feature[#{conditions}]"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Iterates through the result of the current query.
|
|
88
|
+
#
|
|
89
|
+
# @note Requires Feta.client to be set
|
|
90
|
+
#
|
|
91
|
+
# @yield [Feta::Feature]
|
|
92
|
+
def each
|
|
93
|
+
ret = Feta.client.search(self)
|
|
94
|
+
return ret.each if not block_given?
|
|
95
|
+
ret.each { |feature| yield feature }
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
end
|
data/lib/feta/version.rb
ADDED
data/test/helper.rb
ADDED
data/test/test_query.rb
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'helper')
|
|
2
|
+
|
|
3
|
+
class Query_test < Test::Unit::TestCase
|
|
4
|
+
|
|
5
|
+
include ::Feta::Logging
|
|
6
|
+
|
|
7
|
+
def test_active_record_style
|
|
8
|
+
|
|
9
|
+
# No client set yet
|
|
10
|
+
assert_raise RuntimeError do
|
|
11
|
+
Feta::Feature.query.only_actor("dmacvicar@novell.com").with_role("teamlead").each do |feature|
|
|
12
|
+
puts feature
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
Feta.client = Feta::Client.new("https://fate.novell.com")
|
|
17
|
+
query = Feta::Feature.query.only_actor("dmacvicar@novell.com").only_product("openSUSE-11.4")
|
|
18
|
+
|
|
19
|
+
ret = query.each.to_a
|
|
20
|
+
|
|
21
|
+
assert ret.collect(&:id).include?("303793")
|
|
22
|
+
|
|
23
|
+
feature = ret.collect.select {|x| x.id == "303793"}.first
|
|
24
|
+
|
|
25
|
+
assert_equal :rejected, feature.status_for_product("openSUSE-11.2")
|
|
26
|
+
assert_equal :important, feature.priority_for_product_and_role("openSUSE-11.2", :productmanager)
|
|
27
|
+
assert_nil feature.priority_for_product_and_role("openSUSE-11.2", :nobody)
|
|
28
|
+
|
|
29
|
+
assert_equal :done, feature.status_for_product("openSUSE-11.4")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: feta
|
|
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
|
+
- Duncan Mac-Vicar P.
|
|
14
|
+
autorequire:
|
|
15
|
+
bindir: bin
|
|
16
|
+
cert_chain: []
|
|
17
|
+
|
|
18
|
+
date: 2011-08-10 00:00:00 Z
|
|
19
|
+
dependencies:
|
|
20
|
+
- !ruby/object:Gem::Dependency
|
|
21
|
+
name: rest-client
|
|
22
|
+
prerelease: false
|
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
|
24
|
+
none: false
|
|
25
|
+
requirements:
|
|
26
|
+
- - ~>
|
|
27
|
+
- !ruby/object:Gem::Version
|
|
28
|
+
hash: 3
|
|
29
|
+
segments:
|
|
30
|
+
- 1
|
|
31
|
+
- 6
|
|
32
|
+
version: "1.6"
|
|
33
|
+
type: :runtime
|
|
34
|
+
version_requirements: *id001
|
|
35
|
+
- !ruby/object:Gem::Dependency
|
|
36
|
+
name: nokogiri
|
|
37
|
+
prerelease: false
|
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
|
39
|
+
none: false
|
|
40
|
+
requirements:
|
|
41
|
+
- - ~>
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
hash: 5
|
|
44
|
+
segments:
|
|
45
|
+
- 1
|
|
46
|
+
- 5
|
|
47
|
+
version: "1.5"
|
|
48
|
+
type: :runtime
|
|
49
|
+
version_requirements: *id002
|
|
50
|
+
- !ruby/object:Gem::Dependency
|
|
51
|
+
name: inifile
|
|
52
|
+
prerelease: false
|
|
53
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
|
54
|
+
none: false
|
|
55
|
+
requirements:
|
|
56
|
+
- - ~>
|
|
57
|
+
- !ruby/object:Gem::Version
|
|
58
|
+
hash: 13
|
|
59
|
+
segments:
|
|
60
|
+
- 0
|
|
61
|
+
- 4
|
|
62
|
+
- 1
|
|
63
|
+
version: 0.4.1
|
|
64
|
+
type: :runtime
|
|
65
|
+
version_requirements: *id003
|
|
66
|
+
description: Feta is a library to access FATE using the keeper API
|
|
67
|
+
email:
|
|
68
|
+
- dmacvicar@suse.de
|
|
69
|
+
executables: []
|
|
70
|
+
|
|
71
|
+
extensions: []
|
|
72
|
+
|
|
73
|
+
extra_rdoc_files: []
|
|
74
|
+
|
|
75
|
+
files:
|
|
76
|
+
- .gitignore
|
|
77
|
+
- Gemfile
|
|
78
|
+
- README.rdoc
|
|
79
|
+
- Rakefile
|
|
80
|
+
- feta.gemspec
|
|
81
|
+
- lib/feta.rb
|
|
82
|
+
- lib/feta/client.rb
|
|
83
|
+
- lib/feta/common_client.rb
|
|
84
|
+
- lib/feta/feature.rb
|
|
85
|
+
- lib/feta/logging.rb
|
|
86
|
+
- lib/feta/plugins/suse.rb
|
|
87
|
+
- lib/feta/query.rb
|
|
88
|
+
- lib/feta/version.rb
|
|
89
|
+
- test/helper.rb
|
|
90
|
+
- test/test_query.rb
|
|
91
|
+
homepage: http://github.com/dmacvicar/feta
|
|
92
|
+
licenses: []
|
|
93
|
+
|
|
94
|
+
post_install_message:
|
|
95
|
+
rdoc_options: []
|
|
96
|
+
|
|
97
|
+
require_paths:
|
|
98
|
+
- lib
|
|
99
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
100
|
+
none: false
|
|
101
|
+
requirements:
|
|
102
|
+
- - ">="
|
|
103
|
+
- !ruby/object:Gem::Version
|
|
104
|
+
hash: 3
|
|
105
|
+
segments:
|
|
106
|
+
- 0
|
|
107
|
+
version: "0"
|
|
108
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
109
|
+
none: false
|
|
110
|
+
requirements:
|
|
111
|
+
- - ">="
|
|
112
|
+
- !ruby/object:Gem::Version
|
|
113
|
+
hash: 3
|
|
114
|
+
segments:
|
|
115
|
+
- 0
|
|
116
|
+
version: "0"
|
|
117
|
+
requirements: []
|
|
118
|
+
|
|
119
|
+
rubyforge_project:
|
|
120
|
+
rubygems_version: 1.8.6
|
|
121
|
+
signing_key:
|
|
122
|
+
specification_version: 3
|
|
123
|
+
summary: Library to access FATE features
|
|
124
|
+
test_files:
|
|
125
|
+
- test/helper.rb
|
|
126
|
+
- test/test_query.rb
|