function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Uday rajUday raj 

Creation of Issue into jira from Salesforce along with an attachment

 When ever a case has ben created it should directly create an issue  in Jira(wrote a Trigger on case object whenever a case is identified it will fire by applying some condition) now its turn to attach a file or attachment into Jira from Salesforce, myself strucked ova here.



public SalesforcetoJira(){}
//Change values in this class according to you JIRA/Salesforce coordinates
public String baseUrl = '';   // Base URL of your JIRA instance
public String username = ''; // JIRA username
public String password = ''; // JIRA password // Constructs Basic Http Authentication header from provided credentials
public String authHeader(){ Blob headerValue = Blob.valueOf(username+':'+password); return 'Basic ' + EncodingUtil.base64Encode(headerValue);
}
public SalesforcetoJira(JiraUrl,String objectType,String projectKey,String issueType,string summary,string description)
{
HttpRequest req = new HttpRequest();
Http http = new Http();
req.setMethod('GET');
//Set HTTPRequest header properties
req.setHeader('Accept', 'application/json');
req.setHeader('Content-Type','application/json');
Blob headerValue = Blob.valueOf(username+':'+password);
req.setHeader('Authorization','Basic '+ EncodingUtil.base64Encode(headerValue));
req.setEndpoint(baseUrl);
String CreateIssueJsonJIra= '{"fields": {"project":{"key": " "},"summary":" ","description":" " ,"issuetype":{"name": "Bug"}}} ';
String JSONData = JSON.serializePretty(CreateIssueJsonJIra); r
eq.setBody(CreateIssueJsonJIra);
system.debug('after response....'+CreateIssueJsonJIra);
try{
//Execute web service call here
HTTPResponse res = http.send(req);
String ResponseJsonString = res.getBody();
map<string,string> attachment= (map<string,string>)JSON.deserializeUntyped(ResponseJsonStringJIra);
system.debug('after response...'+attachment);
system.debug('after response...'+attachment.get('id'));
System.debug('ResponseJsonStringJIra'+ResponseJsonStringJIra);
JSONParser parser = JSON.createParser(ResponseJsonStringJIra);
}
}
catch(System.CalloutException e)
{
System.debug('Callout error: '+ e);
}
}


I appreciate any help.

 
pconpcon
What does / does not work with your code?  You are attempting to duplicate an attachment from Salesforce to Jira, correct?
Uday rajUday raj
Yes Pcon.

My requirement is to create a Issue with an attchment on a case object using Rest Api. 

I am able to create a Issue into jira by passing my credentials and unable to move forward in attaching a file into jira.
Please help me.

Thanks
Uday
pconpcon
What are you unable to do?  Are you hitting a specific issue or are you just asking how to do it?
Uday rajUday raj
sorry for the late reply,

Pcon, I want to add a file on case object and pass it on to jira.

Here is my code:
global class CaseAttachment {
       @future(callout=true)

    public static void uploadFile(Blob file_body, String file_name,string Jiraid){
      
    
          String boundary = ' ';
          String header = '--'+boundary+'\r\nContent-Disposition: form-data; name="file"; filename="'+file_name+'"\r\nContent-Type: application/octet-stream';
          String footer = '\r\n--'+boundary+'--';              
          String headerEncoded = EncodingUtil.base64Encode(Blob.valueOf(header+'\r\n\r\n'));
           
          // String file_name= '';
          String endpoint = 'http://jira url/rest/api/2/issue/{JiraId}/attachments-addAttachment';
          while(headerEncoded.endsWith('='))
          {
           header+=' ';
           headerEncoded = EncodingUtil.base64Encode(Blob.valueOf(header+'\r\n\r\n'));
          }
          String bodyEncoded = EncodingUtil.base64Encode(file_body);
          String footerEncoded = EncodingUtil.base64Encode(Blob.valueOf(footer));

          Blob bodyBlob = null;
         String last4Bytes = bodyEncoded.substring(bodyEncoded.length()-4,bodyEncoded.length());
          if(last4Bytes.endsWith('='))
          {
               Blob decoded4Bytes = EncodingUtil.base64Decode(last4Bytes);
               HttpRequest tmp = new HttpRequest();
               tmp.setBodyAsBlob(decoded4Bytes);
               String last4BytesFooter = tmp.getBody()+footer;   
               bodyBlob = EncodingUtil.base64Decode(headerEncoded+bodyEncoded.substring(0,bodyEncoded.length()-4)+EncodingUtil.base64Encode(Blob.valueOf(last4BytesFooter)));
          }
          else
          {
                bodyBlob = EncodingUtil.base64Decode(headerEncoded+bodyEncoded+footerEncoded);
          }

          HttpRequest requ = new HttpRequest();
          requ.setHeader('Content-Type','multipart/form-data; boundary='+boundary);
          requ.setMethod('POST');
          requ.setEndpoint(endpoint);
          requ.setBodyAsBlob(bodyBlob);
             requ.setCompressed(true);
          requ.setTimeout(120000);
        
          Http http1 = new Http();
          
    HTTPResponse resp = http1.send(requ);
    }
}
pconpcon
Again, what is not working with the code you provided?  Do you get an error back from Jira?  Does it not attach readable data?  What HTTP status code do you get back from the POST?  I think that you need to change your boundary to something other than ' ' since that could occur in the content.  You probably want something like '----------------------------741e90d31eff' that is more random.

