Forums

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

Salesforce to Jira Integration using REST API- Richtext field

Santosh Kumar June 10, 2020

Hi Everyone,

We have a custom Object i.e., Jira Case on Salesforce side and in this object we have field called "Jira Description" this is type of Rich Text and the use case is whenever the support users add any screen-capture or images in this field or Modify the content having font size, Underlined content etc.,. This has to be shown in the Jira "Description" field using REST API. Basically we have to show the images and the same content from Salesforce Description field to JIRA Description field

We tried to do this but it shows the URL for the image in Salesforce and content with HTML tags.

Is there any way we can decode the "Jira Description" field from Salesforce to JIRA to copy the same information from Salesforce to JIRA by using REST API. Please suggest if any solution.

My findings: In JIRA Description field we can do content formatting and add the screen-captures as same as Rich Text field.

Below is the API we are using to POST from Salesforce

https://domain.atlassian.net/rest/api/2/issue

Below is the Trigger on Salesforce 

Public class SalesforceToJiraCallouts {
public static void getJiraDetails(list<Jira_Case__c> jiraCaseRecordsList){
SalesforceToJiraCalloutsBugParser jsonParserForBug = new SalesforceToJiraCalloutsBugParser();
SalesforceToJiraCalloutsIssueParser jsonParserForEnhancement = new SalesforceToJiraCalloutsIssueParser();
for(Jira_Case__c jiraCaseRecord: jiraCaseRecordsList ){
if(jiraCaseRecord.Link_to_Existing_Jira__c == false){
string jiraCaseDescription = jiraCaseRecord.Jira_Description__c;
jiraCaseDescription = jiraCaseDescription;
if(jiraCaseRecord.Jira_Issue_Type__c == 'Enhancement'){
if(jiraCaseRecord.Product_Sub_Type__c == '3D Sprint' && jiraCaseRecord.Jira_Project_Name__c == '3D Sprint'){
jsonParserForEnhancement.Fields.project.key = system.Label.X3D_Sprint;
list<string> jiraAffectVersionsList = jiraCaseRecord.Jira_Affect_Versions__c.split(';');
system.debug('jiraAffectVersionsList' + jiraAffectVersionsList);
list<SalesforceToJiraCalloutsIssueParser.versions> versionList = new list<SalesforceToJiraCalloutsIssueParser.versions>();
if(jiraAffectVersionsList.size()>0){
for(integer i=0; i<jiraAffectVersionsList.size();i++){
SalesforceToJiraCalloutsIssueParser.versions version = new SalesforceToJiraCalloutsIssueParser.versions();
version.name = jiraAffectVersionsList[i];
versionList.add(version);
system.debug('jiraAffectVersionsList' + jiraAffectVersionsList[i]);
}
}
jsonParserForEnhancement.fields.versions = versionList;
}
else if(jiraCaseRecord.Product_Sub_Type__c == '3D Sprint' && jiraCaseRecord.Jira_Project_Name__c == 'SBU Licensing System'){
jsonParserForEnhancement.Fields.project.key = system.Label.SBU_Licensing_System;
}
jsonParserForEnhancement.fields.issuetype.name = 'Improvement';
jsonParserForEnhancement.fields.summary = jiraCaseRecord.Jira_Summary__c;
jsonParserForEnhancement.fields.description = jiraCaseDescription;
jsonParserForEnhancement.fields.customfield_10408 = jiraCaseRecord.Jira_Customer_Info__c;
jsonParserForEnhancement.fields.customfield_13961 = jiraCaseRecord.Name;
}
else{
if(jiraCaseRecord.Product_Sub_Type__c == '3D Sprint' && jiraCaseRecord.Jira_Project_Name__c == '3D Sprint'){
jsonParserForBug.Fields.project.key = system.Label.X3D_Sprint;
list<string> jiraAffectVersionsList = jiraCaseRecord.Jira_Affect_Versions__c.split(';');
list<SalesforceToJiraCalloutsBugParser.versions> versionList = new list<SalesforceToJiraCalloutsBugParser.versions>();
if(jiraAffectVersionsList.size()>0){
for(integer i=0; i<jiraAffectVersionsList.size();i++){
SalesforceToJiraCalloutsBugParser.versions version = new SalesforceToJiraCalloutsBugParser.versions();
version.name = jiraAffectVersionsList[i];
versionList.add(version);
system.debug('jiraAffectVersionsList' + jiraAffectVersionsList[i]);
}

}
jsonParserForBug.fields.versions = versionList;
}
else if(jiraCaseRecord.Product_Sub_Type__c == '3D Sprint' && jiraCaseRecord.Jira_Project_Name__c == 'SBU Licensing System'){
jsonParserForBug.Fields.project.key = system.Label.SBU_Licensing_System;
}
jsonParserForBug.fields.issuetype.name = 'Bug';
jsonParserForBug.fields.Customfield_11500.value = jiraCaseRecord.Jira_Reproducibility__c;
jsonParserForBug.fields.summary = jiraCaseRecord.Jira_Summary__c;
jsonParserForBug.fields.description = jiraCaseDescription;
jsonParserForBug.fields.customfield_10408 = jiraCaseRecord.Jira_Customer_Info__c;
jsonParserForBug.fields.customfield_13961 = jiraCaseRecord.Name;

}
if(jiraCaseRecord.Jira_Issue_Type__c == 'Enhancement'){
createTicketInJIRA(string.valueof(JSON.Serialize(jsonParserForEnhancement)));
}
else{
createTicketInJIRA(string.valueof(JSON.Serialize(jsonParserForBug)));
}
}
}
}
@VKAPS Dev(callout=true)
public static void createTicketInJIRA(string requestBody){
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setEndpoint('https://domain.atlassian.net/rest/api/2/issue');
request.setMethod('POST');
request.setHeader('Content-Type', 'application/json;charset=UTF-8');
Blob headerValue = Blob.valueOf('USERNAME' + ':' + 'APITOKEN');
String authorizationHeader = 'Basic ' + EncodingUtil.base64Encode(headerValue);
request.setHeader('Authorization', authorizationHeader);
System.debug('requestbody: ' + requestBody);
request.setBody(requestBody);
System.debug('request is: ' + string.valueOf(request));
HttpResponse response = http.send(request);
if (response.getStatusCode() != 200) {
System.debug('The status code returned was not expected: ' +
response.getStatusCode() + ' ' + response.getStatus() + ' Response body: ' + response.getBody());
} else {

System.debug('Respponse body is: ' + response.getBody());
}
}
}

