Can't figure out how to upload Attachments using node-asana

This works fine for me:

// Create a client, getting parameters from the environment.
var client = asana.Client.create({
    clientId: process.env['client_id'],
    clientSecret: process.env['client_secret']
});

client.useOauth();

client.authorize().then(function() {
    // The client is authorized! Make a simple request.
    return client.users.me().then(function(me) {
        console.log('Hello ' + me.name);

        var params = {
            method: 'POST',
            url: "https://app.asana.com/api/1.0/tasks/1234567890/attachments",
            formData: {
                file: fs.createReadStream('test.png')
            },
            headers: {
                "Content-Type": "multipart/form-data"
            },
        };
        client.dispatcher.dispatch(params, {});
    });
}).catch(function(err) {
    console.log('An error occurred', err);
});

Your solution doesn’t work for me…it is asking to open a link in the browser and enter a code in the command line. The link is broken and I can’t figure out what code it wants as input.
In your code I can’t see how you authenticate a specific user with his oauth-token.

I use following code to send the request that is failing:

const {
  taskGid,
  refresh_token,
  clientId,
  clientSecret,
  redirectUri,
} = process.env;

console.log("authenticating client");
const client = asana.Client.create({
   clientId,
   clientSecret,
   redirectUri
});
client.useOauth({ credentials: { refresh_token } });

console.log("loading file from disk");
const file = fs.createReadStream("src/files/test.png");

console.log("starting fileupload");
client.dispatcher
  .dispatch({
    method: "POST",
    url: `https://app.asana.com/api/1.0/tasks/${taskGid}/attachments`,
    formData: { file },
    headers: {
      "Content-Type": "multipart/form-data"
    }
  })
  .then(response => {
    console.log("SUCCESS", response);
  })
  .catch(e => console.log("ERROR", e));

but this request works for me:

const options = {
  method: "POST",
  url: "https://app.asana.com/api/1.0/tasks/1234567890/attachments",
  formData: { file },
  headers: {
    "Content-Type": "multipart/form-data",
    Authorization:
      "Bearer XXXXXXXXXXXXXXXXXXXXXXXXX",
    "X-Asana-Client-Lib":
    "version=0.17.3&language=NodeJS&language_version=v8.17.0&os=win32"
  }
};

request(options, function(err, res, payload) {
  console.log(payload);
});

In our Authentication Basics docs we say “Native or command line applications should use the special redirect URL urn:ietf:wg:oauth:2.0:oob”, which is what my script is doing. If you want it to work, go to your Developer Console in Asana and add urn:ietf:wg:oauth:2.0:oob as a redirect_url.

It appears you’re loading your refresh_token from an env_variable, which is only appropriate if your app runs for a single user. If it’s only ever going to be run for a single user, you should be using a PAT.

If this app is meant to be used for multiple people, then you should load a refresh token for each person on their first authentication.

Are you building a script that a local computer will run, a website, or a server?

I only isolated the attachment code to be sure it is not a problem of my application. In the end it should run on a nodeJS server for multiple users.
Using the client.authorize() method it always ask me for the credentials. Even i I pass them to the useOauth method. So I think your approach is not suited for my production application.

I am glad I finally have a working example with the pure request method that I can use. I just find it very confusing that I can’t make it work using your asana npm package. In the end it was causing more problems then it helped me.
I hope you and me can find the issue and your team can fix it so other people don’t have to waste hours and hours to find the problem.

Still not able to make it work either, that is getting very annoying :frowning:

I got it working by using the raw “request” method:

const options = {
	method: 'POST',
	url: `https://app.asana.com/api/1.0/tasks/${taskGid}/attachments`,
	formData: {
		file: fileStream,
	},
	headers: {
		'Content-Type': 'multipart/form-data',
		Authorization: `Bearer ${bearerToken}`,
	},
}

request(options)

it does not work with the asana-client and its dispatch method. Using an OAuth-Token the request will fail. If I’m using a personal-access-token, the asana-client will successfully upload my file.

I managed to make it work using the http service from Angular, after looking at a what a formData postman request looks like. It appears that the Asana dispatcher does not know how to handle form data request, cc @Ross_Grambo

It does when using an personal-access-token for authentication. The request will only fail using an OAuth authentication.

Same problem with attachments. Is there any way to add an attachment with OAuth at all? To be honest, this problem is really confusing. And even official doc for node.js is wrong as it pointing to method createAttachmentForTask which simply does not exists.

2 Likes

I’ll take another look at this.

I have the same problem. The #createAttachmentForTask method does not exist.

1 Like

+1 on this thread, having the same issue. Would love to see a working solution within node-asana!

3 Likes

Any updates on this? Even with a PAT, it doesn’t look like the asana-client supports uploading files to a task. Please advise.

@JustinAsanaUser did you see I said I managed to do it? Can't figure out how to upload Attachments using node-asana - #27 by Bastien_Siebman

@Bastien_Siebman I was able to get it working via axios and the REST api. Still using the node-asana client for a few endpoints, just not the file upload.

1 Like

Got this working in NodeJs with the below and express-fileupload

const fs = require('fs')
const asana = require('asana')

const client = asana.Client.create().useAccessToken(process.env.ASANA_API_PERSONAL_ACCESS_TOKEN)


/**
 * Docs: https://developers.asana.com/docs/attachment
 */
const uploadTaskAttachment = async ({ taskId, file }) => {
  const fileStream = fs.createReadStream(file.tempFilePath)
  // Asana uses the submitted file's path as the name
  fileStream.path = file.name

  return client.dispatcher.dispatch({
    method: 'POST',
    url: 'https://app.asana.com/api/1.0/attachments',
    formData: {
      file: fileStream,
      parent: taskId,
    },
    headers: {
      'Content-Type': 'multipart/form-data',
    },
  })
}
1 Like

To be clear, the express-fileupload package can be found here for those using the Express framework in NodeJS express-fileupload - npm