pivotal_tracker 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  = pivotal_tracker
2
2
 
3
- This is a simple ruby wrapper for the Pivotal Tracker V2 API utilizing HTTParty and Crack::XML.
3
+ This is a simple ruby wrapper for the Pivotal Tracker V3 API utilizing HTTParty and Crack::XML.
4
4
 
5
5
  == Setup
6
6
 
@@ -27,8 +27,8 @@ Notice that all data returned is parsed and built into Mash objects/arrays for y
27
27
 
28
28
  == Currently implemented methods (More to come!)
29
29
 
30
- * get_all_activities
31
- * get_all_project_activities(project_id)
30
+ * get_all_activities(options = {})
31
+ * get_all_project_activities(project_id, options = {})
32
32
  * get_all_projects
33
33
  * get_project(project_id)
34
34
  * create_project(name, options = {})
@@ -36,8 +36,18 @@ Notice that all data returned is parsed and built into Mash objects/arrays for y
36
36
  * get_project_membership(project_id, membership_id)
37
37
  * add_project_membership(project_id, role, email, options = {})
38
38
  * remove_project_membership(project_id, membership_id)
39
- * get_all_project_iterations(project_id)
40
- * get_all_project_stories(project_id)
39
+ * get_all_project_iterations(project_id, options = {})
40
+ * get_all_project_stories(project_id, options = {})
41
+ * Filter by passing in a filter hash:
42
+ {:filter => {:type => "bug", :id => "1,2,5"}}
43
+ * Read more on filtering here: http://www.pivotaltracker.com/help#howcanasearchberefined
44
+ * add_project_story(project_id, story)
45
+ * update_project_story(project_id, story_id, story)
46
+ * delete_project_story(project_id, story_id)
47
+ * add_project_story_note(project_id, story_id, text)
48
+ * move_project_story(project_id, story_id, direction, target_story_id)
49
+ * direction can be either "before" or "after"
50
+
41
51
 
42
52
  == Note on Patches/Pull Requests
43
53
 
data/Rakefile CHANGED
@@ -5,7 +5,7 @@ begin
5
5
  require 'jeweler'
6
6
  Jeweler::Tasks.new do |gem|
7
7
  gem.name = "pivotal_tracker"
8
- gem.summary = %Q{Ruby wrapper for the Pivotal Tracker V2 API}
8
+ gem.summary = %Q{Ruby wrapper for the Pivotal Tracker V3 API}
9
9
  gem.email = "joslyn.esser@gmail.com"
10
10
  gem.homepage = "http://github.com/joslynesser/pivotal_tracker"
11
11
  gem.authors = ["Joslyn Esser"]
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.1
1
+ 0.3.0
@@ -10,17 +10,17 @@ class PivotalTracker
10
10
  def initialize(api_token, options = {})
11
11
  self.class.headers 'X-TrackerToken' => api_token
12
12
  use_ssl = options.delete(:ssl)
13
- self.class.base_uri "http#{'s' if use_ssl}://www.pivotaltracker.com/services/v2"
13
+ self.class.base_uri "http#{'s' if use_ssl}://www.pivotaltracker.com/services/v3"
14
14
  end
15
15
 
16
- def get_all_activities
17
- response = self.class.get("/activities")
16
+ def get_all_activities(options = {})
17
+ response = self.class.get("/activities", :query => options)
18
18
  raise_errors(response)
19
19
  parse_response(response, 'activities')
20
20
  end
21
21
 
22
- def get_all_project_activities(project_id)
23
- response = self.class.get("/projects/#{project_id}/activities")
22
+ def get_all_project_activities(project_id, options = {})
23
+ response = self.class.get("/projects/#{project_id}/activities", :query => options)
24
24
  raise_errors(response)
25
25
  parse_response(response, 'activities')
26
26
  end
@@ -67,17 +67,51 @@ class PivotalTracker
67
67
  parse_response(response, 'membership')
68
68
  end
69
69
 
70
- def get_all_project_iterations(project_id)
71
- response = self.class.get("/projects/#{project_id}/iterations")
70
+ def get_all_project_iterations(project_id, options = {})
71
+ response = self.class.get("/projects/#{project_id}/iterations", :query => options)
72
72
  raise_errors(response)
