Skip to content

Commit

Permalink
Adds reviews so someone can fetch story.reviews.
Browse files Browse the repository at this point in the history
A review_type is tightly coupled to a review. It is accessible as a field customization for reviews but not accessible via a distinct api endpoint. Since it is difficult to understand a review without the review_type, I made the decision to include default review fields and review_type when calling get story/{story_id}/ reviews. The review types are hydrated automatically.
  • Loading branch information
Michael McCormick committed Jun 4, 2020
1 parent 90e71e7 commit d33b0e7
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 0 deletions.
3 changes: 3 additions & 0 deletions lib/tracker_api.rb
Expand Up @@ -64,6 +64,7 @@ module Endpoints
autoload :Attachments, 'tracker_api/endpoints/attachments'
autoload :Releases, 'tracker_api/endpoints/releases'
autoload :Release, 'tracker_api/endpoints/release'
autoload :Reviews, 'tracker_api/endpoints/reviews'
end

module Resources
Expand Down Expand Up @@ -96,5 +97,7 @@ module Shared
autoload :StoryTransition, 'tracker_api/resources/story_transition'
autoload :FileAttachment, 'tracker_api/resources/file_attachment'
autoload :Release, 'tracker_api/resources/release'
autoload :Review, 'tracker_api/resources/review'
autoload :ReviewType, 'tracker_api/resources/review_type'
end
end
21 changes: 21 additions & 0 deletions lib/tracker_api/endpoints/reviews.rb
@@ -0,0 +1,21 @@
module TrackerApi
module Endpoints
class Reviews
attr_accessor :client

def initialize(client)
@client = client
end

def get(project_id, story_id, params={})
params[:fields] ||= ":default,review_type"
data = client.paginate("/projects/#{project_id}/stories/#{story_id}/reviews", params: params)
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

data.map do |review|
Resources::Review.new({ client: client, project_id: project_id }.merge(review))
end
end
end
end
end
19 changes: 19 additions & 0 deletions lib/tracker_api/resources/review.rb
@@ -0,0 +1,19 @@
module TrackerApi
module Resources
class Review
include Shared::Base

attribute :client

attribute :id, Integer
attribute :story_id, Integer
attribute :review_type_id, Integer
attribute :reviewer_id, Integer
attribute :status, String # (unstarted, in_review, pass, revise)
attribute :created_at, DateTime
attribute :updated_at, DateTime
attribute :kind, String
attribute :review_type, ReviewType
end
end
end
15 changes: 15 additions & 0 deletions lib/tracker_api/resources/review_type.rb
@@ -0,0 +1,15 @@
module TrackerApi
module Resources
class ReviewType
include Shared::Base

attribute :id, Integer
attribute :project_id, Integer
attribute :name, String
attribute :hidden, Boolean
attribute :created_at, DateTime
attribute :updated_at, DateTime
attribute :kind, String
end
end
end
9 changes: 9 additions & 0 deletions lib/tracker_api/resources/story.rb
Expand Up @@ -31,6 +31,7 @@ class Story
attribute :project_id, Integer
attribute :requested_by, Person
attribute :requested_by_id, Integer
attribute :reviews, [Review]
attribute :story_type, String # (feature, bug, chore, release)
attribute :task_ids, [Integer]
attribute :tasks, [Task]
Expand Down Expand Up @@ -199,6 +200,14 @@ def save

Endpoints::Story.new(client).update(self, UpdateRepresenter.new(Story.new(self.dirty_attributes)))
end

def reviews(params = {})
if params.blank? && @reviews.present?
@reviews
else
@reviews = Endpoints::Reviews.new(client).get(project_id, id, params)
end
end
end
end
end
17 changes: 17 additions & 0 deletions test/story_test.rb
Expand Up @@ -302,4 +302,21 @@
end
end
end

describe '.reviews' do
it 'gets all reviews (and review_types field by default) for the story' do
VCR.use_cassette('get story reviews', record: :new_episodes) do
story = TrackerApi::Resources::Story.new( client: client,
project_id: project_id,
id: story_id)