3 answers

0 votes
Santosh Kumar May 15, 2021

@Znanatej Panga @Uma yes I found for the solution

We can use the richtext field in Salesforce but in the Target system i.e., JIRA the description field is not an Richtext field so it does not accept the attachment coming from the salesforce. Salesforce suggest the below option

A] Create a REST Resource Service. First get the Binary Image data into a Blob from RichTextArea field and pass it as Base64 encoded String as a Rest service response.

B] Now you can consume the Rest resource from any third party client e.g. Java to get the Base64 String and Decode the Base64 String from your third party app to get back the Image.

C] For testing we can use Base64 String in any online tool using which we can observe the end result. i.e. Source image

Sample Rest Resource Apex class which you can refer:
======================================================
@RestResource(urlMapping='/ImageService')
global with sharing class RESTImageController {
@HttpGet
global static void getImage(){
String strBase64 = 'Test';
Test_Custom_Object_1__c obj = [SELECT ImageField__c FROM Test_Custom_Object_1__c WHERE Id = 'a0M7F00000LL1D7UAL' LIMIT 1];
String bookimg = obj.ImageField__c.substringBetween('<img', 'img>');
String imgsrc=bookimg.substringBetween('src="', '"');
imgsrc=imgsrc.replace('amp;', '');
PageReference page = new PageReference(imgsrc);
Blob imgblob = page.getContent();
strBase64 = EncodingUtil.base64Encode(imgblob);
strBase64 = JSON.serialize(strBase64);
RestContext.response.addHeader('Content-Type','application/json');
RestContext.response.responseBody = Blob.valueOf('{ "Type" : "strBase64", "Value" : '+ strBase64+'}');
}
}