NOTE: When including code please use the "Add a code sample" button (icon <>) to increase readability and make referencing code easier.
Uday rajUday raj
Pcon, while attaching an Image on Case Obj 
Here is the Response :
|USER_DEBUG|[75]|DEBUG|Response System.HttpResponse[Status=Not Found, StatusCode=404]
 
pconpcon
The 404 means that the endpoint URI does not exist.  Looking at the Jira documentation [1] I think your endpoint is incorrect.  I think that you should be using
 
http://jira url/rest/api/2/issue/{JiraId}/attachments

[1] https://confluence.atlassian.com/display/JIRAKB/How+to+attach+an+attachment+in+a+JIRA+issue+using+REST+API
Uday rajUday raj
Pcon, am just passing the URL but not the file path like : ​"file=@{path/to/file}" , am not providing the path even in the Code,that gonna be a mess..??
 
pconpcon
I am able to run the following from my dev org and successfully create a file
 
String username = 'admin';
String password = 'password';
String jira_host = 'https://host.atlassian.net';
String issue = 'DEVBOARDS-1';
Attachment attach = [select Name, Body from Attachment limit 1];

Blob file_body = attach.Body;
String file_name = attach.Name;

String auth_header = 'Basic ' + EncodingUtil.base64Encode(Blob.valueOf(username + ':' + password));
Attachment attach = [select Name, Body from Attachment limit 1];

String url = jira_host + '/rest/api/2/issue/' + issue + '/attachments';
String 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(file_body);

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);  
}

HttpRequest req = new HttpRequest();
req.setHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);
req.setHeader('Authorization', auth_header);
req.setHeader('X-Atlassian-Token', 'nocheck');
req.setMethod('POST');
req.setEndpoint(url);
req.setBodyAsBlob(bodyBlob);
req.setTimeout(120000);

Http h = new Http();
HTTPResponse res = h.send(req);
Uday rajUday raj
Am able to get the response like"
        This XML file does not appear to have any style information associated with it. The document tree is shown below".
        <status>
             <status-code>404</status-code>
               <message>
                  null for uri: https://xxx.atlassian.net/rest/api/latest/issue/key/attachment-addAttachments
             </message>
      </status>



      
pconpcon
I don't know how many different ways to say this, YOU ARE USING THE WRONG URL.  The URL is not attachment-addAttachments the URL is just attachments.  If you take the code that I supplied above, remove lines 1-8 and place it in your uploadFile method it should do what you want.  You may have to adjust line 10 to use your username / password variable.
Visma ServiceCloudVisma ServiceCloud
@pcon: Thanks' for excelent sample code!!! Have been struggeling a while now to get the correct syntax for uploading file to Jira, especially this code part was the salvation for me:
String boundary = '----------------------------741e90d31eff';
String header = '--' + boundary + '\n' +
    'Content-Disposition: form-data; name="file"; filename="' + file_name + '";\n' +
    'Content-Type: application/octet-stream';
Board salesforceBoard salesforce
Hi Pcon ,

Can i have the referal code to get the attachments from JIRA to saleforce ,eg: when a attachment is created in JIRA then the attachment should be created in salesforce under case object.
can you Please help out.
Thanks ,
Ram
Amul Baranwal 6Amul Baranwal 6
Hi pcon.
Thanks for the attachment solutions. Its really good.
Could you please let me know how can i send JIRA comment status back to Salesforce. I would like to see if JIRA comment can be updated in Salesforce.  Your help is really appreciated. Thanks
pconpcon
@Amul, to do this is a bit more complicated.  You'll have to setup Jira to make a webhook [1] callout and then have the get inserted into Salesforce.  The easiest (and absolutely unsecure) way to do this would be to create an unauthenticated REST endpoint [2] in Salesforce and directly hook the webhook to that.  However, this is a terrible idea and I would highly recommend NOT doing it.  The better way would be to create a REST endpoint in Salesforce and a custom application that sits in between.  This would take the request from Jira, authenticate against Salesforce and then make the REST request to Salesforce.  You would also need to make sure that your intermediary application can verify that the webhook is coming from Jira before passing it on to Salesforce.  I'd recomment going over the Integration Trailhead [3] if you have any questions about this type of integration.

[1] https://developer.atlassian.com/jiradev/jira-apis/webhooks/
[2] http://www.wadewegner.com/2013/03/creating-anonymous-rest-apis-with-salesforce-com/
[3] https://trailhead.salesforce.com/apex_integration_services/apex_integration_webservices
Amul Baranwal 6Amul Baranwal 6
@pcon, I am really honored to see your prompt reply. Thank you so much. Its clear my dobuts. Really appreciated your input.
AmulAmul
@pcon,

