rallytastic 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +51 -0
- data/LICENSE +20 -0
- data/README.rdoc +85 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/lib/iteration.rb +52 -0
- data/lib/parsers/base.rb +23 -0
- data/lib/parsers/madison.rb +23 -0
- data/lib/project.rb +46 -0
- data/lib/rally/parsing_helpers.rb +70 -0
- data/lib/rally/rally_api.rb +102 -0
- data/lib/rallytastic.rb +30 -0
- data/lib/revision.rb +18 -0
- data/lib/revision_history.rb +19 -0
- data/lib/story.rb +101 -0
- data/lib/tasks/docs.thor +31 -0
- data/lib/tasks/scraper.thor +94 -0
- data/spec/fixtures/child_project.txt +1 -0
- data/spec/fixtures/iteration.txt +1 -0
- data/spec/fixtures/project.txt +1 -0
- data/spec/fixtures/story.txt +1 -0
- data/spec/fixtures/story_query.txt +1 -0
- data/spec/iteration_spec.rb +76 -0
- data/spec/project_spec.rb +64 -0
- data/spec/rally/rally_api_spec.rb +114 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/story_spec.rb +82 -0
- metadata +114 -0
@@ -0,0 +1,102 @@
|
|
1
|
+
class RallyAPI
|
2
|
+
class Configure
|
3
|
+
attr_accessor :user, :password
|
4
|
+
end
|
5
|
+
|
6
|
+
class CredentialsError < RestClient::Unauthorized; end
|
7
|
+
|
8
|
+
class << self
|
9
|
+
@configuration = nil
|
10
|
+
|
11
|
+
def configure(&block)
|
12
|
+
@configuration = @configuration || Configure.new
|
13
|
+
yield @configuration
|
14
|
+
end
|
15
|
+
|
16
|
+
def configuration
|
17
|
+
@configuration
|
18
|
+
end
|
19
|
+
|
20
|
+
def all resource, options ={}
|
21
|
+
check_configuration
|
22
|
+
do_get(resource.rally_uri, parsed_options(options))
|
23
|
+
end
|
24
|
+
|
25
|
+
def get resource
|
26
|
+
check_configuration
|
27
|
+
json = JSON.parse(do_get(resource.rally_uri))
|
28
|
+
json[resource.class.node_name]
|
29
|
+
end
|
30
|
+
|
31
|
+
def parsed_options options
|
32
|
+
option_string = {}
|
33
|
+
if options.has_key? :conditions
|
34
|
+
option_string = option_string.merge(parsed_query(options[:conditions]))
|
35
|
+
end
|
36
|
+
|
37
|
+
if options.has_key? :start
|
38
|
+
option_string = option_string.merge(parsed_start(options[:start]))
|
39
|
+
end
|
40
|
+
|
41
|
+
if options.has_key? :pagesize
|
42
|
+
option_string = option_string.merge(parsed_pagesize(options[:pagesize]))
|
43
|
+
end
|
44
|
+
|
45
|
+
if options.has_key? :fetch
|
46
|
+
option_string = option_string.merge(parsed_fetch(options[:fetch]))
|
47
|
+
end
|
48
|
+
option_string
|
49
|
+
end
|
50
|
+
|
51
|
+
def parsed_fetch(fetch)
|
52
|
+
{"fetch" => fetch}
|
53
|
+
end
|
54
|
+
|
55
|
+
def parsed_start(start)
|
56
|
+
{"start" => start.to_i}
|
57
|
+
end
|
58
|
+
|
59
|
+
def parsed_pagesize(pagesize)
|
60
|
+
{"pagesize" => pagesize.to_i}
|
61
|
+
end
|
62
|
+
|
63
|
+
def parsed_query conditions
|
64
|
+
tokens = conditions.collect{|k,v| query_token(k,v)}
|
65
|
+
{"query" => tokens.join(" and ")}
|
66
|
+
end
|
67
|
+
|
68
|
+
def query_token k,v
|
69
|
+
operators ={
|
70
|
+
"gt" => ">",
|
71
|
+
"gte" => ">=",
|
72
|
+
"lt" => "<",
|
73
|
+
"lte" => "<=",
|
74
|
+
"ne" => "!="}
|
75
|
+
field = k.respond_to?("key") ? k.key : k
|
76
|
+
field = field.downcase == field ? field.capitalize : field
|
77
|
+
operator = k.respond_to?("operator") ? operators[k.operator] : "="
|
78
|
+
"(#{field.to_s} #{operator} \"#{v}\")"
|
79
|
+
end
|
80
|
+
|
81
|
+
def do_get endpoint, options = nil
|
82
|
+
if endpoint.match(/https:\/\/.*/)
|
83
|
+
rally_resource(endpoint).get(:params => options)
|
84
|
+
else
|
85
|
+
rally_resource[endpoint].get(:params => options)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def rally_resource uri = nil
|
90
|
+
uri ||= "https://rally1.rallydev.com/slm/webservice/1.19"
|
91
|
+
RestClient::Resource.new(uri,
|
92
|
+
:user => self.configuration.user,
|
93
|
+
:password => self.configuration.password)
|
94
|
+
end
|
95
|
+
|
96
|
+
def check_configuration
|
97
|
+
if self.configuration.user.nil? || self.configuration.password.nil?
|
98
|
+
raise CredentialsError.new
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/lib/rallytastic.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# Set up gems listed in the Gemfile.
|
2
|
+
gemfile = File.expand_path('../Gemfile', __FILE__)
|
3
|
+
|
4
|
+
begin
|
5
|
+
ENV['BUNDLE_GEMFILE'] = gemfile
|
6
|
+
require 'bundler'
|
7
|
+
Bundler.setup
|
8
|
+
rescue Bundler::GemNotFound => e
|
9
|
+
STDERR.puts e.message
|
10
|
+
STDERR.puts "Try running `bundle install`."
|
11
|
+
exit!
|
12
|
+
end if File.exist?(gemfile)
|
13
|
+
|
14
|
+
|
15
|
+
lib = File.dirname(__FILE__)
|
16
|
+
require 'mongoid'
|
17
|
+
require 'rest_client'
|
18
|
+
require File.join(lib, 'rally/parsing_helpers.rb')
|
19
|
+
require File.join(lib, 'story.rb')
|
20
|
+
require File.join(lib, 'project.rb')
|
21
|
+
require File.join(lib, 'iteration.rb')
|
22
|
+
require File.join(lib, 'revision_history.rb')
|
23
|
+
require File.join(lib, 'revision.rb')
|
24
|
+
require File.join(lib, 'rally/rally_api.rb')
|
25
|
+
require File.join(lib, 'parsers/base.rb')
|
26
|
+
require File.join(lib, 'parsers/madison.rb')
|
27
|
+
|
28
|
+
|
29
|
+
#temp for rally and mongoid setup
|
30
|
+
require '~/rally_config'
|
data/lib/revision.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
class Revision
|
2
|
+
include Mongoid::Document
|
3
|
+
include Rally::ParsingHelpers
|
4
|
+
extend Rally::ParsingHelperClassMethods
|
5
|
+
|
6
|
+
field :description
|
7
|
+
field :revision_number
|
8
|
+
field :created_on
|
9
|
+
|
10
|
+
embedded_in :story, :inverse_of => :revisions
|
11
|
+
|
12
|
+
def refresh hash_values=nil
|
13
|
+
@rally_hash = hash_values if hash_values
|
14
|
+
from_rally :description
|
15
|
+
from_rally :revision_number
|
16
|
+
from_rally :created_on, :CreationDate
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class RevisionHistory
|
2
|
+
include Mongoid::Document
|
3
|
+
include Rally::ParsingHelpers
|
4
|
+
extend Rally::ParsingHelperClassMethods
|
5
|
+
|
6
|
+
field :revisions, :type => Array
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def rally_uri
|
10
|
+
"/revisionhistory.js"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def refresh
|
15
|
+
parse_refs :revisions, raw_json["Revisions"]
|
16
|
+
self.save
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
data/lib/story.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
class Story
|
2
|
+
include Mongoid::Document
|
3
|
+
include Rally::ParsingHelpers
|
4
|
+
extend Rally::ParsingHelperClassMethods
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def rally_uri
|
8
|
+
"/hierarchicalrequirement.js"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
field :name
|
13
|
+
field :rally_uri
|
14
|
+
field :created_on, :type => Date
|
15
|
+
field :updated_on, :type => Date
|
16
|
+
field :description
|
17
|
+
field :notes
|
18
|
+
field :formatted_id
|
19
|
+
field :accepted_on, :type => Date
|
20
|
+
field :blocked, :type => Boolean
|
21
|
+
field :plan_estimate, :type => Integer
|
22
|
+
field :predicessor_uris, :type => Array
|
23
|
+
field :rank
|
24
|
+
field :schedule_state
|
25
|
+
field :requested_due_date, :type => Date
|
26
|
+
field :theme
|
27
|
+
field :revision_history_uri
|
28
|
+
field :rally_hash, :type => Hash
|
29
|
+
|
30
|
+
field :sized_on, :type => DateTime
|
31
|
+
field :prioritized_on, :type => DateTime
|
32
|
+
field :started_on, :type => DateTime
|
33
|
+
field :completed_on, :type => DateTime
|
34
|
+
field :accepted_on, :type => DateTime
|
35
|
+
|
36
|
+
embeds_many :revisions, :inverse_of => :story
|
37
|
+
referenced_in :iteration
|
38
|
+
referenced_in :parent, :class_name => "Story", :inverse_of => :children
|
39
|
+
references_many :children, :class_name => "Story", :inverse_of => :parent
|
40
|
+
|
41
|
+
def set_revision_history
|
42
|
+
revision_fields.each do |field|
|
43
|
+
self.send("#{field}=", revision_parser.send(field))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def revision_history
|
48
|
+
set_revision_history
|
49
|
+
revision_fields.inject({}){|h,field| h[field] = self.send(field.to_s); h}
|
50
|
+
end
|
51
|
+
|
52
|
+
def revision_fields
|
53
|
+
[:sized_on, :prioritized_on, :started_on, :completed_on, :accepted_on]
|
54
|
+
end
|
55
|
+
|
56
|
+
def revision_parser
|
57
|
+
if self.iteration && self.iteration.project
|
58
|
+
eval("Parser::#{self.iteration.project.revision_parser}").new(self)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def refresh hash_values=nil
|
63
|
+
@rally_hash = hash_values
|
64
|
+
from_rally :rally_uri, :_ref
|
65
|
+
from_rally :name
|
66
|
+
from_rally :notes
|
67
|
+
from_rally :created_on, :_CreatedAt
|
68
|
+
from_rally :description
|
69
|
+
from_rally :formatted_id, :FormattedID
|
70
|
+
from_rally :updated_on, :LastUpdateDate
|
71
|
+
from_rally :accepted_on, :AcceptedDate
|
72
|
+
from_rally :blocked
|
73
|
+
from_rally :plan_estimate, :PlanEstimate
|
74
|
+
from_rally :rank
|
75
|
+
from_rally :schedule_state, :ScheduleState
|
76
|
+
from_rally :requested_due_date, :RequestedDueDate
|
77
|
+
from_rally :theme
|
78
|
+
|
79
|
+
parse_ref :revision_history_uri, @rally_hash["RevisionHistory"]
|
80
|
+
|
81
|
+
self.save
|
82
|
+
rescue ArgumentError #getting some bad created_on dates
|
83
|
+
puts "Errored on #{self.name}"
|
84
|
+
self.save # save what you can
|
85
|
+
end
|
86
|
+
|
87
|
+
def associate hash_values=nil
|
88
|
+
@rally_hash = hash_values if hash_values
|
89
|
+
if @rally_hash["Iteration"]
|
90
|
+
iteration = Iteration.find_or_create_by(:rally_uri => @rally_hash["Iteration"]["_ref"])
|
91
|
+
self.iteration = iteration
|
92
|
+
end
|
93
|
+
|
94
|
+
if @rally_hash["Parent"]
|
95
|
+
story = Story.find_or_create_by(:rally_uri => @rally_hash["Parent"]["_ref"])
|
96
|
+
self.parent = story
|
97
|
+
end
|
98
|
+
|
99
|
+
self.save
|
100
|
+
end
|
101
|
+
end
|
data/lib/tasks/docs.thor
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'rallytastic')
|
2
|
+
|
3
|
+
class Docs < Thor
|
4
|
+
desc "update_readme", "Updates README.doc with thor task descriptions for project"
|
5
|
+
def update_readme
|
6
|
+
tasks = Scraper.printable_tasks
|
7
|
+
file_path = File.join(File.dirname(__FILE__), '..', '..', 'README.rdoc')
|
8
|
+
|
9
|
+
#indexes of thor section
|
10
|
+
f = File.open(file_path)
|
11
|
+
lines = f.readlines
|
12
|
+
start = lines.index("=== Thor Tasks\n")
|
13
|
+
finish = lines.index("<End of Thor Tasks>\n")
|
14
|
+
f.close
|
15
|
+
|
16
|
+
#write new README
|
17
|
+
f = File.open(file_path, "w+")
|
18
|
+
lines[0..start].each{|l| f << l}
|
19
|
+
tasks.each{|l| write_thor_task f, l}
|
20
|
+
lines[finish..-1].each{|l| f << l}
|
21
|
+
f.close
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def write_thor_task file, task
|
27
|
+
command_parts = task[0].split(" ")
|
28
|
+
file << "==== #{command_parts[1..-1].join(" ")}\n"
|
29
|
+
file << "#{task[1]}\n"
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'rallytastic')
|
2
|
+
require File.join("~", 'rally_config.rb')
|
3
|
+
|
4
|
+
class Scraper < Thor
|
5
|
+
desc "refresh", "Pulls down Rally stories since the last run, and creates Iterations and Projects as needed. This is the task you want to put in your job scheduler."
|
6
|
+
def refresh
|
7
|
+
stories
|
8
|
+
update_non_stories
|
9
|
+
end
|
10
|
+
|
11
|
+
desc "stories", "Walk Rally stories updated since last run"
|
12
|
+
def stories
|
13
|
+
most_recent = Story.desc(:updated_on).limit(1).first
|
14
|
+
if most_recent
|
15
|
+
pull_all_from_rally_of_object Story, :conditions => {:LastUpdateDate.gt => most_recent.updated_on.to_s}
|
16
|
+
else
|
17
|
+
pull_all_from_rally_of_object Story
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "update_non_stories", "The scrape:stroies task creates shell iterations and projects for any that it doesn't know about yet. This task looks for those, and pull their information down from Rally"
|
23
|
+
def update_non_stories
|
24
|
+
Iteration.all(:conditions => {:name => nil, :rally_uri.ne => nil}).each{|i| i.refresh; i.associate}
|
25
|
+
Project.all(:conditions => {:name => nil, :rally_uri.ne => nil}).each{|s| s.refresh; i.associate}
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "revisions iteration_name", "Get the revisions for stories hanging off an iteration"
|
29
|
+
def revisions iteration_name
|
30
|
+
iteration = Iteration.find(:conditions => {:name => iteration_name}).first
|
31
|
+
iteration.stories.each{|story| revisions_for_story(story)}
|
32
|
+
end
|
33
|
+
|
34
|
+
desc "projects", "Bootstrapping method that is primarily useful when you are setting up Rallytastic for the first time. It will walk Rally and pull down all projects. Technically, you don't need to run this because using the scrape:stories; scrape:update_non_stories; will fill this information in for you. It may be a bit quicker though to run this first if you have a lot of projects because it does batch queries"
|
35
|
+
def projects
|
36
|
+
pull_all_from_rally_of_object Project
|
37
|
+
end
|
38
|
+
|
39
|
+
desc "iterations", "Bootstrapping method that is primarily useful when you are setting up Rallytastic for the first time. It will walk Rally and pull down all Iterations. Technically, you don't need to run this because using the scrape:stories; scrape:update_non_stories; will fill this information in for you. It may be a bit quicker though to run this first if you have a lot of iterations because it does batch queries"
|
40
|
+
def iterations
|
41
|
+
pull_all_from_rally_of_object Iteration
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def revisions_for_story(story)
|
46
|
+
story_in_rally = RallyAPI.get(story)
|
47
|
+
revision_list = RevisionHistory.new(:rally_uri => story_in_rally["RevisionHistory"]["_ref"])
|
48
|
+
revision_list.refresh
|
49
|
+
revision_uris = story.revisions.collect{|r| r.rally_uri}
|
50
|
+
if revision_list.revisions
|
51
|
+
revision_list.revisions.each do |uri|
|
52
|
+
unless revision_uris.include?(uri)
|
53
|
+
r = Revision.new(:rally_uri => uri)
|
54
|
+
r.refresh
|
55
|
+
story.revisions << r
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
story.save
|
60
|
+
end
|
61
|
+
|
62
|
+
def pull_all_from_rally_of_object klass, options={}
|
63
|
+
total, page_size, start_index = nil, 100, 1
|
64
|
+
while start_index = offset(total, page_size, start_index) do
|
65
|
+
query = {:fetch => true, :start => start_index, :pagesize => page_size}.merge(options)
|
66
|
+
response = klass.rally_query(query)
|
67
|
+
response_hash = JSON.parse(response)["QueryResult"]
|
68
|
+
process_results klass, response_hash["Results"]
|
69
|
+
total, page_size, start_index = response_hash["TotalResultCount"], response_hash["PageSize"], response_hash["StartIndex"]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def process_results klass, results
|
74
|
+
results.each do |result|
|
75
|
+
local = klass.find_or_create_by(:rally_uri => result["_ref"])
|
76
|
+
local.refresh(result)
|
77
|
+
local.associate(result)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def offset total, page_size, start_index
|
82
|
+
if total.nil? #first pass
|
83
|
+
return start_index
|
84
|
+
elsif start_index >= total
|
85
|
+
return nil # we are at th end
|
86
|
+
else
|
87
|
+
return start_index + page_size
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def updated_since
|
92
|
+
DateTime.parse("1-1-2008")
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/project/440861167.js", "_objectVersion"=>"1", "_refObjectName"=>"Customer Experience", "CreationDate"=>"2009-07-01T22:50:23.000Z", "_CreatedAt"=>"Jul 1, 2009", "ObjectID"=>440861167, "Subscription"=>{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/subscription/411778556.js", "_refObjectName"=>"King Arthur's Court", "_type"=>"Subscription"}, "Workspace"=>{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/workspace/411780799.js", "_refObjectName"=>"King Arthurs Court", "_type"=>"Workspace"}, "BuildDefinitions"=>[{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/builddefinition/440861173.js", "_refObjectName"=>"Default Build Definition", "_type"=>"BuildDefinition"}], "Children"=>[{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/project/43434343.js", "_refObjectName"=>"L&P", "_type"=>"Project"}, {"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/project/2325452.js", "_refObjectName"=>"Kool and the Gang", "_type"=>"Project"}], "Description"=>"A nice description", "Iterations"=>[{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/iteration/4342242.js", "_refObjectName"=>"Week 5 -- July 13 - July 17 2009", "_type"=>"Iteration"}, {"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/iteration/4545454.js", "_refObjectName"=>"Week 6 -- July 20 - July 24 2009", "_type"=>"Iteration"}], "Name"=>"Flounder", "Notes"=>"some notes", "Owner"=>{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/user/43434343.js", "_refObjectName"=>"Peter Pan", "_type"=>"User"}, "Parent"=>{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/project/440864397.js", "_refObjectName"=>"King Arthur's court", "_type"=>"Project"}, "Releases"=>[], "State"=>"Open", "Users"=>[{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/user/342424242.js", "_refObjectName"=>"Bart Simpson", "_type"=>"User"}], "Errors"=>[], "Warnings"=>[]}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/iteration/4623232.js", "_objectVersion"=>"1", "_refObjectName"=>"iteration0", "CreationDate"=>"2009-07-13T15:46:05.000Z", "_CreatedAt"=>"Jul 13, 2009", "ObjectID"=>462353428, "Subscription"=>{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/subscription/3434343.js", "_refObjectName"=>"King Arthur's Court", "_type"=>"Subscription"}, "Workspace"=>{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/workspace/454545454.js", "_refObjectName"=>"Wonderful workspace", "_type"=>"Workspace"}, "EndDate"=>"2009-07-18T23:59:59.000Z", "Name"=>"Iteration1", "Notes"=>"", "Project"=>{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/project/440864397.js", "_refObjectName"=>"Are You Experienced", "_type"=>"Project"}, "Resources"=>"2.0", "RevisionHistory"=>{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/revisionhistory/434344.js", "_type"=>"RevisionHistory"}, "StartDate"=>"2009-07-13T00:00:00.000Z", "State"=>"Planning", "Theme"=>"This is the theme", "UserIterationCapacities"=>[], "Errors"=>[], "Warnings"=>[]}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/project/440864397.js", "_objectVersion"=>"1", "_refObjectName"=>"Customer Experience", "CreationDate"=>"2009-07-01T22:50:23.000Z", "_CreatedAt"=>"Jul 1, 2009", "ObjectID"=>3243423237, "Subscription"=>{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/subscription/411772322.js", "_refObjectName"=>"super subscription", "_type"=>"Subscription"}, "Workspace"=>{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/workspace/4117802322.js", "_refObjectName"=>"Wonderful Workspace", "_type"=>"Workspace"}, "BuildDefinitions"=>[{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/builddefinition/4408632232.js", "_refObjectName"=>"Default Build Definition", "_type"=>"BuildDefinition"}], "Children"=>[{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/project/4409124343.js", "_refObjectName"=>"L&P", "_type"=>"Project"}, {"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/project/423687232.js", "_refObjectName"=>"Moon Landing", "_type"=>"Project"}, {"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/project/4409452323.js", "_refObjectName"=>"Crater landing", "_type"=>"Project"}, {"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/project/440905343.js", "_refObjectName"=>"Modern Architecture", "_type"=>"Project"}, {"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/project/423682323.js", "_refObjectName"=>"Hugenis", "_type"=>"Project"}, {"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/project/4236834343.js", "_refObjectName"=>"Jupiter", "_type"=>"Project"}, {"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/project/4236854534.js", "_refObjectName"=>"ESA", "_type"=>"Project"}, {"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/project/6006813233.js", "_refObjectName"=>"Ion Propulsion", "_type"=>"Project"}, {"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/project/119393323232.js", "_refObjectName"=>"Martian Rover"}], "Description"=>"A nice description", "Iterations"=>[{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/iteration/46235334434.js", "_refObjectName"=>"for launch", "_type"=>"Iteration"}, {"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/iteration/4623534344.js", "_refObjectName"=>"post launch", "_type"=>"Iteration"}], "Name"=>"Down the hatch", "Notes"=>"some notes", "Owner"=>{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/user/411781343443.js", "_refObjectName"=>"Kin Lear", "_type"=>"User"}, "Parent"=>{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/project/411780344343.js", "_refObjectName"=>"King Aurthr's Court", "_type"=>"Project"}, "Releases"=>[{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/release/46233443434.js", "_refObjectName"=>"September Release", "_type"=>"Release"}, {"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/release/46234143434.js", "_refObjectName"=>"October Release", "_type"=>"Release"}, {"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/release/462344434343.js", "_refObjectName"=>"December Release", "_type"=>"Release"}, {"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/release/1289875344343.js", "_refObjectName"=>"May Major Release - May 13", "_type"=>"Release"}, {"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/release/183277434344.js", "_refObjectName"=>"Phase 8", "_type"=>"Release"}], "State"=>"Open", "Users"=>[{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/user/23234444.js", "_refObjectName"=>"Homer Simpson", "_type"=>"User"}], "Errors"=>[], "Warnings"=>[]}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"HierarchicalRequirement"=>{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/hierarchicalrequirement/413668210.js", "_objectVersion"=>"12", "_refObjectName"=>"Run the Marathon", "CreationDate"=>"2009-06-11T16:12:46.000Z", "_CreatedAt"=>"Jun 11, 2009", "ObjectID"=>413668210, "Subscription"=>{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/subscription/413242424.js", "_refObjectName"=>"Getty Images", "_type"=>"Subscription"}, "Workspace"=>{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/workspace/4324242.js", "_refObjectName"=>"Athens", "_type"=>"Workspace"}, "Description"=>"You've trained for it, now do it. Done when, you run 26.2 miles.", "Discussion"=>[], "FormattedID"=>"US26", "LastUpdateDate"=>"2009-09-10T18:57:55.000Z", "Name"=>"Run the marathon", "Notes"=>"some notes", "Owner"=>{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/user/412332424.js", "_refObjectName"=>"Forest Gump", "_type"=>"User"}, "Project"=>{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/project/342424242.js", "_refObjectName"=>"Getting fit", "_type"=>"Project"}, "RevisionHistory"=>{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/revisionhistory/413668211.js", "_type"=>"RevisionHistory"}, "Tags"=>[], "Attachments"=>[], "Package"=>nil, "AcceptedDate"=>"2009-09-10T18:57:55.000Z", "Blocked"=>false, "Blocker"=>nil, "Children"=>[], "Defects"=>[], "Iteration"=>{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/iteration/234242424.js", "_refObjectName"=>"Week 1 -- August 3 - August 7 2009", "_type"=>"Iteration"}, "Parent"=>{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/hierarchicalrequirement/413668210.js", "_refObjectName"=>"Multi-site: Create Jupiter EZA portfolio in SCI", "_type"=>"HierarchicalRequirement"}, "PlanEstimate"=>3.0, "Predecessors"=>[], "Rank"=>"86292398717311774535928819836502410.432", "Release"=>{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/release/3424242.js", "_refObjectName"=>"Release name", "_type"=>"Release"}, "ScheduleState"=>"Accepted", "Successors"=>[], "TaskActualTotal"=>0.0, "TaskEstimateTotal"=>6.0, "TaskRemainingTotal"=>0.0, "Tasks"=>[{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/task/3424243242.js", "_refObjectName"=>"Put down left foot", "_type"=>"Task"}], "TestCases"=>[], "KanbanState"=>nil, "KanbanStateGITest"=>nil, "KanbanStateUnix"=>nil, "RequestedDueDate"=>"2009-06-11T16:12:46.000Z", "Requestor"=>nil, "Theme"=>"this is the theme", "Errors"=>[], "Warnings"=>[]}}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"QueryResult"=>{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "Errors"=>[], "Warnings"=>[], "TotalResultCount"=>10, "StartIndex"=>1, "PageSize"=>20, "Results"=>[{"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/hierarchicalrequirement/413668210.js", "_refObjectName"=>"Multi-site: Create Jupiter EZA portfolio in SCI", "_type"=>"HierarchicalRequirement"}, {"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/hierarchicalrequirement/451159598.js", "_refObjectName"=>"Multi-site: A time/date stamp is needed on the EM fields for each site", "_type"=>"HierarchicalRequirement"}, {"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/hierarchicalrequirement/458369669.js", "_refObjectName"=>"Rights Portal: change text on RightsPortal button and remove old SCI buttons", "_type"=>"HierarchicalRequirement"}, {"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/hierarchicalrequirement/458941309.js", "_refObjectName"=>"UU: NOC ticket 1564534 - Send to Finisher failure", "_type"=>"HierarchicalRequirement"}, {"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/hierarchicalrequirement/466045791.js", "_refObjectName"=>"Multi-site: Update SCI order confirmation email for JI", "_type"=>"HierarchicalRequirement"}, {"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/hierarchicalrequirement/470734293.js", "_refObjectName"=>"Analysis: Cognos: Build queries to populate Cognos with Scorecard metrics", "_type"=>"HierarchicalRequirement"}, {"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/hierarchicalrequirement/486810765.js", "_refObjectName"=>"Bug: On GYI, when you check/uncheck your email preference only from Myprofile, the record does not publish out", "_type"=>"HierarchicalRequirement"}, {"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/hierarchicalrequirement/517412658.js", "_refObjectName"=>"Oracle Event Project Regression Testing", "_type"=>"HierarchicalRequirement"}, {"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/hierarchicalrequirement/519567381.js", "_refObjectName"=>"Request update: CRM request form called \"Contract Billing\"", "_type"=>"HierarchicalRequirement"}, {"_rallyAPIMajor"=>"1", "_rallyAPIMinor"=>"19", "_ref"=>"https://rally1.rallydev.com/slm/webservice/1.19/hierarchicalrequirement/519679148.js", "_refObjectName"=>"Quick Search update: Email query does not work if a hyphen is in the email address", "_type"=>"HierarchicalRequirement"}]}}
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe Iteration do
|
4
|
+
before do
|
5
|
+
Iteration.collection.remove
|
6
|
+
@hash_vals = File.read(File.join(File.dirname(__FILE__), 'fixtures', 'iteration.txt'))
|
7
|
+
RallyAPI.stub!(:get).and_return(eval(@hash_vals))
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
it "should be a iteration" do
|
12
|
+
Iteration.new.class.should == Iteration
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#associate" do
|
16
|
+
before do
|
17
|
+
@iteration = Iteration.new(:rally_uri => "http://testuri.com")
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should associate project" do
|
21
|
+
@iteration.associate(eval(@hash_vals))
|
22
|
+
@iteration.project.should_not be_nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "refreshing" do
|
27
|
+
before do
|
28
|
+
@iteration = Iteration.new(:rally_uri => "http://testuri.com")
|
29
|
+
end
|
30
|
+
it "should assign name" do
|
31
|
+
@iteration.refresh
|
32
|
+
@iteration.name.should == "Iteration1"
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should assign created_on" do
|
36
|
+
@iteration.refresh
|
37
|
+
@iteration.created_on.should == Date.parse("Jul 13, 2009")
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should assign end_date" do
|
41
|
+
@iteration.refresh
|
42
|
+
@iteration.end_date.should == Date.parse("2009-07-18T23:59:59.000Z")
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should assign notes" do
|
46
|
+
@iteration.refresh
|
47
|
+
@iteration.name.should == "Iteration1"
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should assign resources" do
|
51
|
+
@iteration.refresh
|
52
|
+
@iteration.resources.should == "2.0"
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should assign start_date" do
|
56
|
+
@iteration.refresh
|
57
|
+
@iteration.start_date.should == Date.parse("2009-07-13T00:00:00.000Z")
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should assign state" do
|
61
|
+
@iteration.refresh
|
62
|
+
@iteration.state.should == "Planning"
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should assign theme" do
|
66
|
+
@iteration.refresh
|
67
|
+
@iteration.theme.should == "This is the theme"
|
68
|
+
end
|
69
|
+
|
70
|
+
#it "should assign user_iteration_capacities" do
|
71
|
+
# @iteration.refresh
|
72
|
+
# @iteration.user_iteration_capacities.should == "Iteration1"
|
73
|
+
#end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|