Skip to content
This repository was archived by the owner on Apr 13, 2025. It is now read-only.
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,20 @@ comments = story.comments # co

comment = story.create_comment(text: "Use the force!") # Create a new comment on the story

comment = story.create_comment(text: "Use the force again !", # Create a new comment on the story with
files: ['path/to/an/existing/file']) # file attachments

comment.text += " (please be careful)"
comment.save # Update text of an existing comment
comment.delete # Delete an existing comment

comment.create_attachments(files: ['path/to/an/existing/file']) # Add attachments to existing comment
comment.delete_attachments # Delete all attachments from a comment

attachments = comment.attachments # Get attachments associated with a comment
attachments.first.delete # Delete a specific attachment

comment.attachments(reload: true) # Re-load the attachments after modification
task = story.tasks.first # Get story tasks
task.complete = true
task.save # Mark a task complete
Expand Down
6 changes: 6 additions & 0 deletions lib/tracker_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
# dependencies
require 'faraday'
require 'faraday_middleware'
require 'pathname'
require 'mimemagic'

if defined?(ActiveSupport)
require 'active_support/core_ext/object/blank'
Expand All @@ -26,6 +28,7 @@ module TrackerApi
autoload :Error, 'tracker_api/error'
autoload :Client, 'tracker_api/client'
autoload :Logger, 'tracker_api/logger'
autoload :FileUtility, 'tracker_api/file_utility'

module Errors
class UnexpectedData < StandardError; end
Expand Down Expand Up @@ -55,6 +58,8 @@ module Endpoints
autoload :Webhook, 'tracker_api/endpoints/webhook'
autoload :Webhooks, 'tracker_api/endpoints/webhooks'
autoload :StoryTransitions, 'tracker_api/endpoints/story_transitions'
autoload :Attachment, 'tracker_api/endpoints/attachment'
autoload :Attachments, 'tracker_api/endpoints/attachments'
end

module Resources
Expand Down Expand Up @@ -85,5 +90,6 @@ module Shared
autoload :Comment, 'tracker_api/resources/comment'
autoload :Webhook, 'tracker_api/resources/webhook'
autoload :StoryTransition, 'tracker_api/resources/story_transition'
autoload :FileAttachment, 'tracker_api/resources/file_attachment'
end
end
35 changes: 5 additions & 30 deletions lib/tracker_api/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,40 +48,15 @@ def initialize(options={}, &block)
end
end

# Make a HTTP GET request
# HTTP requests methods
#
# @param path [String] The path, relative to api endpoint
# @param options [Hash] Query and header params for request
# @return [Faraday::Response]
def get(path, options = {})
request(:get, parse_query_and_convenience_headers(path, options))
end

# Make a HTTP POST request
#
# @param path [String] The path, relative to api endpoint
# @param options [Hash] Query and header params for request
# @return [Faraday::Response]
def post(path, options = {})
request(:post, parse_query_and_convenience_headers(path, options))
end

# Make a HTTP PUT request
#
# @param path [String] The path, relative to api endpoint
# @param options [Hash] Query and header params for request
# @return [Faraday::Response]
def put(path, options = {})
request(:put, parse_query_and_convenience_headers(path, options))
end

# Make a HTTP DELETE request
#
# @param path [String] The path, relative to api endpoint
# @param options [Hash] Query and header params for request
# @return [Faraday::Response]
def delete(path, options = {})
request(:delete, parse_query_and_convenience_headers(path, options))
%i{get post patch put delete}.each do |verb|
define_method verb do |path, options = {}|
request(verb, parse_query_and_convenience_headers(path, options))
end
end

# Make one or more HTTP GET requests, optionally fetching
Expand Down
38 changes: 38 additions & 0 deletions lib/tracker_api/endpoints/attachment.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
module TrackerApi
module Endpoints
class Attachment
attr_accessor :client

def initialize(client)
@client = client
end

def create(comment, file)
data = client.post("/projects/#{comment.project_id}/uploads", body: FileUtility.get_file_upload(file)).body
Resources::FileAttachment.new({ comment: comment }.merge(data))
end

