Hi all, I am building a Jira plugin and one of my requirements is to upload files to a custom backend.
I am using ReactJS for the custom UI and trying to upload files using form data but I am facing one issue while the request is being sent to the backend nothing is going in the body of the API request.
I tried the same request using Postman, and it’s working fine there.
I have tried all the methods but none is working for me.
Technologies used: React JS for custom UI, Forge for Jira connectivity, node JS for backend.
Can anyone please respond to this
Hi @adeshkumar123, it’s a bit hard to figure out what is going wrong with the information that you’ve provided us. Are you able to provide a code snippet? and more info about your “custom backend”.
one issue while the request is being sent to the backend nothing is going in the body of the API request.
Does this mean that your backend receives the request but doesn’t receive the body of the request?
If you don’t receive the requests at all, one thing that pops to mind is egress controls; have these been enabled for your Forge app?
1 Like
Hi @BoZhang , thanks for the response.
My custom backend is made on node js and express js server. All the functionalities I am handling from my custom backend are working fine. Yes it includes the API calls from the forge app as well. SO my request and all other things are working properly.
But when it comes to sending files, I am using formdata to include files in the body and on the backend I am using multer middleware to handle files. But when I am checking I am not getting any file in the request.
Here you can find the code snippet
const handleFiles = async (files) => {
const formData = new FormData();
console.log('Files to upload:', files);
files.forEach((file) => {
formData.append('files', file);
});
formData.append('attachableType', entityType);
formData.append('attachableId', entityId);
try {
const response = await invokeApiRemote({
config: createAttachment(),
body: formData,
});
if (response) {
console.log('Attachment created successfully:', response);
onUpload && onUpload(response);
}
} catch (error) {
console.error('Error creating attachment:', error);
setError(error.message);
}
};
and on backend this is how I am handling it
routes.route('/attachments')
.post(upload.array('files'), AttachmentsController.createPluginAttachment);
and here is my controller
static async createPluginAttachment(request, response, next) {
const gainssProjectId = request.currentProject.id;
const { jiraAccountId } = request.currentUser;
if (!request.files || request.files.length === 0) {
return response.status(Controller.HttpStatus.UNPROCESSABLE_ENTITY).json({
message: 'Please upload at least 1 file.',
});
}
// I am getting returned from here itself
try {
const attachable = await Controller.finders.findPolymorphic(
request.body.attachableType,
request.body.attachableId
);
const uploads = [];
for (const file of request.files) {
const uploadedFile = await attachable.createAttachment({
...somedata
});
uploads.push(uploadedFile);
}
console.log("uploaded")
return response
.status(Controller.HttpStatus.CREATED)
.json(attachmentIndexView(uploads));
} catch (error) {
console.error('Error creating attachment:', error);
next(error);
}
}
Please let me know if the information provided helps
Here is the invoke function defintion
export const invokeApiRemote = async ({ config, body }) => {
try {
const { path, method, headers } = config;
const fullUrl = `/api/v1/${path}`;
// Make the API request
const response = await invokeRemote({
path: fullUrl,
method,
headers: {
'Content-Type': 'application/json',
...headers,
},
body,
});
// Check if response status indicates an error
if (response.status >= 200 && response.status < 300) {
// Successful response
return response.status === 204 ? { success: true } : response.body;
}
const errorMessage = response.body?.error?.message;
throw new Error(errorMessage);
} catch (error) {
throw new Error(
error.message || 'An unexpected error occurred. Please try again.'
);
}
};
Hi @adeshkumar123 , thank you for providing the sample code.
I can see two issues:
- I can see that when you call
invokeRemote
you set the 'Content-Type': 'application/json'
, this would suggest that the expected body of your request is a JSON, but you are actually sending form-data.
- The other problem, which is a blocker, is that
invokeRemote
via @forge/bridge
does not currently support anything but JSON, so even if you send the correct headers, it won’t work.
However, we have recently released a new variation of invokeRemote
which allows you to call a remote from within a Forge function. I need to verify whether or not this variant allows for form-data bodies, but if it works, you could potentially get things working by going from Custom UI -> Backend Forge resolver function -> remote (via invokeRemote)
.
1 Like
Hi @BoZhang I have tried by removing the application/json but its not working.
How can I use the new functionality of the invokeRemote
@BoZhang is there anything else other then invokeRemove that can I use in the custom UI
Hi @adeshkumar123, sorry I haven’t had a chance to test the invokeRemote
path via the Forge resolver function.
From what I can see the alternatives are this:
- Use
fetch
API directly from your Custom UI Front end. This will mean that you will not get the Forge Invocation Token you get with invokeRemote
and you will have to do something for authentication yourself.
- Try to use
invokeRemote
from the Forge resolver (To do this you will have to call a Forge resolver from your Custom UI FE and then call invokeRemote
from within the Forge resolver function). I don’t know if this will work, it was what I was going to test.
Okar @BoZhang I will test and let you know
@BoZhang unfortunately that is not working. with normal fetch its giving me CSP errors
Hi, thanks for giving it a go. Sorry to hear it didn’t work.
What errors are you getting with invokeRemote
via the Forge resolver function?
When using fetch
in the client you will need to include egress permissions for the domain you are trying to call.
1 Like
@BoZhang I have tried both the solutions and I am not getting any error just files are not going in the request at all.
Hi,
unfortunately that is not working. with normal fetch its giving me CSP errors
Is this after applying the egress permissions for calling fetch
directly in your Custom UI app?
Can you also provide me with your appId? I can have a look at the logs to see where it’s failing for the invokeRemote
solution.
1 Like
Here is my app id 143e5c01-93d3-4430-aec6-3ffe62b88127
Hi, I checked our logs, I could see that your Forge resolver is being called but unfortunately I could not see any egress calls being made by your Forge resolver, suggesting invokeRemote
wasn’t called.
Can you please provide me with sample code for your Forge resolver function?
In terms of using fetch
directly from your Custom UI app, have you checked whether or not you see the requests in the network section of your browser’s developer tools?
1 Like
Hi @BoZhang
I am really very thankful for the help.
I have used the normal fetch function with the egress permission for client and it worked. The only issue I have is jira tokens are not going into it. So for that I have added another checks and authentication for that particular endpoint.
1 Like