How to get an issue description from Jira and render it in the same way in a Confluence table?

ABoerio November 20, 2019

Hi, 

this is midway between Jira and Confluence (server).

I need to generate a summary table from a list of Jira Issues in a Confluence page.

I can't use the Issue macro in Confluence, because the people who will have access to the page won't have permissions in Jira, so they just would see an error message in the field in the table. In addition, I want people to be able to insert inline comments, so what they find in the page should be selectable text, possibly not rendered macro.

Anyway, I had almost done the job with the following code (here I'm not yet sending the data to Confluence, I'll do it then with a REST call).

I could get the data I need from a Jira Structure, or directly from a Jira query (in this case I'm reading a Structure).

//Retrieve information from an LOP stored in Jira Structure

//Name of the Structure to call
String structureName="MARTHA-LOP"

//Generic import
import com.atlassian.jira.component.ComponentAccessor
import groovy.xml.MarkupBuilder

//Librerie per gestione Link con Confluence
import com.atlassian.applinks.api.ApplicationLink
import com.atlassian.applinks.api.ApplicationLinkService
import com.atlassian.applinks.api.application.confluence.ConfluenceApplicationType
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

//Specific import for Jira Structure
import com.onresolve.scriptrunner.runner.customisers.PluginModule
import com.onresolve.scriptrunner.runner.customisers.WithPlugin
import com.almworks.jira.structure.api.structure.StructureManager
import com.almworks.jira.structure.api.permissions.PermissionLevel
import com.almworks.jira.structure.api.forest.ForestSpec
import com.almworks.jira.structure.api.row.StructureRow
import com.almworks.jira.structure.api.row.RowManager
import com.almworks.jira.structure.api.structure.Structure
import com.almworks.jira.structure.api.StructureComponents
import com.almworks.jira.structure.api.item.ItemIdentity
import com.almworks.jira.structure.api.item.CoreIdentities
import com.almworks.jira.structure.api.attribute.StructureAttributeService
import com.almworks.jira.structure.api.attribute.CoreAttributeSpecs
import com.almworks.jira.structure.api.attribute.AttributeSpec
import com.almworks.jira.structure.api.attribute.AttributeSpecBuilder
import com.almworks.jira.structure.api.attribute.ValueFormat
import com.almworks.jira.structure.api.attribute.VersionedRowValues
import com.almworks.integers.LongArray
import com.almworks.integers.LongIterator

def issueManager = ComponentAccessor.getIssueManager()

//Stabilisco un collegamento con Confluence
def ApplicationLink getPrimaryConfluenceLink() {
def applicationLinkService = ComponentLocator.getComponent(ApplicationLinkService.class)
final ApplicationLink conflLink = applicationLinkService.getPrimaryApplicationLink(ConfluenceApplicationType.class)
conflLink
}
def confluenceLink = getPrimaryConfluenceLink()
assert confluenceLink // must have a working app link set up
def authenticatedRequestFactory = confluenceLink.createImpersonatingAuthenticatedRequestFactory()

//Also necessary for Jira Structure
@WithPlugin("com.almworks.jira.structure")
@PluginModule
StructureComponents sc

//Structure handlers
StructureManager sm = sc.getStructureManager()
RowManager rowManager = sc.getRowManager()
def forestService = sc.getForestService()

//Permission access to structure
def permission = PermissionLevel.valueOf("ADMIN")

//Get Structure
def struct = sm.getStructuresByName(structureName, permission)[0]
//Forest Specification
def forestSpec = ForestSpec.structure(struct.getId())
//Forest Source
def forestsrc=forestService.getForestSource(forestSpec)
//Forest - Last version
def forest = forestSrc.getLatest().getForest()
//Forest Rows
def forestRows=forest.getRows()


//

def myStructureId=struct.getId() // get id
def viewManager = sc.getViewManager()
log.warn("Structure Id: "+myStructureId)
def views=viewManager.getViewSettings(myStructureId).getAssociatedViews()
views.each {
associatedView->
def viewId = associatedView.getViewId()
log.warn(viewId)
def viewSpec = viewManager.getView(viewId, null /*permission level*/).getSpecification()
viewSpec.getColumns().forEach({
column ->
//log.warn(column)// request values by column spec (attr id and params)
})
}


//List of attributes to be retrieved and then used somewhere
List attributeSet = new ArrayList<>()
attributeSet.add(CoreAttributeSpecs.KEY)
attributeSet.add(CoreAttributeSpecs.SUMMARY)
attributeSet.add(CoreAttributeSpecs.DESCRIPTION)

//Build a matrix that relates forest rows with the attributes chosen before
VersionedRowValues values = sc.getAttributeService().getAttributeValues(forestSpec, forestRows, attributeSet);
log.warn(values)

//Def writer and xml to build output
def writer = new StringWriter()
def xml = new MarkupBuilder(writer)


//Build table
xml.table(class: "aui") {

thead {
tr {
th("Key")
th("Summary")
th("Description (got from structure)")
th("Description (direct from jira issue)")
}
}

// Row counter
int rowNumber=0

tbody{

//Iterate among forest rows
for (LongIterator ri : forestRows) {
//Get the specific row
StructureRow row = rowManager.getRow(ri.value())
rowNumber=rowNumber+1
//Identify type of item in the row
ItemIdentity itemId = row.getItemId()

tr{

//In case of an Issue
if (CoreIdentities.isIssue(itemId)){
//definisco oggetto issue
def issue = issueManager.getIssueObject(values.get(ri.value(), CoreAttributeSpecs.KEY))
def issueKey =values.get(ri.value(), CoreAttributeSpecs.KEY)
td(values.get(ri.value(), CoreAttributeSpecs.KEY))
td(values.get(ri.value(), CoreAttributeSpecs.SUMMARY))
td(values.get(ri.value(), CoreAttributeSpecs.DESCRIPTION))
td(issue.getDescription())
}
//In case of another item "(normally folders)"
else{
}
}

}
}
}



//Evaluate output in Scriptrunner console
return writer.toString()

 

The problem then is that I'm not able to render the Issue Description in the same way I see it in Jira :-(

Here is how the description is rendered in Jira

 

image.png

 

and here is what I get in my table

 

image.png

When I send the data to confluence I get the same effect above.

Thanks in advance for any suggestment.

Andrea

 

 

 

2 answers

1 accepted

0 votes
Answer accepted
fjodors
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.
November 22, 2019

Hi

You wrote

>>The problem then is that I'm not able to render the Issue Description in the same way I see it in Jira :-(

Have you tried to convert description from wiki to html format?

Here is an example from scriptrunner console, may be it will be helpful

import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.IssueManager
import com.atlassian.event.api.EventPublisher
import com.atlassian.jira.util.velocity.VelocityRequestContextFactory
import com.atlassian.jira.issue.fields.renderer.wiki.AtlassianWikiRenderer
import com.atlassian.jira.issue.fields.renderer.wiki.*
import com.atlassian.jira.issue.fields.renderer.*
import com.atlassian.jira.config.properties.ApplicationProperties
import com.atlassian.jira.config.FeatureManager


def ap = ComponentAccessor.getApplicationProperties();
def fm = ComponentAccessor.getOSGiComponentInstanceOfType(FeatureManager);
def ism = ComponentAccessor.getIssueManager();
MutableIssue iss = ism.getIssueByCurrentKey('<issue Key>');
def desc = iss.getDescription();




EventPublisher eventPublisher = ComponentAccessor.getOSGiComponentInstanceOfType(EventPublisher.class);
VelocityRequestContextFactory velocityRequestContextFactory = ComponentAccessor.getOSGiComponentInstanceOfType(VelocityRequestContextFactory.class);
def wikiRenderer = new AtlassianWikiRenderer(eventPublisher, ap, velocityRequestContextFactory, fm);

String descHtml = wikiRenderer.render(desc, null);

return descHtml
ABoerio November 22, 2019

Hy @fjodors , and thanks for your message.

The content of your descHtml is actually the same the content I got already in the strings I was using.

If I return directly this string to the console, it works perfectly.

The main issue (for me, due to my limited knowledge), is how to generate the equivalent MarkupBuilder instructions from the string, in order to put it in the table cell.

 

If I simply add

td('<p><b>20/11/19-A.Boerio:</b>Ciao</p>')

and then return the writer, I get a table with a cell that shows

   <p><b>20/11/19-A.Boerio:</b>Ciao</p>

and not

   20/11/19-A.Boerio:Ciao

I'm sorry I don't know how can I describe it better.

Andrea

fjodors
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.
November 22, 2019
ABoerio November 22, 2019

Ops, I guess I could solve it by using mkp.yieldUnescaped()

I try.

0 votes
ABoerio November 22, 2019

Done!

 

It's was just a matter of introducing 

 

def mkp = new MarkupBuilderHelper(xml)

 

and then, when defining the table, using

td{

mkp.yieldUnescaped('<p><b>20/11/19-A.Boerio:</b>Ciao</p>')

}

instead of 

td('<p><b>20/11/19-A.Boerio:</b>Ciao</p>')

 

So simple ... when you know it :-(

Suggest an answer

Log in or Sign up to answer