tracker_api 1.8.0 → 1.12.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.
Files changed (50) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +2 -1
  3. data/README.md +4 -0
  4. data/lib/tracker_api.rb +12 -0
  5. data/lib/tracker_api/client.rb +8 -6
  6. data/lib/tracker_api/endpoints/blockers.rb +20 -0
  7. data/lib/tracker_api/endpoints/iteration.rb +35 -0
  8. data/lib/tracker_api/endpoints/release.rb +17 -0
  9. data/lib/tracker_api/endpoints/releases.rb +20 -0
  10. data/lib/tracker_api/endpoints/review.rb +21 -0
  11. data/lib/tracker_api/endpoints/reviews.rb +21 -0
  12. data/lib/tracker_api/endpoints/search.rb +1 -1
  13. data/lib/tracker_api/endpoints/stories.rb +10 -0
  14. data/lib/tracker_api/error.rb +12 -2
  15. data/lib/tracker_api/resources/blocker.rb +18 -0
  16. data/lib/tracker_api/resources/cycle_time_details.rb +21 -0
  17. data/lib/tracker_api/resources/daily_history_container.rb +13 -0
  18. data/lib/tracker_api/resources/iteration.rb +14 -0
  19. data/lib/tracker_api/resources/project.rb +13 -0
  20. data/lib/tracker_api/resources/release.rb +29 -0
  21. data/lib/tracker_api/resources/review.rb +35 -0
  22. data/lib/tracker_api/resources/review_type.rb +15 -0
  23. data/lib/tracker_api/resources/story.rb +26 -0
  24. data/lib/tracker_api/version.rb +1 -1
  25. data/test/client_test.rb +52 -52
  26. data/test/comment_test.rb +13 -13
  27. data/test/error_test.rb +8 -2
  28. data/test/file_attachment_test.rb +4 -4
  29. data/test/iteration_test.rb +31 -0
  30. data/test/minitest_helper.rb +5 -2
  31. data/test/project_test.rb +59 -47
  32. data/test/release_test.rb +22 -0
  33. data/test/review_test.rb +27 -0
  34. data/test/story_test.rb +65 -48
  35. data/test/task_test.rb +3 -3
  36. data/test/vcr/cassettes/create_attachments.json +1 -1
  37. data/test/vcr/cassettes/create_comment_with_attachment.json +1 -1
  38. data/test/vcr/cassettes/delete_an_attachment.json +1 -1
  39. data/test/vcr/cassettes/delete_attachments.json +1 -1
  40. data/test/vcr/cassettes/get_current_iteration.json +1 -1
  41. data/test/vcr/cassettes/get_cycle_time_details.json +1 -0
  42. data/test/vcr/cassettes/get_daily_history_container.json +1 -0
  43. data/test/vcr/cassettes/get_releases.json +1 -0
  44. data/test/vcr/cassettes/get_story_reviews.json +1 -0
  45. data/test/vcr/cassettes/release_stories.json +1 -0
  46. data/test/vcr/cassettes/save_review.json +1 -0
  47. data/test/vcr/cassettes/search_project.json +1 -1
  48. data/test/workspace_test.rb +5 -5
  49. data/tracker_api.gemspec +1 -1
  50. metadata +35 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: b93cdb4a9ba207f6a81cdf67cebfddcdf2f3e99b
4
- data.tar.gz: de0f6b60cd0b86cf669fa499efae9acdacd472e5
2
+ SHA256:
3
+ metadata.gz: 7d0c3f349c8d57bc33c44401068ae359892d33a8d8ed2c5c20ed96110fe02b3b
4
+ data.tar.gz: adac66edc9ddf17883734c6908b1cce46cbac8b932702a7c3f31573b0ad38b13
5
5
  SHA512:
