Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CycleTimeDetails and DailyHistoryContainer Resource #130

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions lib/tracker_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ class ServerError < Error; end

module Endpoints
autoload :Activity, 'tracker_api/endpoints/activity'
autoload :Blockers, 'tracker_api/endpoints/blockers'
autoload :Epic, 'tracker_api/endpoints/epic'
autoload :Epics, 'tracker_api/endpoints/epics'
autoload :Iteration, 'tracker_api/endpoints/iteration'
autoload :Iterations, 'tracker_api/endpoints/iterations'
autoload :Labels, 'tracker_api/endpoints/labels'
autoload :Me, 'tracker_api/endpoints/me'
Expand Down Expand Up @@ -74,6 +76,7 @@ module Shared
end
autoload :Activity, 'tracker_api/resources/activity'
autoload :Account, 'tracker_api/resources/account'
autoload :Blocker, 'tracker_api/resources/blocker'
autoload :Change, 'tracker_api/resources/change'
autoload :Epic, 'tracker_api/resources/epic'
autoload :EpicsSearchResult, 'tracker_api/resources/epics_search_result'
Expand All @@ -97,6 +100,8 @@ module Shared
autoload :StoryTransition, 'tracker_api/resources/story_transition'
autoload :FileAttachment, 'tracker_api/resources/file_attachment'
autoload :Release, 'tracker_api/resources/release'
autoload :CycleTimeDetails, 'tracker_api/resources/cycle_time_details'
autoload :DailyHistoryContainer, 'tracker_api/resources/daily_history_container'
autoload :Review, 'tracker_api/resources/review'
autoload :ReviewType, 'tracker_api/resources/review_type'
end
Expand Down
20 changes: 20 additions & 0 deletions lib/tracker_api/endpoints/blockers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module TrackerApi
module Endpoints
class Blockers
attr_accessor :client

def initialize(client)
@client = client
end

def get(project_id, story_id, params = {})
data = client.get("/projects/#{project_id}/stories/#{story_id}/blockers", params: params).body
raise Errors::UnexpectedData, 'Array of Blockers expected' unless data.is_a? Array

data.map do |blocker|
Resources::Blocker.new({ client: client, project_id: project_id }.merge(blocker))
end
end
end
end
end
35 changes: 35 additions & 0 deletions lib/tracker_api/endpoints/iteration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
module TrackerApi
module Endpoints
class Iteration
attr_accessor :client

def initialize(client)
@client = client
end

def get(project_id, iteration_number)
data = client.get("/projects/#{project_id}/iterations/#{iteration_number}").body

Resources::Iteration.new({ client: client, project_id: project_id }.merge(data))
end

def get_analytics_cycle_time_details(project_id, iteration_number)
data = client.paginate("/projects/#{project_id}/iterations/#{iteration_number}/analytics/cycle_time_details")
raise Errors::UnexpectedData, 'Array of cycle time details expected' unless data.is_a? Array

data.map do |cycle_time_details|
Resources::CycleTimeDetails.new(
{ project_id: project_id, iteration_number: iteration_number }.merge(cycle_time_details)
)
end
end

def get_history(project_id, iteration_number)
data = client.get("/projects/#{project_id}/history/iterations/#{iteration_number}/days").body
raise Errors::UnexpectedData, 'Hash of history data expected' unless data.is_a? Hash

Resources::DailyHistoryContainer.new({ project_id: project_id, iteration_number: iteration_number }.merge(data))
end
end
end
end
18 changes: 18 additions & 0 deletions lib/tracker_api/resources/blocker.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module TrackerApi
module Resources
class Blocker
include Shared::Base

attribute :client
attribute :project_id, Integer

attribute :story_id, Integer
attribute :person_id, Integer
attribute :description, String
attribute :resolved, Boolean
attribute :created_at, DateTime
attribute :updated_at, DateTime
attribute :kind, String
end
end
end
21 changes: 21 additions & 0 deletions lib/tracker_api/resources/cycle_time_details.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module TrackerApi
module Resources
class CycleTimeDetails
include Shared::Base

attribute :project_id, Integer
attribute :iteration_number, Integer
attribute :total_cycle_time, Integer
attribute :started_time, Integer
attribute :started_count, Integer
attribute :finished_time, Integer
attribute :finished_count, Integer
attribute :delivered_time, Integer
attribute :delivered_count, Integer
attribute :rejected_time, Integer
attribute :rejected_count, Integer
attribute :story_id, Integer
attribute :kind, String
end
end
end
13 changes: 13 additions & 0 deletions lib/tracker_api/resources/daily_history_container.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module TrackerApi
module Resources
class DailyHistoryContainer
include Shared::Base