Note: You can also refer above apex logic in your Future method in order to convert a value stored in RichTextArea Field to encoded base64 string and pass that to your third party system so that they can decode to get actual image.

As discussed you will explore more about how to decode a base64 encoded image string at JIRA in order to get an original image or else in which format JIRA is expecting an image to be in, so it will get displayed in JIRA layout as expected.

I have already provided you a steps which you can refer from salesforce end and explore that further as per your requirement in order to send an image which is stored in RichTextArea field using REST API call.

Since Target system do not have the Description field has a Richtext field so below is the solution

From Salesforce richtext field, the text entered will go into the Description without HTML tags and the attachment attached in the Richtext will go as an attachment in Jira i.e., we will be invoking the two API's 1. Create a record in JIRA and 2. Based on the response Status=201 then invoking the attachment API

 

Trigger:

trigger JiraCaseTrigger on Jira_Case__c (after insert) {
if(trigger.isAfter){
if(trigger.isInsert){
JiraCaseTriggerHelper.getJiraDetails(trigger.new);
}
}
}

---------------------------------------------------------------------------
Trigger Helper:


public class JiraCaseTriggerHelper {
public static void getJiraDetails(list<Jira_Case__c> jiraCaseRecordsList){
SalesforceToJiraCalloutsBugParser jsonParserForBug = new SalesforceToJiraCalloutsBugParser();
SalesforceToJiraCalloutsIssueParser jsonParserForEnhancement = new SalesforceToJiraCalloutsIssueParser();
Map<String, Product_Sub_Type__c> prodsubtypeSettings = Product_Sub_Type__c.getAll();
string jiraCaseDescription;
system.debug('jiraCaseDescription1:-'+jiraCaseDescription);
for(Jira_Case__c jiraCaseRecord: jiraCaseRecordsList ){
if(jiraCaseRecord.Link_to_Existing_Jira__c == false){
jiraCaseDescription = (jiraCaseRecord.Jira_Description__c).replaceAll('<[/a-zAZ0-9]*>','').replaceAll('<img(.+?)>','').replaceAll('\\<.*?\\>', '');
system.debug('jiraCaseDescription:-'+jiraCaseDescription);
if(jiraCaseRecord.Jira_Issue_Type__c == 'Enhancement'){
if(prodsubtypeSettings.containsKey(jiraCaseRecord.Product_Sub_Type__c)&&prodsubtypeSettings.containsKey(jiraCaseRecord.Jira_Project_Name__c)){
jsonParserForEnhancement.Fields.project.key = prodsubtypeSettings.get(jiraCaseRecord.Product_Sub_Type__c).Project_Key__c;
list<string> jiraAffectVersionsList = jiraCaseRecord.Jira_Affect_Versions__c.split(';');
list<SalesforceToJiraCalloutsIssueParser.versions> versionList = new list<SalesforceToJiraCalloutsIssueParser.versions>();
if(jiraAffectVersionsList.size()>0){
for(integer i=0; i<jiraAffectVersionsList.size();i++){
SalesforceToJiraCalloutsIssueParser.versions version = new SalesforceToJiraCalloutsIssueParser.versions();
version.name = jiraAffectVersionsList[i];
versionList.add(version);
}
}
jsonParserForEnhancement.fields.versions = versionList;
}
else{
jsonParserForEnhancement.Fields.project.key = system.Label.SBU_Licensing_System;
}
jsonParserForEnhancement.fields.issuetype.name = system.Label.Improvement;
jsonParserForEnhancement.fields.summary = jiraCaseRecord.Jira_Summary__c;
jsonParserForEnhancement.fields.description = jiraCaseDescription;
jsonParserForEnhancement.fields.customfield_10408 = jiraCaseRecord.Jira_Customer_Info__c;
jsonParserForEnhancement.fields.customfield_13961 = jiraCaseRecord.Name;
}
else {
if(prodsubtypeSettings.containsKey(jiraCaseRecord.Product_Sub_Type__c)&&prodsubtypeSettings.containsKey(jiraCaseRecord.Jira_Project_Name__c)){
jsonParserForBug.Fields.project.key = prodsubtypeSettings.get(jiraCaseRecord.Product_Sub_Type__c).Project_Key__c;
list<string> jiraAffectVersionsList = jiraCaseRecord.Jira_Affect_Versions__c.split(';');
list<SalesforceToJiraCalloutsBugParser.versions> versionList = new list<SalesforceToJiraCalloutsBugParser.versions>();
if(jiraAffectVersionsList.size()>0){
for(integer i=0; i<jiraAffectVersionsList.size();i++){
SalesforceToJiraCalloutsBugParser.versions version = new SalesforceToJiraCalloutsBugParser.versions();
version.name = jiraAffectVersionsList[i];
versionList.add(version);
}
}
jsonParserForBug.fields.versions = versionList;
}
else{
jsonParserForBug.Fields.project.key = system.Label.SBU_Licensing_System;
}
jsonParserForBug.fields.issuetype.name = system.Label.Bug;
jsonParserForBug.fields.Customfield_11500.value = jiraCaseRecord.Jira_Reproducibility__c;
jsonParserForBug.fields.summary = jiraCaseRecord.Jira_Summary__c;
jsonParserForBug.fields.description = jiraCaseDescription;
jsonParserForBug.fields.customfield_10408 = jiraCaseRecord.Jira_Customer_Info__c;
jsonParserForBug.fields.customfield_13961 = jiraCaseRecord.Name;
}
}
if(jiraCaseRecord.Jira_Issue_Type__c == 'Enhancement'){
createTicketInJIRA(string.valueof(JSON.Serialize(jsonParserForEnhancement)),jiraCaseRecord.Jira_Description__c);
}
else{
createTicketInJIRA(string.valueof(JSON.Serialize(jsonParserForBug)), jiraCaseRecord.Jira_Description__c);
}
}
}
@future(callout=true)
public static void createTicketInJIRA(string requestBody, string jiraCaseDescription){
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setEndpoint(system.Label.IssueAPI);
request.setMethod(system.Label.Jira_Set_Method);
request.setHeader(system.Label.Jira_Set_Header1, system.Label.Jira_Set_Header2);
Blob headerValue = Blob.valueOf(system.Label.Username + ':' +system.Label.APIToken);
String authorizationHeader = 'Basic ' + EncodingUtil.base64Encode(headerValue);
request.setHeader('Authorization', authorizationHeader);
request.setBody(requestBody);
HttpResponse response = http.send(request);
if (response.getStatusCode() == 201) {
if(jiraCaseDescription.contains('<img')){
Map<String,Object> results=(Map<String,Object>)JSON.deserializeUntyped(response.getBody());
string jiraKey = String.valueOf(results.get('key'));
List<string> jiraCaseDescriptionList = jiraCaseDescription.split('<img');
string imageSrc;
List<blob> attachmentblobList = new List<blob>();
string url;
string boundary;
for(string jiraAttachment : jiraCaseDescriptionList ){
imagesrc=jiraAttachment.substringBetween('src="', '"');
if(imageSrc != null){
string file_name = 'Salesforce to Jira Screen Capture '+ Math.round(Math.random()*1000) ;
string imageUrl = imageSrc.replace('amp;', '');
PageReference page = new PageReference(imageUrl);
blob imageblob;
if(test.isRunningTest()){
imageblob = blob.valueOf('Unit Testing');
}
else{
imageblob = page.getContent();
}
url = system.Label.AttachmentAPI + jiraKey + system.Label.Attachment;
boundary = '----------------------------741e90d31eff';
String header = '--' + boundary + '\n' +
'Content-Disposition: form-data; name="file"; filename="' + file_name + '";\n' +
'Content-Type: application/octet-stream';

String footer = '--' + boundary + '--';
String headerEncoded = EncodingUtil.base64Encode(Blob.valueOf(header + '\r\n\r\n'));
while (headerEncoded.endsWith('=')){
header += ' ';
headerEncoded = EncodingUtil.base64Encode(Blob.valueOf(header+'\r\n\r\n'));
}
string bodyEncoded = EncodingUtil.base64Encode(imageblob);
Blob bodyBlob = null;
String last4Bytes = bodyEncoded.substring(bodyEncoded.length()-4,bodyEncoded.length());
if (last4Bytes.endsWith('==')) {
last4Bytes = last4Bytes.substring(0, 2) + '0K';
bodyEncoded = bodyEncoded.substring(0, bodyEncoded.length() - 4) + last4Bytes;

String footerEncoded = EncodingUtil.base64Encode(Blob.valueOf(footer));
bodyBlob = EncodingUtil.base64Decode(headerEncoded + bodyEncoded + footerEncoded);
} else if (last4Bytes.endsWith('=')) {
last4Bytes = last4Bytes.substring(0, 3) + 'N';
bodyEncoded = bodyEncoded.substring(0, bodyEncoded.length()-4) + last4Bytes;
footer = '\n' + footer;
String footerEncoded = EncodingUtil.base64Encode(Blob.valueOf(footer));
bodyBlob = EncodingUtil.base64Decode(headerEncoded + bodyEncoded + footerEncoded);
} else {
footer = '\r\n' + footer;
String footerEncoded = EncodingUtil.base64Encode(Blob.valueOf(footer));
bodyBlob = EncodingUtil.base64Decode(headerEncoded + bodyEncoded + footerEncoded);
}
attachmentblobList.add(bodyBlob);
}
}
for(blob imageBlob :attachmentblobList ){
Blob headerVal = Blob.valueOf(system.Label.Username + ':' +system.Label.APIToken);
String auth_header = 'Basic ' + EncodingUtil.base64Encode(headerVal);
HttpRequest req = new HttpRequest();
req.setHeader(system.Label.Jira_Set_Header1, system.Label.Jira_Set_Header3 + boundary);
req.setHeader('Authorization', auth_header);
req.setHeader('X-Atlassian-Token', 'nocheck');
req.setMethod(system.Label.Jira_Set_Method);
req.setEndpoint(url);
req.setBodyAsBlob(imageBlob);
req.setTimeout(120000);
Http https = new Http();
HTTPResponse res = https.send(req);
}
}
}

else {
System.debug('Respponse body is: ' + response.getBody());
}
}
}

Based on our requirement I did created the fields and mapped.

Hope this solution works for you!

Thanks,

Santosh

Uma May 19, 2021

thanks  Santhosh

only the text we can store without format right ?

Santosh Kumar May 19, 2021

yes correct!

0 votes
Uma May 13, 2021

@Santosh Kumar  Did you find any solution . I was looking for the same one

0 votes
Znanatej Panga
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!
March 25, 2021

Hey @Santosh Kumar

Did you find a solution to this. I am trying to achieve the same thing.

Thanks.

Uma May 13, 2021

@Znanatej Panga Did you get any solution this .

 

Thanks

Suggest an answer

Log in or Sign up to answer
DEPLOYMENT TYPE
CLOUD
PRODUCT PLAN
STANDARD
PERMISSIONS LEVEL
Product Admin
TAGS
AUG Leaders

Atlassian Community Events