73
73
  parse_response(response, 'iterations')
74
74
  end
75
75
 
76
- def get_all_project_stories(project_id)
77
- response = self.class.get("/projects/#{project_id}/stories")
76
+ def get_all_project_stories(project_id, options = {})
77
+ if options[:filter]
78
+ options[:filter] = options[:filter].inject([]) {|f,(key,value)| f << "#{key}:#{value}"}.join(' ')
79
+ end
80
+
81
+ response = self.class.get("/projects/#{project_id}/stories", :query => options)
78
82
  raise_errors(response)
79
83
  parse_response(response, 'stories')
80
84
  end
85
+
86
+ def add_project_story(project_id, story)
87
+ response = self.class.post("/projects/#{project_id}/stories", :body => {:story => story})
88
+ raise_errors(response)
89
+ parse_response(response, 'story')
90
+ end
91
+
92
+ def update_project_story(project_id, story_id, story)
93
+ response = self.class.put("/projects/#{project_id}/stories/#{story_id}", :body => {:story => story})
94
+ raise_errors(response)
95
+ parse_response(response, 'story')
96
+ end
97
+
98
+ def delete_project_story(project_id, story_id)
99
+ response = self.class.delete("/projects/#{project_id}/stories/#{story_id}")
100
+ raise_errors(response)
101
+ parse_response(response, 'story')
102
+ end
103
+
104
+ def add_project_story_note(project_id, story_id, text)
105
+ response = self.class.post("/projects/#{project_id}/stories/#{story_id}/notes", :body => {:note => {:text => text}})
106
+ raise_errors(response)
107
+ parse_response(response, 'note')
108
+ end
109
+
110
+ def move_project_story(project_id, story_id, direction, target_story_id)
111
+ response = self.class.post("/projects/#{project_id}/stories/#{story_id}/moves", :body => {:move => {:move => direction, :target => target_story_id}})
112
+ raise_errors(response)
113
+ parse_response(response, 'story')
114
+ end
81
115
 
82
116
  private
83
117
 
@@ -91,8 +125,10 @@ class PivotalTracker
91
125
  raise PivotalTracker::General, "(#{response.code}): #{response.message}"
92
126
  when 404
93
127
  raise PivotalTracker::ResourceNotFound, "(#{response.code}): #{response.message}"
128
+ when 422
129
+ raise PivotalTracker::ResourceInvalid, "(#{response.code}): #{response['errors'].inspect if response['errors']}"
94
130
  when 500
95
- raise PivotalTracker::InformPivotal, "Pivotal Tracker had an internal error. Please let them know. (#{response.code}): #{response.message}"
131
+ raise PivotalTracker::InformPivotal, "Pivotal Tracker had an internal error. Please let them know. (#{response.code}): #{response.message} - #{response['message'] if response}"
96
132
  when 502..503
97
133
  raise PivotalTracker::Unavailable, "(#{response.code}): #{response.message}"
98
134
  end
@@ -13,4 +13,5 @@ class PivotalTracker::Unauthorized < PivotalTracker::Error; end
13
13
  class PivotalTracker::General < StandardError; end
14
14
  class PivotalTracker::Unavailable < StandardError; end
15
15
  class PivotalTracker::InformPivotal < StandardError; end
16
- class PivotalTracker::ResourceNotFound < StandardError; end
16
+ class PivotalTracker::ResourceNotFound < StandardError; end
17
+ class PivotalTracker::ResourceInvalid < StandardError; end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{pivotal_tracker}
8
- s.version = "0.2.1"
8
+ s.version = "0.3.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Joslyn Esser"]
12
- s.date = %q{2009-12-04}
12
+ s.date = %q{2010-02-05}
13
13
  s.email = %q{joslyn.esser@gmail.com}
