rally_rest_api 0.5.5
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.txt +4 -0
- data/History.txt +0 -0
- data/Manifest.txt +19 -0
- data/README.txt +111 -0
- data/Rakefile +51 -0
- data/lib/rally_rest_api.rb +1 -0
- data/lib/rally_rest_api/query.rb +170 -0
- data/lib/rally_rest_api/query_result.rb +73 -0
- data/lib/rally_rest_api/rally_rest.rb +100 -0
- data/lib/rally_rest_api/rest_builder.rb +179 -0
- data/lib/rally_rest_api/rest_object.rb +205 -0
- data/lib/rally_rest_api/typedef.rb +23 -0
- data/lib/rally_rest_api/version.rb +9 -0
- data/setup.rb +1585 -0
- data/test/tc_query_result.rb +41 -0
- data/test/tc_rest_api.rb +98 -0
- data/test/tc_rest_object.rb +146 -0
- data/test/tc_rest_query.rb +197 -0
- data/test/test_helper.rb +2 -0
- metadata +75 -0
data/CHANGELOG.txt
ADDED
data/History.txt
ADDED
File without changes
|
data/Manifest.txt
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
CHANGELOG.txt
|
2
|
+
History.txt
|
3
|
+
lib/rally_rest_api/query.rb
|
4
|
+
lib/rally_rest_api/query_result.rb
|
5
|
+
lib/rally_rest_api/rally_rest.rb
|
6
|
+
lib/rally_rest_api.rb
|
7
|
+
lib/rally_rest_api/rest_builder.rb
|
8
|
+
lib/rally_rest_api/rest_object.rb
|
9
|
+
lib/rally_rest_api/typedef.rb
|
10
|
+
lib/rally_rest_api/version.rb
|
11
|
+
Manifest.txt
|
12
|
+
Rakefile
|
13
|
+
README.txt
|
14
|
+
setup.rb
|
15
|
+
test/tc_query_result.rb
|
16
|
+
test/tc_rest_api.rb
|
17
|
+
test/tc_rest_object.rb
|
18
|
+
test/tc_rest_query.rb
|
19
|
+
test/test_helper.rb
|
data/README.txt
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
rally-rest-api -- A Ruby-ized interface to Rally's REST webservice API
|
2
|
+
|
3
|
+
==Introduction:
|
4
|
+
Rally Software Development's on-demand agile software life-cycle management services offers webservices API's for its customers. The API comes in both SOAP and REST style interfaces. This library is for accessing the REST API using Ruby. For more information about Rally's webservice APIs see https://rally1.rallydev.com/slm/doc/webservice/index.jsp.
|
5
|
+
|
6
|
+
This API provides full access to all CRUD operations and an rich interface to the query facility. An Enumerable interface is provided for the paginated query results.
|
7
|
+
|
8
|
+
== Rationale (i.e. Why not SOAP?):
|
9
|
+
Your subscription in Rally can be partitioned into several isolated "Workspaces", where the only thing shared between workspaces are your users. Any custom attributes you create will be specific to each workspace. When using the SOAP interface, the WSDL generated is specific to the workspace you are working in. Therefore the name-space (e.g. package in Java) will be different for each workspace you are working with.
|
10
|
+
|
11
|
+
Because REST webservices do not have WSDL (the XML schema is available for each workspace), there is no per-workspace interface. Combined with the dynamic nature of this API, you don't need to code to different Ruby namespaces when you are working with multiple workspaces. You will however, need to be aware of the workspaces your objects are in when working with multiple workspaces.
|
12
|
+
|
13
|
+
== Getting Started:
|
14
|
+
RallyRestAPI is the entry point to the api. Each instance corresponds to one user logged into Rally. There are several options that may be passed to the constructor:
|
15
|
+
:username => Your Rally login username.
|
16
|
+
:password => Your Rally login password. Username and password will be remembered by this
|
17
|
+
instance of the API and all objects created and read by this instance.
|
18
|
+
:base_url => The base url for the system you are talking to. Defaults to https://rally1.rallydev.com/slm/
|
19
|
+
:raise_on_warning => true|false or the exception class you would like raised. If true, RuntimeError will be raised.
|
20
|
+
:logger => A logger to log to. There is interesting logging info for DEBUG and INFO
|
21
|
+
|
22
|
+
== Rest Object:
|
23
|
+
All rally resources referenced by the api are of type RestObject, there are no subclasses. In its initial form a RestObject is just a URL representing a resource. This URL is accessed using RestObject#ref. When more information is requested about the object, the API will read the content of that resource. This read is done lazily and transparently.
|
24
|
+
|
25
|
+
Objects can reference other objects in Rally. When establishing these relationships using this API, they are done using RestObjects. For example, to associate a user story to a defect, you would use the 'requirement' association on defect to reference the User Story. Here 'defect' and 'user_story' already exist, and the variables contain RestObjects representing them:
|
26
|
+
|
27
|
+
defect.update(:requirement => user_story)
|
28
|
+
|
29
|
+
== CRUD and Query:
|
30
|
+
|
31
|
+
Given an instance of the RallyRestAPI:
|
32
|
+
|
33
|
+
rally = RallyRestAPI.new(:username => <username>,
|
34
|
+
:password => <password>)
|
35
|
+
|
36
|
+
=== Create:
|
37
|
+
|
38
|
+
RallyRestAPI#create(<rally artifact type>, <artifact attributes as a hash>) returns a RestObject:
|
39
|
+
|
40
|
+
defect = rally.create(:defect, :name => "Defect name")
|
41
|
+
|
42
|
+
#create will also accept a block, and yield the newly created reference to the block
|
43
|
+
|
44
|
+
rally.create(:defect, :name => "Defect name") do |defect|
|
45
|
+
# do something with defect here
|
46
|
+
end
|
47
|
+
|
48
|
+
The block form is useful for creating relationships between objects in a readable way. For example, to create a User Story (represented by the type HierarchicalRequirement) with a task:
|
49
|
+
|
50
|
+
rally.create(:hierarchical_requirement, :name => "User Story One", :iteration => iteration_one) do |user_story|
|
51
|
+
rally.create(:task, :name => "Task One", :work_product => user_story)
|
52
|
+
end
|
53
|
+
|
54
|
+
The above example will create a UserStory, pass it to the block, then create a Task on that User Story using the task's 'WorkProduct' relationship.
|
55
|
+
|
56
|
+
=== Read:
|
57
|
+
As mentioned above, RestObject will lazy read themselves on demand. If you need to force a RestObject to re-read itself, call RestObject#refresh.
|
58
|
+
|
59
|
+
=== Update:
|
60
|
+
There are two ways to update an object:
|
61
|
+
|
62
|
+
RallyRestAPI#update(<rest object>, <attributes>)
|
63
|
+
RestObject#update(<attributes>)
|
64
|
+
|
65
|
+
which is to say, a RestObject can update itself
|
66
|
+
|
67
|
+
defect.update(:name => "new name")
|
68
|
+
|
69
|
+
Or the rest api can update it:
|
70
|
+
|
71
|
+
rally.update(defect, :name => "new name")
|
72
|
+
|
73
|
+
=== Delete:
|
74
|
+
There are two ways to delete an object:
|
75
|
+
|
76
|
+
RallyRestAPI#delete(<rest object>)
|
77
|
+
RestObject#delete
|
78
|
+
|
79
|
+
which is to say, a RestObject can delete itself
|
80
|
+
|
81
|
+
defect.delete
|
82
|
+
|
83
|
+
Or the rest api can delete it:
|
84
|
+
|
85
|
+
rally.delete(defect)
|
86
|
+
|
87
|
+
=== Query:
|
88
|
+
RallyRestAPI#find is the interface to the query syntax of Rally's webservice APIs. The query interface in Ruby provides full support for this query syntax including all the operators. A quick example:
|
89
|
+
|
90
|
+
query_result = rally.find(:defect) { equal :name, "Defect name" }
|
91
|
+
|
92
|
+
In addition to the type, #find accepts other arguments as a hash:
|
93
|
+
|
94
|
+
:pagesize => <number> - The number of results per page. Max of 100
|
95
|
+
:start => <number> - The record number to start with. Assuming more then page size records.
|
96
|
+
:fetch => <boolean> - If this is set to true then entire objects will be returned inside the query result. If set to false (the default) then only object references will be returned.
|
97
|
+
:workspace - If not present, then the query will run in the user's default workspace. If present, this should be the RestObject containing the workspace the user wants to search in.
|
98
|
+
:project - If not set, or specified as "null" then the "parent project" in the given workspace is used. If set, this should be the RestObject containing the project. Furthermore, if set you may omit the workspace parameter because the workspace will be inherited from the project.
|
99
|
+
:project_scope_up - Default is true. In addition to the specified project, include projects above the specified one.
|
100
|
+
:project_scope_down - Default is true. In addition to the specified project, include child projects below the current one.
|
101
|
+
|
102
|
+
The return from #find is always a QueryResult. The QueryResult provides an interface to the paginated query result.
|
103
|
+
|
104
|
+
#each will iterate all results on all pages.
|
105
|
+
#total_result_count is the number of results for the whole query.
|
106
|
+
#page_length is the number of elements in the current page of result.
|
107
|
+
#results returns an Array for the current page of results.
|
108
|
+
|
109
|
+
Because of the paginated nature of the result list, deleting elements while using #each is undefined.
|
110
|
+
|
111
|
+
See the rdoc for RestQuery for more query examples.
|
data/Rakefile
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rake/testtask'
|
5
|
+
require 'rake/packagetask'
|
6
|
+
require 'rake/gempackagetask'
|
7
|
+
require 'rake/rdoctask'
|
8
|
+
require 'rake/contrib/rubyforgepublisher'
|
9
|
+
require 'fileutils'
|
10
|
+
require 'hoe'
|
11
|
+
include FileUtils
|
12
|
+
require File.join(File.dirname(__FILE__), 'lib', 'rally_rest_api', 'version')
|
13
|
+
|
14
|
+
AUTHOR = "Bob Cotton" # can also be an array of Authors
|
15
|
+
EMAIL = "bob.cotton@ralldev.com"
|
16
|
+
DESCRIPTION = "A ruby-ized interface to Rally's REST webservices API"
|
17
|
+
GEM_NAME = "rally_rest_api" # what ppl will type to install your gem
|
18
|
+
RUBYFORGE_PROJECT = "rally-rest-api" # The unix name for your project
|
19
|
+
HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
|
20
|
+
RELEASE_TYPES = %w( gem ) # can use: gem, tar, zip
|
21
|
+
|
22
|
+
|
23
|
+
NAME = "rally_rest_api"
|
24
|
+
REV = nil # UNCOMMENT IF REQUIRED: File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
|
25
|
+
VERS = ENV['VERSION'] || (RallyRestAPI::VERSION::STRING + (REV ? ".#{REV}" : ""))
|
26
|
+
CLEAN.include ['**/.*.sw?', '*.gem', '.config']
|
27
|
+
RDOC_OPTS = ['--quiet', '--title', "rally_rest_api documentation",
|
28
|
+
"--opname", "index.html",
|
29
|
+
"--line-numbers",
|
30
|
+
"--main", "README",
|
31
|
+
"--inline-source"]
|
32
|
+
|
33
|
+
# Generate all the Rake tasks
|
34
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
35
|
+
hoe = Hoe.new(GEM_NAME, VERS) do |p|
|
36
|
+
p.author = AUTHOR
|
37
|
+
p.description = DESCRIPTION
|
38
|
+
p.email = EMAIL
|
39
|
+
p.summary = DESCRIPTION
|
40
|
+
p.url = HOMEPATH
|
41
|
+
p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
|
42
|
+
p.test_globs = ["test/**/tc_*.rb"]
|
43
|
+
p.clean_globs = CLEAN #An array of file patterns to delete on clean.
|
44
|
+
p.extra_deps = ["builder"]
|
45
|
+
|
46
|
+
# == Optional
|
47
|
+
#p.changes - A description of the release's latest changes.
|
48
|
+
|
49
|
+
#p.spec_extras - A hash of extra values to set in the gemspec.
|
50
|
+
end
|
51
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
Dir[File.join(File.dirname(__FILE__), 'rally_rest_api/**/*.rb')].sort.each { |lib| require lib }
|
@@ -0,0 +1,170 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
class String # :nodoc: all
|
4
|
+
def to_camel
|
5
|
+
self.split(/\./).map { |word| word.split(/_/).map { |word| word.capitalize }.join}.join('.')
|
6
|
+
end
|
7
|
+
|
8
|
+
alias :to_q :to_s
|
9
|
+
end
|
10
|
+
|
11
|
+
class Symbol # :nodoc: all
|
12
|
+
def to_q
|
13
|
+
self.to_s.to_camel
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# == Generate a query string for Rally's webservice query interface.
|
18
|
+
# Arguments are:
|
19
|
+
# type - the type to query for
|
20
|
+
# args - arguments to the query. Supported values are
|
21
|
+
# :pagesize => <number> - The number of results per page. Max of 100
|
22
|
+
# :start => <number> - The record number to start with. Assuming more then page size records.
|
23
|
+
# :fetch => <boolean> - If this is set to true then entire objects will be returned inside the query result. If set to false (the default) then only object references will be returned.
|
24
|
+
# :workspace - If not present, then the query will run in the user's default workspace. If present, this should be the RestObject containing the workspace the user wants to search in.
|
25
|
+
# :project - If not set, or specified as "null" then the "parent project" in the given workspace is used. If set, this should be the RestObject containing the project. Furthermore, if set you may omit the workspace parameter because the workspace will be inherited from the project.
|
26
|
+
# :project_scope_up - Default is true. In addition to the specified project, include projects above the specified one.
|
27
|
+
# :project_scope_down - Default is true. In addition to the specified project, include child projects below the current one.
|
28
|
+
# &block - the query parameters
|
29
|
+
#
|
30
|
+
# === The query parameters block
|
31
|
+
#
|
32
|
+
# The query parameters block is a DSL the specifying the query parameters. Single attribute specifiers are
|
33
|
+
# written in prefix notation in the form:
|
34
|
+
# <operator> <attribute symbol>, <value>
|
35
|
+
# for example
|
36
|
+
# equal :name, "My Name"
|
37
|
+
# Allowed operators and their corresponding generated query strings are:
|
38
|
+
# equal => "="
|
39
|
+
# not_equal => "!="
|
40
|
+
# contains => "contains"
|
41
|
+
# greater_than => ">"
|
42
|
+
# gt => ">"
|
43
|
+
# less_than => "<"
|
44
|
+
# lt => "<"
|
45
|
+
# greater_than_equal => ">="
|
46
|
+
# gte => ">="
|
47
|
+
# less_then_equal => "<="
|
48
|
+
# lte => "<="
|
49
|
+
#
|
50
|
+
# == Boolean logic.
|
51
|
+
#
|
52
|
+
# By default, if more then one query parameter is specified in the block, then those parameters will be ANDed together.
|
53
|
+
# For example, if the query parameter block contains the follow expression:
|
54
|
+
# equal :name, "My Name"
|
55
|
+
# greater_than :priority, "Fix Immediately"
|
56
|
+
# these expressions will be ANDed together. You may specify explicit AND and OR operators using the
|
57
|
+
# _and_ and _or_ operators, which also accept parameter blocks. For example the above expression could also have
|
58
|
+
# been written:
|
59
|
+
# _and_ {
|
60
|
+
# equal :name, "My Name"
|
61
|
+
# greater_than :priority, "Fix Immediately"
|
62
|
+
# }
|
63
|
+
# \_or_ works in the same fashion. _and_s and _or_s may be nested as needed. See the test cases for RestQuery for
|
64
|
+
# more complex examples
|
65
|
+
#
|
66
|
+
class RestQuery
|
67
|
+
attr_reader :type
|
68
|
+
|
69
|
+
def initialize(type, *args, &block)
|
70
|
+
@type = type
|
71
|
+
@query_string = "query=" << URI.escape(QueryBuilder.new("and", &block).to_q) if block_given?
|
72
|
+
@query_params = process_args(args[0])
|
73
|
+
end
|
74
|
+
|
75
|
+
def process_args(args) # :nodoc"
|
76
|
+
return if args.nil?
|
77
|
+
query_string = ""
|
78
|
+
args.each do |key, value|
|
79
|
+
case key
|
80
|
+
when :order
|
81
|
+
# this is a hack, we need a better way to express descending
|
82
|
+
value = [value].flatten.map { |e| e.to_s.to_camel }.join(", ").gsub(", Desc", " desc")
|
83
|
+
end
|
84
|
+
query_string << "&#{key}=#{URI.escape(value.to_s)}"
|
85
|
+
end
|
86
|
+
query_string
|
87
|
+
end
|
88
|
+
|
89
|
+
def next_page(args)
|
90
|
+
@query_params = process_args(args)
|
91
|
+
self
|
92
|
+
end
|
93
|
+
|
94
|
+
def to_q
|
95
|
+
"#{@query_string}#{@query_params}"
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.query(&block)
|
99
|
+
QueryBuilder.new("and", &block).to_q
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Internal support for generating query string for the Rally Webservice.
|
104
|
+
# See RestQuery for examples.
|
105
|
+
class QueryBuilder # :nodoc: all
|
106
|
+
attr_reader :operator
|
107
|
+
|
108
|
+
# Define the operators on the query terms. I've include perl-like operators for
|
109
|
+
# less_than and greater_then etc.
|
110
|
+
{
|
111
|
+
:equal => "=",
|
112
|
+
:not_equal => "!=",
|
113
|
+
:contains => "contains",
|
114
|
+
:greater_than => ">",
|
115
|
+
:gt => ">",
|
116
|
+
:less_than => "<",
|
117
|
+
:lt => "<",
|
118
|
+
:greater_than_equal => ">=",
|
119
|
+
:gte => ">=",
|
120
|
+
:less_then_equal => "<=",
|
121
|
+
:lte => "<=",
|
122
|
+
}.each do |method, operator|
|
123
|
+
module_eval %{def #{method}(lval, rval)
|
124
|
+
rval = \"\\"\#{rval}\\"\" if rval =~ / /
|
125
|
+
add(QueryString.new(lval, \"#{operator}\", rval), @operator)
|
126
|
+
end}
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
def initialize(operator, &block)
|
131
|
+
@operator = operator
|
132
|
+
instance_eval(&block)
|
133
|
+
end
|
134
|
+
|
135
|
+
def _and_(&block)
|
136
|
+
add(QueryBuilder.new("and", &block), @operator)
|
137
|
+
end
|
138
|
+
|
139
|
+
def _or_(&block)
|
140
|
+
add(QueryBuilder.new("or", &block), @operator)
|
141
|
+
end
|
142
|
+
|
143
|
+
def add(new_value, op)
|
144
|
+
if value.empty?
|
145
|
+
value.push new_value
|
146
|
+
else
|
147
|
+
value.push QueryString.new(value.pop, op, new_value)
|
148
|
+
end
|
149
|
+
self
|
150
|
+
end
|
151
|
+
|
152
|
+
def value
|
153
|
+
@value ||= []
|
154
|
+
end
|
155
|
+
|
156
|
+
def to_q
|
157
|
+
value[0].to_q
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
class QueryString # :nodoc: all
|
163
|
+
def initialize(lhs, op, rhs)
|
164
|
+
@lhs, @op, @rhs = lhs, op, rhs
|
165
|
+
end
|
166
|
+
|
167
|
+
def to_q
|
168
|
+
"(#{@lhs.to_q} #{@op.to_q} #{@rhs.to_q})"
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'rally_rest_api/rest_object'
|
2
|
+
|
3
|
+
# == An interface to the paged query result
|
4
|
+
#
|
5
|
+
# QueryResult is a wrapper around the xml returned from a webservice
|
6
|
+
# query operation. A query could result in a large number of hits
|
7
|
+
# being returned, therefore the query result is paged into page_size
|
8
|
+
# chunks (20 by default). QueryResult will seamlessly deal with the
|
9
|
+
# paging when using the #each iterator.
|
10
|
+
#
|
11
|
+
# === Example
|
12
|
+
# rally = RallyRestAPI.new(...)
|
13
|
+
# results = rally.find(:defect) { equal :name, "My Defects" }
|
14
|
+
# results.each do |defect|
|
15
|
+
# defect.update(:state => "Closed")
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
#
|
19
|
+
class QueryResult < RestObject
|
20
|
+
attr_reader :total_result_count
|
21
|
+
attr_reader :page_size
|
22
|
+
attr_reader :start_index
|
23
|
+
|
24
|
+
def initialize(query, rally_rest, document_content)
|
25
|
+
super(rally_rest, document_content)
|
26
|
+
elements[:results] = case self.results
|
27
|
+
when Array : self.results.flatten
|
28
|
+
when Hash : self.results.values.flatten
|
29
|
+
when nil : []
|
30
|
+
end
|
31
|
+
|
32
|
+
@query = query
|
33
|
+
|
34
|
+
@total_result_count = elements[:total_result_count].to_i
|
35
|
+
@page_size = elements[:page_size].to_i
|
36
|
+
@start_index = elements[:start_index].to_i
|
37
|
+
end
|
38
|
+
|
39
|
+
# fetch the next page of results. Uses the original query to generate the query string.
|
40
|
+
def next_page
|
41
|
+
@rally_rest.query(@query.next_page(:start => self.start_index + self.page_size,
|
42
|
+
:pagesize => self.page_size))
|
43
|
+
end
|
44
|
+
|
45
|
+
# Iteration all pages of the result
|
46
|
+
def each
|
47
|
+
current_result = self
|
48
|
+
while current_result.more_pages?
|
49
|
+
current_result.elements[:results].each do |result|
|
50
|
+
# The collection of refs we are holding onto could grow without bounds, so dup
|
51
|
+
# the ref
|
52
|
+
yield result.dup
|
53
|
+
end
|
54
|
+
current_result = current_result.next_page
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# return the first element. Useful for queries that return only one result
|
59
|
+
def first
|
60
|
+
results.first
|
61
|
+
end
|
62
|
+
|
63
|
+
# The length of the current page of results
|
64
|
+
def page_length
|
65
|
+
return 0 if self.elements[:results].nil?
|
66
|
+
self.elements[:results].length
|
67
|
+
end
|
68
|
+
|
69
|
+
# Are there more pages?
|
70
|
+
def more_pages?
|
71
|
+
(self.start_index + self.page_length) < self.total_result_count
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
require 'rexml/document'
|
4
|
+
require 'ostruct'
|
5
|
+
|
6
|
+
require 'rally_rest_api/rest_builder'
|
7
|
+
require 'rally_rest_api/query'
|
8
|
+
require 'rally_rest_api/rest_object'
|
9
|
+
require 'rally_rest_api/query_result'
|
10
|
+
|
11
|
+
#
|
12
|
+
# RallyRestAPI - A Ruby-ized interface to Rally's REST webservice API
|
13
|
+
#
|
14
|
+
class RallyRestAPI
|
15
|
+
include RestBuilder
|
16
|
+
|
17
|
+
attr_reader :username, :password, :base_url, :raise_on_warning, :logger
|
18
|
+
|
19
|
+
ALLOWED_TYPES = %w[subscription workspace project iteration release defect defect_suite test_case
|
20
|
+
feature supplemental_requirement use_case story actor card
|
21
|
+
program task hierarchical_requirement test_case_result test_case_step]
|
22
|
+
|
23
|
+
# new - Create an instance of RallyRestAPI. Each instance corresponds to one named user.
|
24
|
+
#
|
25
|
+
# options (as a Hash):
|
26
|
+
# * username - The Rally username
|
27
|
+
# * password - The password for the named user
|
28
|
+
# * base_url - The base url of the system. Defaults to https://rally1.rallydev.com/slm
|
29
|
+
# * raise_on_warn - true|false|Exception Class. If you want exceptions raised on warnings. Default is false. if 'true' RuntimeException will be raised. If ExceptionClass, then an instance of that will be raised.
|
30
|
+
# * logger - a Logger to log to.
|
31
|
+
#
|
32
|
+
def initialize(options = {:base_url => "https://rally1.rallydev.com/slm"})
|
33
|
+
parse_options options
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse_options(options)
|
37
|
+
@username = options[:username]
|
38
|
+
@password = options[:password]
|
39
|
+
@base_url = options[:base_url]
|
40
|
+
@raise_on_warning = options[:raise_on_warning]
|
41
|
+
@logger = options[:logger]
|
42
|
+
end
|
43
|
+
|
44
|
+
# return an instance of User, for the currently logged in user.
|
45
|
+
def user
|
46
|
+
RestObject.new(self, read_rest("#{@base_url}/webservice/1.0/user", @username, @password))
|
47
|
+
end
|
48
|
+
alias :start :user # :nodoc:
|
49
|
+
|
50
|
+
# This is deprecated, use create instead
|
51
|
+
def method_missing(type, args, &block) # :nodoc:
|
52
|
+
# raise "'#{type}' is not a supported type. Supported types are: #{ALLOWED_TYPES.inspect}" unless ALLOWED_TYPES.include?(type.to_s)
|
53
|
+
create_rest(type, args, @username, @password)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Create an object.
|
57
|
+
# type - The type to create, as a symbol (e.g. :test_case)
|
58
|
+
# values - The attributes of the new object.
|
59
|
+
#
|
60
|
+
# The created instance will be passed to the block
|
61
|
+
#
|
62
|
+
# returns the created object as a RestObject.
|
63
|
+
def create(type, values) # :yields: new_object
|
64
|
+
# raise "'#{type}' is not a supported type. Supported types are: #{ALLOWED_TYPES.inspect}" unless ALLOWED_TYPES.include?(type.to_s)
|
65
|
+
object = create_rest(type, values, @username, @password)
|
66
|
+
yield object if block_given?
|
67
|
+
object
|
68
|
+
end
|
69
|
+
|
70
|
+
# Query Rally for a collection of objects
|
71
|
+
# Example :
|
72
|
+
# rally.find(:artifact, :page_size => 20, :start_index => 20) { equal :name, "name" }
|
73
|
+
# See RestQuery for more info.
|
74
|
+
def find(type, *args, &query)
|
75
|
+
# pass the args to RestQuery, make it generate the string and handle generating the query for the
|
76
|
+
# next page etc.
|
77
|
+
query = RestQuery.new(type, args, &query)
|
78
|
+
query(query)
|
79
|
+
end
|
80
|
+
|
81
|
+
# update - update an object
|
82
|
+
def update(rest_object, attributes)
|
83
|
+
rest_object.update(attributes)
|
84
|
+
end
|
85
|
+
|
86
|
+
# delete an object
|
87
|
+
def delete(rest_object)
|
88
|
+
rest_object.delete
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
def query(query)
|
93
|
+
query_url = "#{@base_url}/webservice/1.0/#{query.type.to_s.to_camel}?" << query.to_q
|
94
|
+
xml = read_rest(query_url, @username, @password)
|
95
|
+
QueryResult.new(query, self, xml)
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
|