chub-dev 0.1.0 → 0.1.2-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -0
- package/bin/chub-mcp +2 -0
- package/dist/airtable/docs/database/javascript/DOC.md +1437 -0
- package/dist/airtable/docs/database/python/DOC.md +1735 -0
- package/dist/amplitude/docs/analytics/javascript/DOC.md +1282 -0
- package/dist/amplitude/docs/analytics/python/DOC.md +1199 -0
- package/dist/anthropic/docs/claude-api/javascript/DOC.md +503 -0
- package/dist/anthropic/docs/claude-api/python/DOC.md +389 -0
- package/dist/asana/docs/tasks/DOC.md +1396 -0
- package/dist/assemblyai/docs/transcription/DOC.md +1043 -0
- package/dist/atlassian/docs/confluence/javascript/DOC.md +1347 -0
- package/dist/atlassian/docs/confluence/python/DOC.md +1604 -0
- package/dist/auth0/docs/identity/javascript/DOC.md +968 -0
- package/dist/auth0/docs/identity/python/DOC.md +1199 -0
- package/dist/aws/docs/s3/javascript/DOC.md +1773 -0
- package/dist/aws/docs/s3/python/DOC.md +1807 -0
- package/dist/binance/docs/trading/javascript/DOC.md +1315 -0
- package/dist/binance/docs/trading/python/DOC.md +1454 -0
- package/dist/braintree/docs/gateway/javascript/DOC.md +1278 -0
- package/dist/braintree/docs/gateway/python/DOC.md +1179 -0
- package/dist/chromadb/docs/embeddings-db/javascript/DOC.md +1263 -0
- package/dist/chromadb/docs/embeddings-db/python/DOC.md +1707 -0
- package/dist/clerk/docs/auth/javascript/DOC.md +1220 -0
- package/dist/clerk/docs/auth/python/DOC.md +274 -0
- package/dist/cloudflare/docs/workers/javascript/DOC.md +918 -0
- package/dist/cloudflare/docs/workers/python/DOC.md +994 -0
- package/dist/cockroachdb/docs/distributed-db/DOC.md +1500 -0
- package/dist/cohere/docs/llm/DOC.md +1335 -0
- package/dist/datadog/docs/monitoring/javascript/DOC.md +1740 -0
- package/dist/datadog/docs/monitoring/python/DOC.md +1815 -0
- package/dist/deepgram/docs/speech/javascript/DOC.md +885 -0
- package/dist/deepgram/docs/speech/python/DOC.md +685 -0
- package/dist/deepl/docs/translation/javascript/DOC.md +887 -0
- package/dist/deepl/docs/translation/python/DOC.md +944 -0
- package/dist/deepseek/docs/llm/DOC.md +1220 -0
- package/dist/directus/docs/headless-cms/javascript/DOC.md +1128 -0
- package/dist/directus/docs/headless-cms/python/DOC.md +1276 -0
- package/dist/discord/docs/bot/javascript/DOC.md +1090 -0
- package/dist/discord/docs/bot/python/DOC.md +1130 -0
- package/dist/elasticsearch/docs/search/DOC.md +1634 -0
- package/dist/elevenlabs/docs/text-to-speech/javascript/DOC.md +336 -0
- package/dist/elevenlabs/docs/text-to-speech/python/DOC.md +552 -0
- package/dist/firebase/docs/auth/DOC.md +1015 -0
- package/dist/gemini/docs/genai/javascript/DOC.md +691 -0
- package/dist/gemini/docs/genai/python/DOC.md +555 -0
- package/dist/github/docs/octokit/DOC.md +1560 -0
- package/dist/google/docs/bigquery/javascript/DOC.md +1688 -0
- package/dist/google/docs/bigquery/python/DOC.md +1503 -0
- package/dist/hubspot/docs/crm/javascript/DOC.md +1805 -0
- package/dist/hubspot/docs/crm/python/DOC.md +2033 -0
- package/dist/huggingface/docs/transformers/DOC.md +948 -0
- package/dist/intercom/docs/messaging/javascript/DOC.md +1844 -0
- package/dist/intercom/docs/messaging/python/DOC.md +1797 -0
- package/dist/jira/docs/issues/javascript/DOC.md +1420 -0
- package/dist/jira/docs/issues/python/DOC.md +1492 -0
- package/dist/kafka/docs/streaming/javascript/DOC.md +1671 -0
- package/dist/kafka/docs/streaming/python/DOC.md +1464 -0
- package/dist/landingai-ade/docs/api/DOC.md +620 -0
- package/dist/landingai-ade/docs/sdk/python/DOC.md +489 -0
- package/dist/landingai-ade/docs/sdk/typescript/DOC.md +542 -0
- package/dist/landingai-ade/skills/SKILL.md +489 -0
- package/dist/launchdarkly/docs/feature-flags/javascript/DOC.md +1191 -0
- package/dist/launchdarkly/docs/feature-flags/python/DOC.md +1671 -0
- package/dist/linear/docs/tracker/DOC.md +1554 -0
- package/dist/livekit/docs/realtime/javascript/DOC.md +303 -0
- package/dist/livekit/docs/realtime/python/DOC.md +163 -0
- package/dist/mailchimp/docs/marketing/DOC.md +1420 -0
- package/dist/meilisearch/docs/search/DOC.md +1241 -0
- package/dist/microsoft/docs/onedrive/javascript/DOC.md +1421 -0
- package/dist/microsoft/docs/onedrive/python/DOC.md +1549 -0
- package/dist/mongodb/docs/atlas/DOC.md +2041 -0
- package/dist/notion/docs/workspace-api/javascript/DOC.md +1435 -0
- package/dist/notion/docs/workspace-api/python/DOC.md +1400 -0
- package/dist/okta/docs/identity/javascript/DOC.md +1171 -0
- package/dist/okta/docs/identity/python/DOC.md +1401 -0
- package/dist/openai/docs/chat/javascript/DOC.md +407 -0
- package/dist/openai/docs/chat/python/DOC.md +568 -0
- package/dist/paypal/docs/checkout/DOC.md +278 -0
- package/dist/pinecone/docs/sdk/javascript/DOC.md +984 -0
- package/dist/pinecone/docs/sdk/python/DOC.md +1395 -0
- package/dist/plaid/docs/banking/javascript/DOC.md +1163 -0
- package/dist/plaid/docs/banking/python/DOC.md +1203 -0
- package/dist/playwright-community/skills/login-flows/SKILL.md +108 -0
- package/dist/postmark/docs/transactional-email/DOC.md +1168 -0
- package/dist/prisma/docs/orm/javascript/DOC.md +1419 -0
- package/dist/prisma/docs/orm/python/DOC.md +1317 -0
- package/dist/qdrant/docs/vector-search/javascript/DOC.md +1221 -0
- package/dist/qdrant/docs/vector-search/python/DOC.md +1653 -0
- package/dist/rabbitmq/docs/message-queue/javascript/DOC.md +1193 -0
- package/dist/rabbitmq/docs/message-queue/python/DOC.md +1243 -0
- package/dist/razorpay/docs/payments/javascript/DOC.md +1219 -0
- package/dist/razorpay/docs/payments/python/DOC.md +1330 -0
- package/dist/redis/docs/key-value/javascript/DOC.md +1851 -0
- package/dist/redis/docs/key-value/python/DOC.md +2054 -0
- package/dist/registry.json +2817 -0
- package/dist/replicate/docs/model-hosting/DOC.md +1318 -0
- package/dist/resend/docs/email/DOC.md +1271 -0
- package/dist/salesforce/docs/crm/javascript/DOC.md +1241 -0
- package/dist/salesforce/docs/crm/python/DOC.md +1183 -0
- package/dist/search-index.json +1 -0
- package/dist/sendgrid/docs/email-api/javascript/DOC.md +371 -0
- package/dist/sendgrid/docs/email-api/python/DOC.md +656 -0
- package/dist/sentry/docs/error-tracking/javascript/DOC.md +1073 -0
- package/dist/sentry/docs/error-tracking/python/DOC.md +1309 -0
- package/dist/shopify/docs/storefront/DOC.md +457 -0
- package/dist/slack/docs/workspace/javascript/DOC.md +933 -0
- package/dist/slack/docs/workspace/python/DOC.md +271 -0
- package/dist/square/docs/payments/javascript/DOC.md +1855 -0
- package/dist/square/docs/payments/python/DOC.md +1728 -0
- package/dist/stripe/docs/api/DOC.md +1727 -0
- package/dist/stripe/docs/payments/DOC.md +1726 -0
- package/dist/stytch/docs/auth/javascript/DOC.md +1813 -0
- package/dist/stytch/docs/auth/python/DOC.md +1962 -0
- package/dist/supabase/docs/client/DOC.md +1606 -0
- package/dist/twilio/docs/messaging/python/DOC.md +469 -0
- package/dist/twilio/docs/messaging/typescript/DOC.md +946 -0
- package/dist/vercel/docs/platform/DOC.md +1940 -0
- package/dist/weaviate/docs/vector-db/javascript/DOC.md +1268 -0
- package/dist/weaviate/docs/vector-db/python/DOC.md +1388 -0
- package/dist/zendesk/docs/support/javascript/DOC.md +2150 -0
- package/dist/zendesk/docs/support/python/DOC.md +2297 -0
- package/package.json +22 -6
- package/skills/get-api-docs/SKILL.md +84 -0
- package/src/commands/annotate.js +83 -0
- package/src/commands/build.js +12 -1
- package/src/commands/feedback.js +150 -0
- package/src/commands/get.js +83 -42
- package/src/commands/search.js +7 -0
- package/src/index.js +43 -17
- package/src/lib/analytics.js +90 -0
- package/src/lib/annotations.js +57 -0
- package/src/lib/bm25.js +170 -0
- package/src/lib/cache.js +69 -6
- package/src/lib/config.js +8 -3
- package/src/lib/identity.js +99 -0
- package/src/lib/registry.js +103 -20
- package/src/lib/telemetry.js +86 -0
- package/src/mcp/server.js +177 -0
- package/src/mcp/tools.js +251 -0
|
@@ -0,0 +1,1396 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tasks
|
|
3
|
+
description: "Asana API coding guide for tasks, project management, and workflow"
|
|
4
|
+
metadata:
|
|
5
|
+
languages: "javascript"
|
|
6
|
+
versions: "3.1.2"
|
|
7
|
+
updated-on: "2026-03-02"
|
|
8
|
+
source: maintainer
|
|
9
|
+
tags: "asana,tasks,project-management,workflow,api"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Asana API Coding Guide
|
|
13
|
+
|
|
14
|
+
## 1. Golden Rule
|
|
15
|
+
|
|
16
|
+
**Always use the official Asana Node.js SDK package:**
|
|
17
|
+
- Package name: `asana`
|
|
18
|
+
- Official library maintained by Asana for Node.js and browser JavaScript
|
|
19
|
+
|
|
20
|
+
**Never use deprecated or unofficial libraries.** The `asana` package is the only supported library maintained by Asana, Inc.
|
|
21
|
+
|
|
22
|
+
**Current SDK Version:** v3.1.2 (Node.js library)
|
|
23
|
+
|
|
24
|
+
**API Version:** Asana API 1.0
|
|
25
|
+
|
|
26
|
+
## 2. Installation
|
|
27
|
+
|
|
28
|
+
### Node.js Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install asana
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
yarn add asana
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pnpm add asana
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Requirements:** Node.js 12+ (recommended Node.js 18+ for production)
|
|
43
|
+
|
|
44
|
+
### Environment Variables
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Required - Personal Access Token
|
|
48
|
+
ASANA_ACCESS_TOKEN=your_personal_access_token_here
|
|
49
|
+
|
|
50
|
+
# Optional - OAuth credentials
|
|
51
|
+
ASANA_CLIENT_ID=your_client_id
|
|
52
|
+
ASANA_CLIENT_SECRET=your_client_secret
|
|
53
|
+
ASANA_REDIRECT_URI=http://localhost:3000/auth/callback
|
|
54
|
+
|
|
55
|
+
# Optional - Workspace/Organization IDs
|
|
56
|
+
ASANA_WORKSPACE_ID=your_workspace_gid
|
|
57
|
+
ASANA_PROJECT_ID=your_project_gid
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**CRITICAL:** Never commit access tokens to version control. Use environment variables or secure secret management systems.
|
|
61
|
+
|
|
62
|
+
## 3. Initialization
|
|
63
|
+
|
|
64
|
+
### Basic Initialization with Personal Access Token
|
|
65
|
+
|
|
66
|
+
```javascript
|
|
67
|
+
const Asana = require('asana');
|
|
68
|
+
|
|
69
|
+
const client = Asana.ApiClient.instance;
|
|
70
|
+
const token = client.authentications['token'];
|
|
71
|
+
token.accessToken = process.env.ASANA_ACCESS_TOKEN;
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**With ES6 Modules:**
|
|
75
|
+
|
|
76
|
+
```javascript
|
|
77
|
+
import Asana from 'asana';
|
|
78
|
+
|
|
79
|
+
const client = Asana.ApiClient.instance;
|
|
80
|
+
const token = client.authentications['token'];
|
|
81
|
+
token.accessToken = process.env.ASANA_ACCESS_TOKEN;
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Advanced Initialization with OAuth
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
const Asana = require('asana');
|
|
88
|
+
|
|
89
|
+
const client = Asana.ApiClient.instance;
|
|
90
|
+
const oauth = client.authentications['oauth2'];
|
|
91
|
+
oauth.accessToken = 'YOUR_OAUTH_ACCESS_TOKEN';
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Client Configuration Options
|
|
95
|
+
|
|
96
|
+
```javascript
|
|
97
|
+
const Asana = require('asana');
|
|
98
|
+
|
|
99
|
+
const client = Asana.ApiClient.instance;
|
|
100
|
+
client.defaultHeaders = {
|
|
101
|
+
'asana-enable': 'new_user_task_lists,new_project_templates'
|
|
102
|
+
};
|
|
103
|
+
client.timeout = 60000; // 60 seconds
|
|
104
|
+
|
|
105
|
+
const token = client.authentications['token'];
|
|
106
|
+
token.accessToken = process.env.ASANA_ACCESS_TOKEN;
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## 4. Core API Surfaces
|
|
110
|
+
|
|
111
|
+
### Tasks API
|
|
112
|
+
|
|
113
|
+
Tasks are the basic unit of action in Asana. They can be assigned, have due dates, contain notes, and be organized into projects.
|
|
114
|
+
|
|
115
|
+
#### Creating Tasks
|
|
116
|
+
|
|
117
|
+
**Minimal Example:**
|
|
118
|
+
|
|
119
|
+
```javascript
|
|
120
|
+
const Asana = require('asana');
|
|
121
|
+
|
|
122
|
+
const client = Asana.ApiClient.instance;
|
|
123
|
+
const token = client.authentications['token'];
|
|
124
|
+
token.accessToken = process.env.ASANA_ACCESS_TOKEN;
|
|
125
|
+
|
|
126
|
+
const tasksApiInstance = new Asana.TasksApi();
|
|
127
|
+
|
|
128
|
+
const body = {
|
|
129
|
+
data: {
|
|
130
|
+
name: 'Buy milk',
|
|
131
|
+
workspace: '1234567890123456'
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const opts = {
|
|
136
|
+
opt_fields: 'name,completed,assignee,due_on'
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
tasksApiInstance.createTask(body, opts).then((result) => {
|
|
140
|
+
console.log('Task created:', result.data);
|
|
141
|
+
}).catch((error) => {
|
|
142
|
+
console.error('Error creating task:', error.response.body);
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Advanced Example with All Options:**
|
|
147
|
+
|
|
148
|
+
```javascript
|
|
149
|
+
const body = {
|
|
150
|
+
data: {
|
|
151
|
+
name: 'Design new feature mockups',
|
|
152
|
+
notes: 'Create high-fidelity mockups for the new dashboard feature',
|
|
153
|
+
assignee: '9876543210987654',
|
|
154
|
+
workspace: '1234567890123456',
|
|
155
|
+
projects: ['1111111111111111'],
|
|
156
|
+
due_on: '2025-12-31',
|
|
157
|
+
due_at: '2025-12-31T17:00:00.000Z',
|
|
158
|
+
start_on: '2025-01-15',
|
|
159
|
+
followers: ['user_gid_1', 'user_gid_2'],
|
|
160
|
+
tags: ['tag_gid_1'],
|
|
161
|
+
custom_fields: {
|
|
162
|
+
'5678901234567890': 'High',
|
|
163
|
+
'9012345678901234': '42'
|
|
164
|
+
},
|
|
165
|
+
resource_subtype: 'default_task',
|
|
166
|
+
completed: false,
|
|
167
|
+
liked: false,
|
|
168
|
+
html_notes: '<body>Create <strong>high-fidelity</strong> mockups</body>',
|
|
169
|
+
external: {
|
|
170
|
+
gid: 'my_external_id_123',
|
|
171
|
+
data: 'Custom external data'
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const opts = {
|
|
177
|
+
opt_fields: 'name,assignee,assignee.name,due_on,completed,projects,projects.name,tags,tags.name,custom_fields,custom_fields.name,followers,followers.name'
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
tasksApiInstance.createTask(body, opts).then((result) => {
|
|
181
|
+
console.log('Task created:', JSON.stringify(result.data, null, 2));
|
|
182
|
+
}).catch((error) => {
|
|
183
|
+
console.error('Error:', error.response.body);
|
|
184
|
+
});
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
#### Getting a Task
|
|
188
|
+
|
|
189
|
+
```javascript
|
|
190
|
+
const taskGid = '1234567890123456';
|
|
191
|
+
|
|
192
|
+
const opts = {
|
|
193
|
+
opt_fields: 'name,notes,assignee,assignee.name,assignee.email,completed,due_on,due_at,projects,projects.name,tags,tags.name,custom_fields,custom_fields.name,custom_fields.display_value,followers,followers.name,created_at,modified_at,completed_at,memberships,memberships.project.name,memberships.section.name'
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
tasksApiInstance.getTask(taskGid, opts).then((result) => {
|
|
197
|
+
console.log('Task details:', JSON.stringify(result.data, null, 2));
|
|
198
|
+
}).catch((error) => {
|
|
199
|
+
console.error('Error getting task:', error.response.body);
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
#### Updating Tasks
|
|
204
|
+
|
|
205
|
+
```javascript
|
|
206
|
+
const taskGid = '1234567890123456';
|
|
207
|
+
|
|
208
|
+
const body = {
|
|
209
|
+
data: {
|
|
210
|
+
name: 'Updated task name',
|
|
211
|
+
notes: 'Updated task description',
|
|
212
|
+
completed: true,
|
|
213
|
+
assignee: 'another_user_gid',
|
|
214
|
+
due_on: '2025-12-31'
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const opts = {
|
|
219
|
+
opt_fields: 'name,completed,assignee.name,due_on'
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
tasksApiInstance.updateTask(body, taskGid, opts).then((result) => {
|
|
223
|
+
console.log('Task updated:', result.data);
|
|
224
|
+
}).catch((error) => {
|
|
225
|
+
console.error('Error updating task:', error.response.body);
|
|
226
|
+
});
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
#### Deleting Tasks
|
|
230
|
+
|
|
231
|
+
```javascript
|
|
232
|
+
const taskGid = '1234567890123456';
|
|
233
|
+
|
|
234
|
+
tasksApiInstance.deleteTask(taskGid).then((result) => {
|
|
235
|
+
console.log('Task deleted successfully');
|
|
236
|
+
}).catch((error) => {
|
|
237
|
+
console.error('Error deleting task:', error.response.body);
|
|
238
|
+
});
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
#### Searching Tasks in a Workspace
|
|
242
|
+
|
|
243
|
+
```javascript
|
|
244
|
+
const workspaceGid = '1234567890123456';
|
|
245
|
+
|
|
246
|
+
const opts = {
|
|
247
|
+
assignee: 'me',
|
|
248
|
+
completed: false,
|
|
249
|
+
opt_fields: 'name,assignee.name,due_on,projects.name,completed',
|
|
250
|
+
sort_by: 'due_date',
|
|
251
|
+
sort_ascending: true
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
tasksApiInstance.searchTasksForWorkspace(workspaceGid, opts).then((result) => {
|
|
255
|
+
console.log('Tasks found:', result.data);
|
|
256
|
+
}).catch((error) => {
|
|
257
|
+
console.error('Error searching tasks:', error.response.body);
|
|
258
|
+
});
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Projects API
|
|
262
|
+
|
|
263
|
+
Projects represent a prioritized list of tasks or a board with columns of tasks.
|
|
264
|
+
|
|
265
|
+
#### Creating Projects
|
|
266
|
+
|
|
267
|
+
**Minimal Example:**
|
|
268
|
+
|
|
269
|
+
```javascript
|
|
270
|
+
const Asana = require('asana');
|
|
271
|
+
|
|
272
|
+
const client = Asana.ApiClient.instance;
|
|
273
|
+
const token = client.authentications['token'];
|
|
274
|
+
token.accessToken = process.env.ASANA_ACCESS_TOKEN;
|
|
275
|
+
|
|
276
|
+
const projectsApiInstance = new Asana.ProjectsApi();
|
|
277
|
+
|
|
278
|
+
const body = {
|
|
279
|
+
data: {
|
|
280
|
+
name: 'Marketing Campaign Q4',
|
|
281
|
+
workspace: '1234567890123456'
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
const opts = {
|
|
286
|
+
opt_fields: 'name,owner,due_date,created_at'
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
projectsApiInstance.createProject(body, opts).then((result) => {
|
|
290
|
+
console.log('Project created:', result.data);
|
|
291
|
+
}).catch((error) => {
|
|
292
|
+
console.error('Error creating project:', error.response.body);
|
|
293
|
+
});
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
**Advanced Example:**
|
|
297
|
+
|
|
298
|
+
```javascript
|
|
299
|
+
const body = {
|
|
300
|
+
data: {
|
|
301
|
+
name: 'Website Redesign 2025',
|
|
302
|
+
notes: 'Complete redesign of company website with new branding',
|
|
303
|
+
workspace: '1234567890123456',
|
|
304
|
+
team: '9876543210987654',
|
|
305
|
+
owner: 'user_gid',
|
|
306
|
+
due_date: '2025-12-31',
|
|
307
|
+
start_on: '2025-01-01',
|
|
308
|
+
color: 'light-green',
|
|
309
|
+
archived: false,
|
|
310
|
+
public: true,
|
|
311
|
+
default_view: 'board',
|
|
312
|
+
custom_fields: {
|
|
313
|
+
'5678901234567890': 'Active'
|
|
314
|
+
},
|
|
315
|
+
followers: ['user_gid_1', 'user_gid_2']
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
const opts = {
|
|
320
|
+
opt_fields: 'name,owner.name,due_date,team.name,custom_fields,members,members.name,archived,color,created_at,current_status,default_view'
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
projectsApiInstance.createProject(body, opts).then((result) => {
|
|
324
|
+
console.log('Project created:', JSON.stringify(result.data, null, 2));
|
|
325
|
+
}).catch((error) => {
|
|
326
|
+
console.error('Error:', error.response.body);
|
|
327
|
+
});
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
#### Getting Projects
|
|
331
|
+
|
|
332
|
+
```javascript
|
|
333
|
+
const projectGid = '1234567890123456';
|
|
334
|
+
|
|
335
|
+
const opts = {
|
|
336
|
+
opt_fields: 'name,owner.name,notes,due_date,start_on,archived,color,created_at,modified_at,team.name,workspace.name,members,members.name,followers,followers.name,custom_fields,custom_fields.name,custom_fields.display_value'
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
projectsApiInstance.getProject(projectGid, opts).then((result) => {
|
|
340
|
+
console.log('Project details:', JSON.stringify(result.data, null, 2));
|
|
341
|
+
}).catch((error) => {
|
|
342
|
+
console.error('Error getting project:', error.response.body);
|
|
343
|
+
});
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
#### Updating Projects
|
|
347
|
+
|
|
348
|
+
```javascript
|
|
349
|
+
const projectGid = '1234567890123456';
|
|
350
|
+
|
|
351
|
+
const body = {
|
|
352
|
+
data: {
|
|
353
|
+
name: 'Updated Project Name',
|
|
354
|
+
notes: 'Updated project description',
|
|
355
|
+
color: 'dark-blue',
|
|
356
|
+
archived: false,
|
|
357
|
+
public: false
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
projectsApiInstance.updateProject(body, projectGid).then((result) => {
|
|
362
|
+
console.log('Project updated:', result.data);
|
|
363
|
+
}).catch((error) => {
|
|
364
|
+
console.error('Error updating project:', error.response.body);
|
|
365
|
+
});
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
#### Getting Tasks in a Project
|
|
369
|
+
|
|
370
|
+
```javascript
|
|
371
|
+
const projectGid = '1234567890123456';
|
|
372
|
+
|
|
373
|
+
const opts = {
|
|
374
|
+
opt_fields: 'name,assignee.name,completed,due_on,tags.name'
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
tasksApiInstance.getTasksForProject(projectGid, opts).then((result) => {
|
|
378
|
+
console.log('Project tasks:', result.data);
|
|
379
|
+
}).catch((error) => {
|
|
380
|
+
console.error('Error getting project tasks:', error.response.body);
|
|
381
|
+
});
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
#### Adding a Task to a Project
|
|
385
|
+
|
|
386
|
+
```javascript
|
|
387
|
+
const taskGid = '1234567890123456';
|
|
388
|
+
|
|
389
|
+
const body = {
|
|
390
|
+
data: {
|
|
391
|
+
project: '9876543210987654'
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
tasksApiInstance.addProjectForTask(body, taskGid).then((result) => {
|
|
396
|
+
console.log('Task added to project');
|
|
397
|
+
}).catch((error) => {
|
|
398
|
+
console.error('Error adding task to project:', error.response.body);
|
|
399
|
+
});
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### Sections API
|
|
403
|
+
|
|
404
|
+
Sections divide tasks within a project into categories, workflow stages, or priorities.
|
|
405
|
+
|
|
406
|
+
#### Creating Sections
|
|
407
|
+
|
|
408
|
+
```javascript
|
|
409
|
+
const Asana = require('asana');
|
|
410
|
+
|
|
411
|
+
const client = Asana.ApiClient.instance;
|
|
412
|
+
const token = client.authentications['token'];
|
|
413
|
+
token.accessToken = process.env.ASANA_ACCESS_TOKEN;
|
|
414
|
+
|
|
415
|
+
const sectionsApiInstance = new Asana.SectionsApi();
|
|
416
|
+
const projectGid = '1234567890123456';
|
|
417
|
+
|
|
418
|
+
const body = {
|
|
419
|
+
data: {
|
|
420
|
+
name: 'In Progress'
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
const opts = {
|
|
425
|
+
opt_fields: 'name,created_at,project.name'
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
sectionsApiInstance.createSectionForProject(body, projectGid, opts).then((result) => {
|
|
429
|
+
console.log('Section created:', result.data);
|
|
430
|
+
}).catch((error) => {
|
|
431
|
+
console.error('Error creating section:', error.response.body);
|
|
432
|
+
});
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
#### Getting Sections in a Project
|
|
436
|
+
|
|
437
|
+
```javascript
|
|
438
|
+
const projectGid = '1234567890123456';
|
|
439
|
+
|
|
440
|
+
const opts = {
|
|
441
|
+
opt_fields: 'name,created_at,project.name'
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
sectionsApiInstance.getSectionsForProject(projectGid, opts).then((result) => {
|
|
445
|
+
console.log('Sections:', result.data);
|
|
446
|
+
}).catch((error) => {
|
|
447
|
+
console.error('Error getting sections:', error.response.body);
|
|
448
|
+
});
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
#### Adding a Task to a Section
|
|
452
|
+
|
|
453
|
+
```javascript
|
|
454
|
+
const sectionGid = '1234567890123456';
|
|
455
|
+
|
|
456
|
+
const body = {
|
|
457
|
+
data: {
|
|
458
|
+
task: '9876543210987654'
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
sectionsApiInstance.addTaskForSection(body, sectionGid).then((result) => {
|
|
463
|
+
console.log('Task added to section');
|
|
464
|
+
}).catch((error) => {
|
|
465
|
+
console.error('Error adding task to section:', error.response.body);
|
|
466
|
+
});
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
### Workspaces API
|
|
470
|
+
|
|
471
|
+
Workspaces are the highest-level organizational unit in Asana.
|
|
472
|
+
|
|
473
|
+
#### Getting Workspaces
|
|
474
|
+
|
|
475
|
+
```javascript
|
|
476
|
+
const Asana = require('asana');
|
|
477
|
+
|
|
478
|
+
const client = Asana.ApiClient.instance;
|
|
479
|
+
const token = client.authentications['token'];
|
|
480
|
+
token.accessToken = process.env.ASANA_ACCESS_TOKEN;
|
|
481
|
+
|
|
482
|
+
const workspacesApiInstance = new Asana.WorkspacesApi();
|
|
483
|
+
|
|
484
|
+
const opts = {
|
|
485
|
+
opt_fields: 'name,is_organization,email_domains'
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
workspacesApiInstance.getWorkspaces(opts).then((result) => {
|
|
489
|
+
console.log('Workspaces:', result.data);
|
|
490
|
+
}).catch((error) => {
|
|
491
|
+
console.error('Error getting workspaces:', error.response.body);
|
|
492
|
+
});
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
#### Getting a Workspace
|
|
496
|
+
|
|
497
|
+
```javascript
|
|
498
|
+
const workspaceGid = '1234567890123456';
|
|
499
|
+
|
|
500
|
+
const opts = {
|
|
501
|
+
opt_fields: 'name,is_organization,email_domains'
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
workspacesApiInstance.getWorkspace(workspaceGid, opts).then((result) => {
|
|
505
|
+
console.log('Workspace:', result.data);
|
|
506
|
+
}).catch((error) => {
|
|
507
|
+
console.error('Error getting workspace:', error.response.body);
|
|
508
|
+
});
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
#### Getting Projects in a Workspace
|
|
512
|
+
|
|
513
|
+
```javascript
|
|
514
|
+
const workspaceGid = '1234567890123456';
|
|
515
|
+
|
|
516
|
+
const projectsApiInstance = new Asana.ProjectsApi();
|
|
517
|
+
|
|
518
|
+
const opts = {
|
|
519
|
+
archived: false,
|
|
520
|
+
opt_fields: 'name,owner.name,due_date,created_at'
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
projectsApiInstance.getProjectsForWorkspace(workspaceGid, opts).then((result) => {
|
|
524
|
+
console.log('Projects:', result.data);
|
|
525
|
+
}).catch((error) => {
|
|
526
|
+
console.error('Error getting projects:', error.response.body);
|
|
527
|
+
});
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
### Users API
|
|
531
|
+
|
|
532
|
+
Users represent individuals in Asana.
|
|
533
|
+
|
|
534
|
+
#### Getting the Current User
|
|
535
|
+
|
|
536
|
+
```javascript
|
|
537
|
+
const Asana = require('asana');
|
|
538
|
+
|
|
539
|
+
const client = Asana.ApiClient.instance;
|
|
540
|
+
const token = client.authentications['token'];
|
|
541
|
+
token.accessToken = process.env.ASANA_ACCESS_TOKEN;
|
|
542
|
+
|
|
543
|
+
const usersApiInstance = new Asana.UsersApi();
|
|
544
|
+
const userGid = 'me';
|
|
545
|
+
|
|
546
|
+
const opts = {
|
|
547
|
+
opt_fields: 'name,email,photo,workspaces,workspaces.name'
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
usersApiInstance.getUser(userGid, opts).then((result) => {
|
|
551
|
+
console.log('Current user:', result.data);
|
|
552
|
+
}).catch((error) => {
|
|
553
|
+
console.error('Error getting user:', error.response.body);
|
|
554
|
+
});
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
#### Getting Users in a Workspace
|
|
558
|
+
|
|
559
|
+
```javascript
|
|
560
|
+
const workspaceGid = '1234567890123456';
|
|
561
|
+
|
|
562
|
+
const opts = {
|
|
563
|
+
opt_fields: 'name,email,photo'
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
usersApiInstance.getUsersForWorkspace(workspaceGid, opts).then((result) => {
|
|
567
|
+
console.log('Users:', result.data);
|
|
568
|
+
}).catch((error) => {
|
|
569
|
+
console.error('Error getting users:', error.response.body);
|
|
570
|
+
});
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
### Teams API
|
|
574
|
+
|
|
575
|
+
Teams organize people and projects within a workspace.
|
|
576
|
+
|
|
577
|
+
#### Getting Teams
|
|
578
|
+
|
|
579
|
+
```javascript
|
|
580
|
+
const Asana = require('asana');
|
|
581
|
+
|
|
582
|
+
const client = Asana.ApiClient.instance;
|
|
583
|
+
const token = client.authentications['token'];
|
|
584
|
+
token.accessToken = process.env.ASANA_ACCESS_TOKEN;
|
|
585
|
+
|
|
586
|
+
const teamsApiInstance = new Asana.TeamsApi();
|
|
587
|
+
const workspaceGid = '1234567890123456';
|
|
588
|
+
|
|
589
|
+
const opts = {
|
|
590
|
+
opt_fields: 'name,description,organization.name'
|
|
591
|
+
};
|
|
592
|
+
|
|
593
|
+
teamsApiInstance.getTeamsForWorkspace(workspaceGid, opts).then((result) => {
|
|
594
|
+
console.log('Teams:', result.data);
|
|
595
|
+
}).catch((error) => {
|
|
596
|
+
console.error('Error getting teams:', error.response.body);
|
|
597
|
+
});
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
#### Getting a Team
|
|
601
|
+
|
|
602
|
+
```javascript
|
|
603
|
+
const teamGid = '1234567890123456';
|
|
604
|
+
|
|
605
|
+
const opts = {
|
|
606
|
+
opt_fields: 'name,description,organization.name,html_description'
|
|
607
|
+
};
|
|
608
|
+
|
|
609
|
+
teamsApiInstance.getTeam(teamGid, opts).then((result) => {
|
|
610
|
+
console.log('Team:', result.data);
|
|
611
|
+
}).catch((error) => {
|
|
612
|
+
console.error('Error getting team:', error.response.body);
|
|
613
|
+
});
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
### Custom Fields API
|
|
617
|
+
|
|
618
|
+
Custom fields allow you to add structured metadata to tasks and projects.
|
|
619
|
+
|
|
620
|
+
#### Getting Custom Fields in a Workspace
|
|
621
|
+
|
|
622
|
+
```javascript
|
|
623
|
+
const Asana = require('asana');
|
|
624
|
+
|
|
625
|
+
const client = Asana.ApiClient.instance;
|
|
626
|
+
const token = client.authentications['token'];
|
|
627
|
+
token.accessToken = process.env.ASANA_ACCESS_TOKEN;
|
|
628
|
+
|
|
629
|
+
const customFieldsApiInstance = new Asana.CustomFieldsApi();
|
|
630
|
+
const workspaceGid = '1234567890123456';
|
|
631
|
+
|
|
632
|
+
const opts = {
|
|
633
|
+
opt_fields: 'name,resource_subtype,type,enum_options,enum_options.name,precision'
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
customFieldsApiInstance.getCustomFieldsForWorkspace(workspaceGid, opts).then((result) => {
|
|
637
|
+
console.log('Custom fields:', result.data);
|
|
638
|
+
}).catch((error) => {
|
|
639
|
+
console.error('Error getting custom fields:', error.response.body);
|
|
640
|
+
});
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
#### Creating a Custom Field
|
|
644
|
+
|
|
645
|
+
```javascript
|
|
646
|
+
const workspaceGid = '1234567890123456';
|
|
647
|
+
|
|
648
|
+
const body = {
|
|
649
|
+
data: {
|
|
650
|
+
name: 'Priority',
|
|
651
|
+
resource_subtype: 'enum',
|
|
652
|
+
type: 'enum',
|
|
653
|
+
workspace: workspaceGid,
|
|
654
|
+
enum_options: [
|
|
655
|
+
{ name: 'Low', enabled: true, color: 'blue' },
|
|
656
|
+
{ name: 'Medium', enabled: true, color: 'yellow' },
|
|
657
|
+
{ name: 'High', enabled: true, color: 'red' }
|
|
658
|
+
]
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
|
|
662
|
+
customFieldsApiInstance.createCustomField(body).then((result) => {
|
|
663
|
+
console.log('Custom field created:', result.data);
|
|
664
|
+
}).catch((error) => {
|
|
665
|
+
console.error('Error creating custom field:', error.response.body);
|
|
666
|
+
});
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
#### Updating Custom Field Value on a Task
|
|
670
|
+
|
|
671
|
+
```javascript
|
|
672
|
+
const taskGid = '1234567890123456';
|
|
673
|
+
const customFieldGid = '9876543210987654';
|
|
674
|
+
|
|
675
|
+
const body = {
|
|
676
|
+
data: {
|
|
677
|
+
custom_fields: {
|
|
678
|
+
[customFieldGid]: 'High'
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
tasksApiInstance.updateTask(body, taskGid).then((result) => {
|
|
684
|
+
console.log('Custom field updated');
|
|
685
|
+
}).catch((error) => {
|
|
686
|
+
console.error('Error updating custom field:', error.response.body);
|
|
687
|
+
});
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
### Tags API
|
|
691
|
+
|
|
692
|
+
Tags are labels that can be attached to tasks.
|
|
693
|
+
|
|
694
|
+
#### Creating Tags
|
|
695
|
+
|
|
696
|
+
```javascript
|
|
697
|
+
const Asana = require('asana');
|
|
698
|
+
|
|
699
|
+
const client = Asana.ApiClient.instance;
|
|
700
|
+
const token = client.authentications['token'];
|
|
701
|
+
token.accessToken = process.env.ASANA_ACCESS_TOKEN;
|
|
702
|
+
|
|
703
|
+
const tagsApiInstance = new Asana.TagsApi();
|
|
704
|
+
|
|
705
|
+
const body = {
|
|
706
|
+
data: {
|
|
707
|
+
name: 'Bug',
|
|
708
|
+
workspace: '1234567890123456',
|
|
709
|
+
color: 'red'
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
const opts = {
|
|
714
|
+
opt_fields: 'name,color,created_at'
|
|
715
|
+
};
|
|
716
|
+
|
|
717
|
+
tagsApiInstance.createTag(body, opts).then((result) => {
|
|
718
|
+
console.log('Tag created:', result.data);
|
|
719
|
+
}).catch((error) => {
|
|
720
|
+
console.error('Error creating tag:', error.response.body);
|
|
721
|
+
});
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
#### Getting Tags in a Workspace
|
|
725
|
+
|
|
726
|
+
```javascript
|
|
727
|
+
const workspaceGid = '1234567890123456';
|
|
728
|
+
|
|
729
|
+
const opts = {
|
|
730
|
+
opt_fields: 'name,color,created_at'
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
tagsApiInstance.getTagsForWorkspace(workspaceGid, opts).then((result) => {
|
|
734
|
+
console.log('Tags:', result.data);
|
|
735
|
+
}).catch((error) => {
|
|
736
|
+
console.error('Error getting tags:', error.response.body);
|
|
737
|
+
});
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
### Attachments API
|
|
741
|
+
|
|
742
|
+
Attachments are files or URLs associated with tasks.
|
|
743
|
+
|
|
744
|
+
#### Uploading an Attachment to a Task
|
|
745
|
+
|
|
746
|
+
```javascript
|
|
747
|
+
const Asana = require('asana');
|
|
748
|
+
const fs = require('fs');
|
|
749
|
+
|
|
750
|
+
const client = Asana.ApiClient.instance;
|
|
751
|
+
const token = client.authentications['token'];
|
|
752
|
+
token.accessToken = process.env.ASANA_ACCESS_TOKEN;
|
|
753
|
+
|
|
754
|
+
const attachmentsApiInstance = new Asana.AttachmentsApi();
|
|
755
|
+
const taskGid = '1234567890123456';
|
|
756
|
+
|
|
757
|
+
const body = {
|
|
758
|
+
file: fs.createReadStream('/path/to/file.pdf'),
|
|
759
|
+
parent: taskGid
|
|
760
|
+
};
|
|
761
|
+
|
|
762
|
+
const opts = {
|
|
763
|
+
opt_fields: 'name,download_url,size,host'
|
|
764
|
+
};
|
|
765
|
+
|
|
766
|
+
attachmentsApiInstance.createAttachmentForObject(body, opts).then((result) => {
|
|
767
|
+
console.log('Attachment uploaded:', result.data);
|
|
768
|
+
}).catch((error) => {
|
|
769
|
+
console.error('Error uploading attachment:', error.response.body);
|
|
770
|
+
});
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
#### Getting Attachments for a Task
|
|
774
|
+
|
|
775
|
+
```javascript
|
|
776
|
+
const taskGid = '1234567890123456';
|
|
777
|
+
|
|
778
|
+
const opts = {
|
|
779
|
+
opt_fields: 'name,download_url,size,host,created_at'
|
|
780
|
+
};
|
|
781
|
+
|
|
782
|
+
attachmentsApiInstance.getAttachmentsForObject(taskGid, opts).then((result) => {
|
|
783
|
+
console.log('Attachments:', result.data);
|
|
784
|
+
}).catch((error) => {
|
|
785
|
+
console.error('Error getting attachments:', error.response.body);
|
|
786
|
+
});
|
|
787
|
+
```
|
|
788
|
+
|
|
789
|
+
### Webhooks API
|
|
790
|
+
|
|
791
|
+
Webhooks allow applications to be notified of changes in Asana.
|
|
792
|
+
|
|
793
|
+
#### Creating a Webhook
|
|
794
|
+
|
|
795
|
+
```javascript
|
|
796
|
+
const Asana = require('asana');
|
|
797
|
+
|
|
798
|
+
const client = Asana.ApiClient.instance;
|
|
799
|
+
const token = client.authentications['token'];
|
|
800
|
+
token.accessToken = process.env.ASANA_ACCESS_TOKEN;
|
|
801
|
+
|
|
802
|
+
const webhooksApiInstance = new Asana.WebhooksApi();
|
|
803
|
+
|
|
804
|
+
const body = {
|
|
805
|
+
data: {
|
|
806
|
+
resource: '1234567890123456', // project GID
|
|
807
|
+
target: 'https://example.com/webhooks/asana'
|
|
808
|
+
}
|
|
809
|
+
};
|
|
810
|
+
|
|
811
|
+
const opts = {
|
|
812
|
+
opt_fields: 'resource,target,active,last_success_at,last_failure_at'
|
|
813
|
+
};
|
|
814
|
+
|
|
815
|
+
webhooksApiInstance.createWebhook(body, opts).then((result) => {
|
|
816
|
+
console.log('Webhook created:', result.data);
|
|
817
|
+
}).catch((error) => {
|
|
818
|
+
console.error('Error creating webhook:', error.response.body);
|
|
819
|
+
});
|
|
820
|
+
```
|
|
821
|
+
|
|
822
|
+
#### Getting Webhooks
|
|
823
|
+
|
|
824
|
+
```javascript
|
|
825
|
+
const workspaceGid = '1234567890123456';
|
|
826
|
+
|
|
827
|
+
const opts = {
|
|
828
|
+
resource: workspaceGid,
|
|
829
|
+
opt_fields: 'resource,target,active,created_at,last_success_at,last_failure_at'
|
|
830
|
+
};
|
|
831
|
+
|
|
832
|
+
webhooksApiInstance.getWebhooks(opts).then((result) => {
|
|
833
|
+
console.log('Webhooks:', result.data);
|
|
834
|
+
}).catch((error) => {
|
|
835
|
+
console.error('Error getting webhooks:', error.response.body);
|
|
836
|
+
});
|
|
837
|
+
```
|
|
838
|
+
|
|
839
|
+
#### Deleting a Webhook
|
|
840
|
+
|
|
841
|
+
```javascript
|
|
842
|
+
const webhookGid = '1234567890123456';
|
|
843
|
+
|
|
844
|
+
webhooksApiInstance.deleteWebhook(webhookGid).then((result) => {
|
|
845
|
+
console.log('Webhook deleted');
|
|
846
|
+
}).catch((error) => {
|
|
847
|
+
console.error('Error deleting webhook:', error.response.body);
|
|
848
|
+
});
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
#### Handling Webhook Events
|
|
852
|
+
|
|
853
|
+
```javascript
|
|
854
|
+
const express = require('express');
|
|
855
|
+
const crypto = require('crypto');
|
|
856
|
+
const app = express();
|
|
857
|
+
|
|
858
|
+
app.use(express.json());
|
|
859
|
+
|
|
860
|
+
app.post('/webhooks/asana', (req, res) => {
|
|
861
|
+
// Verify webhook signature
|
|
862
|
+
const signature = req.headers['x-hook-signature'];
|
|
863
|
+
const secret = process.env.ASANA_WEBHOOK_SECRET;
|
|
864
|
+
|
|
865
|
+
const hash = crypto
|
|
866
|
+
.createHmac('sha256', secret)
|
|
867
|
+
.update(JSON.stringify(req.body))
|
|
868
|
+
.digest('hex');
|
|
869
|
+
|
|
870
|
+
if (signature !== hash) {
|
|
871
|
+
return res.status(401).send('Invalid signature');
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
// Handle handshake
|
|
875
|
+
if (req.headers['x-hook-secret']) {
|
|
876
|
+
res.setHeader('X-Hook-Secret', req.headers['x-hook-secret']);
|
|
877
|
+
return res.status(200).send();
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// Process webhook events
|
|
881
|
+
const events = req.body.events || [];
|
|
882
|
+
|
|
883
|
+
events.forEach((event) => {
|
|
884
|
+
console.log('Event:', event.action, 'Resource:', event.resource);
|
|
885
|
+
|
|
886
|
+
if (event.action === 'added') {
|
|
887
|
+
console.log('Task added:', event.resource.gid);
|
|
888
|
+
} else if (event.action === 'changed') {
|
|
889
|
+
console.log('Task changed:', event.resource.gid);
|
|
890
|
+
} else if (event.action === 'removed') {
|
|
891
|
+
console.log('Task removed:', event.resource.gid);
|
|
892
|
+
}
|
|
893
|
+
});
|
|
894
|
+
|
|
895
|
+
res.status(200).send();
|
|
896
|
+
});
|
|
897
|
+
|
|
898
|
+
app.listen(3000, () => {
|
|
899
|
+
console.log('Webhook server listening on port 3000');
|
|
900
|
+
});
|
|
901
|
+
```
|
|
902
|
+
|
|
903
|
+
### Stories API (Comments and Activity)
|
|
904
|
+
|
|
905
|
+
Stories represent the activity feed on tasks and projects.
|
|
906
|
+
|
|
907
|
+
#### Creating a Comment on a Task
|
|
908
|
+
|
|
909
|
+
```javascript
|
|
910
|
+
const Asana = require('asana');
|
|
911
|
+
|
|
912
|
+
const client = Asana.ApiClient.instance;
|
|
913
|
+
const token = client.authentications['token'];
|
|
914
|
+
token.accessToken = process.env.ASANA_ACCESS_TOKEN;
|
|
915
|
+
|
|
916
|
+
const storiesApiInstance = new Asana.StoriesApi();
|
|
917
|
+
const taskGid = '1234567890123456';
|
|
918
|
+
|
|
919
|
+
const body = {
|
|
920
|
+
data: {
|
|
921
|
+
text: 'This is a comment on the task',
|
|
922
|
+
is_pinned: false
|
|
923
|
+
}
|
|
924
|
+
};
|
|
925
|
+
|
|
926
|
+
const opts = {
|
|
927
|
+
opt_fields: 'text,created_at,created_by.name,is_pinned'
|
|
928
|
+
};
|
|
929
|
+
|
|
930
|
+
storiesApiInstance.createStoryForTask(body, taskGid, opts).then((result) => {
|
|
931
|
+
console.log('Comment created:', result.data);
|
|
932
|
+
}).catch((error) => {
|
|
933
|
+
console.error('Error creating comment:', error.response.body);
|
|
934
|
+
});
|
|
935
|
+
```
|
|
936
|
+
|
|
937
|
+
#### Getting Comments for a Task
|
|
938
|
+
|
|
939
|
+
```javascript
|
|
940
|
+
const taskGid = '1234567890123456';
|
|
941
|
+
|
|
942
|
+
const opts = {
|
|
943
|
+
opt_fields: 'text,created_at,created_by.name,resource_subtype,type'
|
|
944
|
+
};
|
|
945
|
+
|
|
946
|
+
storiesApiInstance.getStoriesForTask(taskGid, opts).then((result) => {
|
|
947
|
+
console.log('Stories:', result.data);
|
|
948
|
+
}).catch((error) => {
|
|
949
|
+
console.error('Error getting stories:', error.response.body);
|
|
950
|
+
});
|
|
951
|
+
```
|
|
952
|
+
|
|
953
|
+
### Portfolios API
|
|
954
|
+
|
|
955
|
+
Portfolios are collections of projects.
|
|
956
|
+
|
|
957
|
+
#### Creating a Portfolio
|
|
958
|
+
|
|
959
|
+
```javascript
|
|
960
|
+
const Asana = require('asana');
|
|
961
|
+
|
|
962
|
+
const client = Asana.ApiClient.instance;
|
|
963
|
+
const token = client.authentications['token'];
|
|
964
|
+
token.accessToken = process.env.ASANA_ACCESS_TOKEN;
|
|
965
|
+
|
|
966
|
+
const portfoliosApiInstance = new Asana.PortfoliosApi();
|
|
967
|
+
|
|
968
|
+
const body = {
|
|
969
|
+
data: {
|
|
970
|
+
name: 'Product Initiatives',
|
|
971
|
+
workspace: '1234567890123456',
|
|
972
|
+
color: 'light-pink',
|
|
973
|
+
public: false
|
|
974
|
+
}
|
|
975
|
+
};
|
|
976
|
+
|
|
977
|
+
const opts = {
|
|
978
|
+
opt_fields: 'name,color,created_at,owner.name'
|
|
979
|
+
};
|
|
980
|
+
|
|
981
|
+
portfoliosApiInstance.createPortfolio(body, opts).then((result) => {
|
|
982
|
+
console.log('Portfolio created:', result.data);
|
|
983
|
+
}).catch((error) => {
|
|
984
|
+
console.error('Error creating portfolio:', error.response.body);
|
|
985
|
+
});
|
|
986
|
+
```
|
|
987
|
+
|
|
988
|
+
#### Adding a Project to a Portfolio
|
|
989
|
+
|
|
990
|
+
```javascript
|
|
991
|
+
const portfolioGid = '1234567890123456';
|
|
992
|
+
|
|
993
|
+
const body = {
|
|
994
|
+
data: {
|
|
995
|
+
project: '9876543210987654'
|
|
996
|
+
}
|
|
997
|
+
};
|
|
998
|
+
|
|
999
|
+
portfoliosApiInstance.addItemForPortfolio(body, portfolioGid).then((result) => {
|
|
1000
|
+
console.log('Project added to portfolio');
|
|
1001
|
+
}).catch((error) => {
|
|
1002
|
+
console.error('Error adding project to portfolio:', error.response.body);
|
|
1003
|
+
});
|
|
1004
|
+
```
|
|
1005
|
+
|
|
1006
|
+
## Error Handling
|
|
1007
|
+
|
|
1008
|
+
### Basic Error Handling
|
|
1009
|
+
|
|
1010
|
+
```javascript
|
|
1011
|
+
const Asana = require('asana');
|
|
1012
|
+
|
|
1013
|
+
const client = Asana.ApiClient.instance;
|
|
1014
|
+
const token = client.authentications['token'];
|
|
1015
|
+
token.accessToken = process.env.ASANA_ACCESS_TOKEN;
|
|
1016
|
+
|
|
1017
|
+
const tasksApiInstance = new Asana.TasksApi();
|
|
1018
|
+
|
|
1019
|
+
async function getTask(taskGid) {
|
|
1020
|
+
try {
|
|
1021
|
+
const result = await tasksApiInstance.getTask(taskGid);
|
|
1022
|
+
console.log('Task:', result.data);
|
|
1023
|
+
return result.data;
|
|
1024
|
+
} catch (error) {
|
|
1025
|
+
if (error.response) {
|
|
1026
|
+
console.error('API Error:', error.response.body);
|
|
1027
|
+
console.error('Status:', error.response.status);
|
|
1028
|
+
|
|
1029
|
+
if (error.response.status === 401) {
|
|
1030
|
+
console.error('Authentication failed. Check your access token.');
|
|
1031
|
+
} else if (error.response.status === 403) {
|
|
1032
|
+
console.error('Permission denied. You do not have access to this resource.');
|
|
1033
|
+
} else if (error.response.status === 404) {
|
|
1034
|
+
console.error('Resource not found.');
|
|
1035
|
+
} else if (error.response.status === 429) {
|
|
1036
|
+
console.error('Rate limit exceeded. Please retry after some time.');
|
|
1037
|
+
}
|
|
1038
|
+
} else {
|
|
1039
|
+
console.error('Network error:', error.message);
|
|
1040
|
+
}
|
|
1041
|
+
throw error;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
```
|
|
1045
|
+
|
|
1046
|
+
### Retry Pattern for Rate Limiting
|
|
1047
|
+
|
|
1048
|
+
```javascript
|
|
1049
|
+
async function makeRequestWithRetry(apiCall, maxRetries = 3) {
|
|
1050
|
+
for (let i = 0; i < maxRetries; i++) {
|
|
1051
|
+
try {
|
|
1052
|
+
return await apiCall();
|
|
1053
|
+
} catch (error) {
|
|
1054
|
+
if (error.response?.status === 429) {
|
|
1055
|
+
const retryAfter = error.response.headers['retry-after'] || Math.pow(2, i);
|
|
1056
|
+
console.log(`Rate limited. Retrying after ${retryAfter} seconds...`);
|
|
1057
|
+
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
|
|
1058
|
+
} else {
|
|
1059
|
+
throw error;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
throw new Error('Max retries exceeded');
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
// Usage
|
|
1067
|
+
makeRequestWithRetry(() => tasksApiInstance.getTask(taskGid))
|
|
1068
|
+
.then(result => console.log('Task:', result.data))
|
|
1069
|
+
.catch(error => console.error('Failed after retries:', error));
|
|
1070
|
+
```
|
|
1071
|
+
|
|
1072
|
+
## Pagination
|
|
1073
|
+
|
|
1074
|
+
### Handling Paginated Results
|
|
1075
|
+
|
|
1076
|
+
```javascript
|
|
1077
|
+
async function getAllTasks(projectGid) {
|
|
1078
|
+
const allTasks = [];
|
|
1079
|
+
let offset = undefined;
|
|
1080
|
+
|
|
1081
|
+
do {
|
|
1082
|
+
const opts = {
|
|
1083
|
+
limit: 100,
|
|
1084
|
+
offset: offset,
|
|
1085
|
+
opt_fields: 'name,completed,assignee.name'
|
|
1086
|
+
};
|
|
1087
|
+
|
|
1088
|
+
const result = await tasksApiInstance.getTasksForProject(projectGid, opts);
|
|
1089
|
+
allTasks.push(...result.data);
|
|
1090
|
+
|
|
1091
|
+
offset = result.next_page?.offset;
|
|
1092
|
+
} while (offset);
|
|
1093
|
+
|
|
1094
|
+
return allTasks;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
// Usage
|
|
1098
|
+
getAllTasks('1234567890123456').then(tasks => {
|
|
1099
|
+
console.log(`Total tasks: ${tasks.length}`);
|
|
1100
|
+
tasks.forEach(task => console.log(task.name));
|
|
1101
|
+
});
|
|
1102
|
+
```
|
|
1103
|
+
|
|
1104
|
+
## OAuth Authentication Flow
|
|
1105
|
+
|
|
1106
|
+
### Setting Up OAuth
|
|
1107
|
+
|
|
1108
|
+
```javascript
|
|
1109
|
+
const express = require('express');
|
|
1110
|
+
const Asana = require('asana');
|
|
1111
|
+
|
|
1112
|
+
const app = express();
|
|
1113
|
+
|
|
1114
|
+
const ASANA_CLIENT_ID = process.env.ASANA_CLIENT_ID;
|
|
1115
|
+
const ASANA_CLIENT_SECRET = process.env.ASANA_CLIENT_SECRET;
|
|
1116
|
+
const REDIRECT_URI = process.env.ASANA_REDIRECT_URI;
|
|
1117
|
+
|
|
1118
|
+
// Generate authorization URL
|
|
1119
|
+
app.get('/auth', (req, res) => {
|
|
1120
|
+
const authUrl = `https://app.asana.com/-/oauth_authorize?client_id=${ASANA_CLIENT_ID}&redirect_uri=${encodeURIComponent(REDIRECT_URI)}&response_type=code&state=random_state_string`;
|
|
1121
|
+
res.redirect(authUrl);
|
|
1122
|
+
});
|
|
1123
|
+
|
|
1124
|
+
// Handle OAuth callback
|
|
1125
|
+
app.get('/auth/callback', async (req, res) => {
|
|
1126
|
+
const code = req.query.code;
|
|
1127
|
+
|
|
1128
|
+
try {
|
|
1129
|
+
const response = await fetch('https://app.asana.com/-/oauth_token', {
|
|
1130
|
+
method: 'POST',
|
|
1131
|
+
headers: {
|
|
1132
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
1133
|
+
},
|
|
1134
|
+
body: new URLSearchParams({
|
|
1135
|
+
grant_type: 'authorization_code',
|
|
1136
|
+
client_id: ASANA_CLIENT_ID,
|
|
1137
|
+
client_secret: ASANA_CLIENT_SECRET,
|
|
1138
|
+
redirect_uri: REDIRECT_URI,
|
|
1139
|
+
code: code
|
|
1140
|
+
})
|
|
1141
|
+
});
|
|
1142
|
+
|
|
1143
|
+
const data = await response.json();
|
|
1144
|
+
|
|
1145
|
+
if (data.access_token) {
|
|
1146
|
+
// Store access_token and refresh_token securely
|
|
1147
|
+
console.log('Access token:', data.access_token);
|
|
1148
|
+
console.log('Refresh token:', data.refresh_token);
|
|
1149
|
+
|
|
1150
|
+
// Initialize Asana client with OAuth token
|
|
1151
|
+
const client = Asana.ApiClient.instance;
|
|
1152
|
+
const oauth = client.authentications['oauth2'];
|
|
1153
|
+
oauth.accessToken = data.access_token;
|
|
1154
|
+
|
|
1155
|
+
res.send('Authentication successful!');
|
|
1156
|
+
} else {
|
|
1157
|
+
res.status(400).send('Authentication failed');
|
|
1158
|
+
}
|
|
1159
|
+
} catch (error) {
|
|
1160
|
+
console.error('OAuth error:', error);
|
|
1161
|
+
res.status(500).send('Error during authentication');
|
|
1162
|
+
}
|
|
1163
|
+
});
|
|
1164
|
+
|
|
1165
|
+
app.listen(3000, () => {
|
|
1166
|
+
console.log('Server listening on http://localhost:3000');
|
|
1167
|
+
});
|
|
1168
|
+
```
|
|
1169
|
+
|
|
1170
|
+
### Refreshing OAuth Tokens
|
|
1171
|
+
|
|
1172
|
+
```javascript
|
|
1173
|
+
async function refreshAccessToken(refreshToken) {
|
|
1174
|
+
try {
|
|
1175
|
+
const response = await fetch('https://app.asana.com/-/oauth_token', {
|
|
1176
|
+
method: 'POST',
|
|
1177
|
+
headers: {
|
|
1178
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
1179
|
+
},
|
|
1180
|
+
body: new URLSearchParams({
|
|
1181
|
+
grant_type: 'refresh_token',
|
|
1182
|
+
client_id: process.env.ASANA_CLIENT_ID,
|
|
1183
|
+
client_secret: process.env.ASANA_CLIENT_SECRET,
|
|
1184
|
+
refresh_token: refreshToken
|
|
1185
|
+
})
|
|
1186
|
+
});
|
|
1187
|
+
|
|
1188
|
+
const data = await response.json();
|
|
1189
|
+
|
|
1190
|
+
if (data.access_token) {
|
|
1191
|
+
console.log('New access token:', data.access_token);
|
|
1192
|
+
return data.access_token;
|
|
1193
|
+
}
|
|
1194
|
+
} catch (error) {
|
|
1195
|
+
console.error('Token refresh error:', error);
|
|
1196
|
+
throw error;
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
```
|
|
1200
|
+
|
|
1201
|
+
## TypeScript Support
|
|
1202
|
+
|
|
1203
|
+
### Using Asana SDK with TypeScript
|
|
1204
|
+
|
|
1205
|
+
```typescript
|
|
1206
|
+
import Asana from 'asana';
|
|
1207
|
+
|
|
1208
|
+
const client = Asana.ApiClient.instance;
|
|
1209
|
+
const token = client.authentications['token'];
|
|
1210
|
+
token.accessToken = process.env.ASANA_ACCESS_TOKEN!;
|
|
1211
|
+
|
|
1212
|
+
const tasksApiInstance = new Asana.TasksApi();
|
|
1213
|
+
|
|
1214
|
+
interface TaskData {
|
|
1215
|
+
name: string;
|
|
1216
|
+
workspace: string;
|
|
1217
|
+
assignee?: string;
|
|
1218
|
+
due_on?: string;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
async function createTask(taskData: TaskData): Promise<any> {
|
|
1222
|
+
const body = {
|
|
1223
|
+
data: taskData
|
|
1224
|
+
};
|
|
1225
|
+
|
|
1226
|
+
const opts = {
|
|
1227
|
+
opt_fields: 'name,assignee.name,due_on,completed'
|
|
1228
|
+
};
|
|
1229
|
+
|
|
1230
|
+
try {
|
|
1231
|
+
const result = await tasksApiInstance.createTask(body, opts);
|
|
1232
|
+
return result.data;
|
|
1233
|
+
} catch (error) {
|
|
1234
|
+
console.error('Error creating task:', error);
|
|
1235
|
+
throw error;
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
// Usage
|
|
1240
|
+
createTask({
|
|
1241
|
+
name: 'TypeScript task',
|
|
1242
|
+
workspace: '1234567890123456',
|
|
1243
|
+
due_on: '2025-12-31'
|
|
1244
|
+
}).then(task => {
|
|
1245
|
+
console.log('Task created:', task);
|
|
1246
|
+
});
|
|
1247
|
+
```
|
|
1248
|
+
|
|
1249
|
+
## Batch Operations
|
|
1250
|
+
|
|
1251
|
+
### Creating Multiple Tasks
|
|
1252
|
+
|
|
1253
|
+
```javascript
|
|
1254
|
+
async function createMultipleTasks(taskDataArray) {
|
|
1255
|
+
const promises = taskDataArray.map(taskData => {
|
|
1256
|
+
const body = { data: taskData };
|
|
1257
|
+
return tasksApiInstance.createTask(body);
|
|
1258
|
+
});
|
|
1259
|
+
|
|
1260
|
+
try {
|
|
1261
|
+
const results = await Promise.all(promises);
|
|
1262
|
+
console.log(`Created ${results.length} tasks`);
|
|
1263
|
+
return results.map(r => r.data);
|
|
1264
|
+
} catch (error) {
|
|
1265
|
+
console.error('Error creating tasks:', error);
|
|
1266
|
+
throw error;
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
// Usage
|
|
1271
|
+
const tasksToCreate = [
|
|
1272
|
+
{ name: 'Task 1', workspace: '1234567890123456' },
|
|
1273
|
+
{ name: 'Task 2', workspace: '1234567890123456' },
|
|
1274
|
+
{ name: 'Task 3', workspace: '1234567890123456' }
|
|
1275
|
+
];
|
|
1276
|
+
|
|
1277
|
+
createMultipleTasks(tasksToCreate).then(tasks => {
|
|
1278
|
+
console.log('Tasks created:', tasks);
|
|
1279
|
+
});
|
|
1280
|
+
```
|
|
1281
|
+
|
|
1282
|
+
## Environment Variable Validation
|
|
1283
|
+
|
|
1284
|
+
```javascript
|
|
1285
|
+
const Asana = require('asana');
|
|
1286
|
+
require('dotenv').config();
|
|
1287
|
+
|
|
1288
|
+
function validateEnvironment() {
|
|
1289
|
+
if (!process.env.ASANA_ACCESS_TOKEN) {
|
|
1290
|
+
console.error('Error: ASANA_ACCESS_TOKEN is required in .env file');
|
|
1291
|
+
process.exit(1);
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
console.log('Environment validated successfully');
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
validateEnvironment();
|
|
1298
|
+
|
|
1299
|
+
const client = Asana.ApiClient.instance;
|
|
1300
|
+
const token = client.authentications['token'];
|
|
1301
|
+
token.accessToken = process.env.ASANA_ACCESS_TOKEN;
|
|
1302
|
+
```
|
|
1303
|
+
|
|
1304
|
+
## Complete Example Application
|
|
1305
|
+
|
|
1306
|
+
```javascript
|
|
1307
|
+
const Asana = require('asana');
|
|
1308
|
+
require('dotenv').config();
|
|
1309
|
+
|
|
1310
|
+
// Validate environment
|
|
1311
|
+
if (!process.env.ASANA_ACCESS_TOKEN || !process.env.ASANA_WORKSPACE_ID) {
|
|
1312
|
+
console.error('Missing required environment variables');
|
|
1313
|
+
process.exit(1);
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
// Initialize client
|
|
1317
|
+
const client = Asana.ApiClient.instance;
|
|
1318
|
+
const token = client.authentications['token'];
|
|
1319
|
+
token.accessToken = process.env.ASANA_ACCESS_TOKEN;
|
|
1320
|
+
|
|
1321
|
+
const tasksApiInstance = new Asana.TasksApi();
|
|
1322
|
+
const projectsApiInstance = new Asana.ProjectsApi();
|
|
1323
|
+
const usersApiInstance = new Asana.UsersApi();
|
|
1324
|
+
|
|
1325
|
+
async function main() {
|
|
1326
|
+
try {
|
|
1327
|
+
// Get current user
|
|
1328
|
+
console.log('Getting current user...');
|
|
1329
|
+
const userResult = await usersApiInstance.getUser('me', {
|
|
1330
|
+
opt_fields: 'name,email,workspaces.name'
|
|
1331
|
+
});
|
|
1332
|
+
console.log('Logged in as:', userResult.data.name);
|
|
1333
|
+
|
|
1334
|
+
// Create a project
|
|
1335
|
+
console.log('\nCreating project...');
|
|
1336
|
+
const projectBody = {
|
|
1337
|
+
data: {
|
|
1338
|
+
name: 'API Demo Project',
|
|
1339
|
+
workspace: process.env.ASANA_WORKSPACE_ID,
|
|
1340
|
+
notes: 'Project created via Asana API'
|
|
1341
|
+
}
|
|
1342
|
+
};
|
|
1343
|
+
const projectResult = await projectsApiInstance.createProject(projectBody, {
|
|
1344
|
+
opt_fields: 'name,gid'
|
|
1345
|
+
});
|
|
1346
|
+
console.log('Project created:', projectResult.data.name);
|
|
1347
|
+
const projectGid = projectResult.data.gid;
|
|
1348
|
+
|
|
1349
|
+
// Create tasks
|
|
1350
|
+
console.log('\nCreating tasks...');
|
|
1351
|
+
const taskNames = ['Design mockups', 'Implement feature', 'Write tests', 'Deploy'];
|
|
1352
|
+
|
|
1353
|
+
for (const taskName of taskNames) {
|
|
1354
|
+
const taskBody = {
|
|
1355
|
+
data: {
|
|
1356
|
+
name: taskName,
|
|
1357
|
+
workspace: process.env.ASANA_WORKSPACE_ID,
|
|
1358
|
+
projects: [projectGid]
|
|
1359
|
+
}
|
|
1360
|
+
};
|
|
1361
|
+
const taskResult = await tasksApiInstance.createTask(taskBody);
|
|
1362
|
+
console.log('Created task:', taskResult.data.name);
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
// Get all tasks in project
|
|
1366
|
+
console.log('\nFetching project tasks...');
|
|
1367
|
+
const tasksResult = await tasksApiInstance.getTasksForProject(projectGid, {
|
|
1368
|
+
opt_fields: 'name,completed'
|
|
1369
|
+
});
|
|
1370
|
+
console.log(`Project has ${tasksResult.data.length} tasks`);
|
|
1371
|
+
|
|
1372
|
+
// Mark first task as complete
|
|
1373
|
+
if (tasksResult.data.length > 0) {
|
|
1374
|
+
const firstTaskGid = tasksResult.data[0].gid;
|
|
1375
|
+
console.log('\nMarking first task as complete...');
|
|
1376
|
+
await tasksApiInstance.updateTask(
|
|
1377
|
+
{ data: { completed: true } },
|
|
1378
|
+
firstTaskGid
|
|
1379
|
+
);
|
|
1380
|
+
console.log('Task marked as complete');
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
console.log('\nDemo completed successfully!');
|
|
1384
|
+
|
|
1385
|
+
} catch (error) {
|
|
1386
|
+
console.error('Error:', error.response?.body || error.message);
|
|
1387
|
+
process.exit(1);
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
main();
|
|
1392
|
+
```
|
|
1393
|
+
|
|
1394
|
+
## Notes
|
|
1395
|
+
|
|
1396
|
+
The Asana Node.js SDK is auto-generated from the OpenAPI specification, ensuring it stays current with the latest API features. The SDK supports both Personal Access Tokens for simple authentication and OAuth 2.0 for multi-user applications. All API methods return Promises and can be used with async/await syntax. The SDK automatically handles request formatting and response parsing. Rate limits apply: 1500 requests per minute for most operations, with some endpoints having lower limits. Use the `opt_fields` parameter to optimize API responses by requesting only the fields you need.
|