Forums

Articles
Create
cancel
Showing results for 
Search instead for 
Did you mean: 

Copy Attachment using Rest API from Issue to Issue

Alex Cumberland
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
May 29, 2020

Trying to get a way to copy an attachment from one issue to another issue as part of a group of issues.

Using REST API we have a tool for finalizing our test component and it grabs the test documentation (attachment on Company Issue in Jira) and downloads it then uploads it to the related client issue (Issue for client project in JIRA) to share the completed testing documentation.

Both projects are in the same JIRA instance but the clients cannot see the company project based on permissions.  But we still want to be able to share this documentation when we compile our build output.

The problem comes in having to download then upload the attachment, as it sometimes times out and that prevents that issue from being updated with the proper test documentation.  

 

Is there a REST API call that would allow me to copy the attachment and associate it with the client issue without having to download and then upload it?

 

4 answers

3 accepted

1 vote
Answer accepted
Tomasz Bryła
Contributor
May 30, 2020

Hi @Alex Cumberland 

If you don't want to download and upload attachments to issue you can create REST Endpoint by ScriptRunner which copy attachments from source issue to destination issue.

https://scriptrunner.adaptavist.com/latest/jira/rest-endpoints.html

https://docs.atlassian.com/software/jira/docs/api/7.6.1/com/atlassian/jira/issue/AttachmentManager.html

0 votes
Answer accepted
Lukasz Grobelny
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
February 13, 2022

@Alex Cumberland old issue but if needed I can share the code for this

Alex Cumberland
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
February 14, 2022

@Lukasz Grobelny 

 

Yes I would love to see the code.   We ended up having to do the whole copy down and the reupload.   It is extremely time consuming.

Lukasz Grobelny
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
February 17, 2022

OK, so I think I have the same resolution as you:

 

Script checks if there was some value selected for the attachments on the original screen and then this:

 

if(selectedFields.contains("Att")){
for(int i = 0; i < attachment.size(); i++ ){
def attId = attachment.id[i]
def att = Unirest.get('/rest/api/2/attachment/' + attId).asObject(Map).body
def downloadUrl = attachment.content[i]
def readFileResp = Unirest.get(downloadUrl).asBinary()
log.warn(downloadUrl)
def response = Unirest.post('/rest/api/2/issue/' + createdIssueKey + '/attachments')
.header("Accept", "application/json")
.header("X-Atlassian-Token", "nocheck")
.field("file", readFileResp.body, att.title.toString())
.asObject(Map)

}
}
Peter Reiser
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
February 17, 2022

cool - thanks - 

Does anyone has the scriptrunner example for Jira Cloud ? 

Peter Reiser
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
February 17, 2022

As an alternative we use Jira Automation to copy the Attachment information to the new ticket including the download link. This allows to keep the attachments on one place and just provide a download link in the destination issue. 

Automation rule

1. CLONE Issue action
2. Edit Description Field


{{issue.description}}

----

*Attachments links from Original Ticket*


