This is somewhat a lengthy post about the Asana and it's very "not-that-flexible" and "things-left-to-wish-for" API and if duplicating a project can be done in any other way then this.
First off i must state that i like Asana and working in it through their website. So i am not against it or have any quarrel with it except with their API. If you haven't, you should try it UNLESS you want/have to write something that targets the Asana platform through their API.
I posted a question earlier this week about duplicating a project that you can find here: Copy an Asana task/project
Question was simply, can you duplicate a project or a group of tasks with the Asana API. The answer from Agnoster was simply that this was not possible right now but it's in the backlog.
So i wrote some code that actually copies the project (see below) and then recreates the entire project with all tasks and subtasks. This was a rather expensive process since you have to query each task seperatly, meaning that if i have 10 tasks it would be 1+10 queries. One query to get the information that the project contained 10 tasks and then i had to query each and every task to see if it contained any subtasks. If you would have one subtask in each of those tasks listed above then you would have to query their API 1+10*2 times. Imagine if you had deep layers of nested subtasks then you could potentially end up with several hundred or even a thousand queries in the end.
We currently have (in the project i am duplicating) 50 tasks and only one level of subtasks on a few of them, meaning roughly 1+50+50 requests. So a total of around 101 queries to their API.
Now i discovered that the tasks i am querying from Asana lacks information.
I am using this: "/projects/{project-id}/tasks" to get a list of tasks in order to make a duplicate of each and everyone of those tasks. Then i need to query each task this "tasks/{task-id}/subtasks" in order to get each subtask. Pretty straight forward.
Now the problem lies with their API and what i get delivered back from it and here i hope that there is a better solution at hands.
If i query this: "/projects/{project-id}/tasks" i get the following result:
{
"data": [
{
"id": 1248,
"name": "Buy catnip"
},
{
"id": 24816,
"name": "Reflect on role of kittens in society"
},
{
...
}
]
}
Does anyone see the problem here? Namely the fact that i only get the ID and Name back from the server and nothing else. A task consists of notes, an assignee, date created, due date etc but that information is not shipped along in the list above and the same goes for the API endpoint "tasks/{task-id}/subtasks".
So where does that leave us? Well i would have to query each and every task a second time in order to get the information that is associated with the actual task. So my already huge amount of queries is about to get almost doubled. My 1+50+50 queries will end up to be 1+50+50+50+50. So in order to duplicate this project through their API i would be slightly over 200 queries in order to get all information from the first project over to the second, but wait, there is more. This is just the number of queries i need to actually query the tasks. Then you would have to add another 100 queries in order to create all the new tasks in the new project.
So end effect is that my small project that i want to duplicate to a new project through Asanas API will cost me roughly 301 queries to achive. Even a tiny update that indicates if a task has subtasks would have reduced the number of queries significantly. (Example: { "id": 1248, "name": "Buy catnip", "hasSubtasks": false })
My question is simple, is there any way to improve this? Have i missed some vital API endpoints that could save queries and improve performance?
Here is the code that i wrote to create this:
public bool DuplicateAsanaProject(string projectId)
{
// Get the current project
var request = new RestRequest(string.Format("/projects/{0}", projectId), Method.GET);
request.RequestFormat = DataFormat.Json;
var asanaProject = Execute<AsanaProjectTemplate>(request);
if (asanaProject == null)
return false;
// Create a new project
var newAsanaProject = CreateAsanaProjectInTeam(asanaProject.Data.Name, asanaProject.Data.Notes, TeamProductionId);
// Get all the tasks in the current project
var tasksInProject = GetAsanaTasksForProject(asanaProject.Data.Id);
// Reverse all the tasks to get them outputted in the correct order
tasksInProject.Data.Reverse();
// Loop through all project tasks
foreach (var task in tasksInProject.Data)
{
// Get the task from the server
var oldTask = GetAsanaTask(task.Id);
// Create the new task
var newTask = CreateAsanaTask(oldTask.Data.Name, oldTask.Data.Notes, newAsanaProject.Data.Id, WorkspaceId);
// Get the sub-tasks for this task
var tasksList = GetAsanaTasksForTask(task.Id);
// If we have some sub-tasks, then create them
if (tasksList != null && tasksList.Data.Count > 0)
CreateSubTasksForTask(newTask.Data.Id, tasksList);
}
return true;
}
/// <summary>
/// Create sub-tasks for a specific task
/// </summary>
/// <param name="taskId">New task id. This is the task in where the new tasks will be added</param>
/// <param name="tasks">List all sub-tasks</param>
public void CreateSubTasksForTask(string taskId, AsanaTasksTemplate tasks)
{
// Reverse all the tasks to get them outputted in the correct order
tasks.Data.Reverse();
foreach(var task in tasks.Data)
{
// Get the task from the server
var oldTask = GetAsanaTask(task.Id);
// Create the new task
var newTask = CreateAsanaSubTask(oldTask.Data.Name, oldTask.Data.Notes, taskId);
// Get the sub-tasks for this task
var tasksList = GetAsanaTasksForTask(task.Id);
// If we have some sub-tasks, then create them
if (tasksList != null && tasksList.Data.Count > 0)
CreateSubTasksForTask(newTask.Data.Id, tasksList);
}
}
/// <summary>
/// Retrieves all sub-tasks for a specific task
/// </summary>
/// <param name="taskId"></param>
/// <returns></returns>
public AsanaTasksTemplate GetAsanaTasksForTask(string taskId)
{
// Get all tasks in the current task
var request = new RestRequest(string.Format("tasks/{0}/subtasks", taskId), Method.GET);
request.RequestFormat = DataFormat.Json;
var result = Execute<AsanaTasksTemplate>(request);
return result;
}