Create
cancel
Showing results for 
Search instead for 
Did you mean: 
Sign up Log in

copy confluence attachment using Jira script runner

OToole August 3, 2018

Hi All,

On issue creation I need to create a new confluence page from a template that has an attached excel spreadsheet template.   I have successfully generated the new page.

I have put our attachment in a blank page,  now I need to make a copy of the attachment and attach it to my new page.  Each new page needs its own copy of the attachment.  So it can't all link to the same page.

I believe I need to do this using the GET to download the attachment and then POST to put the attachment back up.  But I am confused as to what exactly is returned by the GET command,  and all of the POST examples I see use a File type to upload it.  

Here is the code I have,  this does not work..  It creates the page but I am not sure how to get the attachment in there..  Do I do it similar to the page creation using a params structure?  And where is that documented?

I would appreciate any help

 

import com.atlassian.applinks.api.ApplicationLink
import com.atlassian.applinks.api.ApplicationLinkService
import com.atlassian.applinks.api.application.confluence.ConfluenceApplicationType
import com.atlassian.jira.issue.Issue
import com.atlassian.sal.api.component.ComponentLocator
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.sal.api.net.Request
import com.atlassian.sal.api.net.Response
import com.atlassian.sal.api.net.ResponseException
import com.atlassian.sal.api.net.ResponseHandler
import groovy.json.JsonBuilder
import groovy.json.JsonSlurper
import groovy.xml.MarkupBuilder
import com.atlassian.sal.api.net.RequestFilePart

import org.apache.log4j.Logger
import org.apache.log4j.Level
def log = Logger.getLogger("com.acme.CreateSubtask")
log.setLevel(Level.DEBUG)


static ApplicationLink getPrimaryConfluenceLink() {
def applicationLinkService = ComponentLocator.getComponent(ApplicationLinkService.class)
final ApplicationLink conflLink = applicationLinkService.getPrimaryApplicationLink(ConfluenceApplicationType.class)
conflLink
}

// the issue provided to us in the binding

def issueManager = ComponentAccessor.getIssueManager()
MutableIssue issue = issueManager.getIssueObject("IT-42")

 

def confluenceLink = getPrimaryConfluenceLink()
assert confluenceLink // must have a working app link set up

def authenticatedRequestFactory = confluenceLink.createImpersonatingAuthenticatedRequestFactory()

// write storage format using an XML builder
def writer = new StringWriter()
def xml = new MarkupBuilder(writer)
xml.'ac:structured-macro'('ac:name': "jira") {
'ac:parameter'('ac:name': "key", issue.key)
'ac:parameter' ('ac:name': "server", "MY JIRA")
'ac:parameter' ('ac:name': "serverId", "Some server Id")
}

// retrieve contents of "template page"
def confResponse = null

authenticatedRequestFactory
.createRequest(Request.MethodType.GET, "rest/api/content?type=page&title=MY_Template_Page&expand=body.storage.value")
.addHeader("Content-Type", "application/json")
.execute(
new ResponseHandler<Response>()
{
@Override
void handle(Response response) throws ResponseException {
confResponse = new JsonSlurper().parseText(response.getResponseBodyAsString())
}
})

// set the page title - this should be unique in the space or page creation will fail
def pageTitle = issue.key + " Issue Page"

def params = [
type : "page",
title: pageTitle,
space: [
key: "TES" // set the space key - or calculate it from the project or something
],
body : [
storage: [
value : writer.toString() + confResponse.results.body.storage.value[0],
representation: "storage"
]
]
]

authenticatedRequestFactory
.createRequest(Request.MethodType.POST, "rest/api/content")
.addHeader("Content-Type", "application/json")
.setRequestBody(new JsonBuilder(params).toString())
.execute(new ResponseHandler<Response>() {
@Override
void handle(Response response) throws ResponseException {
if (response.statusCode != HttpURLConnection.HTTP_OK) {
throw new Exception(response.getResponseBodyAsString())
} else {
def webUrl = new JsonSlurper().parseText(response.responseBodyAsString)["_links"]["webui"]
log.debug webUrl
}
}
})


def att_file
// hard code the download link for now.
authenticatedRequestFactory
.createRequest(Request.MethodType.GET, "download/attachments/5242908/FM00872.xlsx?version=1&modificationDate=1533302834924&api=v2")
// .addHeader("Content-Type", "application/json")
//.setRequestBody(new JsonBuilder(params).toString())
//.setEntity(entityBuilder.build())
//.setFiles(partList)
.execute(new ResponseHandler<Response>() {
@Override
void handle(Response response) throws ResponseException {
if(response.statusCode != HttpURLConnection.HTTP_OK) {
throw new Exception(response.getResponseBodyAsString())
}
else
{
att_file = new File(response.responseBodyAsString)
}
}
})


// What do I put here? how to I insert what was returned?