# TODO : Discuss before implementing this as it orphans the file.
# It deletes source, but the file name appears in the comments
# def delete(comment, file_attachment_id)
# client.delete("/projects/#{comment.project_id}/stories/#{comment.story_id}/comments/#{comment.id}/file_attachments/#{file_attachment_id}").body
# end

def get(comment)
data = client.get("/projects/#{comment.project_id}/stories/#{comment.story_id}/comments/#{comment.id}?fields=file_attachments").body["file_attachments"]
raise Errors::UnexpectedData, 'Array of file attachments expected' unless data.is_a? Array

data.map do |file_attachment|
Resources::FileAttachment.new({ comment: comment }.merge(file_attachment))
end
end

# TODO : Implement this properly.
# This results in either content of the file or an S3 link.
# the S3 link is also present in big_url attribute.
# def download(download_path)
# client.get(download_path, url: '').body
# end
end
end
end
22 changes: 22 additions & 0 deletions lib/tracker_api/endpoints/attachments.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module TrackerApi
module Endpoints
class Attachments
attr_accessor :client

def initialize(client)
@client = client
end


def create(comment, files)
return [] if files.to_a.empty?
#Check files before upload to make it all or none.
FileUtility.check_files_exist(files)
attachment = Endpoints::Attachment.new(client)
files.map do | file |
attachment.create(comment, file)
end
end
end
end
end
11 changes: 9 additions & 2 deletions lib/tracker_api/endpoints/comment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,20 @@ def create(project_id, story_id, params={})
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
path = "/projects/#{comment.project_id}/stories/#{comment.story_id}/comments/#{comment.id}"
path += "?fields=:default,file_attachments" if params.represented.file_attachment_ids_to_add.present? || params.represented.file_attachment_ids_to_remove.present?
data = client.put(path, params: params).body

comment.attributes = data
comment.clean!
comment
end

def delete(comment)
raise ArgumentError, 'Valid comment required to update.' unless comment.instance_of?(Resources::Comment)

client.delete("/projects/#{comment.project_id}/stories/#{comment.story_id}/comments/#{comment.id}").body
end
end
end
end
16 changes: 16 additions & 0 deletions lib/tracker_api/file_utility.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module TrackerApi
class FileUtility
class << self
def get_file_upload(file)
mime_type = MimeMagic.by_path(file)
{ :file => Faraday::UploadIO.new(file, mime_type) }
end

def check_files_exist(files)
files.each do | file |
raise ArgumentError, 'Attachment file not found.' unless Pathname.new(file).exist?
end
end
end
end
end
35 changes: 35 additions & 0 deletions lib/tracker_api/resources/comment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ class Comment
attribute :created_at, DateTime
attribute :updated_at, DateTime
attribute :file_attachment_ids, [Integer]
attribute :file_attachments, [FileAttachment]
attribute :google_attachment_ids, [Integer]
attribute :file_attachment_ids_to_add, [Integer]
attribute :file_attachment_ids_to_remove, [Integer]
attribute :commit_identifier, String
attribute :commit_type, String
attribute :kind, String
Expand All @@ -24,13 +27,45 @@ class UpdateRepresenter < Representable::Decorator

property :id
property :text
collection :file_attachment_ids_to_add
collection :file_attachment_ids_to_remove
end

def save
raise ArgumentError, 'Cannot update a comment with an unknown story_id.' if story_id.nil?

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

def delete
raise ArgumentError, 'Cannot delete a comment with an unknown story_id.' if story_id.nil?

Endpoints::Comment.new(client).delete(self)
end

# @param [Hash] params attributes to create the comment with
# @return [Comment] newly created Comment
def create_attachments(params)
self.file_attachment_ids_to_add = Endpoints::Attachments.new(client).create(self, params[:files]).collect(&:id)
save
end

def delete_attachments(attachment_ids = nil)
self.file_attachment_ids_to_remove = attachment_ids || attachments.collect(&:id)
save
end

# Provides a list of all the attachments on the comment.
#
# @reload Boolean to reload the attachments
# @return [Array[FileAttachments]]
def attachments(reload: false)
if !reload && @file_attachments.present?
@file_attachments
else
@file_attachments = Endpoints::Attachment.new(client).get(self)
end
end
end
end
end
9 changes: 9 additions & 0 deletions lib/tracker_api/resources/epic.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ def save

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