attribute :project_id, Integer
attribute :iteration_number, Integer
attribute :header, [String]
attribute :data, [Enumerable]
attribute :kind, String
end
end
end
14 changes: 14 additions & 0 deletions lib/tracker_api/resources/iteration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@ class Iteration
def stories=(data)
super.each { |s| s.client = client }
end

# Provides a list of all the cycle_time_details of each story in the iteration.
#
# @return [Array[CycleTimeDetails]] array of cycle_time_details of iterations in this project
def cycle_time_details
Endpoints::Iteration.new(client).get_analytics_cycle_time_details(project_id, number)
end

# Returns per day information of story points and counts by state for the given iteration.
#
# @return [DailyHistoryContainer]
def get_history
Endpoints::Iteration.new(client).get_history(project_id, number)
end
end
end
end
5 changes: 5 additions & 0 deletions lib/tracker_api/resources/story.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class Story
attribute :accepted_at, DateTime
attribute :after_id, Integer
attribute :before_id, Integer
attribute :blockers, [Blocker]
attribute :comment_ids, [Integer]
attribute :comments, [Comment]
attribute :created_at, DateTime
Expand Down Expand Up @@ -119,6 +120,10 @@ def activity(params = {})
Endpoints::Activity.new(client).get_story(project_id, id, params)
end

def blockers(params = {})
Endpoints::Blockers.new(client).get(project_id, id, params)
end

# Provides a list of all the comments on the story.
#
# @param [Hash] params
Expand Down
31 changes: 31 additions & 0 deletions test/iteration_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
require_relative 'minitest_helper'

describe TrackerApi::Resources::Iteration 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) } }
let(:iteration) { VCR.use_cassette('get current iteration') { project.iterations(scope: "current").first } }

describe "#cycle_time_details" do
it "gets all cycle_time_details for this iteration" do
VCR.use_cassette('get cycle time details', record: :new_episodes) do
cycle_time_details = iteration.cycle_time_details

cycle_time_details.wont_be_empty
cycle_time_detail = cycle_time_details.first
cycle_time_detail.must_be_instance_of TrackerApi::Resources::CycleTimeDetails
end
end
end

describe "#get_history" do
it "gets all history for a particular iteration" do
VCR.use_cassette('get daily history container', record: :new_episodes) do
daily_history_container = iteration.get_history

