diff --git a/lib/tracker_api.rb b/lib/tracker_api.rb index 33faeca..c4f8be7 100644 --- a/lib/tracker_api.rb +++ b/lib/tracker_api.rb @@ -62,6 +62,8 @@ module Endpoints autoload :StoryTransitions, 'tracker_api/endpoints/story_transitions' autoload :Attachment, 'tracker_api/endpoints/attachment' autoload :Attachments, 'tracker_api/endpoints/attachments' + autoload :Releases, 'tracker_api/endpoints/releases' + autoload :Release, 'tracker_api/endpoints/release' end module Resources @@ -93,5 +95,6 @@ module Shared autoload :Webhook, 'tracker_api/resources/webhook' autoload :StoryTransition, 'tracker_api/resources/story_transition' autoload :FileAttachment, 'tracker_api/resources/file_attachment' + autoload :Release, 'tracker_api/resources/release' end end diff --git a/lib/tracker_api/endpoints/release.rb b/lib/tracker_api/endpoints/release.rb new file mode 100644 index 0000000..d5c8615 --- /dev/null +++ b/lib/tracker_api/endpoints/release.rb @@ -0,0 +1,17 @@ +module TrackerApi + module Endpoints + class Release + attr_accessor :client + + def initialize(client) + @client = client + end + + def get(project_id, id, params={}) + data = client.get("/projects/#{project_id}/releases/#{id}", params: params).body + + Resources::Release.new({ client: client }.merge(data)) + end + end + end +end diff --git a/lib/tracker_api/endpoints/releases.rb b/lib/tracker_api/endpoints/releases.rb new file mode 100644 index 0000000..7f732da --- /dev/null +++ b/lib/tracker_api/endpoints/releases.rb @@ -0,0 +1,20 @@ +module TrackerApi + module Endpoints + class Releases + attr_accessor :client + + def initialize(client) + @client = client + end + + def get(project_id, params={}) + data = client.paginate("/projects/#{project_id}/releases", params: params) + raise Errors::UnexpectedData, 'Array of releases expected' unless data.is_a? Array + + data.map do |release| + Resources::Release.new({ client: client, project_id: project_id }.merge(release)) + end + end + end + end +end \ No newline at end of file diff --git a/lib/tracker_api/endpoints/stories.rb b/lib/tracker_api/endpoints/stories.rb index f44f611..3ff01e0 100644 --- a/lib/tracker_api/endpoints/stories.rb +++ b/lib/tracker_api/endpoints/stories.rb @@ -17,6 +17,16 @@ def get(project_id, params={}) Resources::Story.new({ client: client, project_id: project_id }.merge(story)) end end + + def get_release(project_id, release_id, params={}) + data = client.paginate("/projects/#{project_id}/releases/#{release_id}/stories", params: params) + + raise Errors::UnexpectedData, 'Array of stories expected' unless data.is_a? Array + + data.map do |story| + Resources::Story.new({ client: client, project_id: project_id }.merge(story)) + end + end end end end diff --git a/lib/tracker_api/resources/project.rb b/lib/tracker_api/resources/project.rb index 07e156e..68f5385 100644 --- a/lib/tracker_api/resources/project.rb +++ b/lib/tracker_api/resources/project.rb @@ -131,6 +131,19 @@ def stories(params={}) Endpoints::Stories.new(client).get(id, params) end + # Provides a list of all the releases in the project. + # + # @param [Hash] params + # @option params [String] :with_state A release's current_state which all returned releases must match. + # Valid enumeration values: accepted, delivered, finished, started, rejected, unstarted, unscheduled + # @option params [Integer] :offset With the first release in your priority list as 0, + # the index of the first release you want returned. + # @option params [Integer] :limit The number of releases you want returned. + # @return [Array[Release]] releases associated with this project + def releases(params={}) + Endpoints::Releases.new(client).get(id, params) + end + # Provides a list of all the memberships in the project. # # @param [Hash] params diff --git a/lib/tracker_api/resources/release.rb b/lib/tracker_api/resources/release.rb new file mode 100644 index 0000000..ccb5511 --- /dev/null +++ b/lib/tracker_api/resources/release.rb @@ -0,0 +1,29 @@ +module TrackerApi + module Resources + class Release + include Shared::Base + + attribute :client + + attribute :project_id, Integer + attribute :name, String + attribute :description, String + attribute :current_state, String # (accepted, delivered, finished, started, rejected, planned, unstarted, unscheduled) + attribute :accepted_at, DateTime + attribute :deadline, DateTime + attribute :labels, [Label] + attribute :created_at, DateTime + attribute :updated_at, DateTime + attribute :url, String + attribute :kind, String + + # Provides a list of all the stories in the release. + # + # @param [Hash] params + # @return [Array[Story]] stories of this release + def stories(params={}) + Endpoints::Stories.new(client).get_release(project_id, id, params) + end + end + end +end diff --git a/test/project_test.rb b/test/project_test.rb index d25b221..b2b24dc 100644 --- a/test/project_test.rb +++ b/test/project_test.rb @@ -209,4 +209,16 @@ end end end + + describe '.releases' do + it 'gets all of the releases for the project' do + VCR.use_cassette('get releases', record: :new_episodes) do + releases = project.releases + + releases.wont_be_empty + releases.size.must_equal 3 + releases.first.must_be_instance_of TrackerApi::Resources::Release + end + end + end end diff --git a/test/release_test.rb b/test/release_test.rb new file mode 100644 index 0000000..e3b88ab --- /dev/null +++ b/test/release_test.rb @@ -0,0 +1,22 @@ +require_relative 'minitest_helper' + +describe TrackerApi::Resources::Release do + let(:pt_user) { PT_USER_1 } + let(:client) { TrackerApi::Client.new token: pt_user[:token] } + let(:project_id) { pt_user[:project_id] } + let(:project) { VCR.use_cassette('get project') { client.project(project_id) } } + + describe '.stories' do + it 'returns all the stories related to a release' do + releases = VCR.use_cassette('get releases') { project.releases } + release = releases.find { |release| release.name == 'Beta launch' } + + VCR.use_cassette('release stories', record: :new_episodes) do + stories = release.stories + + stories.size.must_equal 9 + stories.first.must_be_instance_of TrackerApi::Resources::Story + end + end + end +end diff --git a/test/vcr/cassettes/get_releases.json b/test/vcr/cassettes/get_releases.json new file mode 100644 index 0000000..55b72df --- /dev/null +++ b/test/vcr/cassettes/get_releases.json @@ -0,0 +1 @@ +{"http_interactions":[{"request":{"method":"get","uri":"https://www.pivotaltracker.com/services/v5/projects/1027488","body":{"encoding":"US-ASCII","string":""},"headers":{"User-Agent":["Ruby/2.4.1 (x86_64-darwin15; ruby) TrackerApi/1.9.0 Faraday/0.15.2"],"X-TrackerToken":["d55c3bc1f74346b843ca84ba340b29bf"],"Accept":["application/json"]}},"response":{"status":{"code":200,"message":null},"headers":{"Access-Control-Allow-Credentials":["false"],"Access-Control-Allow-Headers":["X-TrackerToken,DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,X-Tracker-Warn-Unless-Project-Version-Is"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Access-Control-Allow-Origin":["*"],"Cache-Control":["max-age=0, private, must-revalidate"],"Content-Type":["application/json; charset=utf-8"],"Date":["Tue, 31 Jul 2018 17:55:31 GMT"],"Etag":["W/\"4e54e943972c892eae56a18d566f173e\""],"Server":["nginx + Phusion Passenger"],"Status":["200 OK"],"Strict-Transport-Security":["max-age=31536000; includeSubDomains; preload"],"X-Content-Type-Options":["nosniff, nosniff"],"X-Frame-Options":["SAMEORIGIN"],"X-Powered-By":["Phusion Passenger Enterprise"],"X-Request-Id":["f89b2a3b-b4d8-432c-a0ed-016065dab143"],"X-Runtime":["0.044165"],"X-Tracker-Client-Pinger-Interval":["20"],"X-Tracker-Project-Version":["493"],"X-Vcap-Request-Id":["acf8b574-3947-48a5-66a4-3036e9a4e4ee"],"X-Xss-Protection":["1; mode=block"],"Content-Length":["847"],"Via":["1.1 google"],"Alt-Svc":["clear"]},"body":{"encoding":"ASCII-8BIT","string":"{\"id\":1027488,\"kind\":\"project\",\"name\":\"My Sample Project\",\"version\":493,\"iteration_length\":1,\"week_start_day\":\"Monday\",\"point_scale\":\"0,1,2,3\",\"point_scale_is_custom\":false,\"bugs_and_chores_are_estimatable\":false,\"automatic_planning\":true,\"enable_tasks\":true,\"time_zone\":{\"kind\":\"time_zone\",\"olson_name\":\"America/Los_Angeles\",\"offset\":\"-07:00\"},\"velocity_averaged_over\":3,\"number_of_done_iterations_to_show\":12,\"has_google_domain\":false,\"profile_content\":\"This is a demo project, created by Tracker, with example stories for a simple shopping web site.\",\"enable_incoming_emails\":true,\"initial_velocity\":10,\"public\":false,\"atom_enabled\":false,\"project_type\":\"demo\",\"start_time\":\"2014-02-10T08:00:00Z\",\"created_at\":\"2014-03-02T07:11:04Z\",\"updated_at\":\"2014-03-02T07:11:04Z\",\"account_id\":621384,\"current_iteration_number\":234,\"enable_following\":true}"},"http_version":null},"recorded_at":"Tue, 31 Jul 2018 17:55:31 GMT"},{"request":{"method":"get","uri":"https://www.pivotaltracker.com/services/v5/projects/1027488/releases","body":{"encoding":"US-ASCII","string":""},"headers":{"User-Agent":["Ruby/2.4.1 (x86_64-darwin15; ruby) TrackerApi/1.9.0 Faraday/0.15.2"],"X-TrackerToken":["d55c3bc1f74346b843ca84ba340b29bf"],"Accept":["application/json"]}},"response":{"status":{"code":200,"message":null},"headers":{"Access-Control-Allow-Credentials":["false"],"Access-Control-Allow-Headers":["X-TrackerToken,DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,X-Tracker-Warn-Unless-Project-Version-Is"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Access-Control-Allow-Origin":["*"],"Cache-Control":["max-age=0, private, must-revalidate"],"Content-Type":["application/json; charset=utf-8"],"Date":["Tue, 31 Jul 2018 17:55:31 GMT"],"Etag":["W/\"0bfcc7249b18fb7b53a6b16bd0386774\""],"Server":["nginx + Phusion Passenger"],"Status":["200 OK"],"Strict-Transport-Security":["max-age=31536000; includeSubDomains; preload"],"X-Content-Type-Options":["nosniff, nosniff"],"X-Frame-Options":["SAMEORIGIN"],"X-Powered-By":["Phusion Passenger Enterprise"],"X-Request-Id":["dfa70eae-1373-4717-927d-59493403fd39"],"X-Runtime":["0.049638"],"X-Tracker-Client-Pinger-Interval":["20"],"X-Tracker-Pagination-Limit":["100"],"X-Tracker-Pagination-Offset":["0"],"X-Tracker-Pagination-Returned":["3"],"X-Tracker-Pagination-Total":["3"],"X-Tracker-Project-Version":["493"],"X-Vcap-Request-Id":["41ba3959-d631-4ee3-595e-e643bb4b8a0f"],"X-Xss-Protection":["1; mode=block"],"Content-Length":["798"],"Via":["1.1 google"],"Alt-Svc":["clear"]},"body":{"encoding":"ASCII-8BIT","string":"[{\"kind\":\"release\",\"id\":66728012,\"created_at\":\"2014-02-17T00:00:00Z\",\"updated_at\":\"2014-03-02T07:11:05Z\",\"name\":\"Initial demo to investors\",\"current_state\":\"unstarted\",\"url\":\"https://www.pivotaltracker.com/story/show/66728012\",\"project_id\":1027488,\"labels\":[]},{\"kind\":\"release\",\"id\":66728034,\"created_at\":\"2014-02-17T00:00:00Z\",\"updated_at\":\"2014-03-02T07:11:06Z\",\"deadline\":\"2014-03-17T00:00:00Z\",\"name\":\"Beta launch\",\"current_state\":\"unstarted\",\"url\":\"https://www.pivotaltracker.com/story/show/66728034\",\"project_id\":1027488,\"labels\":[]},{\"kind\":\"release\",\"id\":66728082,\"created_at\":\"2014-02-17T00:00:00Z\",\"updated_at\":\"2014-03-02T07:11:07Z\",\"name\":\"Full production launch\",\"current_state\":\"unstarted\",\"url\":\"https://www.pivotaltracker.com/story/show/66728082\",\"project_id\":1027488,\"labels\":[]}]"},"http_version":null},"recorded_at":"Tue, 31 Jul 2018 17:55:31 GMT"}],"recorded_with":"VCR 4.0.0"} \ No newline at end of file diff --git a/test/vcr/cassettes/release_stories.json b/test/vcr/cassettes/release_stories.json new file mode 100644 index 0000000..cbf696b --- /dev/null +++ b/test/vcr/cassettes/release_stories.json @@ -0,0 +1 @@ +{"http_interactions":[{"request":{"method":"get","uri":"https://www.pivotaltracker.com/services/v5/projects/1027488/releases/66728034/stories","body":{"encoding":"US-ASCII","string":""},"headers":{"User-Agent":["Ruby/2.4.1 (x86_64-darwin15; ruby) TrackerApi/1.9.0 Faraday/0.15.2"],"X-TrackerToken":["d55c3bc1f74346b843ca84ba340b29bf"],"Accept":["application/json"]}},"response":{"status":{"code":200,"message":null},"headers":{"Access-Control-Allow-Credentials":["false"],"Access-Control-Allow-Headers":["X-TrackerToken,DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,X-Tracker-Warn-Unless-Project-Version-Is"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Access-Control-Allow-Origin":["*"],"Cache-Control":["max-age=0, private, must-revalidate"],"Content-Type":["application/json; charset=utf-8"],"Date":["Tue, 31 Jul 2018 18:08:51 GMT"],"Etag":["W/\"a760df243b5eeb972f6f861c6e86fcfd\""],"Server":["nginx + Phusion Passenger"],"Status":["200 OK"],"Strict-Transport-Security":["max-age=31536000; includeSubDomains; preload"],"X-Content-Type-Options":["nosniff, nosniff"],"X-Frame-Options":["SAMEORIGIN"],"X-Powered-By":["Phusion Passenger Enterprise"],"X-Request-Id":["2465f4db-8382-4655-beba-92a7a1c9f9b3"],"X-Runtime":["0.050357"],"X-Tracker-Client-Pinger-Interval":["20"],"X-Tracker-Project-Version":["493"],"X-Vcap-Request-Id":["973dcd63-0081-447d-6557-720c240b7ab6"],"X-Xss-Protection":["1; mode=block"],"Via":["1.1 google"],"Alt-Svc":["clear"]},"body":{"encoding":"ASCII-8BIT","string":"[{\"kind\":\"story\",\"id\":66728014,\"created_at\":\"2014-02-17T00:00:00Z\",\"updated_at\":\"2017-05-03T23:34:05Z\",\"estimate\":1,\"story_type\":\"feature\",\"name\":\"Shopper should be able to enter credit card information and shipping address\",\"current_state\":\"unstarted\",\"requested_by_id\":1266314,\"url\":\"https://www.pivotaltracker.com/story/show/66728014\",\"project_id\":1027488,\"owner_ids\":[],\"labels\":[{\"id\":7849090,\"project_id\":1027488,\"kind\":\"label\",\"name\":\"checkout\",\"created_at\":\"2014-03-02T07:11:05Z\",\"updated_at\":\"2014-03-02T07:11:05Z\"},{\"id\":7849082,\"project_id\":1027488,\"kind\":\"label\",\"name\":\"shopping\",\"created_at\":\"2014-03-02T07:11:04Z\",\"updated_at\":\"2014-03-02T07:11:04Z\"}]},{\"kind\":\"story\",\"id\":66728016,\"created_at\":\"2014-02-17T00:00:00Z\",\"updated_at\":\"2014-03-02T07:11:05Z\",\"story_type\":\"chore\",\"name\":\"Integrate with payment gateway\",\"current_state\":\"unstarted\",\"requested_by_id\":1266314,\"url\":\"https://www.pivotaltracker.com/story/show/66728016\",\"project_id\":1027488,\"owner_ids\":[],\"labels\":[{\"id\":7849090,\"project_id\":1027488,\"kind\":\"label\",\"name\":\"checkout\",\"created_at\":\"2014-03-02T07:11:05Z\",\"updated_at\":\"2014-03-02T07:11:05Z\"},{\"id\":7849082,\"project_id\":1027488,\"kind\":\"label\",\"name\":\"shopping\",\"created_at\":\"2014-03-02T07:11:04Z\",\"updated_at\":\"2014-03-02T07:11:04Z\"}]},{\"kind\":\"story\",\"id\":66728018,\"created_at\":\"2014-02-17T00:00:00Z\",\"updated_at\":\"2014-03-02T07:11:05Z\",\"estimate\":3,\"story_type\":\"feature\",\"name\":\"When shopper submits order, authorize total product amount from payment gateway\",\"current_state\":\"unstarted\",\"requested_by_id\":1266314,\"url\":\"https://www.pivotaltracker.com/story/show/66728018\",\"project_id\":1027488,\"owner_ids\":[],\"labels\":[{\"id\":7849090,\"project_id\":1027488,\"kind\":\"label\",\"name\":\"checkout\",\"created_at\":\"2014-03-02T07:11:05Z\",\"updated_at\":\"2014-03-02T07:11:05Z\"},{\"id\":7849092,\"project_id\":1027488,\"kind\":\"label\",\"name\":\"needs discussion\",\"created_at\":\"2014-03-02T07:11:05Z\",\"updated_at\":\"2014-03-02T07:11:05Z\"},{\"id\":7849082,\"project_id\":1027488,\"kind\":\"label\",\"name\":\"shopping\",\"created_at\":\"2014-03-02T07:11:04Z\",\"updated_at\":\"2014-03-02T07:11:04Z\"}]},{\"kind\":\"story\",\"id\":66728020,\"created_at\":\"2014-02-17T00:00:00Z\",\"updated_at\":\"2014-03-02T07:11:05Z\",\"estimate\":1,\"story_type\":\"feature\",\"name\":\"If system fails to authorize payment amount, display error message to shopper\",\"current_state\":\"unstarted\",\"requested_by_id\":1266314,\"url\":\"https://www.pivotaltracker.com/story/show/66728020\",\"project_id\":1027488,\"owner_ids\":[],\"labels\":[{\"id\":7849090,\"project_id\":1027488,\"kind\":\"label\",\"name\":\"checkout\",\"created_at\":\"2014-03-02T07:11:05Z\",\"updated_at\":\"2014-03-02T07:11:05Z\"},{\"id\":7849082,\"project_id\":1027488,\"kind\":\"label\",\"name\":\"shopping\",\"created_at\":\"2014-03-02T07:11:04Z\",\"updated_at\":\"2014-03-02T07:11:04Z\"}]},{\"kind\":\"story\",\"id\":66728022,\"created_at\":\"2014-02-17T00:00:00Z\",\"updated_at\":\"2014-03-02T07:11:05Z\",\"estimate\":1,\"story_type\":\"feature\",\"name\":\"If authorization is successful, show order number and confirmation message to shopper\",\"current_state\":\"unstarted\",\"requested_by_id\":1266314,\"url\":\"https://www.pivotaltracker.com/story/show/66728022\",\"project_id\":1027488,\"owner_ids\":[],\"labels\":[{\"id\":7849090,\"project_id\":1027488,\"kind\":\"label\",\"name\":\"checkout\",\"created_at\":\"2014-03-02T07:11:05Z\",\"updated_at\":\"2014-03-02T07:11:05Z\"},{\"id\":7849082,\"project_id\":1027488,\"kind\":\"label\",\"name\":\"shopping\",\"created_at\":\"2014-03-02T07:11:04Z\",\"updated_at\":\"2014-03-02T07:11:04Z\"}]},{\"kind\":\"story\",\"id\":66728024,\"created_at\":\"2014-02-17T00:00:00Z\",\"updated_at\":\"2017-05-03T23:32:55Z\",\"estimate\":1,\"story_type\":\"feature\",\"name\":\"Send notification email of order placement to admin\",\"current_state\":\"unstarted\",\"requested_by_id\":1266314,\"url\":\"https://www.pivotaltracker.com/story/show/66728024\",\"project_id\":1027488,\"owner_ids\":[1266314],\"labels\":[{\"id\":7849080,\"project_id\":1027488,\"kind\":\"label\",\"name\":\"admin\",\"created_at\":\"2014-03-02T07:11:04Z\",\"updated_at\":\"2014-03-02T07:11:04Z\"},{\"id\":7849090,\"project_id\":1027488,\"kind\":\"label\",\"name\":\"checkout\",\"created_at\":\"2014-03-02T07:11:05Z\",\"updated_at\":\"2014-03-02T07:11:05Z\"},{\"id\":7849082,\"project_id\":1027488,\"kind\":\"label\",\"name\":\"shopping\",\"created_at\":\"2014-03-02T07:11:04Z\",\"updated_at\":\"2014-03-02T07:11:04Z\"}],\"owned_by_id\":1266314},{\"kind\":\"story\",\"id\":66728026,\"created_at\":\"2014-02-17T00:00:00Z\",\"updated_at\":\"2014-03-02T07:11:05Z\",\"estimate\":2,\"story_type\":\"feature\",\"name\":\"Shopper should be able to check status of order by entering name and order number\",\"current_state\":\"unstarted\",\"requested_by_id\":1266314,\"url\":\"https://www.pivotaltracker.com/story/show/66728026\",\"project_id\":1027488,\"owner_ids\":[],\"labels\":[{\"id\":7849094,\"project_id\":1027488,\"kind\":\"label\",\"name\":\"orders\",\"created_at\":\"2014-03-02T07:11:05Z\",\"updated_at\":\"2014-03-02T07:11:05Z\"}]},{\"kind\":\"story\",\"id\":66728028,\"created_at\":\"2014-02-17T00:00:00Z\",\"updated_at\":\"2014-03-02T07:11:05Z\",\"estimate\":1,\"story_type\":\"feature\",\"name\":\"Shopper should be able to ask question about order\",\"description\":\"When checking status of order, shopper should have the option to ask a question. This should send email to admin.\",\"current_state\":\"unstarted\",\"requested_by_id\":1266314,\"url\":\"https://www.pivotaltracker.com/story/show/66728028\",\"project_id\":1027488,\"owner_ids\":[],\"labels\":[{\"id\":7849094,\"project_id\":1027488,\"kind\":\"label\",\"name\":\"orders\",\"created_at\":\"2014-03-02T07:11:05Z\",\"updated_at\":\"2014-03-02T07:11:05Z\"}]},{\"kind\":\"story\",\"id\":66728032,\"created_at\":\"2014-02-17T00:00:00Z\",\"updated_at\":\"2014-03-02T07:11:06Z\",\"story_type\":\"chore\",\"name\":\"Set up Engine Yard production environment\",\"current_state\":\"unstarted\",\"requested_by_id\":1266314,\"url\":\"https://www.pivotaltracker.com/story/show/66728032\",\"project_id\":1027488,\"owner_ids\":[],\"labels\":[{\"id\":7849078,\"project_id\":1027488,\"kind\":\"label\",\"name\":\"deployment\",\"created_at\":\"2014-03-02T07:11:04Z\",\"updated_at\":\"2014-03-02T07:11:04Z\"}]}]"},"http_version":null},"recorded_at":"Tue, 31 Jul 2018 18:08:51 GMT"}],"recorded_with":"VCR 4.0.0"} \ No newline at end of file