6
- metadata.gz: 992fbbca70adb9c0b3ff361eefae0b18d672e74c733d874d8fab8938b336266a8005c8d4c9579a00dfd0ec8d27ef54169174cd2be1c420cf9aa0f3cf9fe0ef0b
7
- data.tar.gz: d265ae95db9afb5e32d795e2beee4bfad199abb37840d00d8661909fefcef6129cd73f1a540275cc8bd9dee1413278da8526b47652c1984d2b4255c64bd89916
6
+ metadata.gz: ea30f0006b6dff8684c3d46381d65ddfc00d3a481fbcea3489f844c7bb2c687b065e618114af8cd4392c63d770fcf1ad1ea86aad1305a5c8fca4f95ba58d7150
7
+ data.tar.gz: e67ea8cd72e9947862bdd58230aecea038e311a14ecb081f65545003f2fb3d7924c680c55a4d34a1711492f9509a2da6c368d1c93689c7b9f92f2a91e8faf267
@@ -5,7 +5,8 @@ rvm:
5
5
  - 2.2
6
6
  - 2.3
7
7
  - 2.4
8
- # - "jruby"
8
+ - 2.5
9
+ - "jruby"
9
10
  # - rbx
10
11
  # - "1.8.7"
11
12
  # uncomment this line if your project needs to run something other than `rake`:
data/README.md CHANGED
@@ -86,6 +86,10 @@ task = story.tasks.first # Get
86
86
  task.complete = true
87
87
  task.save # Mark a task complete
88
88
 
89
+ review = story.reviews.first # Mark a review as complete
90
+ review.status = 'pass'
91
+ review.save
92
+
89
93
  epics = project.epics # Get all epics for a project
90
94
  epic = epics.first
91
95
  label = epic.label # Get an epic's label
@@ -38,8 +38,10 @@ module TrackerApi
38
38
 
39
39
  module Endpoints
40
40
  autoload :Activity, 'tracker_api/endpoints/activity'
41
+ autoload :Blockers, 'tracker_api/endpoints/blockers'
41
42
  autoload :Epic, 'tracker_api/endpoints/epic'
42
43
  autoload :Epics, 'tracker_api/endpoints/epics'
44
+ autoload :Iteration, 'tracker_api/endpoints/iteration'
43
45
  autoload :Iterations, 'tracker_api/endpoints/iterations'
44
46
  autoload :Labels, 'tracker_api/endpoints/labels'
45
47
  autoload :Me, 'tracker_api/endpoints/me'
@@ -62,6 +64,10 @@ module TrackerApi
62
64
  autoload :StoryTransitions, 'tracker_api/endpoints/story_transitions'
63
65
  autoload :Attachment, 'tracker_api/endpoints/attachment'
64
66
  autoload :Attachments, 'tracker_api/endpoints/attachments'
67
+ autoload :Releases, 'tracker_api/endpoints/releases'
68
+ autoload :Release, 'tracker_api/endpoints/release'
69
+ autoload :Review, 'tracker_api/endpoints/review'
70
+ autoload :Reviews, 'tracker_api/endpoints/reviews'
65
71
  end
66
72
 
67
73
  module Resources
@@ -71,6 +77,7 @@ module TrackerApi
71
77
  end
72
78
  autoload :Activity, 'tracker_api/resources/activity'
73
79
  autoload :Account, 'tracker_api/resources/account'
80
+ autoload :Blocker, 'tracker_api/resources/blocker'
74
81
  autoload :Change, 'tracker_api/resources/change'
75
82
  autoload :Epic, 'tracker_api/resources/epic'
76
83
  autoload :EpicsSearchResult, 'tracker_api/resources/epics_search_result'
@@ -93,5 +100,10 @@ module TrackerApi
93
100
  autoload :Webhook, 'tracker_api/resources/webhook'
94
101
  autoload :StoryTransition, 'tracker_api/resources/story_transition'
95
102
  autoload :FileAttachment, 'tracker_api/resources/file_attachment'
