From 717e3a39d102524c3b605caa0c8e71d51e110704 Mon Sep 17 00:00:00 2001 From: Brian Samson Date: Wed, 16 Nov 2016 08:24:23 -0600 Subject: [PATCH 1/2] Ability to create and update comments on a story. --- README.md | 5 ++++ lib/tracker_api/endpoints/comments.rb | 22 ++++++++++++++-- lib/tracker_api/resources/comment.rb | 16 ++++++++++++ lib/tracker_api/resources/story.rb | 6 +++++ test/comment_test.rb | 35 ++++++++++++++++++++++++++ test/vcr/cassettes/create_comment.json | 1 + test/vcr/cassettes/get_comments.json | 1 + test/vcr/cassettes/save_comment.json | 1 + 8 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 test/comment_test.rb create mode 100644 test/vcr/cassettes/create_comment.json create mode 100644 test/vcr/cassettes/get_comments.json create mode 100644 test/vcr/cassettes/save_comment.json diff --git a/README.md b/README.md index bc026b1..4e75714 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,11 @@ story = TrackerApi::Resources::Story.new( client: client, id: 847762630) # Use the Story resource to get the story comments = story.comments # comments without first fetching the story +comment = story.create_comment(text: "Use the force!") # Create a new comment on the story + +comment.text += " (please be careful)" +comment.save # Update text of an existing comment + task = story.tasks.first # Get story tasks task.complete = true task.save # Mark a task complete diff --git a/lib/tracker_api/endpoints/comments.rb b/lib/tracker_api/endpoints/comments.rb index cf87977..d6e2862 100644 --- a/lib/tracker_api/endpoints/comments.rb +++ b/lib/tracker_api/endpoints/comments.rb @@ -12,9 +12,27 @@ def get(project_id, story_id, params={}) raise Errors::UnexpectedData, 'Array of comments expected' unless data.is_a? Array data.map do |comment| - Resources::Comment.new({ story_id: story_id }.merge(comment)) + Resources::Comment.new({ client: client, + project_id: project_id, + story_id: story_id }.merge(comment)) end end + + def create(project_id, story_id, params={}) + data = client.post("/projects/#{project_id}/stories/#{story_id}/comments", params: params).body + Resources::Comment.new({ client: client, project_id: project_id }.merge(data)) + end + + def update(comment, params={}) + raise ArgumentError, 'Valid comment required to update.' unless comment.instance_of?(Resources::Comment) + + data = client.put("/projects/#{comment.project_id}/stories/#{comment.story_id}/comments/#{comment.id}", + params: params).body + + comment.attributes = data + comment.clean! + comment + end end end -end \ No newline at end of file +end diff --git a/lib/tracker_api/resources/comment.rb b/lib/tracker_api/resources/comment.rb index 73d9f20..74d900e 100644 --- a/lib/tracker_api/resources/comment.rb +++ b/lib/tracker_api/resources/comment.rb @@ -3,6 +3,9 @@ module Resources class Comment include Shared::Base + attribute :client + + attribute :project_id, Integer attribute :story_id, Integer attribute :epic_id, Integer attribute :text, String @@ -14,6 +17,19 @@ class Comment attribute :commit_identifier, String attribute :commit_type, String attribute :kind, String + + class UpdateRepresenter < Representable::Decorator + include Representable::JSON + + property :id + property :text + end + + def save + raise ArgumentError, 'Cannot update a comment with an unknown story_id.' if story_id.nil? + + Endpoints::Comments.new(client).update(self, UpdateRepresenter.new(Comment.new(self.dirty_attributes))) + end end end end diff --git a/lib/tracker_api/resources/story.rb b/lib/tracker_api/resources/story.rb index c42b59d..9cc4f0e 100644 --- a/lib/tracker_api/resources/story.rb +++ b/lib/tracker_api/resources/story.rb @@ -127,6 +127,12 @@ def create_task(params) Endpoints::Task.new(client).create(project_id, id, params) end + # @param [Hash] params attributes to create the comment with + # @return [Comment] newly created Comment + def create_comment(params) + Endpoints::Comments.new(client).create(project_id, id, params) + end + # Save changes to an existing Story. def save raise ArgumentError, 'Can not update a story with an unknown project_id.' if project_id.nil? diff --git a/test/comment_test.rb b/test/comment_test.rb new file mode 100644 index 0000000..10d6b2b --- /dev/null +++ b/test/comment_test.rb @@ -0,0 +1,35 @@ +require_relative 'minitest_helper' + +describe TrackerApi::Resources::Comment 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(:story_id) { '66728004' } + let(:story) { VCR.use_cassette('get story') { project.story(story_id) } } + let(:comments) { VCR.use_cassette('get comments') { story.comments } } + let(:existing_comment) { comments.first } + + it 'can create a comment given a story' do + text = "Test creating a comment" + comment = nil + VCR.use_cassette('create comment', record: :new_episodes) do + comment = story.create_comment(text: text) + end + + comment.text.must_equal text + comment.clean?.must_equal true + end + + it 'can update an existing comment' do + new_text = "#{existing_comment.text}+" + existing_comment.text = new_text + + VCR.use_cassette('save comment', record: :new_episodes) do + existing_comment.save + end + + existing_comment.text.must_equal new_text + existing_comment.clean?.must_equal true + end +end diff --git a/test/vcr/cassettes/create_comment.json b/test/vcr/cassettes/create_comment.json new file mode 100644 index 0000000..f31831f --- /dev/null +++ b/test/vcr/cassettes/create_comment.json @@ -0,0 +1 @@ +{"http_interactions":[{"request":{"method":"post","uri":"https://www.pivotaltracker.com/services/v5/projects/1027488/stories/66728004/comments","body":{"encoding":"UTF-8","string":"{\"text\":\"Test creating a comment\"}"},"headers":{"User-Agent":["Ruby/2.2.3 (x86_64-darwin14; ruby) TrackerApi/1.4.1 Faraday/0.9.2"],"X-TrackerToken":["d55c3bc1f74346b843ca84ba340b29bf"],"Content-Type":["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":["Wed, 16 Nov 2016 14:17:47 GMT"],"Etag":["\"05a4afa88e8d89128c99e3803eb746cd\""],"Server":["nginx + Phusion Passenger"],"Status":["200 OK"],"Strict-Transport-Security":["max-age=31536000; includeSubDomains"],"X-Powered-By":["Phusion Passenger"],"X-Rack-Cache":["invalidate, pass"],"X-Request-Id":["c64522554bfcacb90f63fea5c9025473"],"X-Runtime":["0.148598"],"X-Tracker-Client-Pinger-Interval":["20"],"X-Tracker-Project-Version":["155"],"X-Ua-Compatible":["IE=Edge,chrome=1"],"X-Vcap-Request-Id":["27b5c8de-19f9-46c8-42b0-8fd3f82ed5cb"],"Content-Length":["178"],"Connection":["keep-alive"]},"body":{"encoding":"ASCII-8BIT","string":"{\"kind\":\"comment\",\"id\":155658605,\"story_id\":66728004,\"text\":\"Test creating a comment\",\"person_id\":1266314,\"created_at\":\"2016-11-16T14:17:47Z\",\"updated_at\":\"2016-11-16T14:17:47Z\"}"},"http_version":null},"recorded_at":"Wed, 16 Nov 2016 14:17:48 GMT"}],"recorded_with":"VCR 3.0.3"} \ No newline at end of file diff --git a/test/vcr/cassettes/get_comments.json b/test/vcr/cassettes/get_comments.json new file mode 100644 index 0000000..a5714a9 --- /dev/null +++ b/test/vcr/cassettes/get_comments.json @@ -0,0 +1 @@ +{"http_interactions":[{"request":{"method":"get","uri":"https://www.pivotaltracker.com/services/v5/projects/1027488/stories/66728004/comments","body":{"encoding":"US-ASCII","string":""},"headers":{"User-Agent":["Ruby/2.2.3 (x86_64-darwin14; ruby) TrackerApi/1.4.1 Faraday/0.9.2"],"X-TrackerToken":["d55c3bc1f74346b843ca84ba340b29bf"]}},"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":["Wed, 16 Nov 2016 14:14:33 GMT"],"Etag":["\"0deb8dbe1ce2f0d1f95892eb3114789a\""],"Server":["nginx + Phusion Passenger"],"Status":["200 OK"],"Strict-Transport-Security":["max-age=31536000; includeSubDomains"],"X-Powered-By":["Phusion Passenger"],"X-Rack-Cache":["miss"],"X-Request-Id":["4e0fe5c2f0ad5f4243b5961ebbf3e767"],"X-Runtime":["0.042615"],"X-Tracker-Client-Pinger-Interval":["20"],"X-Tracker-Project-Version":["154"],"X-Ua-Compatible":["IE=Edge,chrome=1"],"X-Vcap-Request-Id":["7b308ac4-1cf5-4318-7843-5dde89abea3d"],"Content-Length":["355"],"Connection":["keep-alive"]},"body":{"encoding":"ASCII-8BIT","string":"[{\"kind\":\"comment\",\"id\":120836920,\"story_id\":66728004,\"text\":\"This is a comment.\",\"person_id\":1266314,\"created_at\":\"2015-12-17T22:13:55Z\",\"updated_at\":\"2015-12-17T22:13:55Z\"},{\"kind\":\"comment\",\"id\":120836938,\"story_id\":66728004,\"text\":\"This is another comment.\",\"person_id\":1266314,\"created_at\":\"2015-12-17T22:14:04Z\",\"updated_at\":\"2015-12-17T22:14:04Z\"}]"},"http_version":null},"recorded_at":"Wed, 16 Nov 2016 14:14:34 GMT"}],"recorded_with":"VCR 3.0.3"} \ No newline at end of file diff --git a/test/vcr/cassettes/save_comment.json b/test/vcr/cassettes/save_comment.json new file mode 100644 index 0000000..1835572 --- /dev/null +++ b/test/vcr/cassettes/save_comment.json @@ -0,0 +1 @@ +{"http_interactions":[{"request":{"method":"put","uri":"https://www.pivotaltracker.com/services/v5/projects/1027488/stories/66728004/comments/120836920","body":{"encoding":"UTF-8","string":"{\"text\":\"This is a comment.+\"}"},"headers":{"User-Agent":["Ruby/2.2.3 (x86_64-darwin14; ruby) TrackerApi/1.4.1 Faraday/0.9.2"],"X-TrackerToken":["d55c3bc1f74346b843ca84ba340b29bf"],"Content-Type":["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":["Wed, 16 Nov 2016 14:20:30 GMT"],"Etag":["\"d4458c709c842d1e8544e2b3397418f9\""],"Server":["nginx + Phusion Passenger"],"Status":["200 OK"],"Strict-Transport-Security":["max-age=31536000; includeSubDomains"],"X-Powered-By":["Phusion Passenger"],"X-Rack-Cache":["invalidate, pass"],"X-Request-Id":["87382a7f1e4c3dd82514cde390832f7f"],"X-Runtime":["0.211884"],"X-Tracker-Client-Pinger-Interval":["20"],"X-Tracker-Project-Version":["156"],"X-Ua-Compatible":["IE=Edge,chrome=1"],"X-Vcap-Request-Id":["3ba8192f-073e-44f9-50b2-74fca707f240"],"Content-Length":["174"],"Connection":["keep-alive"]},"body":{"encoding":"ASCII-8BIT","string":"{\"kind\":\"comment\",\"id\":120836920,\"story_id\":66728004,\"text\":\"This is a comment.+\",\"person_id\":1266314,\"created_at\":\"2015-12-17T22:13:55Z\",\"updated_at\":\"2016-11-16T14:20:30Z\"}"},"http_version":null},"recorded_at":"Wed, 16 Nov 2016 14:20:30 GMT"}],"recorded_with":"VCR 3.0.3"} \ No newline at end of file From d93ca0f5a287078354448892a1c4cdfc54f9f269 Mon Sep 17 00:00:00 2001 From: Brian Samson Date: Wed, 16 Nov 2016 15:12:41 -0600 Subject: [PATCH 2/2] refactor single-resource api calls into a singular endpoint --- lib/tracker_api.rb | 1 + lib/tracker_api/endpoints/comment.rb | 27 +++++++++++++++++++++++++++ lib/tracker_api/endpoints/comments.rb | 16 ---------------- lib/tracker_api/resources/comment.rb | 2 +- lib/tracker_api/resources/story.rb | 2 +- 5 files changed, 30 insertions(+), 18 deletions(-) create mode 100644 lib/tracker_api/endpoints/comment.rb diff --git a/lib/tracker_api.rb b/lib/tracker_api.rb index 2dbc6c6..8dfff91 100644 --- a/lib/tracker_api.rb +++ b/lib/tracker_api.rb @@ -50,6 +50,7 @@ module Endpoints autoload :Task, 'tracker_api/endpoints/task' autoload :Tasks, 'tracker_api/endpoints/tasks' autoload :Comments, 'tracker_api/endpoints/comments' + autoload :Comment, 'tracker_api/endpoints/comment' end module Resources diff --git a/lib/tracker_api/endpoints/comment.rb b/lib/tracker_api/endpoints/comment.rb new file mode 100644 index 0000000..852da2a --- /dev/null +++ b/lib/tracker_api/endpoints/comment.rb @@ -0,0 +1,27 @@ +module TrackerApi + module Endpoints + class Comment + attr_accessor :client + + def initialize(client) + @client = client + end + + def create(project_id, story_id, params={}) + data = client.post("/projects/#{project_id}/stories/#{story_id}/comments", params: params).body + Resources::Comment.new({ client: client, project_id: project_id }.merge(data)) + end + + def update(comment, params={}) + raise ArgumentError, 'Valid comment required to update.' unless comment.instance_of?(Resources::Comment) + + data = client.put("/projects/#{comment.project_id}/stories/#{comment.story_id}/comments/#{comment.id}", + params: params).body + + comment.attributes = data + comment.clean! + comment + end + end + end +end diff --git a/lib/tracker_api/endpoints/comments.rb b/lib/tracker_api/endpoints/comments.rb index d6e2862..9da1f91 100644 --- a/lib/tracker_api/endpoints/comments.rb +++ b/lib/tracker_api/endpoints/comments.rb @@ -17,22 +17,6 @@ def get(project_id, story_id, params={}) story_id: story_id }.merge(comment)) end end - - def create(project_id, story_id, params={}) - data = client.post("/projects/#{project_id}/stories/#{story_id}/comments", params: params).body - Resources::Comment.new({ client: client, project_id: project_id }.merge(data)) - end - - def update(comment, params={}) - raise ArgumentError, 'Valid comment required to update.' unless comment.instance_of?(Resources::Comment) - - data = client.put("/projects/#{comment.project_id}/stories/#{comment.story_id}/comments/#{comment.id}", - params: params).body - - comment.attributes = data - comment.clean! - comment - end end end end diff --git a/lib/tracker_api/resources/comment.rb b/lib/tracker_api/resources/comment.rb index 74d900e..415c123 100644 --- a/lib/tracker_api/resources/comment.rb +++ b/lib/tracker_api/resources/comment.rb @@ -28,7 +28,7 @@ class UpdateRepresenter < Representable::Decorator def save raise ArgumentError, 'Cannot update a comment with an unknown story_id.' if story_id.nil? - Endpoints::Comments.new(client).update(self, UpdateRepresenter.new(Comment.new(self.dirty_attributes))) + Endpoints::Comment.new(client).update(self, UpdateRepresenter.new(Comment.new(self.dirty_attributes))) end end end diff --git a/lib/tracker_api/resources/story.rb b/lib/tracker_api/resources/story.rb index 9cc4f0e..a7058ec 100644 --- a/lib/tracker_api/resources/story.rb +++ b/lib/tracker_api/resources/story.rb @@ -130,7 +130,7 @@ def create_task(params) # @param [Hash] params attributes to create the comment with # @return [Comment] newly created Comment def create_comment(params) - Endpoints::Comments.new(client).create(project_id, id, params) + Endpoints::Comment.new(client).create(project_id, id, params) end # Save changes to an existing Story.