Could you please let me know . how can i fetch the attachment from JIRA and Save it to Salesforce under Case Record? Your help is really appreciated.

 
pconpcon
@Amul There's not an easy way to do this.  There are 3 different approaches to get the attachments from Jira into Salesforce
  1. You can use Webhooks in Jira to alert Salesforce that there is a new attachment, then download and insert it.  I have some caveats earlier in this thread about how to do taht
  2. You can stand up a secondary system that polls Jira for new attachments and then inserts them directly into Salesforce
  3. You have a scheduled Apex job that polls Jira for new attachments and inserts them
If I were doing this for an enterprise org, I would look at doing this with a secondary system.  The limitations around runtime and such would make it much easier to do.  You could do a hybrid of 1 & 2 and use the webhook to notify the secondary system and then have that push into Salesforce.
AmulAmul
Thanks for your prompt reply. Its really appreciated. I am getting attachment Id in my Webhook URL setup in Salesforce. But my Concern is which JIRA API we need to call and insert the attachment in SFDC.? Is there any way of doing this using REST API or not.?
pconpcon
If you have the URL for the attachment, it should be as simple as pulling down the data with a GET call to the Jira endpoint, convert that data from base64 to a blob and insert the blob.  You can do conversion using EncodingUtil.base64Decode.  And you can read over this article [1] on how to create the attachment in Apex.

[1] http://bobbuzzard.blogspot.com/2011/07/creating-attachments-in-apex-code.html
AmulAmul
@Pon,

I am good in Apex. But not clear which JIRA REST API we need to call to grab the attachment from JIRA. I have attachment Id in Webhook.
 
SAURABH RASTOGI 13SAURABH RASTOGI 13

Hi Pcon,

Your above stuff was very helpfull. Now I need to get Attachment Blob from JIRA Instance to SF Instance, I got attachment ID 
from JIRA Webhook and when I request for attachmnet with attachment Id, reponse back to SF as :
ResPonse: {"id":10505,"self":"https://convertio.atlassian.net/rest/api/2/attachment/10505","filename":"04e4277617e779bdac7c27a2aaab455d_png-small-medium-large-blue-cloud-clipart-png_600-358.png","author":{"self":"https://convertio.atlassian.net/rest/api/2/user?username=asinghal","key":"asinghal","accountId":"557058:eae85e9b-dbb3-4f16-99cd-142549243580","name":"asinghal","avatarUrls":{"16x16":"https://avatar-cdn.atlassian.com/012b7836666487f4e6d10372273e2d00?s=16&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2F012b7836666487f4e6d10372273e2d00%3Fd%3Dmm%26s%3D16%26noRedirect%3Dtrue","24x24":"https://avatar-cdn.atlassian.com/012b7836666487f4e6d10372273e2d00?s=24&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2F012b7836666487f4e6d10372273e2d00%3Fd%3Dmm%26s%3D24%26noRedirect%3Dtrue","32x32":"https://avatar-cdn.atlassian.com/012b7836666487f4e6d10372273e2d00?s=32&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2F012b7836666487f4e6d10372273e2d00%3Fd%3Dmm%26s%3D32%26noRedirect%3Dtrue","48x48":"https://avatar-cdn.atlassian.com/012b7836666487f4e6d10372273e2d00?s=48&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2F012b7836666487f4e6d10372273e2d00%3Fd%3Dmm%26s%3D48%26noRedirect%3Dtrue"},"displayName":"Atul Singhal","active":true},"created":"2017-05-16T14:55:19.606+0530","size":61164,"mimeType":"image/png","properties":{},"content":"https://convertio.atlassian.net/secure/attachment/10505/04e4277617e779bdac7c27a2aaab455d_png-small-medium-large-blue-cloud-clipart-png_600-358.png","thumbnail":"https://convertio.atlassian.net/secure/thumbnail/10505/04e4277617e779bdac7c27a2aaab455d_png-small-medium-large-blue-cloud-clipart-png_600-358.png"}  
 
In ResPonse Content have url for this attachment but link request need JIRA login.