authenticatedRequestFactory
.createRequest(Request.MethodType.POST, "rest/api/content/IT-42+MY_Template_Page/child/attachment")
.addHeader("X-Atlassian-Token", "nocheck")
//.setRequestBody(new JsonBuilder(params).toString())
//.setEntity(entityBuilder.build())
//.setFiles(partList)
.execute(new ResponseHandler<Response>() {
@Override
void handle(Response response) throws ResponseException {
if(response.statusCode != HttpURLConnection.HTTP_OK) {
throw new Exception(response.getResponseBodyAsString())
}
}
})

 

1 answer

Suggest an answer

Log in or Sign up to answer
3 votes
OToole November 6, 2018

Ok So I never got an answer, but I did get it working. 

Here is my code.  I create a confluence page and link it to our issue.  I added the template as a n attachment to a page in confluence, which is why I can hard code the downlink address,  then pull down the excel template and then add the template as an attachment to a my new confluence page.   I then add a link to the attachements page inside the jira issue.  This way when viewing the issue any user can access the spreadsheet by selecting the link.

I left out the code to create the new page and get the page_id since that code is available in the scriptrunner examples

 

The trick was to use getResponseAsStream()  instead of getResponseAsString()  the conversion to string was corrupting the excel file.

 

 

import com.atlassian.applinks.api.ApplicationLink
import com.atlassian.applinks.api.ApplicationLinkService
import com.atlassian.applinks.api.application.confluence.ConfluenceApplicationType
import com.atlassian.jira.issue.Issue
import com.atlassian.sal.api.component.ComponentLocator
import com.atlassian.sal.api.net.Request
import com.atlassian.sal.api.net.Response
import com.atlassian.sal.api.net.ResponseException
import com.atlassian.sal.api.net.ResponseHandler
import groovy.json.JsonBuilder
import groovy.json.JsonSlurper
import groovy.xml.MarkupBuilder
import com.atlassian.sal.api.net.RequestFilePart
import org.apache.commons.io.FileUtils
import com.atlassian.jira.issue.link.RemoteIssueLinkBuilder
import com.atlassian.jira.issue.link.RemoteIssueLink
import com.atlassian.jira.issue.link.RemoteIssueLinkManager
import com.atlassian.jira.component.ComponentAccessor

 

// Get attachment and save to temporary file
def pathName = "C:\\temp\\TEMP.xlsx"
File testfile = new File(pathName);
testfile.createNewFile();
InputStream att_file;
authenticatedRequestFactory
.createRequest(Request.MethodType.GET, "download/attachments/5242908/Workbook.xlsx?version=1&modificationDate=1533302834924&api=v2&download=true")
.addHeader("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
.execute(new ResponseHandler<Response>() {
@Override
void handle(Response response) throws ResponseException {
if(response.statusCode != HttpURLConnection.HTTP_OK) {
throw new Exception(response.getResponseBodyAsString())
}
else
{
att_file = response.getResponseBodyAsStream()
//log.debug "GET " << response.statusCode
}
}
})

FileUtils.copyInputStreamToFile(att_file, testfile);

File fileUpload = new File(pathName)
assert fileUpload
def part = new RequestFilePart(fileUpload, "file")
assert part
def partList = new ArrayList<RequestFilePart>()
partList.add(part)
assert partList
assert partList.size() == 1
//log.debug fileUpload
//log.debug fileUpload.size();


// Add attachment as child
authenticatedRequestFactory
.createRequest(Request.MethodType.POST, "rest/api/content/" + page_id + "/child/attachment")
.addHeader("X-Atlassian-Token", "nocheck")
.setFiles(partList)
.execute(new ResponseHandler<Response>() {
@Override
void handle(Response response) throws ResponseException {
if(response.statusCode != HttpURLConnection.HTTP_OK) {
//log.debug response.statusCode
//log.debug response.getResponseBodyAsString()
throw new Exception(response.getResponseBodyAsString())
}
}
})

def link = "http://yourconfluenceserver:8090/pages/viewpageattachments.action?pageId="+ page_id + "&metadataLink=true"

def customFieldManager = ComponentAccessor.getCustomFieldManager()
def code_field = customFieldManager.getCustomFieldObjectByName("Project Code / Name")
def project_code = issue.getCustomFieldValue(code_field)
def workbook_name = project_code.toString() + " Design Maturity Workbook"

RemoteIssueLinkBuilder remoteIssueLinkBuilder = new RemoteIssueLinkBuilder();
remoteIssueLinkBuilder.issueId(issue.getId());
remoteIssueLinkBuilder.relationship("Link");
remoteIssueLinkBuilder.title(workbook_name.toString());
remoteIssueLinkBuilder.url(link);

RemoteIssueLink remoteIssueLink = remoteIssueLinkBuilder.build();

def user = issue.getAssigneeUser();
TAGS
AUG Leaders

Atlassian Community Events