1

I am writing a Google App Script code that will submit attachments to Jira's API. The user uploads a file and submits, at which point my code should send a request to the API to send an attachment. My code does not raise any errors, but the attachments are not added to the Jira issue. I think it might be how I am formatting the payload? I used the same settings in PostMan and the API call works fine. My code is as follows:

index.html

function formSubmit(){
     var form = $("#bugReportRequest")[0];
     google.script.run.withSuccessHandler(BugReportSubmitted).submitBugReport(form)
     //where form is:
     //<form id="bugReportRequest" name="bugReportRequest" action="#">
      // <input type="text" class="form-control" id="BugSubmitterName"  name="BugSubmitterName">
      // <input type="text" class="form-control" id="BugSubmitterEmail"  name="BugSubmitterEmail">
        //<input type="file" class="form-control" id="BugReportFileUpload" name ="BugReportFileUpload" />  
     </form>
}

Code.gs

function submitBugReport(data){
       var file = data.BugReportFileUpload;
       var url = Jira_URL + "rest/api/2/issue/ABC-2/attachments";
       var credentials = Utilities.base64Encode(Jira_Email_Address + ":" + Jira_API_Token);
       let formdata = {'file' : file };
        var header = { 
             "Content-Type": "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW",
             "X-Atlassian-Token": "no-check",
             "muteHttpExceptions": true,
             "Authorization": "Basic " + credentials
        } 
       var options = {
          "method": "POST",
          "headers": header,
          "body": formdata,
          "redirect": "follow",
          "muteHttpExceptions": true       
      } 
    var resp;
    try {
      resp = UrlFetchApp.fetch(url, options ); 
      console.error(url);
      console.log(resp.getContentText());
   }  catch (e) {    
     console.error('myFunction() yielded an error: ' + e);
     console.error(resp.getContentText);
     console.error(resp.getResponseCode);
  }
}

The response code I get is 200 but resp.getContentText() only prints "[]". When I check ABC-2 there is no attachment added. Any suggestions on how to format the data payload? Or, is there another reason this would happen?

2
  • In GAS you cannot explicitly set the Content-Type header in the headers object, you have to set it using the contentType property instead. Check out the UrlFetchApp.fetch(url, params) method in the reference documentation for details. Make sure to read the Advanced Parameters section. Commented Feb 2, 2021 at 20:24
  • @TheAddonDepot Thanks for the response. I changed Content-Type to contentType and moved it from the header to the options object, but the issue remains the same. Am I missing something or not understanding what you are suggesting? Commented Feb 3, 2021 at 0:02

1 Answer 1

4

Modification points:

  • When I saw the document of How to add an attachment to a JIRA issue using REST API, it seems that the file is sent as multipart/form-data
  • muteHttpExceptions is not used in the header.
  • params of fetch(url, params) has no properties of body and redirect.
  • When the blob is used to the request body, Content-Type is automatically generated by giving the boundary.
  • From your HTML & Javascript, if the uploaded file is the binary file and you are using V8 runtime, var file = data.BugReportFileUpload is not the correct file blob. Ref I'm worry about this.

When above points are reflected to your script, it becomes as follows.

Modified script:

HTML&Javascript side:

In this modified script, I modified it for testing. So please modify this for your actual situation.

<form id="bugReportRequest" name="bugReportRequest" action="#">
<input type="text" class="form-control" id="BugSubmitterName"  name="BugSubmitterName">
<input type="text" class="form-control" id="BugSubmitterEmail"  name="BugSubmitterEmail">
<input type="file" class="form-control" id="BugReportFileUpload" name ="BugReportFileUpload">
</form>

<input type="button" value="ok" onclick="formSubmit()"> <!-- Added for testing script. -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script> <!-- Added for testing script. -->

<script>
// I modified this function.
function formSubmit(){
  var BugSubmitterName = $('#BugSubmitterName').val();
  var BugSubmitterEmail = $('#BugSubmitterEmail').val();
  var BugReportFileUpload = $('#BugReportFileUpload').prop('files');

  if (BugReportFileUpload.length == 1) {
    const file = BugReportFileUpload[0];
    const fr = new FileReader();
    fr.onload = function(e) {
      const obj = {
        BugSubmitterName: BugSubmitterName,
        BugSubmitterEmail: BugSubmitterEmail,
        BugReportFileUpload: {
          filename: file.name,
          mimeType: file.type,
          bytes: [...new Int8Array(e.target.result)]
        }
      };
      google.script.run.withSuccessHandler(BugReportSubmitted).submitBugReport(obj);
    };
    fr.readAsArrayBuffer(file);
  } else {
    const obj = {
      BugSubmitterName: BugSubmitterName,
      BugSubmitterEmail: BugSubmitterEmail,
      BugReportFileUpload: null
    };
    google.script.run.withSuccessHandler(BugReportSubmitted).submitBugReport(obj);
  }
}

function BugReportSubmitted(e) {
  console.log(e)
}
</script>

Google Apps Script side:

Please confirm Jira_Email_Address and Jira_API_Token are declared.

function submitBugReport(data){
  if (!data.BugReportFileUpload) return;  // Or, for example, return "no file."
  var file = Utilities.newBlob(data.BugReportFileUpload.bytes, data.BugReportFileUpload.mimeType, data.BugReportFileUpload.filename);
  var url = Jira_URL + "rest/api/2/issue/ABC-2/attachments";
  var credentials = Utilities.base64Encode(Jira_Email_Address + ":" + Jira_API_Token);
  let formdata = {'file' : file};
  var header = { 
    "X-Atlassian-Token": "no-check",
    "Authorization": "Basic " + credentials
  } 
  var options = {
    "method": "POST",
    "headers": header,
    "payload": formdata,
    "muteHttpExceptions": true
  } 
  var resp;
  try {
    resp = UrlFetchApp.fetch(url, options);
    console.error(url);
    console.log(resp.getContentText());
  }  catch (e) {
    console.error('myFunction() yielded an error: ' + e);
    console.error(resp.getContentText);
    console.error(resp.getResponseCode);
  }
}

Note:

  • When I saw your script, it seems that the values of "BugSubmitterName" and "BugSubmitterEmail" are not used. So in this modification, these values are not used.
  • This modified script supposes that your token and URL can be used for achieving your goal. So please be careful this.

References:

Sign up to request clarification or add additional context in comments.

2 Comments

Wow, that is a great answer and worked well! Can I ask, in the code bytes: "[...new Int8Array(e.target.result)]" what are the three ellipses for? But, this really impressive, thank you!
@jason Thank you for replying and testing it. I'm glad your issue was resolved. About in the code bytes: "[...new Int8Array(e.target.result)]" what are the three ellipses for?, if you are saying ..., this is spread syntax. Ref I used this for converting Int8Array to an array with the numbers for sending to Google Apps Script side. In this case, I think that it is the same with Array.from(new Int8Array(e.target.result)). If I misunderstood your additional question, I apologize.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.