tracker_api 1.7.1 → 1.11.0
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.
- checksums.yaml +4 -4
- data/.travis.yml +2 -1
- data/README.md +16 -0
- data/lib/tracker_api.rb +19 -0
- data/lib/tracker_api/client.rb +16 -35
- data/lib/tracker_api/endpoints/attachment.rb +38 -0
- data/lib/tracker_api/endpoints/attachments.rb +22 -0
- data/lib/tracker_api/endpoints/blockers.rb +20 -0
- data/lib/tracker_api/endpoints/comment.rb +9 -2
- data/lib/tracker_api/endpoints/iteration.rb +35 -0
- data/lib/tracker_api/endpoints/release.rb +17 -0
- data/lib/tracker_api/endpoints/releases.rb +20 -0
- data/lib/tracker_api/endpoints/reviews.rb +21 -0
- data/lib/tracker_api/endpoints/search.rb +1 -1
- data/lib/tracker_api/endpoints/stories.rb +10 -0
- data/lib/tracker_api/error.rb +12 -2
- data/lib/tracker_api/file_utility.rb +16 -0
- data/lib/tracker_api/resources/activity.rb +1 -1
- data/lib/tracker_api/resources/blocker.rb +18 -0
- data/lib/tracker_api/resources/comment.rb +35 -0
- data/lib/tracker_api/resources/cycle_time_details.rb +21 -0
- data/lib/tracker_api/resources/daily_history_container.rb +13 -0
- data/lib/tracker_api/resources/epic.rb +9 -0
- data/lib/tracker_api/resources/file_attachment.rb +37 -0
- data/lib/tracker_api/resources/iteration.rb +14 -0
- data/lib/tracker_api/resources/project.rb +13 -0
- data/lib/tracker_api/resources/release.rb +29 -0
- data/lib/tracker_api/resources/review.rb +19 -0
- data/lib/tracker_api/resources/review_type.rb +15 -0
- data/lib/tracker_api/resources/story.rb +49 -6
- data/lib/tracker_api/version.rb +1 -1
- data/lib/virtus/attribute/nullify_blank.rb +1 -1
- data/test/client_test.rb +52 -52
- data/test/comment_test.rb +46 -4
- data/test/error_test.rb +47 -0
- data/test/file_attachment_test.rb +19 -0
- data/test/iteration_test.rb +31 -0
- data/test/minitest_helper.rb +5 -2
- data/test/project_test.rb +59 -47
- data/test/release_test.rb +22 -0
- data/test/story_test.rb +65 -52
- data/test/task_test.rb +3 -3
- data/test/vcr/cassettes/create_attachments.json +1 -0
- data/test/vcr/cassettes/create_comment.json +1 -1
- data/test/vcr/cassettes/create_comment_with_attachment.json +1 -0
- data/test/vcr/cassettes/create_story_comment.json +1 -1
- data/test/vcr/cassettes/delete_an_attachment.json +1 -0
- data/test/vcr/cassettes/delete_attachments.json +1 -0
- data/test/vcr/cassettes/delete_comment.json +1 -0
- data/test/vcr/cassettes/delete_comments.json +1 -0
- data/test/vcr/cassettes/get_current_iteration.json +1 -1
- data/test/vcr/cassettes/get_cycle_time_details.json +1 -0
- data/test/vcr/cassettes/get_daily_history_container.json +1 -0
- data/test/vcr/cassettes/get_releases.json +1 -0
- data/test/vcr/cassettes/get_story_in_epic.json +1 -1
- data/test/vcr/cassettes/get_story_reviews.json +1 -0
- data/test/vcr/cassettes/release_stories.json +1 -0
- data/test/vcr/cassettes/search_project.json +1 -1
- data/test/workspace_test.rb +5 -5
- data/tracker_api.gemspec +3 -2
- metadata +68 -9
data/lib/tracker_api/error.rb
CHANGED
@@ -5,14 +5,24 @@ module TrackerApi
|
|
5
5
|
def initialize(wrapped_exception)
|
6
6
|
@wrapped_exception = wrapped_exception
|
7
7
|
@response = wrapped_exception.response
|
8
|
-
message = if wrapped_exception.is_a?(Faraday::
|
8
|
+
message = if wrapped_exception.is_a?(Faraday::ParsingError)
|
9
9
|
wrapped_exception.message
|
10
|
-
elsif
|
10
|
+
elsif faraday_response_error?(wrapped_exception)
|
11
11
|
wrapped_exception.response.inspect
|
12
12
|
else
|
13
13
|
wrapped_exception.instance_variable_get(:@wrapped_exception).inspect
|
14
14
|
end
|
15
15
|
super(message)
|
16
16
|
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
# faraday 16.0 re-organized their errors. The errors we're interested in,
|
21
|
+
# Faraday::ClientError before 16.0 and Faraday::ServerError introduced in
|
22
|
+
# 16.0, are represented by this conditional.
|
23
|
+
def faraday_response_error?(wrapped_exception)
|
24
|
+
wrapped_exception.is_a?(Faraday::Error) &&
|
25
|
+
wrapped_exception.respond_to?(:response)
|
26
|
+
end
|
17
27
|
end
|
18
28
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module TrackerApi
|
2
|
+
class FileUtility
|
3
|
+
class << self
|
4
|
+
def get_file_upload(file)
|
5
|
+
mime_type = MimeMagic.by_path(file)
|
6
|
+
{ :file => Faraday::UploadIO.new(file, mime_type) }
|
7
|
+
end
|
8
|
+
|
9
|
+
def check_files_exist(files)
|
10
|
+
files.each do | file |
|
11
|
+
raise ArgumentError, 'Attachment file not found.' unless Pathname.new(file).exist?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module TrackerApi
|
2
|
+
module Resources
|
3
|
+
class Blocker
|
4
|
+
include Shared::Base
|
5
|
+
|
6
|
+
attribute :client
|
7
|
+
attribute :project_id, Integer
|
8
|
+
|
9
|
+
attribute :story_id, Integer
|
10
|
+
attribute :person_id, Integer
|
11
|
+
attribute :description, String
|
12
|
+
attribute :resolved, Boolean
|
13
|
+
attribute :created_at, DateTime
|
14
|
+
attribute :updated_at, DateTime
|
15
|
+
attribute :kind, String
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -14,7 +14,10 @@ module TrackerApi
|
|
14
14
|
attribute :created_at, DateTime
|
15
15
|
attribute :updated_at, DateTime
|
16
16
|
attribute :file_attachment_ids, [Integer]
|
17
|
+
attribute :file_attachments, [FileAttachment]
|
17
18
|
attribute :google_attachment_ids, [Integer]
|
19
|
+
attribute :file_attachment_ids_to_add, [Integer]
|
20
|
+
attribute :file_attachment_ids_to_remove, [Integer]
|
18
21
|
attribute :commit_identifier, String
|
19
22
|
attribute :commit_type, String
|
20
23
|
attribute :kind, String
|
@@ -24,6 +27,8 @@ module TrackerApi
|
|
24
27
|
|
25
28
|
property :id
|
26
29
|
property :text
|
30
|
+
collection :file_attachment_ids_to_add
|
31
|
+
collection :file_attachment_ids_to_remove
|
27
32
|
end
|
28
33
|
|
29
34
|
def save
|
@@ -31,6 +36,36 @@ module TrackerApi
|
|
31
36
|
|
32
37
|
Endpoints::Comment.new(client).update(self, UpdateRepresenter.new(Comment.new(self.dirty_attributes)))
|
33
38
|
end
|
39
|
+
|
40
|
+
def delete
|
41
|
+
raise ArgumentError, 'Cannot delete a comment with an unknown story_id.' if story_id.nil?
|
42
|
+
|
43
|
+
Endpoints::Comment.new(client).delete(self)
|
44
|
+
end
|
45
|
+
|
46
|
+
# @param [Hash] params attributes to create the comment with
|
47
|
+
# @return [Comment] newly created Comment
|
48
|
+
def create_attachments(params)
|
49
|
+
self.file_attachment_ids_to_add = Endpoints::Attachments.new(client).create(self, params[:files]).collect(&:id)
|
50
|
+
save
|
51
|
+
end
|
52
|
+
|
53
|
+
def delete_attachments(attachment_ids = nil)
|
54
|
+
self.file_attachment_ids_to_remove = attachment_ids || attachments.collect(&:id)
|
55
|
+
save
|
56
|
+
end
|
57
|
+
|
58
|
+
# Provides a list of all the attachments on the comment.
|
59
|
+
#
|
60
|
+
# @reload Boolean to reload the attachments
|
61
|
+
# @return [Array[FileAttachments]]
|
62
|
+
def attachments(reload: false)
|
63
|
+
if !reload && @file_attachments.present?
|
64
|
+
@file_attachments
|
65
|
+
else
|
66
|
+
@file_attachments = Endpoints::Attachment.new(client).get(self)
|
67
|
+
end
|
68
|
+
end
|
34
69
|
end
|
35
70
|
end
|
36
71
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module TrackerApi
|
2
|
+
module Resources
|
3
|
+
class CycleTimeDetails
|
4
|
+
include Shared::Base
|
5
|
+
|
6
|
+
attribute :project_id, Integer
|
7
|
+
attribute :iteration_number, Integer
|
8
|
+
attribute :total_cycle_time, Integer
|
9
|
+
attribute :started_time, Integer
|
10
|
+
attribute :started_count, Integer
|
11
|
+
attribute :finished_time, Integer
|
12
|
+
attribute :finished_count, Integer
|
13
|
+
attribute :delivered_time, Integer
|
14
|
+
attribute :delivered_count, Integer
|
15
|
+
attribute :rejected_time, Integer
|
16
|
+
attribute :rejected_count, Integer
|
17
|
+
attribute :story_id, Integer
|
18
|
+
attribute :kind, String
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module TrackerApi
|
2
|
+
module Resources
|
3
|
+
class DailyHistoryContainer
|
4
|
+
include Shared::Base
|
5
|
+
|
6
|
+
attribute :project_id, Integer
|
7
|
+
attribute :iteration_number, Integer
|
8
|
+
attribute :header, [String]
|
9
|
+
attribute :data, [Enumerable]
|
10
|
+
attribute :kind, String
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -34,6 +34,15 @@ module TrackerApi
|
|
34
34
|
|
35
35
|
Endpoints::Epic.new(client).update(self, UpdateRepresenter.new(self))
|
36
36
|
end
|
37
|
+
|
38
|
+
# @param [Hash] params attributes to create the comment with
|
39
|
+
# @return [Comment] newly created Comment
|
40
|
+
def create_comment(params)
|
41
|
+
files = params.delete(:files)
|
42
|
+
comment = Endpoints::Comment.new(client).create(project_id, id, params)
|
43
|
+
comment.create_attachments(files: files) if files.present?
|
44
|
+
comment
|
45
|
+
end
|
37
46
|
end
|
38
47
|
end
|
39
48
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module TrackerApi
|
2
|
+
module Resources
|
3
|
+
class FileAttachment
|
4
|
+
include Shared::Base
|
5
|
+
|
6
|
+
attribute :comment, Comment
|
7
|
+
|
8
|
+
attribute :id, Integer
|
9
|
+
attribute :big_url, String
|
10
|
+
attribute :content_type, String
|
11
|
+
attribute :created_at, DateTime
|
12
|
+
attribute :download_url, String
|
13
|
+
attribute :filename, String
|
14
|
+
attribute :height, Integer
|
15
|
+
attribute :kind, String
|
16
|
+
attribute :size, Integer
|
17
|
+
attribute :thumbnail_url, String
|
18
|
+
attribute :thumbnailable, Boolean
|
19
|
+
attribute :uploaded, Boolean
|
20
|
+
attribute :uploader_id, Integer
|
21
|
+
attribute :width, Integer
|
22
|
+
|
23
|
+
def delete
|
24
|
+
comment.delete_attachments([id])
|
25
|
+
end
|
26
|
+
|
27
|
+
# TODO : Implement download properly.
|
28
|
+
# Look at Attchment#download for more details
|
29
|
+
# The big_url actually has the AWS S3 link for the file
|
30
|
+
# def download
|
31
|
+
# file_data = Endpoints::Attachment.new(comment.client).download(download_url)
|
32
|
+
# File.open(filename, 'wb') { |fp| fp.write(file_data) }
|
33
|
+
# end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
@@ -24,6 +24,20 @@ module TrackerApi
|
|
24
24
|
def stories=(data)
|
25
25
|
super.each { |s| s.client = client }
|
26
26
|
end
|
27
|
+
|
28
|
+
# Provides a list of all the cycle_time_details of each story in the iteration.
|
29
|
+
#
|
30
|
+
# @return [Array[CycleTimeDetails]] array of cycle_time_details of iterations in this project
|
31
|
+
def cycle_time_details
|
32
|
+
Endpoints::Iteration.new(client).get_analytics_cycle_time_details(project_id, number)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns per day information of story points and counts by state for the given iteration.
|
36
|
+
#
|
37
|
+
# @return [DailyHistoryContainer]
|
38
|
+
def get_history
|
39
|
+
Endpoints::Iteration.new(client).get_history(project_id, number)
|
40
|
+
end
|
27
41
|
end
|
28
42
|
end
|
29
43
|
end
|
@@ -131,6 +131,19 @@ module TrackerApi
|
|
131
131
|
Endpoints::Stories.new(client).get(id, params)
|
132
132
|
end
|
133
133
|
|
134
|
+
# Provides a list of all the releases in the project.
|
135
|
+
#
|
136
|
+
# @param [Hash] params
|
137
|
+
# @option params [String] :with_state A release's current_state which all returned releases must match.
|
138
|
+
# Valid enumeration values: accepted, delivered, finished, started, rejected, unstarted, unscheduled
|
139
|
+
# @option params [Integer] :offset With the first release in your priority list as 0,
|
140
|
+
# the index of the first release you want returned.
|
141
|
+
# @option params [Integer] :limit The number of releases you want returned.
|
142
|
+
# @return [Array[Release]] releases associated with this project
|
143
|
+
def releases(params={})
|
144
|
+
Endpoints::Releases.new(client).get(id, params)
|
145
|
+
end
|
146
|
+
|
134
147
|
# Provides a list of all the memberships in the project.
|
135
148
|
#
|
136
149
|
# @param [Hash] params
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module TrackerApi
|
2
|
+
module Resources
|
3
|
+
class Release
|
4
|
+
include Shared::Base
|
5
|
+
|
6
|
+
attribute :client
|
7
|
+
|
8
|
+
attribute :project_id, Integer
|
9
|
+
attribute :name, String
|
10
|
+
attribute :description, String
|
11
|
+
attribute :current_state, String # (accepted, delivered, finished, started, rejected, planned, unstarted, unscheduled)
|
12
|
+
attribute :accepted_at, DateTime
|
13
|
+
attribute :deadline, DateTime
|
14
|
+
attribute :labels, [Label]
|
15
|
+
attribute :created_at, DateTime
|
16
|
+
attribute :updated_at, DateTime
|
17
|
+
attribute :url, String
|
18
|
+
attribute :kind, String
|
19
|
+
|
20
|
+
# Provides a list of all the stories in the release.
|
21
|
+
#
|
22
|
+
# @param [Hash] params
|
23
|
+
# @return [Array[Story]] stories of this release
|
24
|
+
def stories(params={})
|
25
|
+
Endpoints::Stories.new(client).get_release(project_id, id, params)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module TrackerApi
|
2
|
+
module Resources
|
3
|
+
class Review
|
4
|
+
include Shared::Base
|
5
|
+
|
6
|
+
attribute :client
|
7
|
+
|
8
|
+
attribute :id, Integer
|
9
|
+
attribute :story_id, Integer
|
10
|
+
attribute :review_type_id, Integer
|
11
|
+
attribute :reviewer_id, Integer
|
12
|
+
attribute :status, String # (unstarted, in_review, pass, revise)
|
13
|
+
attribute :created_at, DateTime
|
14
|
+
attribute :updated_at, DateTime
|
15
|
+
attribute :kind, String
|
16
|
+
attribute :review_type, ReviewType
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module TrackerApi
|
2
|
+
module Resources
|
3
|
+
class ReviewType
|
4
|
+
include Shared::Base
|
5
|
+
|
6
|
+
attribute :id, Integer
|
7
|
+
attribute :project_id, Integer
|
8
|
+
attribute :name, String
|
9
|
+
attribute :hidden, Boolean
|
10
|
+
attribute :created_at, DateTime
|
11
|
+
attribute :updated_at, DateTime
|
12
|
+
attribute :kind, String
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -8,6 +8,7 @@ module TrackerApi
|
|
8
8
|
attribute :accepted_at, DateTime
|
9
9
|
attribute :after_id, Integer
|
10
10
|
attribute :before_id, Integer
|
11
|
+
attribute :blockers, [Blocker]
|
11
12
|
attribute :comment_ids, [Integer]
|
12
13
|
attribute :comments, [Comment]
|
13
14
|
attribute :created_at, DateTime
|
@@ -21,7 +22,7 @@ module TrackerApi
|
|
21
22
|
attribute :integration_id, Integer
|
22
23
|
attribute :kind, String
|
23
24
|
attribute :label_ids, [Integer]
|
24
|
-
attribute :labels, [Label]
|
25
|
+
attribute :labels, [Label]
|
25
26
|
attribute :name, String
|
26
27
|
attribute :owned_by_id, Integer # deprecated!
|
27
28
|
attribute :owned_by, Person
|
@@ -31,6 +32,7 @@ module TrackerApi
|
|
31
32
|
attribute :project_id, Integer
|
32
33
|
attribute :requested_by, Person
|
33
34
|
attribute :requested_by_id, Integer
|
35
|
+
attribute :reviews, [Review]
|
34
36
|
attribute :story_type, String # (feature, bug, chore, release)
|
35
37
|
attribute :task_ids, [Integer]
|
36
38
|
attribute :tasks, [Task]
|
@@ -54,7 +56,21 @@ module TrackerApi
|
|
54
56
|
property :deadline
|
55
57
|
property :requested_by_id
|
56
58
|
property :owner_ids, if: ->(_) { !owner_ids.blank? }
|
57
|
-
|
59
|
+
property :project_id
|
60
|
+
|
61
|
+
# Use render_empty: false to address: https://github.com/dashofcode/tracker_api/issues/110
|
62
|
+
# - The default value of the labels attribute in Resources::Story is an empty array.
|
63
|
+
# - If the value of labels is not change (i.e. not dirty) then when a new Story
|
64
|
+
# is created from the dirty attributes in the save method the labels attributes becomes
|
65
|
+
# an empty array again. render_empty: false keeps this from rendering in the json passed
|
66
|
+
# in the API PUT request. It is is empty then the labels will be cleared.
|
67
|
+
# - The next issue is that there is no way to delete all the labels from a Story with
|
68
|
+
# the current implementation.
|
69
|
+
#
|
70
|
+
# NOTE: There are two solutions: 1) remove dirty tracking 2) rewrite without virtus
|
71
|
+
# SEE: https://github.com/dashofcode/tracker_api/pull/98
|
72
|
+
collection :labels, class: Label, decorator: Label::UpdateRepresenter, render_empty: false
|
73
|
+
|
58
74
|
property :integration_id
|
59
75
|
property :external_id
|
60
76
|
end
|
@@ -104,15 +120,19 @@ module TrackerApi
|
|
104
120
|
Endpoints::Activity.new(client).get_story(project_id, id, params)
|
105
121
|
end
|
106
122
|
|
123
|
+
def blockers(params = {})
|
124
|
+
Endpoints::Blockers.new(client).get(project_id, id, params)
|
125
|
+
end
|
126
|
+
|
107
127
|
# Provides a list of all the comments on the story.
|
108
128
|
#
|
109
129
|
# @param [Hash] params
|
110
130
|
# @return [Array[Comment]]
|
111
|
-
def comments(
|
112
|
-
if
|
131
|
+
def comments(reload: false)
|
132
|
+
if !reload && @comments.present?
|
113
133
|
@comments
|
114
134
|
else
|
115
|
-
@comments = Endpoints::Comments.new(client).get(project_id, id
|
135
|
+
@comments = Endpoints::Comments.new(client).get(project_id, id)
|
116
136
|
end
|
117
137
|
end
|
118
138
|
|
@@ -152,6 +172,17 @@ module TrackerApi
|
|
152
172
|
end
|
153
173
|
end
|
154
174
|
|
175
|
+
# Returns the story's original ("undirtied") project_id
|
176
|
+
#
|
177
|
+
# @return Integer
|
178
|
+
def project_id
|
179
|
+
if dirty_attributes.key?(:project_id)
|
180
|
+
original_attributes[:project_id]
|
181
|
+
else
|
182
|
+
@project_id
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
155
186
|
# @param [Hash] params attributes to create the task with
|
156
187
|
# @return [Task] newly created Task
|
157
188
|
def create_task(params)
|
@@ -161,15 +192,27 @@ module TrackerApi
|
|
161
192
|
# @param [Hash] params attributes to create the comment with
|
162
193
|
# @return [Comment] newly created Comment
|
163
194
|
def create_comment(params)
|
164
|
-
|
195
|
+
files = params.delete(:files)
|
196
|
+
comment = Endpoints::Comment.new(client).create(project_id, id, params)
|
197
|
+
comment.create_attachments(files: files) if files.present?
|
198
|
+
comment
|
165
199
|
end
|
166
200
|
|
167
201
|
# Save changes to an existing Story.
|
168
202
|
def save
|
169
203
|
raise ArgumentError, 'Can not update a story with an unknown project_id.' if project_id.nil?
|
204
|
+
return self unless dirty?
|
170
205
|
|
171
206
|
Endpoints::Story.new(client).update(self, UpdateRepresenter.new(Story.new(self.dirty_attributes)))
|
172
207
|
end
|
208
|
+
|
209
|
+
def reviews(params = {})
|
210
|
+
if params.blank? && @reviews.present?
|
211
|
+
@reviews
|
212
|
+
else
|
213
|
+
@reviews = Endpoints::Reviews.new(client).get(project_id, id, params)
|
214
|
+
end
|
215
|
+
end
|
173
216
|
end
|
174
217
|
end
|
175
218
|
end
|