Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"createInvoiceAttachmentByFileName" Create attachment but not correct content file #679

Closed
nguyenphudung93dn opened this issue Mar 13, 2024 · 23 comments

Comments

@nguyenphudung93dn
Copy link

nguyenphudung93dn commented Mar 13, 2024

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
Screenshot 2024-03-13 at 11 51 54

Copy link

PETOSS-402

Copy link

Thanks for raising an issue, a ticket has been created to track your request

@nguyenphudung93dn
Copy link
Author

nguyenphudung93dn commented Mar 13, 2024

When open file by editor
Screenshot 2024-03-13 at 16 15 47

@mryraghi
Copy link

mryraghi commented Mar 25, 2024

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.

@mryraghi
Copy link

Why closing it? The issue is still there

@nguyenphudung93dn
Copy link
Author

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.

@mryraghi
Copy link

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.

@tsdevau
Copy link

tsdevau commented Apr 7, 2024

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 failed logged to the console (not from my app), which I'm yet to find the root of.

@nguyenphudung93dn
Copy link
Author

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

@rworkduo
Copy link

+1
tried Readable, Buffer and ReadStream with png, jpeg and pdf. None of it can be viewed in the console

@bhrigushr
Copy link

@nguyenphudung93dn @mryraghi can you share the solution that fixed the problem, I am also facing the same issue as reported above.
Thanks

@utkarsh3020
Copy link

@nguyenphudung93dn @mryraghi Kindly share the solution that you used to resolve the attachment issue.

@mryraghi
Copy link

mryraghi commented May 1, 2024

Hi guys, I ended up using the API...

@utkarsh3020
Copy link

@mryraghi But previously you said that you got the solution.

@utkarsh3020
Copy link

@nguyenphudung93dn Can u please share the solution to fix the create attachment problem.

@nguyenphudung93dn
Copy link
Author

nguyenphudung93dn commented May 2, 2024

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

@utkarsh3020
Copy link

utkarsh3020 commented May 3, 2024

@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

@tsdevau
Copy link

tsdevau commented May 3, 2024

@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" })
  }
}

@utkarsh3020
Copy link

@tsdevau Thanks, now it is working absolutely fine.

@paulcorke
Copy link

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?

@tsdevau
Copy link

tsdevau commented Jun 26, 2024

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

@bobsquito
Copy link

Still an issue with v8.0.0

@sangeet-joy-tw
Copy link
Contributor

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.

sangeet-joy-tw pushed a commit that referenced this issue Aug 29, 2024
********* Testing *************

Build from OAS 5.0.0

## What's Changed
* Bugfix #679 | createInvoiceAttachmentByFileName Create attachment but not correct content file
* Bugfix #686 | Attempt to parse object as JSON results in JSON parse body failed written to console

## Breaking Change
* Removed PageInfo metadata object and added pagination metadata object for select Api.Accounting endpoints
* Added ultimate sub plans for Api.Accounting
sangeet-joy-tw pushed a commit that referenced this issue Aug 29, 2024
Build from OAS 5.0.0

## What's Changed 2
* Bugfix #679
* Bugfix #686

## Breaking Change
* Removed PageInfo metadata object and added pagination metadata object for select Api.Accounting endpoints
* Added ultimate sub plans for Api.Accounting
sangeet-joy-tw pushed a commit that referenced this issue Aug 29, 2024
********* Testing *************

Build from OAS 5.0.0

## What's Changed 2
* Bugfix #679
* Bugfix #686

## Breaking Change
* Removed PageInfo metadata object and added pagination metadata object for select Api.Accounting endpoints
* Added ultimate sub plans for Api.Accounting
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants