sumoheavy / jira-ruby

A Ruby gem for the JIRA REST API
MIT License
655 stars 412 forks source link

Can't get attachment contents, seems jira-ruby might be using the wrong endpoint? #414

Closed morphine00 closed 6 months ago

morphine00 commented 12 months ago

Hi there!

I'm trying to fetch an attachment's actual content, as described by this JIRA API documentation. However, despite trying multiple variations of accomplishing this, I can't figure out a way to make it work. Additionally, it seems that my efforts bring up the wrong endpoint. In more detail:

issue = issue.find('MY-ISSUE')
jira.Attachment.find( "10000", issue: MY_ISSUE )

That results in a 405 Method Not Allowed.

jira.Attachment.find( "10000", issue_id: "10101" )

That also resutls in a 405 Method Not Allowed.

After additional digging, it seems that jira-ruby is never querying the actual /attachments/[id] endpoint, but is instead going through the /issue/{issueIdOrKey}/attachments endpoint that seems to have write-only functionality.

Can you folks shed some light on this?

marlinpierce commented 11 months ago

Try this

issue =  JIRA::Resource::Issue.find(client, 'MY-ISSUE', { fields: 'attachment' } )
attachments = issue.attachments.find(10000)
attachment = attachments.first
morphine00 commented 11 months ago

Hi Marlin, and thanks for your reply. The thing is, when you do that, you get the attachment's metadata, but not the actual file's contents. In fact, calling "content" just retrieves a string with the endpoint to be queried, returned by the Jira API, in the form: /rest/api/3/attachment/content/10000 . Unless I missed something in the source, jira-ruby library apparently doesn't query that endpoint at all to retrieve a file's contents.

My current workaround is to monkey-patch the Attachment class with a get_content function like so:


module JIRA
    module Resource
        class Attachment < JIRA::Base

            # Returns an attachment's contents in the form of a temporary file in order to avoid hold the data in RAM
            # It's up to the caller function to close the file handle.
            def get_content

                # "redirect=false" to avoid the possibility of the built-in CGI client not following redirects
                url = client.options[:rest_base_path] + "/attachment/content/#{CGI.escape( id )}?redirect=false"

                # TODO: handle error conditions here, see https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-attachments/#api-rest-api-3-attachment-content-id-get
                begin
                    response = client.get( url )
                rescue JIRA::HTTPError
                    return nil
                end

                # TODO: handle error conditions here
                tempfile = Tempfile.new('jira_attachment', binmode: true )

                tempfile.write( response.body )

                # Rewind the file location pointer else when the caller function tries to read the file, it'll get nothing
                tempfile.seek(0, IO::SEEK_SET)

                return tempfile
            end
    end
end
marlinpierce commented 10 months ago

I think that is correct. You get the "content" field which is a URL to download the contents. This is how the Jira REST API works, so as a wrapper around the REST API , the jira-ruby gem does the same. I agree and convenience method to get the content might be nice.

marlinpierce commented 9 months ago

I have a PR #422 which implements a convenience method to download the attachment file contents.