Code:
public class JIRA_ATTACHMENT_PULL {
    
public static void jiraPullAtt(String strIdAtt,String StrCaseId,String StrJIRA_Key)
{    
      
     UtilClass objUtilClass=new UtilClass();
     UtilClass.ConnectionWrp obj=objUtilClass.getConnectionDetail();
     Http h2 = new Http();
     HttpRequest req2 = new HttpRequest();
       
    String auth_header2 = 'Basic ' + EncodingUtil.base64Encode(Blob.valueOf(obj.strusername + ':' + obj.strpassword));
        req2.setHeader('Content-Type','application/json');
        req2.setHeader('accept','application/json');
        req2.setHeader('Authorization',auth_header2);
        req2.setMethod('GET');
        req2.getBodyAsBlob();
        string sEndPoint2='https://convertio.atlassian.net/rest/api/2/attachment/'+strIdAtt;
        system.debug('----- sEndPoint: '+ sEndPoint2);
        req2.setEndpoint(sEndPoint2);
        req2.setTimeout(120000);
        HttpResponse res2 = h2.send(req2);
        system.debug('---- req: ' + req2);
        system.debug('---- ResPonse: ' + res2.getBody());
        
        JSONParser parser = JSON.createParser(res2.getBody());
        string JIRASFDCAttachment='';
        while (parser.nextToken() != null) {
            if ((parser.getCurrentToken() == JSONToken.FIELD_NAME) && 
                (parser.getText() == 'content')) {
                // Get the value.
                parser.nextToken();
                JIRASFDCAttachment = parser.getText();
            }
        }
        JSONParser parser2 = JSON.createParser(res2.getBody());
        string JIRASFDCAttFileName='';
        while (parser2.nextToken() != null) {
            if ((parser2.getCurrentToken() == JSONToken.FIELD_NAME) && 
                (parser2.getText() == 'filename')) {
                // Get the value.
                parser2.nextToken();
                JIRASFDCAttFileName= parser2.getText();
            }
        }
    JIRA_Incoming_Attchment__c ObjJIA= new JIRA_Incoming_Attchment__c();
    ObjJIA.name=StrJIRA_Key+' | '+JIRASFDCAttFileName;
    ObjJIA.Case__c= StrCaseId;
    ObjJIA.JIRA_Attachment_URL__c=JIRASFDCAttachment;
    Insert ObjJIA;
    system.debug('ObjJIA'+ObjJIA);
    System.debug('JIRASFDCAttachment: '+JIRASFDCAttachment);
    System.debug('JIRASFDCAttFileName: '+JIRASFDCAttFileName);
    }   
    
}

 

 

pconpcon
Here is an example of getting an attachment from an existing Jira.  I'm using one of the public Jiras so authentication is not required.  However, if you need authentication, you just have to add the Authentication header and it should work.

NOTE: When adding code please use the "Add a code sample" button (icon <>) to increase readability and make it easier to reference.
 
Id parentId = 'xxxxxxx';
String jira_host = 'https://jira.atlassian.com';
String issue = 'JRASERVER-65408';

String url = jira_host + '/rest/api/latest/issue/' + issue;

public class Jira_Attachment {
    public String filename;
	public String content;
}

public class Jira_Fields {
    public List<Jira_Attachment> attachment;
}

public class Jira_Issue {
    public String id;
    public String key;
    public Jira_Fields fields;
}

HttpRequest req = new HttpRequest();
req.setMethod('GET');
req.setEndpoint(url);

Http h = new Http();
HttpResponse res = h.send(req);
Jira_Issue i = (Jira_Issue) JSON.deserialize(res.getBody(), Jira_Issue.class);

req.setEndpoint(i.fields.attachment.get(0).content);
res = h.send(req);

Attachment attach = new Attachment();
attach.Body = res.getBodyAsBlob();
attach.Name = i.fields.attachment.get(0).filename;
attach.ParentId = parentId;
insert attach;

You will of course have to set your own parentId to get this to work.  There are obviously many more fields being returned in both the Issue and the Attachment, but I've just put the minimum in here so I could get the data needed to write it out to the object.
SAURABH RASTOGI 13SAURABH RASTOGI 13

Hi pcon,

Thanks for your prompt reply. Its really appreciated.

In above code when we request and getting a response that content only URL which is a secure URL not a public so when we click on this to open that attachment that asks for JIRA login.