14
14
  s.extra_rdoc_files = [
15
15
  "LICENSE",
@@ -29,6 +29,7 @@ Gem::Specification.new do |s|
29
29
  "spec/fixtures/iterations.xml",
30
30
  "spec/fixtures/membership.xml",
31
31
  "spec/fixtures/memberships.xml",
32
+ "spec/fixtures/note.xml",
32
33
  "spec/fixtures/project.xml",
33
34
  "spec/fixtures/projects.xml",
34
35
  "spec/fixtures/stories.xml",
@@ -41,7 +42,7 @@ Gem::Specification.new do |s|
41
42
  s.rdoc_options = ["--charset=UTF-8"]
42
43
  s.require_paths = ["lib"]
43
44
  s.rubygems_version = %q{1.3.5}
44
- s.summary = %q{Ruby wrapper for the Pivotal Tracker V2 API}
45
+ s.summary = %q{Ruby wrapper for the Pivotal Tracker V3 API}
45
46
  s.test_files = [
46
47
  "spec/pivotal_tracker_spec.rb",
47
48
  "spec/spec_helper.rb"
@@ -2,18 +2,38 @@
2
2
  <activities type="array">
3
3
  <activity>
4
4
  <id type="integer">1031</id>
5
- <project>Sample Project</project>
6
- <story>More power to shields</story>
5
+ <version type="integer">175</version>
6
+ <event_type>story_update</event_type>
7
+ <occurred_at type="datetime">2009/12/14 14:12:09 PST</occurred_at>
8
+ <author>James Kirk</author>
9
+ <project_id type="integer">26</project_id>
7
10
  <description>James Kirk accepted &quot;More power to shields&quot;</description>
8
- <author>James Kirk </author>
9
- <when>06/01/2009 08:22 AM</when>
11
+ <stories>
12
+ <story>
13
+ <id type="integer">109</id>
14
+ <url>https://www.pivotaltracker.com/services/v3/projects/26/stories/109</url>
15
+ <lighthouse_id>43</lighthouse_id>
16
+ <lighthouse_url>http://mylighthouseapp.com/projects/100/tickets/43</lighthouse_url>
17
+ <accepted_at type="datetime">2009/12/14 22:12:09 UTC</accepted_at>
18
+ <current_state>accepted</current_state>
19
+ </story>
20
+ </stories>
10
21
  </activity>
11
22
  <activity>
12
23
  <id type="integer">1030</id>
13
- <project>Another Sample Project</project>
14
- <story>Warp speed</story>
15
- <description>Montgomery Scott rejected &quot;Warp speend&quot; with comments: &quot;Warp speed isn't working&quot;</description>
24
+ <version type="integer">175</version>
25
+ <event_type>story_update</event_type>
26
+ <occurred_at type="datetime">2009/12/14 14:12:09 PST</occurred_at>
16
27
  <author>Montgomery Scott</author>
17
- <when>06/01/2009 12:22 AM</when>
28
+ <project_id type="integer">28</project_id>
29
+ <description>Montgomery Scott rejected &quot;Warp speed&quot; with comments: &quot;Warp speed isn't working&quot;</description>
30
+ <stories>
31
+ <story>
32
+ <id type="integer">119</id>
33
+ <url>https://www.pivotaltracker.com/services/v3/projects/28/stories/119</url>
34
+ <accepted_at type="datetime">2009/12/14 22:12:09 UTC</accepted_at>
35
+ <current_state>rejected</current_state>
36
+ </story>
37
+ </stories>
18
38
  </activity>
19
39
  </activities>
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <note>
3
+ <id type="integer">42</id>
4
+ <text>new note via API</text>
5
+ <author>Spock (young)</author>
6
+ <noted_at type="datetime">2009/01/16 18:53:51 UTC</noted_at>
7
+ </note>
@@ -1,18 +1,38 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
2
  <project>
3
- <id>27</id>
4
- <name>Cardassian War Plans</name>
3
+ <id>1</id>
4
+ <name>Sample Project</name>
5
5
  <iteration_length type="integer">2</iteration_length>
6
6
  <week_start_day>Monday</week_start_day>
7
7
  <point_scale>0,1,2,3</point_scale>
8
+ <account>James Kirks Account</account>
8
9
  <velocity_scheme>Average of 4 iterations</velocity_scheme>
10
+ <current_velocity>10</current_velocity>
9
11
  <initial_velocity>10</initial_velocity>
10
12
  <number_of_done_iterations_to_show>12</number_of_done_iterations_to_show>
13
+ <labels>shields,transporter</labels>
11
14
  <allow_attachments>true</allow_attachments>
12
15
  <public>false</public>
13
16
  <use_https>true</use_https>
14
17
  <bugs_and_chores_are_estimatable>false</bugs_and_chores_are_estimatable>
15
18
  <commit_mode>false</commit_mode>
19
+ <last_activity_at type="datetime">2010/01/16 17:39:10 CST</last_activity_at>
16
20
  <memberships>
21
+ <membership>
22
+ <id>1006</id>
23
+ <person>
24
+ <email>kirkybaby@earth.ufp</email>
25
+ <name>James T. Kirk</name>
26
+ <initials>JTK</initials>
27
+ </person>
28
+ <role>Owner</role>
29
+ </membership>
17
30
  </memberships>
18
- </project>
31
+ <integrations>
32
+ <integration>
33
+ <id>4532</id>
34
+ <title>Lighthouse Sample Integration</title>
35
+ <active type="boolean">true</active>
36
+ </integration>
37
+ </integrations>
38
+ </project>
@@ -35,11 +35,23 @@ describe PivotalTracker do
35
35
  activities.first.description.should == "James Kirk accepted \"More power to shields\""
36
36
  end
37
37
 
38
+ it "should filter recent activities" do
39
+ stub_get('/activities?newer_than_version=170', 'activities.xml')
40
+ activities = @tracker.get_all_activities(:newer_than_version => 170)
41
+ activities.first.description.should == "James Kirk accepted \"More power to shields\""
42
+ end
43
+
38
44
  it "should get all recent activities for a given project" do
39
45
  stub_get('/projects/1/activities', 'activities.xml')
40
46
  activities = @tracker.get_all_project_activities(1)
41
47
  activities.first.description.should == "James Kirk accepted \"More power to shields\""
42
48
  end
49
+
50
+ it "should filter recent activities for a given project" do
51
+ stub_get('/projects/1/activities?limit=50', 'activities.xml')
52
+ activities = @tracker.get_all_project_activities(1, :limit => 50)
53
+ activities.first.description.should == "James Kirk accepted \"More power to shields\""
54
+ end
43
55
  end
44
56
 
45
57
  describe "Projects" do
@@ -53,13 +65,19 @@ describe PivotalTracker do
53
65
  it "should get a project" do
54
66
  stub_get("/projects/27", 'project.xml')
55
67
  project = @tracker.get_project(27)
56
- project.name.should == 'Cardassian War Plans'
68
+ project.name.should == 'Sample Project'
57
69
  end
58
70
 
59
71
  it "should create a project" do
60
72
  stub_post('/projects', 'project.xml')
61
73
  project = @tracker.create_project('Cardassian War Plans')
62
- project.name.should == 'Cardassian War Plans'
74
+ project.name.should == 'Sample Project'
75
+ end
76
+
77
+ it "should create a project with options" do
78
+ stub_post('/projects', 'project.xml')
79
+ project = @tracker.create_project('Cardassian War Plans', :no_owner => true)
80
+ project.name.should == 'Sample Project'
63
81
  end
64
82
  end
65
83
 
@@ -101,7 +119,9 @@ describe PivotalTracker do
101
119
  end
102
120
 
103
121
  it "should get iterations by limit and offset" do
104
- pending
122
+ stub_get('/projects/1/iterations?offset=1&limit=2', 'iterations.xml')
123
+ iterations = @tracker.get_all_project_iterations(1, :offset => 1, :limit => 2)
124
+ iterations.first.stories.first.story_type.should == 'feature'
105
125
  end
106
126
  end
107
127
 
@@ -112,16 +132,46 @@ describe PivotalTracker do
112
132
  stories.first.owned_by.should == 'Montgomery Scott'
113
133
  end
114
134
 
115
- it "should get all stories for a project based on a given filter" do
116
- pending
135
+ it "should get all stories for a project based on a given filter hash" do
136
+ stub_get("/projects/1/stories\?filter=type%3Afeature", 'stories.xml')
137
+ stories = @tracker.get_all_project_stories(1, :filter => {:type => 'feature'})
138
+ stories.first.owned_by.should == 'Montgomery Scott'
117
139
  end
118
140
 
119
141
  it "should get all stories for a project paginated by a limit and offset" do
120
- pending
142
+ stub_get("/projects/1/stories\?limit=10&offset=20", 'stories.xml')
143
+ stories = @tracker.get_all_project_stories(1, :limit => 10, :offset => 20)
144
+ stories.first.owned_by.should == 'Montgomery Scott'
121
145
  end
122
146
 
123
147
  it "should create a new story for a given project" do
124
- pending
148
+ stub_post('/projects/1/stories', 'story.xml')
149
+ story = @tracker.add_project_story(1, {})
150
+ story.name.should == 'Fire torpedoes'
151
+ end
152
+
153
+ it "should update an existing story for a given project and story" do
154
+ stub_put('/projects/1/stories/12', 'story.xml')
155
+ story = @tracker.update_project_story(1, 12, :name => "Fire torpedoes")
156
+ story.name.should == 'Fire torpedoes'
157
+ end
158
+
159
+ it "removes an existing story for a given project" do
160
+ stub_delete('/projects/1/stories/12', 'story.xml')
161
+ story = @tracker.delete_project_story(1, 12)
162
+ story.name.should == 'Fire torpedoes'
163
+ end
164
+
165
+ it "should create a new note for a given project and story" do
166
+ stub_post('/projects/1234/stories/5678/notes', 'note.xml')
167
+ note = @tracker.add_project_story_note(1234, 5678, 'new note via API')
168
+ note.text.should == 'new note via API'
169
+ end
170
+
171
+ it "should move a story before or after another story" do
172
+ stub_post('/projects/1/stories/1234/moves', 'story.xml')
173
+ story = @tracker.move_project_story(1, 1234, "after", 5678)
174
+ story.name.should == 'Fire torpedoes'
125
175
  end
126
176
  end
127
177
  end
@@ -9,7 +9,7 @@ require 'fakeweb'
9
9
  require 'ruby-debug'
10
10
 
11
11
  Spec::Runner.configure do |config|
12
-
12
+ config.before(:each) { FakeWeb.clean_registry }
13
13
  end
14
14
 
15
15
  FakeWeb.allow_net_connect = false
@@ -21,7 +21,7 @@ def fixture_file(filename)
21
21
  end
22
22
 
23
23
  def pivotal_tracker_url(url)
24
- url =~ /^http/ ? url : "http://www.pivotaltracker.com/services/v2#{url}"
24
+ url =~ /^http/ ? url : "http://www.pivotaltracker.com/services/v3#{url}"
25
25
  end
26
26
 
27
27
  def stub_get(url, filename, status=nil)
@@ -41,4 +41,4 @@ end
41
41
 
42
42
  def stub_delete(url, filename)
43
43
  FakeWeb.register_uri(:delete, pivotal_tracker_url(url), :body => fixture_file(filename))
44
- end
44
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pivotal_tracker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joslyn Esser
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-12-04 00:00:00 -08:00
12
+ date: 2010-02-05 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -75,6 +75,7 @@ files:
75
75
  - spec/fixtures/iterations.xml
76
76
  - spec/fixtures/membership.xml
77
77
  - spec/fixtures/memberships.xml
78
+ - spec/fixtures/note.xml
78
79
  - spec/fixtures/project.xml
79
80
  - spec/fixtures/projects.xml
80
81
  - spec/fixtures/stories.xml
@@ -109,7 +110,7 @@ rubyforge_project:
109
110
  rubygems_version: 1.3.5
110
111
  signing_key:
111
112
  specification_version: 3
112
- summary: Ruby wrapper for the Pivotal Tracker V2 API
113
+ summary: Ruby wrapper for the Pivotal Tracker V3 API
113
114
  test_files:
114
115
  - spec/pivotal_tracker_spec.rb
115
116
  - spec/spec_helper.rb