{{#issue.attachment}}


- [ {{filename}} | {{content}} ] - {{size}} Bytes - {{author.displayName}} - {{created.jiraDate}}


{{/issue.attachment}} 
Lukasz Grobelny
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
February 17, 2022

The one I've posted is for Cloud. If you want, I have one for server as well

Peter Reiser
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
February 17, 2022

@Lukasz Grobelny upps sorry - Cloud code is perfect - Thanks

Hirar pahar
I'm New Here
I'm New Here
Those new to the Atlassian Community have posted less than three times. Give them a warm welcome!
May 19, 2022

@Lukasz Grobelny 

Thank you!  Your post was very helpful.  

Could you please post the server one here as well?  I think I might go that route.

Teodor Hoza
Contributor
June 24, 2022

@Lukasz Grobelny man, that was exactly what I needed.


Had to modify it a bit for my tests but this is ready to be integrated with my stuff

Leaving my edited code here so maybe someone else needs it

 

def issueKey = "TEST-1"
def issueFields = get('/rest/api/2/issue/' + issueKey)
.header('Content-Type', 'application/json')
.asObject(Map)
.body.fields
def targetIssueKey = "TEST-2"
def attaches = issueFields.attachment as List <String,String>
attaches.each{ it ->
def attId = it.id
def att = Unirest.get('/rest/api/2/attachment/' + attId).asObject(Map).body
def downloadUrl = it.content
def readFileResp = Unirest.get(downloadUrl).asBinary()
logger.info("${downloadUrl}")
def response = Unirest.post('/rest/api/2/issue/' + targetIssueKey + '/attachments')
.header("Accept", "application/json")
.header("X-Atlassian-Token", "nocheck")
.field("file", readFileResp.body, att.filename.toString())
.asObject(Map)
}

Thank you @!!!!

0 votes
Answer accepted
Phill Fox
Community Champion
May 29, 2020

Hi @Alex Cumberland 

I am not aware of any publicly available REST API call to allow the copy of attachments between issues. 

However, you might consider changing the time out for your API calls. 

 Usually, the REST API uses the default_socket_timeout parameter which defaults to 300 seconds, however, this parameter can be changed in some consoles like CURL.

You can check the link below to know exactly what parameters you can use to change the timeout:

Default cURL timeout value

 

Hope this helps 

 

Phill

0 votes
Lukasz Grobelny
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
July 22, 2022

So this is code to move entire issue between projects - comments included

 

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.context.IssueContext
import com.atlassian.jira.issue.context.IssueContextImpl
import com.atlassian.jira.issue.fields.config.manager.PrioritySchemeManager
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.ModifiedValue
import org.apache.log4j.Logger


def summarytest = issue.getSummary()
def assignee = issue.getAssignee().getDisplayName()
def projetPickerValue = ""
def issueManager = ComponentAccessor.getIssueManager()
def customFieldManager = ComponentAccessor.getCustomFieldManager()
def cField = customFieldManager.getCustomFieldObject("customfield_10201")
def originalIssueType = issue.getIssueType().getName()
def additionalFieldsSelector = customFieldManager.getCustomFieldObject("customfield_10300")
def additionalFields = issue.getCustomFieldValue(additionalFieldsSelector) as List
def value = (String) issue.getCustomFieldValue(cField)

def projectKeyFromPicker = value.minus("Project: ")
log.warn(additionalFields.getAt(0))
//log.warn(assignee)



def commentManager = ComponentAccessor.getCommentManager()
def existngComments = commentManager.getComments(issue);

log.warn(summarytest)
log.warn(existngComments.getAt(0).getBody())
//Czesc peirwsza pobieranie issue key

// the project key under which the issue will get created
final projectKey = 'TEST'

// the issue type for the new issue
final issueTypeName = 'Bug'

// user with that user key will be the reporter of the issue
final reporterKey = 'auser'

// the summary of the new issue
final summary = 'Test'

// the priority of the new issue
final priorityName = 'Major'

def issueService = ComponentAccessor.issueService
def constantsManager = ComponentAccessor.constantsManager
def loggedInUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser
def prioritySchemeManager = ComponentAccessor.getComponent(PrioritySchemeManager)

def project = ComponentAccessor.projectManager.getProjectObjByKey(projectKeyFromPicker)
assert project : "Could not find project with key $projectKey"

def issueType = constantsManager.allIssueTypeObjects.findByName(originalIssueType)
assert issueType : "Could not find issue type with name $issueTypeName"

// if we cannot find user with the specified key or this is null, then set as a reporter the logged in user
def reporter = ComponentAccessor.userManager.getUserByKey(reporterKey) ?: loggedInUser
def tet = ComponentAccessor.userManager.getUserByName(assignee) ?: loggedInUser
log.warn(reporter)
log.warn(tet)

// if we cannot find the priority with the given name or if this is null, then set the default priority
def issueContext = new IssueContextImpl(project, issueType) as IssueContext
def priorityId = constantsManager.priorities.findByName(priorityName)?.id ?: prioritySchemeManager.getDefaultOption(issueContext)

def issueInputParameters = issueService.newIssueInputParameters().with {
setProjectId(project.id)
setIssueTypeId(issueType.id)
setReporterId(reporter.name)
setSummary(summarytest)
setPriorityId(priorityId)
setAssigneeId(tet.name)
}

def validationResult = issueService.validateCreate(loggedInUser, issueInputParameters)
assert validationResult.valid : validationResult.errorCollection

def result = issueService.create(loggedInUser, validationResult)
assert result.valid : result.errorCollection

log.warn(result.getIssue())
//Przenosimy komentarze
if(additionalFields.contains("Komentarze")){
log.warn("Komentarze")
if(existngComments.size() > 0){
for(int i = 0; i < existngComments.size(); i++){
def commentAuthor = existngComments.getAt(i)getAuthorApplicationUser()
def newIssue = result.getIssue()
def newBody = existngComments.getAt(i).getBody()
commentManager.create(newIssue,commentAuthor,newBody,true)
log.warn(existngComments.getAt(i).getBody())
}
}
}

Suggest an answer

Log in or Sign up to answer
DEPLOYMENT TYPE
SERVER
VERSION
8.8.1
TAGS
AUG Leaders

Atlassian Community Events