07:35:24:141 USER_DEBUG [44]|DEBUG|iJira_Issue:[fields=Jira_Fields:[attachment=(Jira_Attachment:[content=https://convertio.atlassian.net/secure/attachment/11301/images.jpg, filename=images.jpg], Jira_Attachment:[content=https://convertio.atlassian.net/secure/attachment/11300/JIRA_logo.svg.png, filename=JIRA_logo.svg.png], Jira_Attachment:[content=https://convertio.atlassian.net/secure/attachment/11302/KEY.txt, filename=KEY.txt], Jira_Attachment:[content=https://convertio.atlassian.net/secure/attachment/11219/Salesforce_Superhero_bmqitn.png, filename=Salesforce_Superhero_bmqitn.png], Jira_Attachment:[content=https://convertio.atlassian.net/secure/attachment/11218/Salesforce_Superhero_bmqitn.png, filename=Salesforce_Superhero_bmqitn.png])], id=11011, key=PGCRUSCW-14]

 

As I wish to do save the attachment in SF Instance so we can open again that attachment without Login to JIRA.

Is there any way to get BLOB value of attachments Instant of URL.  

@pcon really I need your help on this, Your help is really appreciated.

pconpcon
The code example above pulls down the data and saves it as an attachment in Salesforce.  As stated above, you will need to add the authentication headers to the HttpRequest on line 22 as well as iterating over all of the attachment records.
SAURABH RASTOGI 13SAURABH RASTOGI 13
@pcon I have done the same but my issue at line 34, BLOB data not coming its show a null value. 

 
Id parentId = '500N00000062Tly';
	String jira_host = 'https://convertio.atlassian.net';
	UtilClass objUtilClass=new UtilClass();
    UtilClass.ConnectionWrp obj=objUtilClass.getConnectionDetail();
     
       
    
String issue = 'PGCRUSCW-14';

String url = jira_host + '/rest/api/latest/issue/' + issue;

public class Jira_Attachment {
    public String filename;
	public String content;
}

public class Jira_Fields {
    public List<Jira_Attachment> attachment;
}

public class Jira_Issue {
    public String id;
    public String key;
    public Jira_Fields fields;
}
String auth_header2 = 'Basic ' + EncodingUtil.base64Encode(Blob.valueOf(obj.strusername + ':' + obj.strpassword));

HttpRequest req = new HttpRequest();
			req.setMethod('GET');
			req.setEndpoint('https://convertio.atlassian.net/secure/attachment/11301/images.jpg');
			req.setHeader('Content-Type','application/json');
			req.setHeader('accept','application/json');
			req.setHeader('Authorization',auth_header2);
       

Http h = new Http();
HttpResponse res = h.send(req);
System.debug('res'+res.getBodyAsBlob());
Jira_Issue i = (Jira_Issue) JSON.deserialize(res.getBody(), Jira_Issue.class);
System.debug('i.fields.attachment.get(0).content'+i.fields.attachment.get(0).content);
req.setEndpoint(i.fields.attachment.get(0).content);
res = h.send(req);
System.debug('res'+res.getBody());
System.debug('blob'+res.getBodyAsBlob());
Attachment attach = new Attachment();
attach.Body = res.getBodyAsBlob();
attach.Name = i.fields.attachment.get(0).filename;
attach.ParentId = parentId;
insert attach;

 
pconpcon
This is because your line 30 is incorrect.  It should be
 
req.setEndpoint(url);

The endpoint you have set is the attachment not the Jira.  The code provided deserializes the returning JSON into and object that can be used.  Line 38 won't work because there is no Blob value, it's JSON text data.  Additionally, you should remove the accept and content-type headers.  You're not sending anything, so Content-Type isn't used and appart from the initial request for the full Jira data, you're not going to be accepting back JSON, you'll be accepting back whatever the attachment type is.  If you want to include the accept header, you will need to see if Jira returns that data in it's attachment list, add it to the Jira_Attachment object and then add that to the headers for each attachment.
SAURABH RASTOGI 13SAURABH RASTOGI 13

Hi Pcon,

First of all,  Thank you very much for all your help. I really appreciate all your effort and giving your valuable time... 
I need one more suggestion on getting JIRA Attachment BLOB value in SF instance.

As I followed your suggestions and prepare below code for this purpose but the instant of Blob value res1.getBodyAsBlob() // getting null value here

Id parentId = '500N00000062Tly';
	String jira_host = 'https://convertio.atlassian.net';
	UtilClass objUtilClass=new UtilClass();
    UtilClass.ConnectionWrp obj=objUtilClass.getConnectionDetail();
	String issue = 'PGCRUSCW-18';
String url = jira_host + '/rest/api/latest/issue/' + issue;

public class Jira_Attachment {
    public String filename;
	public String content;
}

public class Jira_Fields {
    public List<Jira_Attachment> attachment;
}

public class Jira_Issue {
    public String id;
    public String key;
    public Jira_Fields fields;
}
String auth_header2 = 'Basic ' + EncodingUtil.base64Encode(Blob.valueOf(obj.strusername + ':' + obj.strpassword));

HttpRequest req = new HttpRequest();
			req.setMethod('GET');
			req.setEndpoint(url);
		//	req.setHeader('Content-Type','application/json');
		//	req.setHeader('accept','application/json');
			req.setHeader('Authorization',auth_header2);

Http h = new Http();
HttpResponse res = h.send(req);
Jira_Issue i = (Jira_Issue) JSON.deserialize(res.getBody(), Jira_Issue.class);

Http h1 = new Http();
HttpRequest req1 = new HttpRequest();
			req1.setMethod('GET');
			req1.setHeader('Authorization',auth_header2);			
			req1.setEndpoint(i.fields.attachment.get(0).content);
HttpResponse res1 = h1.send(req1);
System.debug('res1'+res1);//Status=Founded and Status Code=302
System.debug('resbody1'+res1.getBodyAsBlob()); //@pcon getting null value here

Attachment attach = new Attachment();
attach.Body = res.getBodyAsBlob();
attach.Name = i.fields.attachment.get(0).filename;
attach.ParentId = parentId;
insert attach;
 

@Pcon please help me on this because of this unable to get the scenario of getting JIRA attachment in SF Attachment. 

pconpcon
A 302 status code is not valid for this.  That means it's trying to redirect the request somewhere else.  If you update your code to the following, what do you get from the debug logs
 
Id parentId = '500N00000062Tly';
String jira_host = 'https://convertio.atlassian.net';
UtilClass objUtilClass = new UtilClass();
UtilClass.ConnectionWrp obj = objUtilClass.getConnectionDetail();
String issue = 'PGCRUSCW-18';
String url = jira_host + '/rest/api/latest/issue/' + issue;

public class Jira_Attachment {
    public String filename;
    public String content;
}

public class Jira_Fields {
    public List<Jira_Attachment> attachment;
}

public class Jira_Issue {
    public String id; 
    public String key;
    public Jira_Fields fields;
}

String auth_header = 'Basic ' + EncodingUtil.base64Encode(Blob.valueOf(obj.strusername + ':' + obj.strpassword));

HttpRequest req = new HttpRequest();
req.setMethod('GET');
req.setEndpoint(url);
req.setHeader('Authorization', auth_header);

Http h = new Http();
HttpResponse res = h.send(req);
Jira_Issue i = (Jira_Issue) JSON.deserialize(res.getBody(), Jira_Issue.class);

req = new HttpRequest();
req.setMethod('GET');
req.setHeader('Authorization', auth_header);
req.setEndpoint(i.fields.attachment.get(0).content);

System.debug(System.LoggingLevel.ERROR, '[endpoint] - "' + req.getEndpoint() + '"');

res = h.send(req);

for (String key : res.getHeaderKeys()) {
    System.debug(System.LoggingLevel.ERROR, '[' + key + '] - "' + res.getHeader(key) + '"');
}

We need to see where it's trying to redirect you to
SAURABH RASTOGI 13SAURABH RASTOGI 13

@Pcon,

Yes, You are right that was redirecting on JIRA login page, So added authentication header in request Now response code receive:
StatusCode: 200 and Status OK but response body is ' ' and ResponseAsblob is equaled to null. 

Id parentId = '500N00000062Tly';
	String jira_host = 'https://convertio.atlassian.net';
	UtilClass objUtilClass=new UtilClass();
    UtilClass.ConnectionWrp obj=objUtilClass.getConnectionDetail();
	String issue = 'PGCRUSCW-18';
String url = jira_host + '/rest/api/latest/issue/' + issue;

public class Jira_Attachment {
    public String filename;
	public String content;
}

public class Jira_Fields {
    public List<Jira_Attachment> attachment;
}

public class Jira_Issue {
    public String id;
    public String key;
    public Jira_Fields fields;
}
String auth_header2 = 'Basic ' + EncodingUtil.base64Encode(Blob.valueOf(obj.strusername + ':' + obj.strpassword));

HttpRequest req = new HttpRequest();
			req.setMethod('GET');
			req.setEndpoint(url);
		//	req.setHeader('Content-Type','application/json');
		//	req.setHeader('accept','application/json');
			req.setHeader('Authorization',auth_header2);

Http h = new Http();
HttpResponse res = h.send(req);
Jira_Issue i = (Jira_Issue) JSON.deserialize(res.getBody(), Jira_Issue.class);
System.debug('i='+i);
System.debug('i.fields.attachment.get(0).content'+i.fields.attachment.get(0).content);
String auth_headerNew = 'Basic'+EncodingUtil.base64Encode(Blob.valueOf(obj.strusername + ':' + obj.strpassword));
system.debug('auth_headerNew ='+auth_headerNew);
Http h1 = new Http();
HttpRequest req1 = new HttpRequest();
			req1.setMethod('GET');
			req1.setHeader('Authorization',auth_headerNew);			
			req1.setEndpoint(i.fields.attachment.get(0).content);
			
HttpResponse res1 = h1.send(req1);
System.debug('res1'+res1.getBody()); //' ' 
system.debug('response status='+res1.getStatus()); // @pcon Status=OK
system.debug('response status='+res1.getStatusCode()); // @pcon Status Code=200
System.debug('resbody1'+res1.getBodyAsBlob()); // @pcon receiving null here

User-added image

@pcon please help me on this in postman when I did same authentication and request, receive response as body but for request from salesforce receiving null, Unable to understand why it is its working f9 in postman testing but here response receive status code is 200 and status is OK but body in null.    
pconpcon
One problem I see is that your auth header is incorrect, it should be
 
String auth_headerNew = 'Basic ' + EncodingUtil.base64Encode(Blob.valueOf(obj.strusername + ':' + obj.strpassword));

You're missing the space after Basic.  Oddly enough, with a broken Authorization header, atlassian's API returns a 200.  That's why in my example above, I reused variables whenever possible.  There is no reason to create new ones, they just cause confusion.

I think that if you fix that, you'll see that you are still getting a 302.  If you were to use cURL to get that URL with your Authorization header, I bet that you would see that it is a 302 and a redirect to a different location.  That location is probably a hosted server that is not under the same atlassian domain.
 
curl -v -H "Authorization: Basic XXXXXXXXXX" https://convertio.atlassian.net/secure/attachment/11403/hathwayInternet.png > test.png

Becuase of this, you'd have to do something like
 
Id parentId = '500N00000062Tly';
String jira_host = 'https://convertio.atlassian.net';
UtilClass objUtilClass = new UtilClass();
UtilClass.ConnectionWrp obj = objUtilClass.getConnectionDetail();
String issue = 'PGCRUSCW-18';
String url = jira_host + '/rest/api/latest/issue/' + issue;

public class Jira_Attachment {
    public String filename;
    public String content;
}

public class Jira_Fields {
    public List<Jira_Attachment> attachment;
}

public class Jira_Issue {
    public String id; 
    public String key;
    public Jira_Fields fields;
}

String auth_header = 'Basic ' + EncodingUtil.base64Encode(Blob.valueOf(obj.strusername + ':' + obj.strpassword));

HttpRequest req = new HttpRequest();
req.setMethod('GET');
req.setEndpoint(url);
req.setHeader('Authorization', auth_header);

Http h = new Http();
HttpResponse res = h.send(req);
Jira_Issue i = (Jira_Issue) JSON.deserialize(res.getBody(), Jira_Issue.class);

req = new HttpRequest();
req.setMethod('GET');
req.setHeader('Authorization', auth_header);
req.setEndpoint(i.fields.attachment.get(0).content);

res = h.send(req);

if (res.getStatusCode == 302) {
    req = new HttpRequest();
    req.setMethod('GET');
    req.setEndpoint(res.getHeader('Location');

    res = h.send(req);
}

Attachment attach = new Attachment();
attach.body = res.getBodyAsBlob();
attach.Name = i.fields.attachment.get(0).filename;
attach.ParentId = parentId;
insert attach;

I would also recommend that you change your password immediately since the Basic hash that you have in this forum post could be used to access your Jira account even without knowing the username or password.
SAURABH RASTOGI 13SAURABH RASTOGI 13

 

Hi Pcon,

I really appreciate all your effort and giving your valuable time.

You are amazing!!

 I did the same as you suggest me and able to get download an attachment through a link received in Location Header which has token value.

But for next to save the attachment in salesforce need BLOB value so as you did in above code i did same but getting.

 

System.CalloutException:
Unauthorized endpoint, please check Setup-&gt;Security-&gt;Remote site settings. endpoint = https://media-api.atlassian.io/file/115ff7a4-af1d-45d2-a0c9-b1abccf58d15/binary?token=eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiI5YjI2YjVjMi1hM2U5LTRlMGMtYjRlYy0yNGM0MTU2ZjQzMTkiLCJhY2Nlc3MiOnsidXJuOmZpbGVzdG9yZTpmaWxlOjExNWZmN2E0LWFmMWQtNDVkMi1hMGM5LWIxYWJjY2Y1OGQxNSI6WyJyZWFkIl19LCJleHAiOjE0OTczNjk3ODgsIm5iZiI6MTQ5NzM2OTEyOH0.jq0EyEeBIXCfq9qYNwQYskvleJhAM80bOsehw0WlXJo&amp;client=9b26b5c2-a3e9-4e0c-b4ec-24c4156f4319&amp;name=images.jpg

please help me on this so I can  get blob value after requesting

 
 EndPoint[https://media-api.atlassian.io/file/115ff7a4-af1d-45d2-a0c9-b1abccf58d15/binary?token=eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiI5YjI2YjVjMi1hM2U5LTRlMGMtYjRlYy0yNGM0MTU2ZjQzMTkiLCJhY2Nlc3MiOnsidXJuOmZpbGVzdG9yZTpmaWxlOjExNWZmN2E0LWFmMWQtNDVkMi1hMGM5LWIxYWJjY2Y1OGQxNSI6WyJyZWFkIl19LCJleHAiOjE0OTczNjk3ODgsIm5iZiI6MTQ5NzM2OTEyOH0.jq0EyEeBIXCfq9qYNwQYskvleJhAM80bOsehw0WlXJo&amp;client=9b26b5c2-a3e9-4e0c-b4ec-24c4156f4319&amp;name=images.jpg] 

 Please, its really needed for my pending scenario saving JIRA attachment in salesforce attachment.

pconpcon
You'll have to goto setup -> Security -> Remote site settings [1] and add the "media-api.atlassian.io" endpoint.

[1] https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_callouts_remote_site_settings.htm
SAURABH RASTOGI 13SAURABH RASTOGI 13

Thanks!  Pcon you are superb really so much happy finally.

I really appreciate all your effort and for giving your valuable time. please share your blogger link so I can connect to you for future.

Amazing Job! Thanks once again @Pcon

SAURABH RASTOGI 13SAURABH RASTOGI 13

@ Pcon thanks for your help really I appreciate your effort,

Now, I am facing issue for sending attachment from salesforce to JIRA as JIRA may have attachment size 10MB and In Salesforce easily possible for 15MB so when I tried for sending an attachment more then 3MB facing [Apex Heap Size too large] error ,

Please help me on this how I can sent attachment more than 3 MB from salesforce to JIRA.

Code:

@future(callout=true)
    public static void FutureAddAttachment(Id attachId,Id caseAtchParentId)
    {   
        Attachment attach=new Attachment(id=attachId,ParentId=caseAtchParentId);
        //attach.id=attachId;
        AddAttachment(attach);
    }
    
 
 
 public static void AddAttachment(Attachment attch){
        try{
        // get Connection Detail
        UtilClass objUtilClass=new UtilClass();
        UtilClass.ConnectionWrp obj=objUtilClass.getConnectionDetail();
        
         // get Case Detail
        Case CaseRec=new Case();
        CaseRec.Id=attch.ParentId;
        UtilClass.CaseWrapper objCase=objUtilClass.getCaseDetail(CaseRec);
        String jira_host = 'https://host.atlassian.net';
        String issue =UtilClass.getTicketIssueType(CaseRec.Id).Issue_Type__c;
        Attachment attach = [select Name, Body,BodyLength from Attachment where id=:attch.Id limit 1];

        Blob file_body = attach.Body;
        String file_name = attach.Name;
        System.debug('BodyLength'+attach.BodyLength );
        System.debug('heap size is ' + Limits.getHeapSize() + ' out of ' + Limits.getLimitHeapSize());

        
        String auth_header = 'Basic ' + EncodingUtil.base64Encode(Blob.valueOf(obj.strusername + ':' + obj.strpassword));
        //Attachment attach = [select Name, Body from Attachment limit 1];

        String url = UtilClass.CreateJIRAURL+'/' + objCase.JIRAKEY + '/attachments';
        String 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(file_body);

        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);  
        }

        HttpRequest req = new HttpRequest();
        req.setHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);
        req.setHeader('Authorization', auth_header);
        req.setHeader('X-Atlassian-Token', 'nocheck');
        req.setMethod('POST');
        req.setEndpoint(url);
        req.setBodyAsBlob(bodyBlob);
        req.setTimeout(120000);

        Http h = new Http();
        HTTPResponse res = h.send(req);
        System.debug(res);
        }catch(Exception ex)
        {
        
            System.debug('Exception'+ex);
        }
    }
 

Its working fine for till 2MB file but if attachment size more then 2.5 MB facing that issue [Apex Heap Size is too large].

@Pcon once again I hope for your help on this.  

pconpcon
You'll want to reduce your heapsize by clearing out the variables as you go along.  The szie of the header + headerEncoded + body + bodyEncoded + footer + footerEncoded + bodyBlob are going to be fairly large.  Add to that calling "setBodyAsBlob" is going to make your HttpRequest variable large.  Since the max heapsize is 12Mb, you're probably not going to be able to handle an attachment larger that 6Mb (at the absolute most) without some really creative solutions.  You could call your base64Decode of the bodyBlob and set it directly in the setBodyAsBlob but that's still going to leave you some at least two large variables.  Your best bet is to either do the attachment in a seperate system outside of Salesforce or get into the enhanced future's pilot.
SAURABH RASTOGI 13SAURABH RASTOGI 13

Hi Pcon,

So can you help me on this, Like if attachment size is too large and facing that issue so, can send a comment msg to JIRA So that in JIRA get confirmation attachment not Uploaded in JIRA. 

Because I tried for that if I get this error I will send a comment but error does not get handled by try catch.

 

pconpcon
It is not a catchable exception because it is a platform level error to protect the multi-tenant environment.  You will have to clear out your variables as you go along in order to remove them from memory.  As stated above, you may not be able to do this because of the heapsize limitations in Apex.  If you are unable to do them directly in Apex, then you may have to do the processing in a 3rd party system.
swapnil alekarswapnil alekar
@pcon - your given solution is really working. it solves my big issue. thanks lot :)
Sergii Grushai 25Sergii Grushai 25
Hello Uday, this may potentially also help you. It's an ivocable code which can be invoced by Salesforce process builder:
 
@InvocableMethod(label='Create Jira Issue' description='Create Jira Issue from Salesforce.')

public static void createJiraIssue(List ids) {
try{
createIssue(ids);
}
catch(Exception ex){
System.debug('ERROR:' + ex.getMessage());
}
}
@Future(callout=true)
public static void createIssue(List ids){
List caseList = [SELECT Subject,Summary__c, Assignee__c, Reporter__c,Priority FROM Case WHERE id = :ids LIMIT 1];
System.debug('caseList::::::'+caseList);
if(caseList.size()>0){
String isssueSummary = caseList[0].Summary__c;
String issueAssignee = caseList[0].Assignee__c;
String issueReporter = caseList[0].Reporter__c;
Jira_Credential__c jiraCreds = [Select id, Jira_API_Token__c,Jira_Password__c,Jira_URL__c,Jira_UserName__c from Jira_Credential__c Limit 1];
HttpRequest req = new HttpRequest();
HttpResponse res = new HttpResponse();
Http http = new Http();
//Construct Authorization and Content header
Blob headerValue = Blob.valueOf(jiraCreds.Jira_UserName__c+':'+jiraCreds.Jira_API_Token__c);
String authorizationHeader = 'Basic ' + EncodingUtil.base64Encode(headerValue);
req.setHeader('Authorization', authorizationHeader);
req.setHeader('content-type','application/json');
req.setHeader('accept','application/json');
//Set Method and Endpoint and Body
req.setMethod('POST');
//req.setHeader('cache-control','no-cache');
//Construct Endpoint
req.setBody('{ "fields": { "project": { "id": "10000" }, "summary": "'+isssueSummary+'", "issuetype": { "id": "10001" }, "assignee": { "id": "'+issueAssignee+'" }, "reporter": { "id": "'+issueReporter+'" }, "priority": { "id": "1" }, "labels": [ "bugfix", "blitz_test" ], "description": "description", "duedate": "2021-08-11" } }');                req.setEndpoint(jiraCreds.DemoNa__Jira_URL__c+'/rest/api/2/issue');
res = http.send(req);
System.debug('ResponseBody::'+res.getBody());
}
}
}

The code is taken from blog: 7 Ways to Integrate Salesforce and Jira (https://www.peeklogic.com/article/7-ways-to-integrate-salesforce-and-jira/)