Skip to content

Commit

Permalink
CycleTimeDetails and DailyHistoryContainer Resource (#130)
Browse files Browse the repository at this point in the history
* add cycle_time_details and daily_history_container resource

* add iteration endpoint and add methods to Iteration to return cycle_time_details and daily_history_container

* add blocker resource

* allow retrieving of blockers from story resource

Authored-by: kang sheng <taykangsheng@spgroup.com.sg>
  • Loading branch information
TayKangSheng committed Jun 4, 2020
1 parent 2e3418e commit dcc3622
Show file tree
Hide file tree
Showing 12 changed files with 165 additions and 1 deletion.
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"}

0 comments on commit dcc3622

Please sign in to comment.