103
+ autoload :Release, 'tracker_api/resources/release'
104
+ autoload :CycleTimeDetails, 'tracker_api/resources/cycle_time_details'
105
+ autoload :DailyHistoryContainer, 'tracker_api/resources/daily_history_container'
106
+ autoload :Review, 'tracker_api/resources/review'
107
+ autoload :ReviewType, 'tracker_api/resources/review_type'
96
108
  end
97
109
  end
@@ -25,7 +25,7 @@ module TrackerApi
25
25
  @url = Addressable::URI.parse(url).to_s
26
26
  @api_version = options.fetch(:api_version, '/services/v5')
27
27
  @logger = options.fetch(:logger, ::Logger.new(nil))
28
- adapter = options.fetch(:adapter, :excon)
28
+ adapter = options.fetch(:adapter) { defined?(JRUBY_VERSION) ? :net_http : :excon }
29
29
  connection_options = options.fetch(:connection_options, { ssl: { verify: true } })
30
30
  @auto_paginate = options.fetch(:auto_paginate, true)
31
31
  @token = options[:token]
@@ -36,7 +36,7 @@ module TrackerApi
36
36
  @connection = Faraday.new({ url: @url }.merge(connection_options)) do |builder|
37
37
  # response
38
38
  builder.use Faraday::Response::RaiseError
39
- builder.response :json
39
+ builder.response :json, content_type: /\bjson/ # e.g., 'application/json; charset=utf-8'
40
40
 
41
41
  # request
42
42
  builder.request :multipart
@@ -190,7 +190,8 @@ module TrackerApi
190
190
  opts[:params] = options[:params] || {}
191
191
  opts[:token] = options[:token] || @token
192
192
  headers = { 'User-Agent' => USER_AGENT,
193
- 'X-TrackerToken' => opts.fetch(:token) }.merge(options.fetch(:headers, {}))
193
+ 'X-TrackerToken' => opts.fetch(:token),
194
+ 'Accept' => 'application/json' }.merge(options.fetch(:headers, {}))
194
195
 
195
196
  CONVENIENCE_HEADERS.each do |h|
196
197
  if header = options[h]
@@ -222,11 +223,12 @@ module TrackerApi
222
223
  req.body = body
223
224
  end
224
225
  response
225
- rescue Faraday::Error::ClientError => e
226
- case e.response[:status]
226
+ rescue Faraday::ClientError, Faraday::ServerError => e
227
+ status_code = e.response[:status]
228
+ case status_code
227
229
  when 400..499 then raise TrackerApi::Errors::ClientError.new(e)
228
230
  when 500..599 then raise TrackerApi::Errors::ServerError.new(e)
229
- else raise "Expected 4xx or 5xx HTTP status code"
231
+ else raise "Expected 4xx or 5xx HTTP status code; got #{status_code} instead."
230
232
  end
231
233
  end
232
234
 
