tracker_api 1.7.1 → 1.11.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cce8ed0f32ae28c44915d1a2aa0dff3f7deb44f9
|
4
|
+
data.tar.gz: 6478d86c0e8e8e826e0ffd50b8111758f0dec9c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 34b10827d2868cf6e0de4039ef23444b412fcb5d162e6fca2a20258f22d3ceede1e45cb4f7e829cb57ccd8a66a3506144e53eed8f7ae6954b2d2196c6244e0f1
|
7
|
+
data.tar.gz: 69eb1bbad1ea7a6ef35b84e36c9b856ebc300eda60369769c242a9ec77fd5cf3c436c7280101341f657d46beab1fc382e16bbb43c074bcb6b89c97b74d71105b
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -68,9 +68,20 @@ comments = story.comments # co
|
|
68
68
|
|
69
69
|
comment = story.create_comment(text: "Use the force!") # Create a new comment on the story
|
70
70
|
|
71
|
+
comment = story.create_comment(text: "Use the force again !", # Create a new comment on the story with
|
72
|
+
files: ['path/to/an/existing/file']) # file attachments
|
73
|
+
|
71
74
|
comment.text += " (please be careful)"
|
72
75
|
comment.save # Update text of an existing comment
|
76
|
+
comment.delete # Delete an existing comment
|
77
|
+
|
78
|
+
comment.create_attachments(files: ['path/to/an/existing/file']) # Add attachments to existing comment
|
79
|
+
comment.delete_attachments # Delete all attachments from a comment
|
73
80
|
|
81
|
+
attachments = comment.attachments # Get attachments associated with a comment
|
82
|
+
attachments.first.delete # Delete a specific attachment
|
83
|
+
|
84
|
+
comment.attachments(reload: true) # Re-load the attachments after modification
|
74
85
|
task = story.tasks.first # Get story tasks
|
75
86
|
task.complete = true
|
76
87
|
task.save # Mark a task complete
|
@@ -95,6 +106,10 @@ client.project(project_id).stories(fields: ':default,tasks') # Eage
|
|
95
106
|
story.comments(fields: ':default,person') # Eagerly get comments and the person that made the comment for a story
|
96
107
|
```
|
97
108
|
|
109
|
+
## Error Handling
|
110
|
+
`TrackerApi::Errors::ClientError` is raised for 4xx HTTP status codes
|
111
|
+
`TrackerApi::Errors::ServerError` is raised for 5xx HTTP status codes
|
112
|
+
|
98
113
|
## Warning
|
99
114
|
|
100
115
|
Direct mutation of an attribute value skips coercion and dirty tracking. Please use direct assignment or the specialized add_* methods to get expected behavior.
|
@@ -118,6 +133,7 @@ story.save
|
|
118
133
|
|
119
134
|
- Add missing resources and endpoints
|
120
135
|
- Add create, update, delete for resources
|
136
|
+
- Error handling for [error responses](https://www.pivotaltracker.com/help/api#Error_Responses)
|
121
137
|
|
122
138
|
## Semantic Versioning
|
123
139
|
http://semver.org/
|
data/lib/tracker_api.rb
CHANGED
@@ -3,6 +3,8 @@ require 'tracker_api/version'
|
|
3
3
|
# dependencies
|
4
4
|
require 'faraday'
|
5
5
|
require 'faraday_middleware'
|
6
|
+
require 'pathname'
|
7
|
+
require 'mimemagic'
|
6
8
|
|
7
9
|
if defined?(ActiveSupport)
|
8
10
|
require 'active_support/core_ext/object/blank'
|
@@ -26,15 +28,20 @@ module TrackerApi
|
|
26
28
|
autoload :Error, 'tracker_api/error'
|
27
29
|
autoload :Client, 'tracker_api/client'
|
28
30
|
autoload :Logger, 'tracker_api/logger'
|
31
|
+
autoload :FileUtility, 'tracker_api/file_utility'
|
29
32
|
|
30
33
|
module Errors
|
31
34
|
class UnexpectedData < StandardError; end
|
35
|
+
class ClientError < Error; end
|
36
|
+
class ServerError < Error; end
|
32
37
|
end
|
33
38
|
|
34
39
|
module Endpoints
|
35
40
|
autoload :Activity, 'tracker_api/endpoints/activity'
|
41
|
+
autoload :Blockers, 'tracker_api/endpoints/blockers'
|
36
42
|
autoload :Epic, 'tracker_api/endpoints/epic'
|
37
43
|
autoload :Epics, 'tracker_api/endpoints/epics'
|
44
|
+
autoload :Iteration, 'tracker_api/endpoints/iteration'
|
38
45
|
autoload :Iterations, 'tracker_api/endpoints/iterations'
|
39
46
|
autoload :Labels, 'tracker_api/endpoints/labels'
|
40
47
|
autoload :Me, 'tracker_api/endpoints/me'
|
@@ -55,6 +62,11 @@ module TrackerApi
|
|
55
62
|
autoload :Webhook, 'tracker_api/endpoints/webhook'
|
56
63
|
autoload :Webhooks, 'tracker_api/endpoints/webhooks'
|
57
64
|
autoload :StoryTransitions, 'tracker_api/endpoints/story_transitions'
|
65
|
+
autoload :Attachment, 'tracker_api/endpoints/attachment'
|
66
|
+
autoload :Attachments, 'tracker_api/endpoints/attachments'
|
67
|
+
autoload :Releases, 'tracker_api/endpoints/releases'
|
68
|
+
autoload :Release, 'tracker_api/endpoints/release'
|
69
|
+
autoload :Reviews, 'tracker_api/endpoints/reviews'
|
58
70
|
end
|
59
71
|
|
60
72
|
module Resources
|
@@ -64,6 +76,7 @@ module TrackerApi
|
|
64
76
|
end
|
65
77
|
autoload :Activity, 'tracker_api/resources/activity'
|
66
78
|
autoload :Account, 'tracker_api/resources/account'
|
79
|
+
autoload :Blocker, 'tracker_api/resources/blocker'
|
67
80
|
autoload :Change, 'tracker_api/resources/change'
|
68
81
|
autoload :Epic, 'tracker_api/resources/epic'
|
69
82
|
autoload :EpicsSearchResult, 'tracker_api/resources/epics_search_result'
|
@@ -85,5 +98,11 @@ module TrackerApi
|
|
85
98
|
autoload :Comment, 'tracker_api/resources/comment'
|
86
99
|
autoload :Webhook, 'tracker_api/resources/webhook'
|
87
100
|
autoload :StoryTransition, 'tracker_api/resources/story_transition'
|
101
|
+
autoload :FileAttachment, 'tracker_api/resources/file_attachment'
|
102
|
+
autoload :Release, 'tracker_api/resources/release'
|
103
|
+
autoload :CycleTimeDetails, 'tracker_api/resources/cycle_time_details'
|
104
|
+
autoload :DailyHistoryContainer, 'tracker_api/resources/daily_history_container'
|
105
|
+
autoload :Review, 'tracker_api/resources/review'
|
106
|
+
autoload :ReviewType, 'tracker_api/resources/review_type'
|
88
107
|
end
|
89
108
|
end
|
data/lib/tracker_api/client.rb
CHANGED
@@ -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
|
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
|
@@ -48,40 +48,15 @@ module TrackerApi
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
-
#
|
51
|
+
# HTTP requests methods
|
52
52
|
#
|
53
53
|
# @param path [String] The path, relative to api endpoint
|
54
54
|
# @param options [Hash] Query and header params for request
|
55
55
|
# @return [Faraday::Response]
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
# Make a HTTP POST request
|
61
|
-
#
|
62
|
-
# @param path [String] The path, relative to api endpoint
|
63
|
-
# @param options [Hash] Query and header params for request
|
64
|
-
# @return [Faraday::Response]
|
65
|
-
def post(path, options = {})
|
66
|
-
request(:post, parse_query_and_convenience_headers(path, options))
|
67
|
-
end
|
68
|
-
|
69
|
-
# Make a HTTP PUT request
|
70
|
-
#
|
71
|
-
# @param path [String] The path, relative to api endpoint
|
72
|
-
# @param options [Hash] Query and header params for request
|
73
|
-
# @return [Faraday::Response]
|
74
|
-
def put(path, options = {})
|
75
|
-
request(:put, parse_query_and_convenience_headers(path, options))
|
76
|
-
end
|
77
|
-
|
78
|
-
# Make a HTTP DELETE request
|
79
|
-
#
|
80
|
-
# @param path [String] The path, relative to api endpoint
|
81
|
-
# @param options [Hash] Query and header params for request
|
82
|
-
# @return [Faraday::Response]
|
83
|
-
def delete(path, options = {})
|
84
|
-
request(:delete, parse_query_and_convenience_headers(path, options))
|
56
|
+
%i{get post patch put delete}.each do |verb|
|
57
|
+
define_method verb do |path, options = {}|
|
58
|
+
request(verb, parse_query_and_convenience_headers(path, options))
|
59
|
+
end
|
85
60
|
end
|
86
61
|
|
87
62
|
# Make one or more HTTP GET requests, optionally fetching
|
@@ -215,7 +190,8 @@ module TrackerApi
|
|
215
190
|
opts[:params] = options[:params] || {}
|
216
191
|
opts[:token] = options[:token] || @token
|
217
192
|
headers = { 'User-Agent' => USER_AGENT,
|
218
|
-
'X-TrackerToken' => opts.fetch(:token)
|
193
|
+
'X-TrackerToken' => opts.fetch(:token),
|
194
|
+
'Accept' => 'application/json' }.merge(options.fetch(:headers, {}))
|
219
195
|
|
220
196
|
CONVENIENCE_HEADERS.each do |h|
|
221
197
|
if header = options[h]
|
@@ -247,8 +223,13 @@ module TrackerApi
|
|
247
223
|
req.body = body
|
248
224
|
end
|
249
225
|
response
|
250
|
-
rescue Faraday::
|
251
|
-
|
226
|
+
rescue Faraday::ClientError, Faraday::ServerError => e
|
227
|
+
status_code = e.response[:status]
|
228
|
+
case status_code
|
229
|
+
when 400..499 then raise TrackerApi::Errors::ClientError.new(e)
|
230
|
+
when 500..599 then raise TrackerApi::Errors::ServerError.new(e)
|
231
|
+
else raise "Expected 4xx or 5xx HTTP status code; got #{status_code} instead."
|
232
|
+
end
|
252
233
|
end
|
253
234
|
|
254
235
|
class Pagination
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module TrackerApi
|
2
|
+
module Endpoints
|
3
|
+
class Attachment
|
4
|
+
attr_accessor :client
|
5
|
+
|
6
|
+
def initialize(client)
|
7
|
+
@client = client
|
8
|
+
end
|
9
|
+
|
10
|
+
def create(comment, file)
|
11
|
+
data = client.post("/projects/#{comment.project_id}/uploads", body: FileUtility.get_file_upload(file)).body
|
12
|
+
Resources::FileAttachment.new({ comment: comment }.merge(data))
|
13
|
+
end
|
14
|
+
|
15
|
+
# TODO : Discuss before implementing this as it orphans the file.
|
16
|
+
# It deletes source, but the file name appears in the comments
|
17
|
+
# def delete(comment, file_attachment_id)
|
18
|
+
# client.delete("/projects/#{comment.project_id}/stories/#{comment.story_id}/comments/#{comment.id}/file_attachments/#{file_attachment_id}").body
|
19
|
+
# end
|
20
|
+
|
21
|
+
def get(comment)
|
22
|
+
data = client.get("/projects/#{comment.project_id}/stories/#{comment.story_id}/comments/#{comment.id}?fields=file_attachments").body["file_attachments"]
|
23
|
+
raise Errors::UnexpectedData, 'Array of file attachments expected' unless data.is_a? Array
|
24
|
+
|
25
|
+
data.map do |file_attachment|
|
26
|
+
Resources::FileAttachment.new({ comment: comment }.merge(file_attachment))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# TODO : Implement this properly.
|
31
|
+
# This results in either content of the file or an S3 link.
|
32
|
+
# the S3 link is also present in big_url attribute.
|
33
|
+
# def download(download_path)
|
34
|
+
# client.get(download_path, url: '').body
|
35
|
+
# end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module TrackerApi
|
2
|
+
module Endpoints
|
3
|
+
class Attachments
|
4
|
+
attr_accessor :client
|
5
|
+
|
6
|
+
def initialize(client)
|
7
|
+
@client = client
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
def create(comment, files)
|
12
|
+
return [] if files.to_a.empty?
|
13
|
+
#Check files before upload to make it all or none.
|
14
|
+
FileUtility.check_files_exist(files)
|
15
|
+
attachment = Endpoints::Attachment.new(client)
|
16
|
+
files.map do | file |
|
17
|
+
attachment.create(comment, file)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -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
|
@@ -15,13 +15,20 @@ module TrackerApi
|
|
15
15
|
def update(comment, params={})
|
16
16
|
raise ArgumentError, 'Valid comment required to update.' unless comment.instance_of?(Resources::Comment)
|
17
17
|
|
18
|
-
|
19
|
-
|
18
|
+
path = "/projects/#{comment.project_id}/stories/#{comment.story_id}/comments/#{comment.id}"
|
19
|
+
path += "?fields=:default,file_attachments" if params.represented.file_attachment_ids_to_add.present? || params.represented.file_attachment_ids_to_remove.present?
|
20
|
+
data = client.put(path, params: params).body
|
20
21
|
|
21
22
|
comment.attributes = data
|
22
23
|
comment.clean!
|
23
24
|
comment
|
24
25
|
end
|
26
|
+
|
27
|
+
def delete(comment)
|
28
|
+
raise ArgumentError, 'Valid comment required to update.' unless comment.instance_of?(Resources::Comment)
|
29
|
+
|
30
|
+
client.delete("/projects/#{comment.project_id}/stories/#{comment.story_id}/comments/#{comment.id}").body
|
31
|
+
end
|
25
32
|
end
|
26
33
|
end
|
27
34
|
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 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
|
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
|