"createInvoiceAttachmentByFileName" Create attachment but not correct content file
SDK xero-node Version 5.1.0
Describe the bug I tested API with this code:
const pathToUpload = path.resolve(__dirname, "../public/images/xero-dev.jpg"); // determine the path to your file
const body = fs.createReadStream(pathToUpload); // {fs.ReadStream} read the file
const contentType = mime.lookup(fileName);
const response = await xero.accountingApi.createInvoiceAttachmentByFileName(xeroTenantId, invoiceID, fileName, body, includeOnline,idempotencyKey, {
headers: {
"Content-Type": contentType,
}
});
But result: File uploaded but not content
PETOSS-402
Thanks for raising an issue, a ticket has been created to track your request
When open file by editor
We should get paid for all the time wasted in understanding and using your crapy API and following your uselessly lengthy approval processes... what a shame.
Why closing it? The issue is still there
Why closing it? The issue is still there
It seems like you are complaining about this problem of mine. And I replaced it with another solution without using xero-node. So I thought I should close it to avoid bothering everyone.
I'm complaining about Xero. The issue is still unresolved, and it affects production too.
I would share your solution here for other people to avoid wasting time trying to upload attachments.
This needs to be re-opened. I'm having the same issue when trying to upload files via createInvoiceAttachmentByFileName in v5.1.0.
If it makes a difference, I'm trying to attach file.xls Excel files with the application/vnd.ms-excel mime type.
- The file attaches OK, but when downloaded and opened, its contents are just a buffer object.
- I also now get
JSON parse body failedlogged to the console (not from my app), which I'm yet to find the root of.
I'm complaining about Xero. The issue is still unresolved, and it affects production too.
I would share your solution here for other people to avoid wasting time trying to upload attachments.
I'm so sorry, my mistake. I will reopen it now
+1 tried Readable, Buffer and ReadStream with png, jpeg and pdf. None of it can be viewed in the console
@nguyenphudung93dn @mryraghi can you share the solution that fixed the problem, I am also facing the same issue as reported above. Thanks
@nguyenphudung93dn @mryraghi Kindly share the solution that you used to resolve the attachment issue.
Hi guys, I ended up using the API...
@mryraghi But previously you said that you got the solution.
@nguyenphudung93dn Can u please share the solution to fix the create attachment problem.
@mryraghi, @utkarsh3020 This is my solution that is working for me ` const data = { invoiceID: '', fileName: 'invoice.pdf', contentType: 'multipart/form-data', path: path.join('./public', fileName); } const content = fs.readFileSync(pathFile); const xeroAccessToken = ''; const getTenantId = '';
const url = `https://api.xero.com/api.xro/2.0/Invoices/${data.invoiceID}/Attachments/${data.fileName}`;
const formData = new FormData();
const headers = formData.getHeaders();
headers['Authorization'] = `Bearer ${xeroAccessToken}`;
headers['xero-tenant-id'] = getTenantId;
headers['Content-type'] = data.contentType;
headers['Accept'] = 'text/xml';
const body = new Uint8Array(content);
headers['Content-Length'] = body.length;
try {
const uploadResponse = await fetch(url, {
method: 'POST',
body: body,
headers,
});
if (!uploadResponse.ok) {
throw new Error('Failed to upload file to Xero');
}
return true;
} catch (error) {
console.error(error);
}`
@nguyenphudung93dn Hi, I am Sending image url and invoiceId from frontend and remaining processing performing like this const createAttachment = async (req, res) => { const { payload } = req.body;
const invoiceID = payload.invoiceID; const img = payload.img;
const fileName = "xero-inv.jpeg"; const includeOnline = true; const idempotencyKey = uuidv4();
const refresh_token = req.refresh_token; const tokenSet = refresh_token || JSON.parse(req.headers["authorization"]); const xeroTenantId = req.headers["xero-tenant-id"];
await xero.setTokenSet(tokenSet);
try { // Download the image from the provided URL const response = await axios.get(img, { responseType: "arraybuffer" });
// Determine the content type of the image
const contentType = mime.lookup(fileName);
// Create a temporary file to save the downloaded image
const tempFilePath = path.join(__dirname, "../public/images/xero-inv.png");
fs.writeFileSync(tempFilePath, response.data);
// Create a readable stream from the temporary file
const body = fs.createReadStream(tempFilePath);
body.on('data', (chunk) => {
console.log('Data received:', chunk.length, 'bytes');
});
// Listen for the 'end' event, which indicates that the entire file has been read
body.on('end', () => {
console.log('File reading completed');
});
// Listen for the 'error' event, which indicates any errors that occur during reading
body.on('error', (err) => {
console.error('Error reading file:', err);
});
// Upload the attachment to Xero
const uploadResponse =
await xero.accountingApi.createInvoiceAttachmentByFileName(
xeroTenantId,
invoiceID,
fileName,
body,
includeOnline,
idempotencyKey,
{
headers: {
"Content-Type": contentType,
},
}
);
console.log("Res success", uploadResponse.body);
res.send(uploadResponse.body);
} catch (err) { console.log("Error occurred during attachment upload:", err); res.status(500).send({ error: "Attachment upload failed" }); } };
Can u please tell me how to resolve this issue
@utkarsh3020 You can't use the SDK method for file attachments - it doesn't work - that's the whole point of this issue. Instead you'll need to make a request to the API endpoint directly with Axios or fetch or similar. Since you've used Axios in the code you provided, I've shown an Axios example.
Basically the same as what you had, I just added Accept to the request config to be explicit.
const createAttachment = async (req, res) => {
const { payload } = req.body
const invoiceID = payload.invoiceID
const img = payload.img // Presuming this is the image url
const fileName = "xero-inv.jpeg"
// Determine the content type of the image
const contentType = mime.lookup(fileName)
const includeOnline = true
const idempotencyKey = uuidv4()
const refresh_token = req.refresh_token
const tokenSet = refresh_token || JSON.parse(req.headers["authorization"])
const xeroTenantId = req.headers["xero-tenant-id"]
await xero.setTokenSet(tokenSet)
try {
// Download the image from the provided URL
const response = await axios.get(
img,
{
headers: { Accept: contentType }, // Should work without this, but...
responseType: "arraybuffer"
})
You don't need any of this:
// Create a temporary file to save the downloaded image
const tempFilePath = path.join(__dirname, "../public/images/xero-inv.png")
fs.writeFileSync(tempFilePath, response.data)
// Create a readable stream from the temporary file
const body = fs.createReadStream(tempFilePath)
body.on("data", (chunk) => {
console.log("Data received:", chunk.length, "bytes")
})
// Listen for the 'end' event, which indicates that the entire file has been read
body.on("end", () => {
console.log("File reading completed")
})
// Listen for the 'error' event, which indicates any errors that occur during reading
body.on("error", (err) => {
console.error("Error reading file:", err)
})
Example of the Axios request:
// Upload the attachment to Xero
const uploadResponse = await axios.post(
`https://api.xero.com/api.xro/2.0/Invoices/${invoiceID}/Attachments/${fileName}?IncludeOnline=${includeOnline}&IdempotencyKey=${idempotencyKey}`,
response.data,
{
headers: {
Authorization: `Bearer ${tokenSet.access_token}`,
"Xero-tenant-id": xeroTenantId,
"Content-Type": contentType,
"Accept": "application/json",
},
responseType: "json",
}
)
Change uploadResponse.body to updloadResponse.data to match the response shape.
console.log("Res success", uploadResponse.data)
res.send(uploadResponse.data)
} catch (err) {
console.log("Error occurred during attachment upload:", err)
res.status(500).send({ error: "Attachment upload failed" })
}
}
@tsdevau Thanks, now it is working absolutely fine.
The fault seems to be in accountingApi.js, around line 2202:
}).then((fileContents) => {
localVarRequestOptions.data = fileContents;
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
Changing the middle line to be:
localVarRequestOptions.data = fileContents[0];
Appears to fix it in my case. I suspect in the general case you would need to coalesce all the elements of that array.
Of course this isn't something that us end users can do as the auto-generated package file is likely get overwritten breaking functionality.
Maybe this is helpful to the developers to get it fixed?
Not that I can find any specific mention of it, but this seems to be corrected in SDK v7.0.0
I've run a few tests with v7.0.0, and I can now successfully upload attachments using the SDK again. It may have been fixed in v6.x releases, but I did not try those.
Scratch that, still an issue with v7.0.0
Still an issue with v8.0.0
Hey @nguyenphudung93dn, @bobsquito , @tsdevau , @rworkduo, @paulcorke
Apologies for the inconvenience caused by this bug.
This attachment bug has been addressed in xero-node v9.0.0.
Please try with the new version and let us know with your feedback.