reviews = story.reviews
review = reviews.first
_(review).must_be_instance_of TrackerApi::Resources::Review
_(review.review_type).must_be_instance_of TrackerApi::Resources::ReviewType
_(review.review_type.name).must_equal 'Test (QA)'
_(review.status).must_equal 'unstarted'
end
end
end
end
1 change: 1 addition & 0 deletions test/vcr/cassettes/get_story_reviews.json
@@ -0,0 +1 @@
{"http_interactions":[{"request":{"method":"get","uri":"https://www.pivotaltracker.com/services/v5/projects/1027488/stories/66728004/reviews","body":{"encoding":"US-ASCII","string":""},"headers":{"User-Agent":["Ruby/2.7.1 (x86_64-darwin19; ruby) TrackerApi/1.10.0 Faraday/1.0.1"],"X-TrackerToken":["d55c3bc1f74346b843ca84ba340b29bf"],"Accept":["application/json"]}},"response":{"status":{"code":200,"message":"OK"},"headers":{"Content-Type":["application/json; charset=utf-8"],"Status":["200 OK"],"Cache-Control":["max-age=0, private, must-revalidate"],"X-Tracker-Project-Version":["599"],"X-Request-Id":["46c972d8-156a-4f99-b355-99b1392fea03"],"ETag":["W/\"75e4521136187daece8228d69d3170e4\""],"X-Frame-Options":["SAMEORIGIN"],"X-Runtime":["0.059777"],"X-Content-Type-Options":["nosniff, nosniff"],"Date":["Wed, 03 Jun 2020 19:09:51 GMT"],"X-Powered-By":["Phusion Passenger"],"Server":["nginx + Phusion Passenger"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Credentials":["false"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"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"],"X-Tracker-Client-Pinger-Interval":["20"],"Strict-Transport-Security":["max-age=31536000; includeSubDomains; preload"],"X-XSS-Protection":["1; mode=block"],"Via":["1.1 google"],"Alt-Svc":["clear"]},"body":{"encoding":"ASCII-8BIT","string":"[{\"kind\":\"review\",\"id\":1129224,\"story_id\":66728004,\"review_type_id\":2293634,\"reviewer_id\":1266314,\"status\":\"unstarted\",\"created_at\":\"2020-06-03T15:55:47Z\",\"updated_at\":\"2020-06-03T15:56:00Z\"},{\"kind\":\"review\",\"id\":1129225,\"story_id\":66728004,\"review_type_id\":2293635,\"status\":\"in_review\",\"created_at\":\"2020-06-03T15:55:53Z\",\"updated_at\":\"2020-06-03T15:56:09Z\"},{\"kind\":\"review\",\"id\":1129226,\"story_id\":66728004,\"review_type_id\":2293636,\"status\":\"pass\",\"created_at\":\"2020-06-03T15:55:55Z\",\"updated_at\":\"2020-06-03T15:56:53Z\"},{\"kind\":\"review\",\"id\":1129227,\"story_id\":66728004,\"review_type_id\":2293637,\"reviewer_id\":1266318,\"status\":\"revise\",\"created_at\":\"2020-06-03T15:56:08Z\",\"updated_at\":\"2020-06-03T15:57:23Z\"}]"}},"recorded_at":"Wed, 03 Jun 2020 19:09:51 GMT"},{"request":{"method":"get","uri":"https://www.pivotaltracker.com/services/v5/projects/1027488/stories/66728004/reviews?fields=%3Adefault%2Creview_type","body":{"encoding":"US-ASCII","string":""},"headers":{"User-Agent":["Ruby/2.7.1 (x86_64-darwin19; ruby) TrackerApi/1.10.0 Faraday/1.0.1"],"X-TrackerToken":["d55c3bc1f74346b843ca84ba340b29bf"],"Accept":["application/json"]}},"response":{"status":{"code":200,"message":"OK"},"headers":{"Content-Type":["application/json; charset=utf-8"],"Status":["200 OK"],"Cache-Control":["max-age=0, private, must-revalidate"],"X-Tracker-Project-Version":["599"],"X-Request-Id":["207069c4-0ae1-4f03-8603-fdb9c44cf71c"],"ETag":["W/\"3222abe3914a443abb732d7e97f44aeb\""],"X-Frame-Options":["SAMEORIGIN"],"X-Runtime":["0.027998"],"X-Content-Type-Options":["nosniff, nosniff"],"Date":["Thu, 04 Jun 2020 12:38:07 GMT"],"X-Powered-By":["Phusion Passenger"],"Server":["nginx + Phusion Passenger"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Credentials":["false"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"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"],"X-Tracker-Client-Pinger-Interval":["20"],"Strict-Transport-Security":["max-age=31536000; includeSubDomains; preload"],"X-XSS-Protection":["1; mode=block"],"Via":["1.1 google"],"Alt-Svc":["clear"]},"body":{"encoding":"ASCII-8BIT","string":"[{\"kind\":\"review\",\"review_type\":{\"kind\":\"review_type\",\"id\":2293634,\"project_id\":1027488,\"name\":\"Test (QA)\",\"hidden\":false,\"created_at\":\"2019-02-26T01:59:38Z\",\"updated_at\":\"2019-02-26T01:59:38Z\"},\"id\":1129224,\"story_id\":66728004,\"review_type_id\":2293634,\"reviewer_id\":1266314,\"status\":\"unstarted\",\"created_at\":\"2020-06-03T15:55:47Z\",\"updated_at\":\"2020-06-03T15:56:00Z\"},{\"kind\":\"review\",\"review_type\":{\"kind\":\"review_type\",\"id\":2293635,\"project_id\":1027488,\"name\":\"Design\",\"hidden\":false,\"created_at\":\"2019-02-26T01:59:38Z\",\"updated_at\":\"2019-02-26T01:59:38Z\"},\"id\":1129225,\"story_id\":66728004,\"review_type_id\":2293635,\"status\":\"in_review\",\"created_at\":\"2020-06-03T15:55:53Z\",\"updated_at\":\"2020-06-03T15:56:09Z\"},{\"kind\":\"review\",\"review_type\":{\"kind\":\"review_type\",\"id\":2293636,\"project_id\":1027488,\"name\":\"Code\",\"hidden\":false,\"created_at\":\"2019-02-26T01:59:38Z\",\"updated_at\":\"2019-02-26T01:59:38Z\"},\"id\":1129226,\"story_id\":66728004,\"review_type_id\":2293636,\"status\":\"pass\",\"created_at\":\"2020-06-03T15:55:55Z\",\"updated_at\":\"2020-06-03T15:56:53Z\"},{\"kind\":\"review\",\"review_type\":{\"kind\":\"review_type\",\"id\":2293637,\"project_id\":1027488,\"name\":\"Security\",\"hidden\":false,\"created_at\":\"2019-02-26T01:59:38Z\",\"updated_at\":\"2019-02-26T01:59:38Z\"},\"id\":1129227,\"story_id\":66728004,\"review_type_id\":2293637,\"reviewer_id\":1266318,\"status\":\"revise\",\"created_at\":\"2020-06-03T15:56:08Z\",\"updated_at\":\"2020-06-03T15:57:23Z\"}]"}},"recorded_at":"Thu, 04 Jun 2020 12:38:07 GMT"}],"recorded_with":"VCR 6.0.0"}

0 comments on commit d33b0e7

Please sign in to comment.