daily_history_container.must_be_instance_of TrackerApi::Resources::DailyHistoryContainer
end
end
end
end
2 changes: 1 addition & 1 deletion test/vcr/cassettes/get_current_iteration.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions test/vcr/cassettes/get_cycle_time_details.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"http_interactions":[{"request":{"method":"get","uri":"https://www.pivotaltracker.com/services/v5/projects/1027488/iterations/283/analytics/cycle_time_details","body":{"encoding":"US-ASCII","string":""},"headers":{"User-Agent":["Ruby/2.5.0 (x86_64-darwin17; ruby) TrackerApi/1.9.1 Faraday/0.15.4"],"X-TrackerToken":["d55c3bc1f74346b843ca84ba340b29bf"],"Accept":["application/json"]}},"response":{"status":{"code":200,"message":"OK"},"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":["Wed, 10 Jul 2019 04:15:28 GMT"],"Etag":["W/\"ce501d87f5dcf9ca6273dba59992b807\""],"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"],"X-Request-Id":["a814d6a5-f16f-4076-8d18-1ebcb52ee40c"],"X-Runtime":["0.030541"],"X-Tracker-Client-Pinger-Interval":["20"],"X-Tracker-Project-Version":["588"],"X-Vcap-Request-Id":["6d7e803c-115c-41d7-5f81-38aec3d55d34"],"X-Xss-Protection":["1; mode=block"],"Via":["1.1 google"],"Alt-Svc":["clear"]},"body":{"encoding":"ASCII-8BIT","string":"[{\"kind\":\"cycle_time_details\",\"total_cycle_time\":79244519000,\"started_time\":0,\"started_count\":0,\"finished_time\":0,\"finished_count\":0,\"delivered_time\":0,\"delivered_count\":0,\"rejected_time\":79244519000,\"rejected_count\":1,\"story_id\":66728000},{\"kind\":\"cycle_time_details\",\"started_time\":0,\"started_count\":0,\"finished_time\":0,\"finished_count\":0,\"delivered_time\":0,\"delivered_count\":0,\"rejected_time\":0,\"rejected_count\":0,\"story_id\":66727998},{\"kind\":\"cycle_time_details\",\"started_time\":0,\"started_count\":0,\"finished_time\":0,\"finished_count\":0,\"delivered_time\":0,\"delivered_count\":0,\"rejected_time\":0,\"rejected_count\":0,\"story_id\":66728002},{\"kind\":\"cycle_time_details\",\"started_time\":0,\"started_count\":0,\"finished_time\":0,\"finished_count\":0,\"delivered_time\":0,\"delivered_count\":0,\"rejected_time\":0,\"rejected_count\":0,\"story_id\":66728004},{\"kind\":\"cycle_time_details\",\"started_time\":0,\"started_count\":0,\"finished_time\":0,\"finished_count\":0,\"delivered_time\":0,\"delivered_count\":0,\"rejected_time\":0,\"rejected_count\":0,\"story_id\":82330186},{\"kind\":\"cycle_time_details\",\"started_time\":0,\"started_count\":0,\"finished_time\":0,\"finished_count\":0,\"delivered_time\":0,\"delivered_count\":0,\"rejected_time\":0,\"rejected_count\":0,\"story_id\":82330712},{\"kind\":\"cycle_time_details\",\"total_cycle_time\":69326134000,\"started_time\":69326134000,\"started_count\":6,\"finished_time\":0,\"finished_count\":0,\"delivered_time\":0,\"delivered_count\":0,\"rejected_time\":0,\"rejected_count\":0,\"story_id\":66728030},{\"kind\":\"cycle_time_details\",\"started_time\":0,\"started_count\":0,\"finished_time\":0,\"finished_count\":0,\"delivered_time\":0,\"delivered_count\":0,\"rejected_time\":0,\"rejected_count\":0,\"story_id\":124273751},{\"kind\":\"cycle_time_details\",\"started_time\":0,\"started_count\":0,\"finished_time\":0,\"finished_count\":0,\"delivered_time\":0,\"delivered_count\":0,\"rejected_time\":0,\"rejected_count\":0,\"story_id\":66728006},{\"kind\":\"cycle_time_details\",\"started_time\":0,\"started_count\":0,\"finished_time\":0,\"finished_count\":0,\"delivered_time\":0,\"delivered_count\":0,\"rejected_time\":0,\"rejected_count\":0,\"story_id\":66728008}]"},"http_version":null},"recorded_at":"Wed, 10 Jul 2019 04:15:25 GMT"}],"recorded_with":"VCR 5.0.0"}
1 change: 1 addition & 0 deletions test/vcr/cassettes/get_daily_history_container.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"http_interactions":[{"request":{"method":"get","uri":"https://www.pivotaltracker.com/services/v5/projects/1027488/history/iterations/283/days","body":{"encoding":"US-ASCII","string":""},"headers":{"User-Agent":["Ruby/2.5.0 (x86_64-darwin17; ruby) TrackerApi/1.9.1 Faraday/0.15.4"],"X-TrackerToken":["d55c3bc1f74346b843ca84ba340b29bf"],"Accept":["application/json"]}},"response":{"status":{"code":200,"message":"OK"},"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":["Wed, 10 Jul 2019 04:20:08 GMT"],"Etag":["W/\"28d10f48867d1b3680a81a878bd4ba4e\""],"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"],"X-Request-Id":["8e55283c-cc89-48b6-bb8b-8b74833aba72"],"X-Runtime":["0.236481"],"X-Tracker-Client-Pinger-Interval":["20"],"X-Tracker-Project-Version":["588"],"X-Vcap-Request-Id":["e0e3defe-a75c-4b64-4a3e-7ad525b12a01"],"X-Xss-Protection":["1; mode=block"],"Content-Length":["418"],"Via":["1.1 google"],"Alt-Svc":["clear"]},"body":{"encoding":"ASCII-8BIT","string":"{\"kind\":\"daily_history_container\",\"header\":[\"date\",\"points_accepted\",\"points_delivered\",\"points_finished\",\"points_started\",\"points_rejected\",\"points_planned\",\"points_unstarted\",\"points_unscheduled\",\"counts_accepted\",\"counts_delivered\",\"counts_finished\",\"counts_started\",\"counts_rejected\",\"counts_planned\",\"counts_unstarted\",\"counts_unscheduled\"],\"data\":[[\"2019-07-09\",0.0,2.0,2.0,3.0,1.0,0.0,1.0,0.0,0,1,2,3,1,0,3,0]]}"},"http_version":null},"recorded_at":"Wed, 10 Jul 2019 04:20:05 GMT"}],"recorded_with":"VCR 5.0.0"}