@@ -0,0 +1,20 @@
1
+ module TrackerApi
2
+ module Endpoints
3
+ class Blockers
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def get(project_id, story_id, params = {})
11
+ data = client.get("/projects/#{project_id}/stories/#{story_id}/blockers", params: params).body
12
+ raise Errors::UnexpectedData, 'Array of Blockers expected' unless data.is_a? Array
13
+
14
+ data.map do |blocker|
15
+ Resources::Blocker.new({ client: client, project_id: project_id }.merge(blocker))
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,35 @@
1
+ module TrackerApi
2
+ module Endpoints
3
+ class Iteration
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def get(project_id, iteration_number)
11
+ data = client.get("/projects/#{project_id}/iterations/#{iteration_number}").body
12
+
13
+ Resources::Iteration.new({ client: client, project_id: project_id }.merge(data))
14
+ end
15
+
16
+ def get_analytics_cycle_time_details(project_id, iteration_number)
17
+ data = client.paginate("/projects/#{project_id}/iterations/#{iteration_number}/analytics/cycle_time_details")
18
+ raise Errors::UnexpectedData, 'Array of cycle time details expected' unless data.is_a? Array
19
+
20
+ data.map do |cycle_time_details|
21
+ Resources::CycleTimeDetails.new(
22
+ { project_id: project_id, iteration_number: iteration_number }.merge(cycle_time_details)
23
+ )
24
+ end
25
+ end
26
+
27
+ def get_history(project_id, iteration_number)
28
+ data = client.get("/projects/#{project_id}/history/iterations/#{iteration_number}/days").body
29
+ raise Errors::UnexpectedData, 'Hash of history data expected' unless data.is_a? Hash
30
+
31
+ Resources::DailyHistoryContainer.new({ project_id: project_id, iteration_number: iteration_number }.merge(data))
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,17 @@
1
+ module TrackerApi
2
+ module Endpoints
3
+ class Release
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def get(project_id, id, params={})
11
+ data = client.get("/projects/#{project_id}/releases/#{id}", params: params).body
12
+
13
+ Resources::Release.new({ client: client }.merge(data))
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ module TrackerApi
2
+ module Endpoints
3
+ class Releases
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def get(project_id, params={})
11
+ data = client.paginate("/projects/#{project_id}/releases", params: params)
12
+ raise Errors::UnexpectedData, 'Array of releases expected' unless data.is_a? Array
13
+
14
+ data.map do |release|
15
+ Resources::Release.new({ client: client, project_id: project_id }.merge(release))
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ module TrackerApi
2
+ module Endpoints
3
+ class Review
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def update(review, params = {})
11
+ raise ArgumentError, 'Valid review required to update.' unless review.instance_of?(Resources::Review)
12
+
13
+ data = client.put("/projects/#{review.project_id}/stories/#{review.story_id}/reviews/#{review.id}", params: params).body
14
+
15
+ review.attributes = data
16
+ review.clean!
17
+ review
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ module TrackerApi
2
+ module Endpoints
3
+ class Reviews
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def get(project_id, story_id, params={})
11
+ params[:fields] ||= ":default,review_type"
12
+ data = client.paginate("/projects/#{project_id}/stories/#{story_id}/reviews", params: params)
13
+ raise Errors::UnexpectedData, 'Successful responses to this request return an array containing zero or more instances of the review resource. This response was not an array.' unless data.is_a? Array
14
+
15
+ data.map do |review|
16
+ Resources::Review.new({ client: client, project_id: project_id }.merge(review))
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -10,7 +10,7 @@ module TrackerApi
10
10
  def get(project_id, query, options={})
11
11
  raise ArgumentError, 'Valid query string required to search' unless query.is_a?(String)
12
12
 
13
- options.key?(:body) ? options[:body][:query] = query : options[:body] = { query: query }
13
+ options[:params] = { query: query }
14
14
  data = client.get("/projects/#{project_id}/search", options).body
15
15
 
16
16
  raise Errors::UnexpectedData, 'Hash of search results expect' unless data.is_a? Hash
@@ -17,6 +17,16 @@ module TrackerApi
17
17
  Resources::Story.new({ client: client, project_id: project_id }.merge(story))
18
18
  end
19
19
  end
20
+
21
+ def get_release(project_id, release_id, params={})
22
+ data = client.paginate("/projects/#{project_id}/releases/#{release_id}/stories", params: params)
23
+
24
+ raise Errors::UnexpectedData, 'Array of stories expected' unless data.is_a? Array
25
+
26
+ data.map do |story|
27
+ Resources::Story.new({ client: client, project_id: project_id }.merge(story))
28
+ end
29
+ end
20
30
  end
21
31
  end
22
32
  end
@@ -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::Error::ParsingError)
8
+ message = if wrapped_exception.is_a?(Faraday::ParsingError)
9
9
  wrapped_exception.message
10
- elsif wrapped_exception.is_a?(Faraday::Error::ClientError)
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,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
@@ -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
@@ -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