google-apps-scriptasanaasana-api

Creating Asana tasks from Google Apps Script


I am trying to create tasks in Asana using google apps scripts. I do manage to read (GET method) any kind of information from asana, but when I try to do a POST like creating a new task in a specific workspace and project, it creates the task but using default values ignoring json data that I pass.

this is the code I've been using:

function createTask (taskName, wsId, projectId, asigneeId) {
  var encoded = Utilities.base64Encode(asanaKey + ":");
  var options = {
        "method" : "POST",
        "headers" : {
            "Accept": "application/json",
            "Content-Type": "application/json",
            "Authorization": "Basic " + Utilities.base64Encode(asanaKey + ":")
        }, 
        "body" : {
          "data" : {
              "name" : "\"" + taskName + "\"" ,
              "asignee" : asigneeId,
              "projects" : [projectId],
              "workspace" : wsId
           } 
        }
    };
  try {
        var url = "https://app.asana.com/api/1.0/workspaces/" + wsId + "/tasks";
        var result = UrlFetchApp.fetch(url, options);
        var salida = result.getContentText();
      } 
   catch (e) {
        Logger.log(e);
        var salida = "";
      }
  finally {
      return salida;
  }
}

I've tried with data outside body, workspace outside data, i've changed the order but it always create tasks with default values. ¿any ideas? Thanks


Solution

  • I found two aspects that were causing the issue, though it took a great deal of trial and error and debug.

    OPTIONS object format

    I think the main issue is the format of the 'options' object. I think it needs to have the main elements "method": "headers": and "payload": and not the "body" and "data" elements.

    Authoriziation The authorisation aspect took me ages to figure out. for small apps like this use the Personal Access Token method. the important thing is to use the Authorization option in the header with the parameter of "Bearer " + PERSONAL_ACCESS_TOKEN

    The PERSONAL_ACCESS_TOKEN is exactly the string as given to you in the Asana Web app at the time of registering a Personal Access Token. it DOES NOT need any further authorisation / exchanges / OAuths /refreshes, nor does it need any encoding in base 64 or any colons.

    Debugging

    I used Postman (https://www.getpostman.com/) and the explorer in the asana developers API reference to test out how the options worked, particularly around the authorisation.

    I also setup a dummy function to create a defined name task so I could access the debugger in the google script editor.

    CODE: Note I have adjusted the ids etc. so you have to put your own in.

    /*************************
     * Asana     Functions    *
     *************************/
    
    // first Global constants ... Key Ids / tokens etc.
    PERSONAL_ACCESS_TOKEN = "0/d3c41c435b0c3f70b399999952edee5";  // Put your unique Personal access token here
    WORKSPACE_ID = "49489999934875"; // Put in the main workspace key you want to access (you can copy from asana web address)
    ASSIGNEE = "jondoe@nomail.com";  // put in the e-mail addresss you use to log into asana
    
    
    // ** testTask() **  is useful for using as a Debug start point.  "select function" on script editor menu
    // choose "testTask" then debug functionality is enabled
    
    function testTask() {
        quickTask("a quick task")  
    };
    
    
    // ** quickTask(taskName) ** Made a short function so I could just add simple tasks easily
    function quickTask(taskName) {
        var newTask = {
        name: taskName,
        workspace: WORKSPACE_ID,
        project: "",       // if you have a project you like to add add it here
        assignee: "me"     // Me is understood by asana
      };
      createAsanaTask(newTask);
    
    };
    
    /******************************************************************************************
     **  createAsanaTask(task) **
     ************************ 
     * creates a new asana task with information (like task name, project, notes etc.) contained in  
     * the  object 'newTask" passed to it.
     * 'task' should be of the format an object with option pairs that match the Asana task
     * key parameters, as many or as few as you want.
     * e.g. 
     * var newTask = {
     *   name: taskName,
     *   workspace: WORKSPACE_ID,
     *   project: "My Project",       // if you have a project you like to add add it here
     *   assignee: "JohnDoe@madeupmail.com"     // person the task should be assigned to.
     * }
     *  you could add other info like due dates etc.
     * it returns a "task" object containing all asana task elements of the one task created including the id.
     *************************************************************************************************/   
    
    
    
    function createAsanaTask(task) {
    
        // when creating an Asana task you must have at least a workspace id and an assignee
        // this routine checks if you defined one in the argument you passed
    if (task.workspace == null) {
        task.workspace=WORKSPACE_ID
        }
    if (task.assignee == null) {
        task.assignee="me";
        }
    
     /* first setup  the "options" object with the following key elements:
     *
     *   method: can be GET,POST typically
     *
     *   headers: object containing header option pairs
     *                    "Accept": "application/json",        // accept JSON format
    *                    "Content-Type": "application/json",  //content I'm passing is JSON format
    *                    "Authorization": "Bearer " + PERSONAL_ACCESS_TOKEN // authorisation
    *  the authorisation aspect took me ages to figure out.
    *  for small apps like this use the Personal Access Token method.
    *  the important thing is to use the Authorization option in the header with the 
    *  parameter of  "Bearer " + PERSONAL_ACCESS_TOKEN
    *  the PERSONAL_ACCESS_TOKEN  is exactly the string as given to you in the Asana Web app at
    *  the time of registering a Personal Access Token.  it DOES NOT need any further authorisation / exchanges
    *  NOR does it needo any encoding in base 64 or any colon.
    *
    *  payload: this can be an object with option pairs  required for each element to be created... in this case 
    *           its the task elements as passed to this function in the argument "task" object.
    *            I found it doesn't need stringifying or anything.   
    *
           ********************************************************************************************************/      
    
    var options = {
        "method": "POST",
        "headers": {
            "Accept": "application/json",
            "Content-Type": "application/json",
            "Authorization": "Bearer " + PERSONAL_ACCESS_TOKEN
                   },
            "payload": task
            };
      // using try to capture errors 
    try {
                              // set the base url to appropriate endpoint - 
                              // this case is "https://app.asana.com/api/1.0"  plus "/tasks"
                              // note workspace id or project id not in base url as they are in the payload options
                              // use asana API reference for the requirements for each method
    var url = "https://app.asana.com/api/1.0/tasks";
                              // using url of endpoint and options object do a urlfetch.
                              // this returns an object that contains JSON data structure into the 'result' variable 
                              // see below for sample structure
    var result = UrlFetchApp.fetch(url, options);
                              // 
    var taskJSON = result.getContentText();
    } catch (e) {
            Logger.log(e);
            return null;
    } finally {
     // parse the result text with JSON format to get object, then get the "data" element from that object and return it.
     // this will be an object containing all the elements of the task.
        return JSON.parse(taskJSON).data;
        }
    };