# @param [Hash] params attributes to create the comment with
# @return [Comment] newly created Comment
def create_comment(params)
files = params.delete(:files)
comment = Endpoints::Comment.new(client).create(project_id, id, params)
comment.create_attachments(files: files) if files.present?
comment
end
end
end
end
37 changes: 37 additions & 0 deletions lib/tracker_api/resources/file_attachment.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
module TrackerApi
module Resources
class FileAttachment
include Shared::Base

attribute :comment, Comment

attribute :id, Integer
attribute :big_url, String
attribute :content_type, String
attribute :created_at, DateTime
attribute :download_url, String
attribute :filename, String
attribute :height, Integer
attribute :kind, String
attribute :size, Integer
attribute :thumbnail_url, String
attribute :thumbnailable, Boolean
attribute :uploaded, Boolean
attribute :uploader_id, Integer
attribute :width, Integer

def delete
comment.delete_attachments([id])
end

# TODO : Implement download properly.
# Look at Attchment#download for more details
# The big_url actually has the AWS S3 link for the file
# def download
# file_data = Endpoints::Attachment.new(comment.client).download(download_url)
# File.open(filename, 'wb') { |fp| fp.write(file_data) }
# end
end
end
end

11 changes: 7 additions & 4 deletions lib/tracker_api/resources/story.rb
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,11 @@ def activity(params = {})
#
# @param [Hash] params
# @return [Array[Comment]]
def comments(params = {})
if params.blank? && @comments.present?
def comments(reload: false)
if !reload && @comments.present?
@comments
else
@comments = Endpoints::Comments.new(client).get(project_id, id, params)
@comments = Endpoints::Comments.new(client).get(project_id, id)
end
end

Expand Down Expand Up @@ -161,7 +161,10 @@ def create_task(params)
# @param [Hash] params attributes to create the comment with
# @return [Comment] newly created Comment
def create_comment(params)
Endpoints::Comment.new(client).create(project_id, id, params)
files = params.delete(:files)
comment = Endpoints::Comment.new(client).create(project_id, id, params)
comment.create_attachments(files: files) if files.present?
comment
end

# Save changes to an existing Story.
Expand Down
42 changes: 42 additions & 0 deletions test/comment_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@
comment.clean?.must_equal true
end

it 'can create a comment with file attachment' do
text = "Test creating a comment"
comment = nil
files = [File.expand_path('../Gemfile', File.dirname(__FILE__))]
VCR.use_cassette('create comment with attachment', record: :new_episodes) do
comment = story.create_comment(text: text, files: files)
end
comment.text.must_equal text
comment.attachments.size.must_equal 1
comment.clean?.must_equal true
end

it 'can update an existing comment' do
new_text = "#{existing_comment.text}+"
existing_comment.text = new_text
Expand All @@ -32,4 +44,34 @@
existing_comment.text.must_equal new_text
existing_comment.clean?.must_equal true
end

it 'can create attachments in a comment' do
files = [File.expand_path('../Gemfile', File.dirname(__FILE__))]
VCR.use_cassette('create attachments', record: :new_episodes) do
existing_comment.create_attachments(files: files)
existing_comment.attachments.size.must_equal 1
existing_comment.clean?.must_equal true
end
end

it 'can delete attachments in a comment' do
files = [File.expand_path('../Gemfile', File.dirname(__FILE__))]
VCR.use_cassette('delete attachments', record: :new_episodes) do
existing_comment.create_attachments(files: files)
existing_comment.attachments.size.must_equal 1
existing_comment.delete_attachments
existing_comment.attachments.size.must_equal 0
end
end

it 'can delete a comment' do
VCR.use_cassette('delete comment', record: :new_episodes) do
current_story = project.story(story_id)
new_comment_id = current_story.create_comment(text: "test comment").id
current_story.comments.last.id.must_equal new_comment_id
current_story.comments.last.delete
current_story = project.story(story_id)
current_story.comments.last.id.wont_equal new_comment_id
end
end
end
Loading