Forums

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

trying to add attachment in issue via jira rest api

Ankit kharola March 18, 2025

 

 

i am trying to build an app where i am trying to transition the status of an issue and when user tries to change status if an issue i am displaying a popup which displays the fields like we have in transition screen and i am trying to add attachment to the issue but i am unable to add attachments

1 answer

1 accepted

0 votes
Answer accepted
Tuncay Senturk _Snapbytes_
Community Champion
March 19, 2025

Hi @Ankit kharola 

Could you please provide a bit more detail about how you're trying to add the attachment? For example, are you using the REST API, Forge, Connect, or some other method? Also, sharing any relevant code snippets or error messages would help in understanding the issue better.

Ankit kharola March 19, 2025

Hello sir @Tuncay Senturk _Snapbytes_  i am working on forge  

 {field.schema.items === "attachment" && (
                    <div className="file-upload-wrapper">
                      <input
                        type="file"
                        className="custom-file-input"
                        multiple // Allow multiple files
                        onChange={(e) => {
                          const newFiles = Array.from(e.target.files); // Convert FileList to an array
                          setSelectedFiles((prevFiles) => [
                            ...prevFiles,
                            ...newFiles,
                          ]); // Append new files to existing files
                          setAreFilesSelected(true);
                          setFieldValues((prev) => ({
                            ...prev,
                            [fieldId]: [...(prev[fieldId] || []), ...newFiles], // Append new files to existing files in fieldValues
                          }));
                        }}
                      />  i am using this to let user upload a file 
 if (fields.attachment) {
        // Serialize File objects
        const serializedFiles = await Promise.all(
          fields.attachment.map(async (file) => {
            const arrayBuffer = await file.arrayBuffer(); // Read file content as ArrayBuffer
            const base64 = arrayBufferToBase64(arrayBuffer); // Convert to base64
            return {
              name: file.name,
              type: file.type,
              content: base64, // Send file content as base64
            };
          })
        );
 
        console.log("Serialized files:", serializedFiles); // Debugging
 
        const temporaryAttachmentIds = await invoke("uploadFilesToJira", {
          issueId: issue.id,
          attachment: serializedFiles, // Send serialized files
        });

        console.log(temporaryAttachmentIds, "check response")
        fields.attachment = temporaryAttachmentIds; // Replace files with temporary attachment IDs
      }
 
      console.log("Fields being sent to backend:", fields);   in the backend i am using this function 
resolver.define("uploadFilesToJira", async (req) => {
  const { issueId, attachment } = req.payload;
  console.log("Received files:", attachment); // Debugging

  try {
    // Create a new FormData instance
    const form = new FormData();

    // Append each file to the FormData object
    attachment.forEach((file) => {
      // Convert base64 content to a Buffer
      const fileBuffer = Buffer.from(file.content, "base64");
      form.append("file", fileBuffer, {
        filename: file.name, // Set the file name
        knownLength: fileBuffer.length, // Set the file size
      });
    });

    // Make a POST request to upload files to Jira
    const response = await api
      .asApp() // Use asApp() for app-level authentication
      .requestJira(route`/rest/api/3/issue/${issueId}/attachments`, {
        method: "POST",
        body: form,
        headers: {
          'Accept': 'application/json', // Include FormData headers
          "X-Atlassian-Token": "no-check", // Required for file uploads
        },
      });

    // if (!response.ok) {
    //   const errorResponse = await response.json();
    //   throw new Error(`Failed to upload files: ${JSON.stringify(errorResponse)}`);
    // }

    const uploadedFiles = await response.json();
    return uploadedFiles.map((file) => file.temporaryAttachmentId); // Return temporary attachment IDs
  } catch (error) {
    console.error("Error uploading files:", error);
    throw new Error(`Failed to upload files: ${error.message}`);
  }
});
Tuncay Senturk _Snapbytes_
Community Champion
March 19, 2025

Hi

Do you see any error logs in the console or the Forge tunnel output?

What’s the exact behaviour you're observing — is uploadFilesToJira being called successfully?

Also, are you seeing a response from Jira’s /attachments API, or is it failing silently?

You have to provide more information :) 

By the way Atlassian developer community might be the better place to ask this sort of questions as you will find more answers there

 

 

Ankit kharola March 19, 2025

hi @Tuncay Senturk _Snapbytes_  i am getting this error Error uploading files: FetchError: invalid json response body at https://jira/rest/api/3/issue/10361/attachments reason: Unexpected end of JSON input this is my resolverfunction 

resolver.define("uploadFilesToJira", async (req) => {
  const { issueId, attachment } = req.payload;
  console.log("Received files:", attachment); // Debugging

  try {
    // Create a new FormData instance
    const form = new FormData();

    // Append each file to the FormData object
    attachment.forEach((file) => {
      // Convert base64 content to a Buffer
      const fileBuffer = Buffer.from(file.content, "base64");
      form.append("file", fileBuffer, {
        filename: file.name, // Set the file name
        knownLength: fileBuffer.length, // Set the file size
      });
    });
    console.log(form, "form elements")

    // Make a POST request to upload files to Jira
    const response = await api
      .asApp() // Use asApp() for app-level authentication
      .requestJira(route`/rest/api/3/issue/${issueId}/attachments`, {
        method: "POST",
        body: form,
        headers: {
          'Accept': 'application/json', // Include FormData headers
          "X-Atlassian-Token": "no-check", // Required for file uploads
        },
      });

    if (!response.ok) {
      const errorResponse = await response.json();
      throw new Error(`Failed to upload files: ${JSON.stringify(errorResponse)}`);
    }

    const uploadedFiles = await response.json();
    return uploadedFiles.map((file) => file.temporaryAttachmentId); // Return temporary attachment IDs
  } catch (error) {
    console.error("Error uploading files:", error);
    throw new Error(`Failed to upload files: ${error.message}`);
  }
});
Ankit kharola March 19, 2025

HI @Tuncay Senturk _Snapbytes_  i am bale to add the attachment but do we get something like temporary attachment because i cant see that in response that i am getting after adding the attachment 

Tuncay Senturk _Snapbytes_
Community Champion
March 20, 2025

Hi @Ankit kharola 

Sorry, I'm confused. Are you getting invalid json error or are you able to ad the attachment? 

Ankit kharola March 20, 2025

Hi @Tuncay Senturk _Snapbytes_  i am trying to build a forge app where i am displaying some issues and i am also displaying status of the issues when user tries to move a issue from To Do to in progress i am  trying to replicate a transition screen where user has to fill some required fields before changing the status of the issue  we also have a field to upload attachment in that screen so i have created two functions one for uploading the attachment it is working attachment is being added but my tranisition function throws an error about attachment 

this is the error i am facing  Error transitioning issue: Error: Failed to transition issue: 400 Bad Request. {"errorMessages":[],"errors":{"attachment":"Temporary attachment not found. Session may have timed out before submitting the form."}}  this is my transition function 

resolver.define("transitionIssue", async (req) => {
  const { issueId, transitionId, fields } = req.payload;
  console.log(fields, "fields"); // Debugging

  try {
    const updatedFields = {};

    // Handle file names (if present)
    // if (fields.attachment) {
    //   updatedFields.attachment = fields.attachment
    // }

    // Loop through each field in the fields object
    for (const fieldKey in fields) {
      const fieldValue = fields[fieldKey];

      // If it's a custom field (starting with 'customfield_'), format it as a doc
      if (fieldKey.startsWith('customfield_') || fieldKey === "description") {
        updatedFields[fieldKey] = {
          version: 1,
          type: "doc",
          content: [{
            type: "paragraph",
            content: [{
              type: "text",
              text: fieldValue,
            }],
          }],
        };
      } else if (fieldKey === "priority") {
        updatedFields[fieldKey] = {
          id: fieldValue
        };
      } else {
        updatedFields[fieldKey] = fieldValue;
      }
    }

    // Make a POST request to transition the issue
    const response = await api
      .asUser()
      .requestJira(route`/rest/api/3/issue/${issueId}/transitions`, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          transition: {
            id: transitionId,
          },
          fields: updatedFields
        }),
      });

    if (!response.ok) {
      const errorResponse = await response.json();
      throw new Error(
        `Failed to transition issue: ${response.status} ${response.statusText}. ${JSON.stringify(errorResponse)}`
      );
    }

    return { success: true, message: "Issue status updated successfully!" };
  } catch (error) {
    console.error("Error transitioning issue:", error);
    throw new Error(`Failed to transition issue: ${error.message}`);
  }
});
Tuncay Senturk _Snapbytes_
Community Champion
March 21, 2025

I see, you upload correctly but the transition API call fails due to temp attachment ID.

I'd suggest uploading the attachment using asUser() instead of asApp() — since the transition call is made via asUser(), Jira expects the attachments to be uploaded by the same user session in order to see the attachment ID in the same transaction.

Ankit kharola March 28, 2025

Suggest an answer

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

Atlassian Community Events