{ "kind": "open-flow-workflow", "version": 1, "name": "Reschedule Overdue and Delete Completed Asana Tasks", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_fa722c32399b41e1": { "name": "Every day at 7 in Paris", "kind": "cron", "cronTimes": [ { "type": "cron", "expression": "0 7 * * *", "timezone": "Europe/Paris" } ] }, "n_b4a6e2bd5b3aa703": { "inputs": { "workspace": { "kind": "value", "value": "YOUR_WORKSPACE_ID" }, "assignee": { "kind": "value", "value": "YOUR_ASSIGNEE_ID" } }, "name": "Choose workspace and assignee", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "workspace" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "assignee" } ], "outputs": [ { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "workspace" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "assignee" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "today" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "completedSince" } ], "name": "Choose workspace and assignee", "moduleId": "n_cc9d25c79be71b77", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_624b9f20271f345d": { "inputs": { "previous": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_624b9f20271f345d", "output": "state" } ] }, "page": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_dd0342cb935e3cfc", "output": "page" } ] } }, "name": "Collect task pages", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object" }, "nullable": true, "handle": "page" }, { "jsonSchema": { "type": "object" }, "nullable": true, "handle": "previous" } ], "outputs": [ { "jsonSchema": { "type": "object" }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "array", "items": { "type": "string" } }, "nullable": false, "handle": "ids" }, { "jsonSchema": { "type": [ "string", "null" ] }, "nullable": true, "handle": "cursor" } ], "name": "Collect task pages", "moduleId": "n_20bb9194683dfde5", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_9f73b19082473544": { "inputs": {}, "name": "More task pages", "kind": "condition", "matchMode": "first", "cases": [ { "output": "yes", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_624b9f20271f345d", "output": "more" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_dd0342cb935e3cfc": { "inputs": { "workspace": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_b4a6e2bd5b3aa703", "output": "workspace" } ] }, "assignee": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_b4a6e2bd5b3aa703", "output": "assignee" } ] }, "completedSince": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_b4a6e2bd5b3aa703", "output": "completedSince" } ] }, "cursor": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_624b9f20271f345d", "output": "cursor" } ] } }, "name": "List a task page", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "workspace" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "assignee" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "completedSince" }, { "jsonSchema": { "type": [ "string", "null" ] }, "nullable": true, "handle": "cursor" } ], "outputs": [ { "jsonSchema": { "type": "object" }, "nullable": false, "handle": "page" } ], "name": "List a task page", "moduleId": "n_345548e41e7f409a", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [ { "action": "asana.list_tasks" } ] } ] } }, "n_be381a561620913d": { "inputs": { "ids": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_624b9f20271f345d", "output": "ids" } ] }, "previous": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_be381a561620913d", "output": "state" } ] }, "task": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_477620d73af17b74", "output": "task" } ] } }, "name": "Prepare the next task", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "string" } }, "nullable": false, "handle": "ids" }, { "jsonSchema": { "type": "object" }, "nullable": true, "handle": "previous" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "task" } ], "outputs": [ { "jsonSchema": { "type": "object" }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "taskId" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "tasks" } ], "name": "Prepare the next task", "moduleId": "n_129760f6e10e0515", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_45124c7a5eff7da7": { "inputs": {}, "name": "More task details", "kind": "condition", "matchMode": "first", "cases": [ { "output": "yes", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_be381a561620913d", "output": "more" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_477620d73af17b74": { "inputs": { "taskId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_be381a561620913d", "output": "taskId" } ] } }, "name": "Read a task", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "taskId" } ], "outputs": [ { "jsonSchema": { "type": "object", "properties": { "gid": { "type": "string", "description": "The task gid." }, "resource_type": { "type": "string", "description": "The resource type." }, "name": { "type": "string", "description": "The task name." }, "resource_subtype": { "type": "string", "description": "The task subtype." }, "completed": { "type": "boolean", "description": "Whether the task is completed." }, "notes": { "type": "string", "description": "The task notes." }, "due_on": { "type": [ "string", "null" ], "description": "The task due date." }, "due_at": { "type": [ "string", "null" ], "description": "The task due date-time." }, "start_on": { "type": [ "string", "null" ], "description": "The task start date." }, "start_at": { "type": [ "string", "null" ], "description": "The task start date-time." }, "created_at": { "type": "string", "description": "The task creation timestamp." }, "modified_at": { "type": "string", "description": "The task update timestamp." }, "completed_at": { "type": [ "string", "null" ], "description": "The task completion timestamp." }, "permalink_url": { "type": "string", "description": "The task permalink URL." }, "approval_status": { "type": "string", "description": "The task approval status." }, "assignee": { "type": [ "object", "null" ], "properties": { "gid": { "type": "string", "description": "The resource gid." }, "resource_type": { "type": "string", "description": "The resource type." }, "name": { "type": "string", "description": "The resource name." } }, "additionalProperties": true, "description": "A compact Asana user." }, "workspace": { "type": "object", "properties": { "gid": { "type": "string", "description": "The resource gid." }, "resource_type": { "type": "string", "description": "The resource type." }, "name": { "type": "string", "description": "The resource name." } }, "additionalProperties": true, "description": "A compact Asana workspace." }, "parent": { "type": [ "object", "null" ], "properties": { "gid": { "type": "string", "description": "The task gid." }, "resource_type": { "type": "string", "description": "The resource type." }, "name": { "type": "string", "description": "The task name." }, "resource_subtype": { "type": "string", "enum": [ "default_task", "milestone", "approval", "custom" ], "description": "The task subtype." } }, "additionalProperties": true, "description": "A compact Asana task." }, "projects": { "type": "array", "items": { "type": "object", "properties": { "gid": { "type": "string", "description": "The resource gid." }, "resource_type": { "type": "string", "description": "The resource type." }, "name": { "type": "string", "description": "The resource name." } }, "additionalProperties": true, "description": "A compact Asana project." }, "description": "Projects linked to the task." }, "memberships": { "type": "array", "items": { "type": "object", "properties": { "project": { "type": "object", "properties": { "gid": { "type": "string", "description": "The resource gid." }, "resource_type": { "type": "string", "description": "The resource type." }, "name": { "type": "string", "description": "The resource name." } }, "additionalProperties": true, "description": "A compact Asana project." }, "section": { "type": "object", "properties": { "gid": { "type": "string", "description": "The resource gid." }, "resource_type": { "type": "string", "description": "The resource type." }, "name": { "type": "string", "description": "The resource name." } }, "additionalProperties": true, "description": "A compact Asana project section." } }, "additionalProperties": true, "description": "A task membership." }, "description": "Memberships returned by Asana." }, "custom_fields": { "type": "array", "items": { "type": "object", "additionalProperties": { "description": "A custom field value." }, "description": "A custom field response object." }, "description": "Custom fields returned by Asana." } }, "additionalProperties": true, "description": "An Asana task." }, "nullable": false, "handle": "task" } ], "name": "Read a task", "moduleId": "n_8800b6f722fcba28", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [ { "action": "asana.get_task" } ] } ] } }, "n_f86ac0e07f087e0f": { "inputs": { "tasks": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_be381a561620913d", "output": "tasks" } ] }, "today": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_b4a6e2bd5b3aa703", "output": "today" } ] } }, "name": "Select updates and deletions", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "tasks" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "today" } ], "outputs": [ { "jsonSchema": { "type": "array", "items": { "type": "string" } }, "nullable": false, "handle": "update" }, { "jsonSchema": { "type": "array", "items": { "type": "string" } }, "nullable": false, "handle": "remove" } ], "name": "Select updates and deletions", "moduleId": "n_17126b29388c1cbb", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_524ed5954b9e6779": { "inputs": { "ids": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_f86ac0e07f087e0f", "output": "update" } ] }, "previous": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_524ed5954b9e6779", "output": "state" } ] } }, "name": "Prepare next update", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "string" } }, "nullable": false, "handle": "ids" }, { "jsonSchema": { "type": "object" }, "nullable": true, "handle": "previous" } ], "outputs": [ { "jsonSchema": { "type": "object" }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "taskId" } ], "name": "Prepare next update", "moduleId": "n_1408dfd8bb6f7a5a", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_009d5ec4a6993d6e": { "inputs": {}, "name": "More tasks to reschedule", "kind": "condition", "matchMode": "first", "cases": [ { "output": "yes", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_524ed5954b9e6779", "output": "more" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_16596f26cbf745b0": { "inputs": { "taskId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_524ed5954b9e6779", "output": "taskId" } ] }, "today": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_b4a6e2bd5b3aa703", "output": "today" } ] } }, "name": "Reschedule an overdue task", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "taskId" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "today" } ], "outputs": [], "name": "Reschedule an overdue task", "moduleId": "n_a0e296d627136fc5", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [ { "action": "asana.update_task" } ] } ] } }, "n_eba04f1aded353d3": { "inputs": { "ids": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_f86ac0e07f087e0f", "output": "remove" } ] }, "previous": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_eba04f1aded353d3", "output": "state" } ] } }, "name": "Prepare next deletion", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "string" } }, "nullable": false, "handle": "ids" }, { "jsonSchema": { "type": "object" }, "nullable": true, "handle": "previous" } ], "outputs": [ { "jsonSchema": { "type": "object" }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "taskId" } ], "name": "Prepare next deletion", "moduleId": "n_64b1de6c302410ed", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_4aabe19db546ba43": { "inputs": {}, "name": "More tasks to delete", "kind": "condition", "matchMode": "first", "cases": [ { "output": "yes", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_eba04f1aded353d3", "output": "more" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_3c6887e6599f5afa": { "inputs": { "taskId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_eba04f1aded353d3", "output": "taskId" } ] } }, "name": "Delete a completed task", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "taskId" } ], "outputs": [], "name": "Delete a completed task", "moduleId": "n_7b53445efcc087e4", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [ { "action": "asana.delete_task" } ] } ] } } }, "edges": [ { "source": "n_524ed5954b9e6779", "target": "n_009d5ec4a6993d6e" }, { "source": "n_009d5ec4a6993d6e", "target": "n_16596f26cbf745b0", "sourceHandle": "yes" }, { "source": "n_16596f26cbf745b0", "target": "n_524ed5954b9e6779" }, { "source": "n_eba04f1aded353d3", "target": "n_4aabe19db546ba43" }, { "source": "n_4aabe19db546ba43", "target": "n_3c6887e6599f5afa", "sourceHandle": "yes" }, { "source": "n_3c6887e6599f5afa", "target": "n_eba04f1aded353d3" }, { "source": "n_fa722c32399b41e1", "target": "n_b4a6e2bd5b3aa703" }, { "source": "n_b4a6e2bd5b3aa703", "target": "n_624b9f20271f345d" }, { "source": "n_624b9f20271f345d", "target": "n_9f73b19082473544" }, { "source": "n_9f73b19082473544", "target": "n_dd0342cb935e3cfc", "sourceHandle": "yes" }, { "source": "n_dd0342cb935e3cfc", "target": "n_624b9f20271f345d" }, { "source": "n_9f73b19082473544", "target": "n_be381a561620913d", "sourceHandle": "otherwise" }, { "source": "n_be381a561620913d", "target": "n_45124c7a5eff7da7" }, { "source": "n_45124c7a5eff7da7", "target": "n_477620d73af17b74", "sourceHandle": "yes" }, { "source": "n_477620d73af17b74", "target": "n_be381a561620913d" }, { "source": "n_45124c7a5eff7da7", "target": "n_f86ac0e07f087e0f", "sourceHandle": "otherwise" }, { "source": "n_f86ac0e07f087e0f", "target": "n_524ed5954b9e6779" }, { "source": "n_009d5ec4a6993d6e", "target": "n_eba04f1aded353d3", "sourceHandle": "otherwise" } ] } }, "modules": { "n_cc9d25c79be71b77": { "name": "Choose workspace and assignee", "imports": [], "source": "export default function configure({workspace,assignee}) {\n if(!workspace||workspace==='YOUR_WORKSPACE_ID'||!assignee||assignee==='YOUR_ASSIGNEE_ID')throw Error('Set the workspace and assignee IDs');\n const fmt=new Intl.DateTimeFormat('en-CA',{timeZone:'Europe/Paris',year:'numeric',month:'2-digit',day:'2-digit',hour:'2-digit',minute:'2-digit',second:'2-digit',hourCycle:'h23'});\n const parts=Object.fromEntries(fmt.formatToParts(new Date()).map(x=>[x.type,x.value]));const today=`${parts.year}-${parts.month}-${parts.day}`;\n const desired=Date.UTC(+parts.year,+parts.month-1,+parts.day);let utc=desired;\n for(let i=0;i<3;i++){const p=Object.fromEntries(fmt.formatToParts(new Date(utc)).map(x=>[x.type,x.value]));utc+=desired-Date.UTC(+p.year,+p.month-1,+p.day,+p.hour,+p.minute,+p.second);}\n return {workspace,assignee,today,completedSince:new Date(utc).toISOString()};\n}\n" }, "n_20bb9194683dfde5": { "name": "Collect task pages", "imports": [], "source": "export default function page({page,previous}) {\n let state=previous??{ids:[],seen:[],count:0,cursor:null};\n if(page!=null){if(!Array.isArray(page.tasks))throw Error('Invalid task list');const ids=[...state.ids];for(const task of page.tasks){if(typeof task?.gid!=='string'||!task.gid)throw Error('Task is missing its ID');ids.push(task.gid);}const count=state.count+1,next=page.nextCursor;if(next==null)return {state:{ids,seen:state.seen,count,cursor:null},more:false,ids,cursor:null};if(typeof next!=='string'||!next||state.seen.includes(next))throw Error('Invalid or repeated task cursor');if(count>=100)throw Error('Task listing exceeded 100 pages');state={ids,seen:[...state.seen,next],count,cursor:next};}\n return {state,more:true,ids:state.ids,cursor:state.cursor};\n}\n" }, "n_345548e41e7f409a": { "name": "List a task page", "imports": [], "source": "export default async function list({workspace,assignee,completedSince,cursor},context) {\n return {page:await context.actions.call('asana.list_tasks',{workspaceId:workspace,assignee,completedSince,limit:100,...(cursor?{cursor}:{})})};\n}\n" }, "n_129760f6e10e0515": { "name": "Prepare the next task", "imports": [], "source": "export default function next({ids,previous,task}) {\n let state=previous??{index:0,tasks:[]};\n if(task!=null){if(task.gid!==ids[state.index-1]||typeof task.completed!=='boolean')throw Error('Invalid task detail');state={index:state.index,tasks:[...state.tasks,task]};}\n if(state.index===ids.length)return {state,more:false,taskId:'-',tasks:state.tasks};\n const taskId=ids[state.index];return {state:{index:state.index+1,tasks:state.tasks},more:true,taskId,tasks:state.tasks};\n}\n" }, "n_8800b6f722fcba28": { "name": "Read a task", "imports": [], "source": "export default async function read({taskId},context) {return await context.actions.call('asana.get_task',{taskId});}\n" }, "n_17126b29388c1cbb": { "name": "Select updates and deletions", "imports": [], "source": "export default function plan({tasks,today}) {\n const update=[],remove=[];\n for(const task of tasks){if(task.completed){remove.push(task.gid);continue;}const due=task.due_on;if(due==null||due==='')continue;if(typeof due!=='string'||!/^\\d{4}-\\d{2}-\\d{2}$/.test(due)||Number.isNaN(Date.parse(due+'T00:00:00Z'))||new Date(due+'T00:00:00Z').toISOString().slice(0,10)!==due)throw Error('Invalid due date');if(due