@doist/todoist-api-typescript 5.9.0 → 6.0.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/dist/cjs/authentication.js +158 -0
- package/dist/{consts → cjs/consts}/endpoints.js +10 -12
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/rest-client.js +124 -0
- package/dist/{test-utils → cjs/test-utils}/asserts.js +1 -1
- package/dist/{test-utils → cjs/test-utils}/mocks.js +8 -4
- package/dist/cjs/test-utils/msw-setup.js +27 -0
- package/dist/{test-utils → cjs/test-utils}/test-defaults.js +41 -52
- package/dist/cjs/todoist-api.js +1235 -0
- package/dist/{types → cjs/types}/entities.js +19 -30
- package/dist/cjs/types/errors.js +22 -0
- package/dist/cjs/types/http.js +22 -0
- package/dist/cjs/utils/case-conversion.js +69 -0
- package/dist/{utils → cjs/utils}/colors.js +3 -3
- package/dist/cjs/utils/fetch-with-retry.js +150 -0
- package/dist/cjs/utils/multipart-upload.js +126 -0
- package/dist/{utils → cjs/utils}/processing-helpers.js +3 -3
- package/dist/{utils → cjs/utils}/sanitization.js +17 -28
- package/dist/{utils → cjs/utils}/url-helpers.js +15 -15
- package/dist/{utils → cjs/utils}/validators.js +2 -2
- package/dist/esm/authentication.js +151 -0
- package/dist/esm/consts/endpoints.js +65 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/rest-client.js +119 -0
- package/dist/esm/test-utils/asserts.js +8 -0
- package/dist/esm/test-utils/mocks.js +10 -0
- package/dist/esm/test-utils/msw-setup.js +22 -0
- package/dist/esm/test-utils/test-defaults.js +198 -0
- package/dist/esm/todoist-api.js +1231 -0
- package/dist/esm/types/entities.js +366 -0
- package/dist/esm/types/errors.js +18 -0
- package/dist/esm/types/http.js +18 -0
- package/dist/esm/types/index.js +3 -0
- package/dist/esm/types/requests.js +1 -0
- package/dist/esm/types/sync.js +1 -0
- package/dist/esm/utils/activity-helpers.js +36 -0
- package/dist/esm/utils/case-conversion.js +61 -0
- package/dist/esm/utils/colors.js +215 -0
- package/dist/esm/utils/fetch-with-retry.js +147 -0
- package/dist/esm/utils/index.js +3 -0
- package/dist/esm/utils/multipart-upload.js +120 -0
- package/dist/esm/utils/processing-helpers.js +12 -0
- package/dist/esm/utils/sanitization.js +112 -0
- package/dist/esm/utils/url-helpers.js +68 -0
- package/dist/esm/utils/validators.js +97 -0
- package/dist/{authentication.d.ts → types/authentication.d.ts} +6 -1
- package/dist/types/index.d.ts +4 -3
- package/dist/{rest-client.d.ts → types/rest-client.d.ts} +3 -4
- package/dist/types/test-utils/msw-setup.d.ts +3 -0
- package/dist/types/types/http.d.ts +68 -0
- package/dist/types/types/index.d.ts +3 -0
- package/dist/types/utils/case-conversion.d.ts +12 -0
- package/dist/types/utils/fetch-with-retry.d.ts +11 -0
- package/package.json +24 -8
- package/dist/authentication.js +0 -220
- package/dist/index.d.ts +0 -4
- package/dist/rest-client.js +0 -178
- package/dist/todoist-api.js +0 -1845
- package/dist/types/errors.js +0 -39
- package/dist/types/http.d.ts +0 -1
- package/dist/types/http.js +0 -2
- package/dist/utils/multipart-upload.js +0 -171
- /package/dist/{index.js → cjs/index.js} +0 -0
- /package/dist/{types → cjs/types}/index.js +0 -0
- /package/dist/{types → cjs/types}/requests.js +0 -0
- /package/dist/{types → cjs/types}/sync.js +0 -0
- /package/dist/{utils → cjs/utils}/activity-helpers.js +0 -0
- /package/dist/{utils → cjs/utils}/index.js +0 -0
- /package/dist/{consts → types/consts}/endpoints.d.ts +0 -0
- /package/dist/{test-utils → types/test-utils}/asserts.d.ts +0 -0
- /package/dist/{test-utils → types/test-utils}/mocks.d.ts +0 -0
- /package/dist/{test-utils → types/test-utils}/test-defaults.d.ts +0 -0
- /package/dist/{todoist-api.d.ts → types/todoist-api.d.ts} +0 -0
- /package/dist/types/{entities.d.ts → types/entities.d.ts} +0 -0
- /package/dist/types/{errors.d.ts → types/errors.d.ts} +0 -0
- /package/dist/types/{requests.d.ts → types/requests.d.ts} +0 -0
- /package/dist/types/{sync.d.ts → types/sync.d.ts} +0 -0
- /package/dist/{utils → types/utils}/activity-helpers.d.ts +0 -0
- /package/dist/{utils → types/utils}/colors.d.ts +0 -0
- /package/dist/{utils → types/utils}/index.d.ts +0 -0
- /package/dist/{utils → types/utils}/multipart-upload.d.ts +0 -0
- /package/dist/{utils → types/utils}/processing-helpers.d.ts +0 -0
- /package/dist/{utils → types/utils}/sanitization.d.ts +0 -0
- /package/dist/{utils → types/utils}/url-helpers.d.ts +0 -0
- /package/dist/{utils → types/utils}/validators.d.ts +0 -0
|
@@ -0,0 +1,1231 @@
|
|
|
1
|
+
import { request, isSuccess } from './rest-client.js';
|
|
2
|
+
import { getSyncBaseUri, ENDPOINT_REST_TASKS, ENDPOINT_REST_TASKS_FILTER, ENDPOINT_REST_TASKS_COMPLETED_BY_COMPLETION_DATE, ENDPOINT_REST_TASKS_COMPLETED_BY_DUE_DATE, ENDPOINT_REST_TASKS_COMPLETED_SEARCH, ENDPOINT_REST_PROJECTS, ENDPOINT_SYNC_QUICK_ADD, ENDPOINT_REST_TASK_CLOSE, ENDPOINT_REST_TASK_REOPEN, ENDPOINT_REST_TASK_MOVE, ENDPOINT_REST_LABELS, ENDPOINT_REST_PROJECT_COLLABORATORS, ENDPOINT_REST_SECTIONS, ENDPOINT_REST_COMMENTS, ENDPOINT_REST_LABELS_SHARED, ENDPOINT_REST_LABELS_SHARED_RENAME, ENDPOINT_REST_LABELS_SHARED_REMOVE, ENDPOINT_SYNC, PROJECT_ARCHIVE, PROJECT_UNARCHIVE, ENDPOINT_REST_PROJECTS_ARCHIVED, ENDPOINT_REST_USER, ENDPOINT_REST_PRODUCTIVITY, ENDPOINT_REST_ACTIVITIES, ENDPOINT_REST_UPLOADS, ENDPOINT_WORKSPACE_INVITATIONS, ENDPOINT_WORKSPACE_INVITATIONS_ALL, ENDPOINT_WORKSPACE_INVITATIONS_DELETE, getWorkspaceInvitationAcceptEndpoint, getWorkspaceInvitationRejectEndpoint, ENDPOINT_WORKSPACE_JOIN, ENDPOINT_WORKSPACE_LOGO, ENDPOINT_WORKSPACE_PLAN_DETAILS, ENDPOINT_WORKSPACE_USERS, getWorkspaceActiveProjectsEndpoint, getWorkspaceArchivedProjectsEndpoint, } from './consts/endpoints.js';
|
|
3
|
+
import { validateAttachment, validateComment, validateCommentArray, validateCurrentUser, validateLabel, validateLabelArray, validateProject, validateProjectArray, validateSection, validateSectionArray, validateTask, validateTaskArray, validateUserArray, validateProductivityStats, validateActivityEventArray, validateWorkspaceUserArray, validateWorkspaceInvitation, validateWorkspaceInvitationArray, validateWorkspacePlanDetails, validateJoinWorkspaceResult, } from './utils/validators.js';
|
|
4
|
+
import { formatDateToYYYYMMDD } from './utils/url-helpers.js';
|
|
5
|
+
import { uploadMultipartFile } from './utils/multipart-upload.js';
|
|
6
|
+
import { normalizeObjectTypeForApi, denormalizeObjectTypeFromApi } from './utils/activity-helpers.js';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
9
|
+
import { TodoistRequestError } from './types/index.js';
|
|
10
|
+
const MAX_COMMAND_COUNT = 100;
|
|
11
|
+
/**
|
|
12
|
+
* Joins path segments using `/` separator.
|
|
13
|
+
* @param segments A list of **valid** path segments.
|
|
14
|
+
* @returns A joined path.
|
|
15
|
+
*/
|
|
16
|
+
function generatePath(...segments) {
|
|
17
|
+
return segments.join('/');
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A client for interacting with the Todoist API v1.
|
|
21
|
+
* This class provides methods to manage tasks, projects, sections, labels, and comments in Todoist.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* const api = new TodoistApi('your-api-token');
|
|
26
|
+
*
|
|
27
|
+
* // Get all tasks
|
|
28
|
+
* const tasks = await api.getTasks();
|
|
29
|
+
*
|
|
30
|
+
* // Create a new task
|
|
31
|
+
* const newTask = await api.addTask({
|
|
32
|
+
* content: 'My new task',
|
|
33
|
+
* projectId: '12345'
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* For more information about the Todoist API v1, see the [official documentation](https://todoist.com/api/v1).
|
|
38
|
+
* If you're migrating from v9, please refer to the [migration guide](https://todoist.com/api/v1/docs#tag/Migrating-from-v9).
|
|
39
|
+
*/
|
|
40
|
+
export class TodoistApi {
|
|
41
|
+
constructor(
|
|
42
|
+
/**
|
|
43
|
+
* Your Todoist API token.
|
|
44
|
+
*/
|
|
45
|
+
authToken,
|
|
46
|
+
/**
|
|
47
|
+
* Optional custom API base URL. If not provided, defaults to Todoist's standard API endpoint
|
|
48
|
+
*/
|
|
49
|
+
baseUrl) {
|
|
50
|
+
this.authToken = authToken;
|
|
51
|
+
this.syncApiBase = getSyncBaseUri(baseUrl);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Retrieves information about the authenticated user.
|
|
55
|
+
*
|
|
56
|
+
* @returns A promise that resolves to the current user's information.
|
|
57
|
+
*/
|
|
58
|
+
async getUser() {
|
|
59
|
+
const response = await request({
|
|
60
|
+
httpMethod: 'GET',
|
|
61
|
+
baseUri: this.syncApiBase,
|
|
62
|
+
relativePath: ENDPOINT_REST_USER,
|
|
63
|
+
apiToken: this.authToken,
|
|
64
|
+
});
|
|
65
|
+
return validateCurrentUser(response.data);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Retrieves a single active (non-completed) task by its ID.
|
|
69
|
+
*
|
|
70
|
+
* @param id - The unique identifier of the task.
|
|
71
|
+
* @returns A promise that resolves to the requested task.
|
|
72
|
+
*/
|
|
73
|
+
async getTask(id) {
|
|
74
|
+
z.string().parse(id);
|
|
75
|
+
const response = await request({
|
|
76
|
+
httpMethod: 'GET',
|
|
77
|
+
baseUri: this.syncApiBase,
|
|
78
|
+
relativePath: generatePath(ENDPOINT_REST_TASKS, id),
|
|
79
|
+
apiToken: this.authToken,
|
|
80
|
+
});
|
|
81
|
+
return validateTask(response.data);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Retrieves a list of active tasks filtered by specific parameters.
|
|
85
|
+
*
|
|
86
|
+
* @param args - Filter parameters such as project ID, label ID, or due date.
|
|
87
|
+
* @returns A promise that resolves to an array of tasks.
|
|
88
|
+
*/
|
|
89
|
+
async getTasks(args = {}) {
|
|
90
|
+
const { data: { results, nextCursor }, } = await request({
|
|
91
|
+
httpMethod: 'GET',
|
|
92
|
+
baseUri: this.syncApiBase,
|
|
93
|
+
relativePath: ENDPOINT_REST_TASKS,
|
|
94
|
+
apiToken: this.authToken,
|
|
95
|
+
payload: args,
|
|
96
|
+
});
|
|
97
|
+
return {
|
|
98
|
+
results: validateTaskArray(results),
|
|
99
|
+
nextCursor,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Retrieves tasks filtered by a filter string.
|
|
104
|
+
*
|
|
105
|
+
* @param args - Parameters for filtering tasks, including the query string and optional language.
|
|
106
|
+
* @returns A promise that resolves to a paginated response of tasks.
|
|
107
|
+
*/
|
|
108
|
+
async getTasksByFilter(args) {
|
|
109
|
+
const { data: { results, nextCursor }, } = await request({
|
|
110
|
+
httpMethod: 'GET',
|
|
111
|
+
baseUri: this.syncApiBase,
|
|
112
|
+
relativePath: ENDPOINT_REST_TASKS_FILTER,
|
|
113
|
+
apiToken: this.authToken,
|
|
114
|
+
payload: args,
|
|
115
|
+
});
|
|
116
|
+
return {
|
|
117
|
+
results: validateTaskArray(results),
|
|
118
|
+
nextCursor,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Retrieves completed tasks by completion date.
|
|
123
|
+
*
|
|
124
|
+
* @param args - Parameters for filtering, including required since, until.
|
|
125
|
+
* @returns A promise that resolves to a paginated response of completed tasks.
|
|
126
|
+
*/
|
|
127
|
+
async getCompletedTasksByCompletionDate(args) {
|
|
128
|
+
const { data: { items, nextCursor }, } = await request({
|
|
129
|
+
httpMethod: 'GET',
|
|
130
|
+
baseUri: this.syncApiBase,
|
|
131
|
+
relativePath: ENDPOINT_REST_TASKS_COMPLETED_BY_COMPLETION_DATE,
|
|
132
|
+
apiToken: this.authToken,
|
|
133
|
+
payload: args,
|
|
134
|
+
});
|
|
135
|
+
return {
|
|
136
|
+
items: validateTaskArray(items),
|
|
137
|
+
nextCursor,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Retrieves completed tasks by due date.
|
|
142
|
+
*
|
|
143
|
+
* @param args - Parameters for filtering, including required since, until.
|
|
144
|
+
* @returns A promise that resolves to a paginated response of completed tasks.
|
|
145
|
+
*/
|
|
146
|
+
async getCompletedTasksByDueDate(args) {
|
|
147
|
+
const { data: { items, nextCursor }, } = await request({
|
|
148
|
+
httpMethod: 'GET',
|
|
149
|
+
baseUri: this.syncApiBase,
|
|
150
|
+
relativePath: ENDPOINT_REST_TASKS_COMPLETED_BY_DUE_DATE,
|
|
151
|
+
apiToken: this.authToken,
|
|
152
|
+
payload: args,
|
|
153
|
+
});
|
|
154
|
+
return {
|
|
155
|
+
items: validateTaskArray(items),
|
|
156
|
+
nextCursor,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Searches completed tasks by query string.
|
|
161
|
+
*
|
|
162
|
+
* @param args - Parameters for searching, including the query string.
|
|
163
|
+
* @returns A promise that resolves to a paginated response of completed tasks.
|
|
164
|
+
*/
|
|
165
|
+
async searchCompletedTasks(args) {
|
|
166
|
+
const { data: { items, nextCursor }, } = await request({
|
|
167
|
+
httpMethod: 'GET',
|
|
168
|
+
baseUri: this.syncApiBase,
|
|
169
|
+
relativePath: ENDPOINT_REST_TASKS_COMPLETED_SEARCH,
|
|
170
|
+
apiToken: this.authToken,
|
|
171
|
+
payload: args,
|
|
172
|
+
});
|
|
173
|
+
return {
|
|
174
|
+
items: validateTaskArray(items),
|
|
175
|
+
nextCursor,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Creates a new task with the provided parameters.
|
|
180
|
+
*
|
|
181
|
+
* @param args - Task creation parameters such as content, due date, or priority.
|
|
182
|
+
* @param requestId - Optional custom identifier for the request.
|
|
183
|
+
* @returns A promise that resolves to the created task.
|
|
184
|
+
*/
|
|
185
|
+
async addTask(args, requestId) {
|
|
186
|
+
const response = await request({
|
|
187
|
+
httpMethod: 'POST',
|
|
188
|
+
baseUri: this.syncApiBase,
|
|
189
|
+
relativePath: ENDPOINT_REST_TASKS,
|
|
190
|
+
apiToken: this.authToken,
|
|
191
|
+
payload: args,
|
|
192
|
+
requestId: requestId,
|
|
193
|
+
});
|
|
194
|
+
return validateTask(response.data);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Quickly adds a task using natural language processing for due dates.
|
|
198
|
+
*
|
|
199
|
+
* @param args - Quick add task parameters, including content and due date.
|
|
200
|
+
* @returns A promise that resolves to the created task.
|
|
201
|
+
*/
|
|
202
|
+
async quickAddTask(args) {
|
|
203
|
+
const response = await request({
|
|
204
|
+
httpMethod: 'POST',
|
|
205
|
+
baseUri: this.syncApiBase,
|
|
206
|
+
relativePath: ENDPOINT_SYNC_QUICK_ADD,
|
|
207
|
+
apiToken: this.authToken,
|
|
208
|
+
payload: args,
|
|
209
|
+
});
|
|
210
|
+
return validateTask(response.data);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Updates an existing task by its ID with the provided parameters.
|
|
214
|
+
*
|
|
215
|
+
* @param id - The unique identifier of the task to update.
|
|
216
|
+
* @param args - Update parameters such as content, priority, or due date.
|
|
217
|
+
* @param requestId - Optional custom identifier for the request.
|
|
218
|
+
* @returns A promise that resolves to the updated task.
|
|
219
|
+
*/
|
|
220
|
+
async updateTask(id, args, requestId) {
|
|
221
|
+
z.string().parse(id);
|
|
222
|
+
const response = await request({
|
|
223
|
+
httpMethod: 'POST',
|
|
224
|
+
baseUri: this.syncApiBase,
|
|
225
|
+
relativePath: generatePath(ENDPOINT_REST_TASKS, id),
|
|
226
|
+
apiToken: this.authToken,
|
|
227
|
+
payload: args,
|
|
228
|
+
requestId: requestId,
|
|
229
|
+
});
|
|
230
|
+
return validateTask(response.data);
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Moves existing tasks by their ID to either a different parent/section/project.
|
|
234
|
+
*
|
|
235
|
+
* @param ids - The unique identifier of the tasks to be moved.
|
|
236
|
+
* @param args - The paramets that should contain only one of projectId, sectionId, or parentId
|
|
237
|
+
* @param requestId - Optional custom identifier for the request.
|
|
238
|
+
* @returns - A promise that resolves to an array of the updated tasks.
|
|
239
|
+
* @deprecated Use `moveTask` for single task operations. This method uses the Sync API and may be removed in a future version.
|
|
240
|
+
*/
|
|
241
|
+
async moveTasks(ids, args, requestId) {
|
|
242
|
+
var _a;
|
|
243
|
+
if (ids.length > MAX_COMMAND_COUNT) {
|
|
244
|
+
throw new TodoistRequestError(`Maximum number of items is ${MAX_COMMAND_COUNT}`, 400);
|
|
245
|
+
}
|
|
246
|
+
const commands = ids.map((id) => ({
|
|
247
|
+
type: 'item_move',
|
|
248
|
+
uuid: uuidv4(),
|
|
249
|
+
args: Object.assign(Object.assign(Object.assign({ id }, (args.projectId && { project_id: args.projectId })), (args.sectionId && { section_id: args.sectionId })), (args.parentId && { parent_id: args.parentId })),
|
|
250
|
+
}));
|
|
251
|
+
const syncRequest = {
|
|
252
|
+
commands,
|
|
253
|
+
resource_types: ['items'],
|
|
254
|
+
};
|
|
255
|
+
const response = await request({
|
|
256
|
+
httpMethod: 'POST',
|
|
257
|
+
baseUri: this.syncApiBase,
|
|
258
|
+
relativePath: ENDPOINT_SYNC,
|
|
259
|
+
apiToken: this.authToken,
|
|
260
|
+
payload: syncRequest,
|
|
261
|
+
requestId: requestId,
|
|
262
|
+
hasSyncCommands: true,
|
|
263
|
+
});
|
|
264
|
+
if (response.data.sync_status) {
|
|
265
|
+
Object.entries(response.data.sync_status).forEach(([_, value]) => {
|
|
266
|
+
if (value === 'ok')
|
|
267
|
+
return;
|
|
268
|
+
throw new TodoistRequestError(value.error, value.http_code, value.error_extra);
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
if (!((_a = response.data.items) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
272
|
+
throw new TodoistRequestError('Tasks not found', 404);
|
|
273
|
+
}
|
|
274
|
+
const syncTasks = response.data.items.filter((task) => ids.includes(task.id));
|
|
275
|
+
if (!syncTasks.length) {
|
|
276
|
+
throw new TodoistRequestError('Tasks not found', 404);
|
|
277
|
+
}
|
|
278
|
+
return validateTaskArray(syncTasks);
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Moves a task by its ID to either a different parent/section/project.
|
|
282
|
+
*
|
|
283
|
+
* @param id - The unique identifier of the task to be moved.
|
|
284
|
+
* @param args - The parameters that should contain exactly one of projectId, sectionId, or parentId
|
|
285
|
+
* @param requestId - Optional custom identifier for the request.
|
|
286
|
+
* @returns A promise that resolves to the updated task.
|
|
287
|
+
*/
|
|
288
|
+
async moveTask(id, args, requestId) {
|
|
289
|
+
z.string().parse(id);
|
|
290
|
+
const response = await request({
|
|
291
|
+
httpMethod: 'POST',
|
|
292
|
+
baseUri: this.syncApiBase,
|
|
293
|
+
relativePath: generatePath(ENDPOINT_REST_TASKS, id, ENDPOINT_REST_TASK_MOVE),
|
|
294
|
+
apiToken: this.authToken,
|
|
295
|
+
payload: Object.assign(Object.assign(Object.assign({}, (args.projectId && { project_id: args.projectId })), (args.sectionId && { section_id: args.sectionId })), (args.parentId && { parent_id: args.parentId })),
|
|
296
|
+
requestId: requestId,
|
|
297
|
+
});
|
|
298
|
+
return validateTask(response.data);
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Closes (completes) a task by its ID.
|
|
302
|
+
*
|
|
303
|
+
* @param id - The unique identifier of the task to close.
|
|
304
|
+
* @param requestId - Optional custom identifier for the request.
|
|
305
|
+
* @returns A promise that resolves to `true` if successful.
|
|
306
|
+
*/
|
|
307
|
+
async closeTask(id, requestId) {
|
|
308
|
+
z.string().parse(id);
|
|
309
|
+
const response = await request({
|
|
310
|
+
httpMethod: 'POST',
|
|
311
|
+
baseUri: this.syncApiBase,
|
|
312
|
+
relativePath: generatePath(ENDPOINT_REST_TASKS, id, ENDPOINT_REST_TASK_CLOSE),
|
|
313
|
+
apiToken: this.authToken,
|
|
314
|
+
requestId: requestId,
|
|
315
|
+
});
|
|
316
|
+
return isSuccess(response);
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Reopens a previously closed (completed) task by its ID.
|
|
320
|
+
*
|
|
321
|
+
* @param id - The unique identifier of the task to reopen.
|
|
322
|
+
* @param requestId - Optional custom identifier for the request.
|
|
323
|
+
* @returns A promise that resolves to `true` if successful.
|
|
324
|
+
*/
|
|
325
|
+
async reopenTask(id, requestId) {
|
|
326
|
+
z.string().parse(id);
|
|
327
|
+
const response = await request({
|
|
328
|
+
httpMethod: 'POST',
|
|
329
|
+
baseUri: this.syncApiBase,
|
|
330
|
+
relativePath: generatePath(ENDPOINT_REST_TASKS, id, ENDPOINT_REST_TASK_REOPEN),
|
|
331
|
+
apiToken: this.authToken,
|
|
332
|
+
requestId: requestId,
|
|
333
|
+
});
|
|
334
|
+
return isSuccess(response);
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Deletes a task by its ID.
|
|
338
|
+
*
|
|
339
|
+
* @param id - The unique identifier of the task to delete.
|
|
340
|
+
* @param requestId - Optional custom identifier for the request.
|
|
341
|
+
* @returns A promise that resolves to `true` if successful.
|
|
342
|
+
*/
|
|
343
|
+
async deleteTask(id, requestId) {
|
|
344
|
+
z.string().parse(id);
|
|
345
|
+
const response = await request({
|
|
346
|
+
httpMethod: 'DELETE',
|
|
347
|
+
baseUri: this.syncApiBase,
|
|
348
|
+
relativePath: generatePath(ENDPOINT_REST_TASKS, id),
|
|
349
|
+
apiToken: this.authToken,
|
|
350
|
+
requestId: requestId,
|
|
351
|
+
});
|
|
352
|
+
return isSuccess(response);
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Retrieves a project by its ID.
|
|
356
|
+
*
|
|
357
|
+
* @param id - The unique identifier of the project.
|
|
358
|
+
* @returns A promise that resolves to the requested project.
|
|
359
|
+
*/
|
|
360
|
+
async getProject(id) {
|
|
361
|
+
z.string().parse(id);
|
|
362
|
+
const response = await request({
|
|
363
|
+
httpMethod: 'GET',
|
|
364
|
+
baseUri: this.syncApiBase,
|
|
365
|
+
relativePath: generatePath(ENDPOINT_REST_PROJECTS, id),
|
|
366
|
+
apiToken: this.authToken,
|
|
367
|
+
});
|
|
368
|
+
return validateProject(response.data);
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Retrieves all projects with optional filters.
|
|
372
|
+
*
|
|
373
|
+
* @param args - Optional filters for retrieving projects.
|
|
374
|
+
* @returns A promise that resolves to an array of projects.
|
|
375
|
+
*/
|
|
376
|
+
async getProjects(args = {}) {
|
|
377
|
+
const { data: { results, nextCursor }, } = await request({
|
|
378
|
+
httpMethod: 'GET',
|
|
379
|
+
baseUri: this.syncApiBase,
|
|
380
|
+
relativePath: ENDPOINT_REST_PROJECTS,
|
|
381
|
+
apiToken: this.authToken,
|
|
382
|
+
payload: args,
|
|
383
|
+
});
|
|
384
|
+
return {
|
|
385
|
+
results: validateProjectArray(results),
|
|
386
|
+
nextCursor,
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Retrieves all archived projects with optional filters.
|
|
391
|
+
*
|
|
392
|
+
* @param args - Optional filters for retrieving archived projects.
|
|
393
|
+
* @returns A promise that resolves to an array of archived projects.
|
|
394
|
+
*/
|
|
395
|
+
async getArchivedProjects(args = {}) {
|
|
396
|
+
const { data: { results, nextCursor }, } = await request({
|
|
397
|
+
httpMethod: 'GET',
|
|
398
|
+
baseUri: this.syncApiBase,
|
|
399
|
+
relativePath: ENDPOINT_REST_PROJECTS_ARCHIVED,
|
|
400
|
+
apiToken: this.authToken,
|
|
401
|
+
payload: args,
|
|
402
|
+
});
|
|
403
|
+
return {
|
|
404
|
+
results: validateProjectArray(results),
|
|
405
|
+
nextCursor,
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Creates a new project with the provided parameters.
|
|
410
|
+
*
|
|
411
|
+
* @param args - Project creation parameters such as name or color.
|
|
412
|
+
* @param requestId - Optional custom identifier for the request.
|
|
413
|
+
* @returns A promise that resolves to the created project.
|
|
414
|
+
*/
|
|
415
|
+
async addProject(args, requestId) {
|
|
416
|
+
const response = await request({
|
|
417
|
+
httpMethod: 'POST',
|
|
418
|
+
baseUri: this.syncApiBase,
|
|
419
|
+
relativePath: ENDPOINT_REST_PROJECTS,
|
|
420
|
+
apiToken: this.authToken,
|
|
421
|
+
payload: args,
|
|
422
|
+
requestId: requestId,
|
|
423
|
+
});
|
|
424
|
+
return validateProject(response.data);
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Updates an existing project by its ID with the provided parameters.
|
|
428
|
+
*
|
|
429
|
+
* @param id - The unique identifier of the project to update.
|
|
430
|
+
* @param args - Update parameters such as name or color.
|
|
431
|
+
* @param requestId - Optional custom identifier for the request.
|
|
432
|
+
* @returns A promise that resolves to the updated project.
|
|
433
|
+
*/
|
|
434
|
+
async updateProject(id, args, requestId) {
|
|
435
|
+
z.string().parse(id);
|
|
436
|
+
const response = await request({
|
|
437
|
+
httpMethod: 'POST',
|
|
438
|
+
baseUri: this.syncApiBase,
|
|
439
|
+
relativePath: generatePath(ENDPOINT_REST_PROJECTS, id),
|
|
440
|
+
apiToken: this.authToken,
|
|
441
|
+
payload: args,
|
|
442
|
+
requestId: requestId,
|
|
443
|
+
});
|
|
444
|
+
return validateProject(response.data);
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Deletes a project by its ID.
|
|
448
|
+
*
|
|
449
|
+
* @param id - The unique identifier of the project to delete.
|
|
450
|
+
* @param requestId - Optional custom identifier for the request.
|
|
451
|
+
* @returns A promise that resolves to `true` if successful.
|
|
452
|
+
*/
|
|
453
|
+
async deleteProject(id, requestId) {
|
|
454
|
+
z.string().parse(id);
|
|
455
|
+
const response = await request({
|
|
456
|
+
httpMethod: 'DELETE',
|
|
457
|
+
baseUri: this.syncApiBase,
|
|
458
|
+
relativePath: generatePath(ENDPOINT_REST_PROJECTS, id),
|
|
459
|
+
apiToken: this.authToken,
|
|
460
|
+
requestId: requestId,
|
|
461
|
+
});
|
|
462
|
+
return isSuccess(response);
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Archives a project by its ID.
|
|
466
|
+
*
|
|
467
|
+
* @param id - The unique identifier of the project to archive.
|
|
468
|
+
* @param requestId - Optional custom identifier for the request.
|
|
469
|
+
* @returns A promise that resolves to the updated project.
|
|
470
|
+
*/
|
|
471
|
+
async archiveProject(id, requestId) {
|
|
472
|
+
z.string().parse(id);
|
|
473
|
+
const response = await request({
|
|
474
|
+
httpMethod: 'POST',
|
|
475
|
+
baseUri: this.syncApiBase,
|
|
476
|
+
relativePath: generatePath(ENDPOINT_REST_PROJECTS, id, PROJECT_ARCHIVE),
|
|
477
|
+
apiToken: this.authToken,
|
|
478
|
+
requestId: requestId,
|
|
479
|
+
});
|
|
480
|
+
return validateProject(response.data);
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Unarchives a project by its ID.
|
|
484
|
+
*
|
|
485
|
+
* @param id - The unique identifier of the project to unarchive.
|
|
486
|
+
* @param requestId - Optional custom identifier for the request.
|
|
487
|
+
* @returns A promise that resolves to the updated project.
|
|
488
|
+
*/
|
|
489
|
+
async unarchiveProject(id, requestId) {
|
|
490
|
+
z.string().parse(id);
|
|
491
|
+
const response = await request({
|
|
492
|
+
httpMethod: 'POST',
|
|
493
|
+
baseUri: this.syncApiBase,
|
|
494
|
+
relativePath: generatePath(ENDPOINT_REST_PROJECTS, id, PROJECT_UNARCHIVE),
|
|
495
|
+
apiToken: this.authToken,
|
|
496
|
+
requestId: requestId,
|
|
497
|
+
});
|
|
498
|
+
return validateProject(response.data);
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Retrieves a list of collaborators for a specific project.
|
|
502
|
+
*
|
|
503
|
+
* @param projectId - The unique identifier of the project.
|
|
504
|
+
* @param args - Optional parameters to filter collaborators.
|
|
505
|
+
* @returns A promise that resolves to an array of collaborators for the project.
|
|
506
|
+
*/
|
|
507
|
+
async getProjectCollaborators(projectId, args = {}) {
|
|
508
|
+
z.string().parse(projectId);
|
|
509
|
+
const { data: { results, nextCursor }, } = await request({
|
|
510
|
+
httpMethod: 'GET',
|
|
511
|
+
baseUri: this.syncApiBase,
|
|
512
|
+
relativePath: generatePath(ENDPOINT_REST_PROJECTS, projectId, ENDPOINT_REST_PROJECT_COLLABORATORS),
|
|
513
|
+
apiToken: this.authToken,
|
|
514
|
+
payload: args,
|
|
515
|
+
});
|
|
516
|
+
return {
|
|
517
|
+
results: validateUserArray(results),
|
|
518
|
+
nextCursor,
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Retrieves all sections within a specific project or matching criteria.
|
|
523
|
+
*
|
|
524
|
+
* @param args - Filter parameters such as project ID.
|
|
525
|
+
* @returns A promise that resolves to an array of sections.
|
|
526
|
+
*/
|
|
527
|
+
async getSections(args) {
|
|
528
|
+
const { data: { results, nextCursor }, } = await request({
|
|
529
|
+
httpMethod: 'GET',
|
|
530
|
+
baseUri: this.syncApiBase,
|
|
531
|
+
relativePath: ENDPOINT_REST_SECTIONS,
|
|
532
|
+
apiToken: this.authToken,
|
|
533
|
+
payload: args,
|
|
534
|
+
});
|
|
535
|
+
return {
|
|
536
|
+
results: validateSectionArray(results),
|
|
537
|
+
nextCursor,
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Retrieves a single section by its ID.
|
|
542
|
+
*
|
|
543
|
+
* @param id - The unique identifier of the section.
|
|
544
|
+
* @returns A promise that resolves to the requested section.
|
|
545
|
+
*/
|
|
546
|
+
async getSection(id) {
|
|
547
|
+
z.string().parse(id);
|
|
548
|
+
const response = await request({
|
|
549
|
+
httpMethod: 'GET',
|
|
550
|
+
baseUri: this.syncApiBase,
|
|
551
|
+
relativePath: generatePath(ENDPOINT_REST_SECTIONS, id),
|
|
552
|
+
apiToken: this.authToken,
|
|
553
|
+
});
|
|
554
|
+
return validateSection(response.data);
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Creates a new section within a project.
|
|
558
|
+
*
|
|
559
|
+
* @param args - Section creation parameters such as name or project ID.
|
|
560
|
+
* @param requestId - Optional custom identifier for the request.
|
|
561
|
+
* @returns A promise that resolves to the created section.
|
|
562
|
+
*/
|
|
563
|
+
async addSection(args, requestId) {
|
|
564
|
+
const response = await request({
|
|
565
|
+
httpMethod: 'POST',
|
|
566
|
+
baseUri: this.syncApiBase,
|
|
567
|
+
relativePath: ENDPOINT_REST_SECTIONS,
|
|
568
|
+
apiToken: this.authToken,
|
|
569
|
+
payload: args,
|
|
570
|
+
requestId: requestId,
|
|
571
|
+
});
|
|
572
|
+
return validateSection(response.data);
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Updates a section by its ID with the provided parameters.
|
|
576
|
+
*
|
|
577
|
+
* @param id - The unique identifier of the section to update.
|
|
578
|
+
* @param args - Update parameters such as name or project ID.
|
|
579
|
+
* @param requestId - Optional custom identifier for the request.
|
|
580
|
+
* @returns A promise that resolves to the updated section.
|
|
581
|
+
*/
|
|
582
|
+
async updateSection(id, args, requestId) {
|
|
583
|
+
z.string().parse(id);
|
|
584
|
+
const response = await request({
|
|
585
|
+
httpMethod: 'POST',
|
|
586
|
+
baseUri: this.syncApiBase,
|
|
587
|
+
relativePath: generatePath(ENDPOINT_REST_SECTIONS, id),
|
|
588
|
+
apiToken: this.authToken,
|
|
589
|
+
payload: args,
|
|
590
|
+
requestId: requestId,
|
|
591
|
+
});
|
|
592
|
+
return validateSection(response.data);
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Deletes a section by its ID.
|
|
596
|
+
*
|
|
597
|
+
* @param id - The unique identifier of the section to delete.
|
|
598
|
+
* @param requestId - Optional custom identifier for the request.
|
|
599
|
+
* @returns A promise that resolves to `true` if successful.
|
|
600
|
+
*/
|
|
601
|
+
async deleteSection(id, requestId) {
|
|
602
|
+
z.string().parse(id);
|
|
603
|
+
const response = await request({
|
|
604
|
+
httpMethod: 'DELETE',
|
|
605
|
+
baseUri: this.syncApiBase,
|
|
606
|
+
relativePath: generatePath(ENDPOINT_REST_SECTIONS, id),
|
|
607
|
+
apiToken: this.authToken,
|
|
608
|
+
requestId: requestId,
|
|
609
|
+
});
|
|
610
|
+
return isSuccess(response);
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Retrieves a label by its ID.
|
|
614
|
+
*
|
|
615
|
+
* @param id - The unique identifier of the label.
|
|
616
|
+
* @returns A promise that resolves to the requested label.
|
|
617
|
+
*/
|
|
618
|
+
async getLabel(id) {
|
|
619
|
+
z.string().parse(id);
|
|
620
|
+
const response = await request({
|
|
621
|
+
httpMethod: 'GET',
|
|
622
|
+
baseUri: this.syncApiBase,
|
|
623
|
+
relativePath: generatePath(ENDPOINT_REST_LABELS, id),
|
|
624
|
+
apiToken: this.authToken,
|
|
625
|
+
});
|
|
626
|
+
return validateLabel(response.data);
|
|
627
|
+
}
|
|
628
|
+
/**
|
|
629
|
+
* Retrieves all labels.
|
|
630
|
+
*
|
|
631
|
+
* @param args - Optional filter parameters.
|
|
632
|
+
* @returns A promise that resolves to an array of labels.
|
|
633
|
+
*/
|
|
634
|
+
async getLabels(args = {}) {
|
|
635
|
+
const { data: { results, nextCursor: nextCursor }, } = await request({
|
|
636
|
+
httpMethod: 'GET',
|
|
637
|
+
baseUri: this.syncApiBase,
|
|
638
|
+
relativePath: ENDPOINT_REST_LABELS,
|
|
639
|
+
apiToken: this.authToken,
|
|
640
|
+
payload: args,
|
|
641
|
+
});
|
|
642
|
+
return {
|
|
643
|
+
results: validateLabelArray(results),
|
|
644
|
+
nextCursor,
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Adds a new label.
|
|
649
|
+
*
|
|
650
|
+
* @param args - Label creation parameters such as name.
|
|
651
|
+
* @param requestId - Optional custom identifier for the request.
|
|
652
|
+
* @returns A promise that resolves to the created label.
|
|
653
|
+
*/
|
|
654
|
+
async addLabel(args, requestId) {
|
|
655
|
+
const response = await request({
|
|
656
|
+
httpMethod: 'POST',
|
|
657
|
+
baseUri: this.syncApiBase,
|
|
658
|
+
relativePath: ENDPOINT_REST_LABELS,
|
|
659
|
+
apiToken: this.authToken,
|
|
660
|
+
payload: args,
|
|
661
|
+
requestId: requestId,
|
|
662
|
+
});
|
|
663
|
+
return validateLabel(response.data);
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Updates an existing label by its ID.
|
|
667
|
+
*
|
|
668
|
+
* @param id - The unique identifier of the label to update.
|
|
669
|
+
* @param args - Update parameters such as name or color.
|
|
670
|
+
* @param requestId - Optional custom identifier for the request.
|
|
671
|
+
* @returns A promise that resolves to the updated label.
|
|
672
|
+
*/
|
|
673
|
+
async updateLabel(id, args, requestId) {
|
|
674
|
+
z.string().parse(id);
|
|
675
|
+
const response = await request({
|
|
676
|
+
httpMethod: 'POST',
|
|
677
|
+
baseUri: this.syncApiBase,
|
|
678
|
+
relativePath: generatePath(ENDPOINT_REST_LABELS, id),
|
|
679
|
+
apiToken: this.authToken,
|
|
680
|
+
payload: args,
|
|
681
|
+
requestId: requestId,
|
|
682
|
+
});
|
|
683
|
+
return validateLabel(response.data);
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Deletes a label by its ID.
|
|
687
|
+
*
|
|
688
|
+
* @param id - The unique identifier of the label to delete.
|
|
689
|
+
* @param requestId - Optional custom identifier for the request.
|
|
690
|
+
* @returns A promise that resolves to `true` if successful.
|
|
691
|
+
*/
|
|
692
|
+
async deleteLabel(id, requestId) {
|
|
693
|
+
z.string().parse(id);
|
|
694
|
+
const response = await request({
|
|
695
|
+
httpMethod: 'DELETE',
|
|
696
|
+
baseUri: this.syncApiBase,
|
|
697
|
+
relativePath: generatePath(ENDPOINT_REST_LABELS, id),
|
|
698
|
+
apiToken: this.authToken,
|
|
699
|
+
requestId: requestId,
|
|
700
|
+
});
|
|
701
|
+
return isSuccess(response);
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Retrieves a list of shared labels.
|
|
705
|
+
*
|
|
706
|
+
* @param args - Optional parameters to filter shared labels.
|
|
707
|
+
* @returns A promise that resolves to an array of shared labels.
|
|
708
|
+
*/
|
|
709
|
+
async getSharedLabels(args) {
|
|
710
|
+
const { data: { results, nextCursor: nextCursor }, } = await request({
|
|
711
|
+
httpMethod: 'GET',
|
|
712
|
+
baseUri: this.syncApiBase,
|
|
713
|
+
relativePath: ENDPOINT_REST_LABELS_SHARED,
|
|
714
|
+
apiToken: this.authToken,
|
|
715
|
+
payload: args,
|
|
716
|
+
});
|
|
717
|
+
return { results, nextCursor };
|
|
718
|
+
}
|
|
719
|
+
/**
|
|
720
|
+
* Renames an existing shared label.
|
|
721
|
+
*
|
|
722
|
+
* @param args - Parameters for renaming the shared label, including the current and new name.
|
|
723
|
+
* @returns A promise that resolves to `true` if successful.
|
|
724
|
+
*/
|
|
725
|
+
async renameSharedLabel(args) {
|
|
726
|
+
const response = await request({
|
|
727
|
+
httpMethod: 'POST',
|
|
728
|
+
baseUri: this.syncApiBase,
|
|
729
|
+
relativePath: ENDPOINT_REST_LABELS_SHARED_RENAME,
|
|
730
|
+
apiToken: this.authToken,
|
|
731
|
+
payload: args,
|
|
732
|
+
});
|
|
733
|
+
return isSuccess(response);
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Removes a shared label.
|
|
737
|
+
*
|
|
738
|
+
* @param args - Parameters for removing the shared label.
|
|
739
|
+
* @returns A promise that resolves to `true` if successful.
|
|
740
|
+
*/
|
|
741
|
+
async removeSharedLabel(args) {
|
|
742
|
+
const response = await request({
|
|
743
|
+
httpMethod: 'POST',
|
|
744
|
+
baseUri: this.syncApiBase,
|
|
745
|
+
relativePath: ENDPOINT_REST_LABELS_SHARED_REMOVE,
|
|
746
|
+
apiToken: this.authToken,
|
|
747
|
+
payload: args,
|
|
748
|
+
});
|
|
749
|
+
return isSuccess(response);
|
|
750
|
+
}
|
|
751
|
+
/**
|
|
752
|
+
* Retrieves all comments associated with a task or project.
|
|
753
|
+
*
|
|
754
|
+
* @param args - Parameters for retrieving comments, such as task ID or project ID.
|
|
755
|
+
* @returns A promise that resolves to an array of comments.
|
|
756
|
+
*/
|
|
757
|
+
async getComments(args) {
|
|
758
|
+
const { data: { results, nextCursor }, } = await request({
|
|
759
|
+
httpMethod: 'GET',
|
|
760
|
+
baseUri: this.syncApiBase,
|
|
761
|
+
relativePath: ENDPOINT_REST_COMMENTS,
|
|
762
|
+
apiToken: this.authToken,
|
|
763
|
+
payload: args,
|
|
764
|
+
});
|
|
765
|
+
return {
|
|
766
|
+
results: validateCommentArray(results),
|
|
767
|
+
nextCursor,
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Retrieves a specific comment by its ID.
|
|
772
|
+
*
|
|
773
|
+
* @param id - The unique identifier of the comment to retrieve.
|
|
774
|
+
* @returns A promise that resolves to the requested comment.
|
|
775
|
+
*/
|
|
776
|
+
async getComment(id) {
|
|
777
|
+
z.string().parse(id);
|
|
778
|
+
const response = await request({
|
|
779
|
+
httpMethod: 'GET',
|
|
780
|
+
baseUri: this.syncApiBase,
|
|
781
|
+
relativePath: generatePath(ENDPOINT_REST_COMMENTS, id),
|
|
782
|
+
apiToken: this.authToken,
|
|
783
|
+
});
|
|
784
|
+
return validateComment(response.data);
|
|
785
|
+
}
|
|
786
|
+
/**
|
|
787
|
+
* Adds a comment to a task or project.
|
|
788
|
+
*
|
|
789
|
+
* @param args - Parameters for creating the comment, such as content and the target task or project ID.
|
|
790
|
+
* @param requestId - Optional custom identifier for the request.
|
|
791
|
+
* @returns A promise that resolves to the created comment.
|
|
792
|
+
*/
|
|
793
|
+
async addComment(args, requestId) {
|
|
794
|
+
const response = await request({
|
|
795
|
+
httpMethod: 'POST',
|
|
796
|
+
baseUri: this.syncApiBase,
|
|
797
|
+
relativePath: ENDPOINT_REST_COMMENTS,
|
|
798
|
+
apiToken: this.authToken,
|
|
799
|
+
payload: args,
|
|
800
|
+
requestId: requestId,
|
|
801
|
+
});
|
|
802
|
+
return validateComment(response.data);
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* Updates an existing comment by its ID.
|
|
806
|
+
*
|
|
807
|
+
* @param id - The unique identifier of the comment to update.
|
|
808
|
+
* @param args - Update parameters such as new content.
|
|
809
|
+
* @param requestId - Optional custom identifier for the request.
|
|
810
|
+
* @returns A promise that resolves to the updated comment.
|
|
811
|
+
*/
|
|
812
|
+
async updateComment(id, args, requestId) {
|
|
813
|
+
z.string().parse(id);
|
|
814
|
+
const response = await request({
|
|
815
|
+
httpMethod: 'POST',
|
|
816
|
+
baseUri: this.syncApiBase,
|
|
817
|
+
relativePath: generatePath(ENDPOINT_REST_COMMENTS, id),
|
|
818
|
+
apiToken: this.authToken,
|
|
819
|
+
payload: args,
|
|
820
|
+
requestId: requestId,
|
|
821
|
+
});
|
|
822
|
+
return validateComment(response.data);
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* Deletes a comment by its ID.
|
|
826
|
+
*
|
|
827
|
+
* @param id - The unique identifier of the comment to delete.
|
|
828
|
+
* @param requestId - Optional custom identifier for the request.
|
|
829
|
+
* @returns A promise that resolves to `true` if successful.
|
|
830
|
+
*/
|
|
831
|
+
async deleteComment(id, requestId) {
|
|
832
|
+
z.string().parse(id);
|
|
833
|
+
const response = await request({
|
|
834
|
+
httpMethod: 'DELETE',
|
|
835
|
+
baseUri: this.syncApiBase,
|
|
836
|
+
relativePath: generatePath(ENDPOINT_REST_COMMENTS, id),
|
|
837
|
+
apiToken: this.authToken,
|
|
838
|
+
requestId: requestId,
|
|
839
|
+
});
|
|
840
|
+
return isSuccess(response);
|
|
841
|
+
}
|
|
842
|
+
/**
|
|
843
|
+
* Retrieves productivity stats for the authenticated user.
|
|
844
|
+
*
|
|
845
|
+
* @returns A promise that resolves to the productivity stats.
|
|
846
|
+
*/
|
|
847
|
+
async getProductivityStats() {
|
|
848
|
+
const response = await request({
|
|
849
|
+
httpMethod: 'GET',
|
|
850
|
+
baseUri: this.syncApiBase,
|
|
851
|
+
relativePath: ENDPOINT_REST_PRODUCTIVITY,
|
|
852
|
+
apiToken: this.authToken,
|
|
853
|
+
});
|
|
854
|
+
return validateProductivityStats(response.data);
|
|
855
|
+
}
|
|
856
|
+
/**
|
|
857
|
+
* Retrieves activity logs with optional filters.
|
|
858
|
+
*
|
|
859
|
+
* @param args - Optional filter parameters for activity logs.
|
|
860
|
+
* @returns A promise that resolves to a paginated response of activity events.
|
|
861
|
+
*/
|
|
862
|
+
async getActivityLogs(args = {}) {
|
|
863
|
+
// Convert Date objects to YYYY-MM-DD strings and modern object types to legacy API types
|
|
864
|
+
const processedArgs = Object.assign(Object.assign(Object.assign(Object.assign({}, args), (args.since instanceof Date && { since: formatDateToYYYYMMDD(args.since) })), (args.until instanceof Date && { until: formatDateToYYYYMMDD(args.until) })), (args.objectType && { objectType: normalizeObjectTypeForApi(args.objectType) }));
|
|
865
|
+
const { data: { results, nextCursor }, } = await request({
|
|
866
|
+
httpMethod: 'GET',
|
|
867
|
+
baseUri: this.syncApiBase,
|
|
868
|
+
relativePath: ENDPOINT_REST_ACTIVITIES,
|
|
869
|
+
apiToken: this.authToken,
|
|
870
|
+
payload: processedArgs,
|
|
871
|
+
});
|
|
872
|
+
// Convert legacy API object types back to modern SDK types
|
|
873
|
+
const normalizedResults = results.map((event) => {
|
|
874
|
+
const normalizedType = denormalizeObjectTypeFromApi(event.objectType);
|
|
875
|
+
return Object.assign(Object.assign({}, event), { objectType: normalizedType || event.objectType });
|
|
876
|
+
});
|
|
877
|
+
return {
|
|
878
|
+
results: validateActivityEventArray(normalizedResults),
|
|
879
|
+
nextCursor,
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* Uploads a file and returns attachment metadata.
|
|
884
|
+
* This creates an upload record that can be referenced in tasks or comments.
|
|
885
|
+
*
|
|
886
|
+
* @param args - Upload parameters including file content, filename, and optional project ID.
|
|
887
|
+
* @param requestId - Optional custom identifier for the request.
|
|
888
|
+
* @returns A promise that resolves to the uploaded file's attachment metadata.
|
|
889
|
+
*
|
|
890
|
+
* @example
|
|
891
|
+
* ```typescript
|
|
892
|
+
* // Upload from a file path
|
|
893
|
+
* const upload = await api.uploadFile({
|
|
894
|
+
* file: '/path/to/document.pdf',
|
|
895
|
+
* projectId: '12345'
|
|
896
|
+
* })
|
|
897
|
+
*
|
|
898
|
+
* // Upload from a Buffer
|
|
899
|
+
* const buffer = fs.readFileSync('/path/to/document.pdf')
|
|
900
|
+
* const upload = await api.uploadFile({
|
|
901
|
+
* file: buffer,
|
|
902
|
+
* fileName: 'document.pdf', // Required for Buffer/Stream
|
|
903
|
+
* projectId: '12345'
|
|
904
|
+
* })
|
|
905
|
+
*
|
|
906
|
+
* // Use the returned fileUrl in a comment
|
|
907
|
+
* await api.addComment({
|
|
908
|
+
* content: 'See attached document',
|
|
909
|
+
* taskId: '67890',
|
|
910
|
+
* attachment: {
|
|
911
|
+
* fileUrl: upload.fileUrl,
|
|
912
|
+
* fileName: upload.fileName,
|
|
913
|
+
* fileType: upload.fileType,
|
|
914
|
+
* resourceType: upload.resourceType
|
|
915
|
+
* }
|
|
916
|
+
* })
|
|
917
|
+
* ```
|
|
918
|
+
*/
|
|
919
|
+
async uploadFile(args, requestId) {
|
|
920
|
+
const additionalFields = {};
|
|
921
|
+
if (args.projectId) {
|
|
922
|
+
additionalFields.project_id = args.projectId;
|
|
923
|
+
}
|
|
924
|
+
const data = await uploadMultipartFile({
|
|
925
|
+
baseUrl: this.syncApiBase,
|
|
926
|
+
authToken: this.authToken,
|
|
927
|
+
endpoint: ENDPOINT_REST_UPLOADS,
|
|
928
|
+
file: args.file,
|
|
929
|
+
fileName: args.fileName,
|
|
930
|
+
additionalFields: additionalFields,
|
|
931
|
+
requestId: requestId,
|
|
932
|
+
});
|
|
933
|
+
return validateAttachment(data);
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* Deletes an uploaded file by its URL.
|
|
937
|
+
*
|
|
938
|
+
* @param args - The file URL to delete.
|
|
939
|
+
* @param requestId - Optional custom identifier for the request.
|
|
940
|
+
* @returns A promise that resolves to `true` if deletion was successful.
|
|
941
|
+
*
|
|
942
|
+
* @example
|
|
943
|
+
* ```typescript
|
|
944
|
+
* await api.deleteUpload({
|
|
945
|
+
* fileUrl: 'https://cdn.todoist.com/...'
|
|
946
|
+
* })
|
|
947
|
+
* ```
|
|
948
|
+
*/
|
|
949
|
+
async deleteUpload(args, requestId) {
|
|
950
|
+
const response = await request({
|
|
951
|
+
httpMethod: 'DELETE',
|
|
952
|
+
baseUri: this.syncApiBase,
|
|
953
|
+
relativePath: ENDPOINT_REST_UPLOADS,
|
|
954
|
+
apiToken: this.authToken,
|
|
955
|
+
payload: args,
|
|
956
|
+
requestId: requestId,
|
|
957
|
+
});
|
|
958
|
+
return isSuccess(response);
|
|
959
|
+
}
|
|
960
|
+
/* Workspace methods */
|
|
961
|
+
/**
|
|
962
|
+
* Gets pending invitations for a workspace.
|
|
963
|
+
*
|
|
964
|
+
* @param args - Arguments including workspace ID.
|
|
965
|
+
* @param requestId - Optional request ID for idempotency.
|
|
966
|
+
* @returns Array of email addresses with pending invitations.
|
|
967
|
+
*/
|
|
968
|
+
async getWorkspaceInvitations(args, requestId) {
|
|
969
|
+
const response = await request({
|
|
970
|
+
httpMethod: 'GET',
|
|
971
|
+
baseUri: this.syncApiBase,
|
|
972
|
+
relativePath: ENDPOINT_WORKSPACE_INVITATIONS,
|
|
973
|
+
apiToken: this.authToken,
|
|
974
|
+
payload: { workspace_id: args.workspaceId },
|
|
975
|
+
requestId: requestId,
|
|
976
|
+
});
|
|
977
|
+
return response.data;
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Gets all workspace invitations (admin only).
|
|
981
|
+
*
|
|
982
|
+
* @param requestId - Optional request ID for idempotency.
|
|
983
|
+
* @returns Array of email addresses with pending invitations.
|
|
984
|
+
*/
|
|
985
|
+
async getAllWorkspaceInvitations(args = {}, requestId) {
|
|
986
|
+
const queryParams = {};
|
|
987
|
+
if (args.workspaceId) {
|
|
988
|
+
queryParams.workspace_id = args.workspaceId;
|
|
989
|
+
}
|
|
990
|
+
const response = await request({
|
|
991
|
+
httpMethod: 'GET',
|
|
992
|
+
baseUri: this.syncApiBase,
|
|
993
|
+
relativePath: ENDPOINT_WORKSPACE_INVITATIONS_ALL,
|
|
994
|
+
apiToken: this.authToken,
|
|
995
|
+
payload: queryParams,
|
|
996
|
+
requestId: requestId,
|
|
997
|
+
});
|
|
998
|
+
return validateWorkspaceInvitationArray(response.data);
|
|
999
|
+
}
|
|
1000
|
+
/**
|
|
1001
|
+
* Deletes a workspace invitation (admin only).
|
|
1002
|
+
*
|
|
1003
|
+
* @param args - Arguments including workspace ID and user email.
|
|
1004
|
+
* @param requestId - Optional request ID for idempotency.
|
|
1005
|
+
* @returns The deleted invitation.
|
|
1006
|
+
*/
|
|
1007
|
+
async deleteWorkspaceInvitation(args, requestId) {
|
|
1008
|
+
const response = await request({
|
|
1009
|
+
httpMethod: 'POST',
|
|
1010
|
+
baseUri: this.syncApiBase,
|
|
1011
|
+
relativePath: ENDPOINT_WORKSPACE_INVITATIONS_DELETE,
|
|
1012
|
+
apiToken: this.authToken,
|
|
1013
|
+
payload: {
|
|
1014
|
+
workspace_id: args.workspaceId,
|
|
1015
|
+
user_email: args.userEmail,
|
|
1016
|
+
},
|
|
1017
|
+
requestId: requestId,
|
|
1018
|
+
});
|
|
1019
|
+
return validateWorkspaceInvitation(response.data);
|
|
1020
|
+
}
|
|
1021
|
+
/**
|
|
1022
|
+
* Accepts a workspace invitation.
|
|
1023
|
+
*
|
|
1024
|
+
* @param args - Arguments including invite code.
|
|
1025
|
+
* @param requestId - Optional request ID for idempotency.
|
|
1026
|
+
* @returns The accepted invitation.
|
|
1027
|
+
*/
|
|
1028
|
+
async acceptWorkspaceInvitation(args, requestId) {
|
|
1029
|
+
const response = await request({
|
|
1030
|
+
httpMethod: 'PUT',
|
|
1031
|
+
baseUri: this.syncApiBase,
|
|
1032
|
+
relativePath: getWorkspaceInvitationAcceptEndpoint(args.inviteCode),
|
|
1033
|
+
apiToken: this.authToken,
|
|
1034
|
+
requestId: requestId,
|
|
1035
|
+
});
|
|
1036
|
+
return validateWorkspaceInvitation(response.data);
|
|
1037
|
+
}
|
|
1038
|
+
/**
|
|
1039
|
+
* Rejects a workspace invitation.
|
|
1040
|
+
*
|
|
1041
|
+
* @param args - Arguments including invite code.
|
|
1042
|
+
* @param requestId - Optional request ID for idempotency.
|
|
1043
|
+
* @returns The rejected invitation.
|
|
1044
|
+
*/
|
|
1045
|
+
async rejectWorkspaceInvitation(args, requestId) {
|
|
1046
|
+
const response = await request({
|
|
1047
|
+
httpMethod: 'PUT',
|
|
1048
|
+
baseUri: this.syncApiBase,
|
|
1049
|
+
relativePath: getWorkspaceInvitationRejectEndpoint(args.inviteCode),
|
|
1050
|
+
apiToken: this.authToken,
|
|
1051
|
+
requestId: requestId,
|
|
1052
|
+
});
|
|
1053
|
+
return validateWorkspaceInvitation(response.data);
|
|
1054
|
+
}
|
|
1055
|
+
/**
|
|
1056
|
+
* Joins a workspace via invitation link or domain auto-join.
|
|
1057
|
+
*
|
|
1058
|
+
* @param args - Arguments including invite code or workspace ID.
|
|
1059
|
+
* @param requestId - Optional request ID for idempotency.
|
|
1060
|
+
* @returns Workspace user information.
|
|
1061
|
+
*/
|
|
1062
|
+
async joinWorkspace(args, requestId) {
|
|
1063
|
+
const response = await request({
|
|
1064
|
+
httpMethod: 'POST',
|
|
1065
|
+
baseUri: this.syncApiBase,
|
|
1066
|
+
relativePath: ENDPOINT_WORKSPACE_JOIN,
|
|
1067
|
+
apiToken: this.authToken,
|
|
1068
|
+
payload: {
|
|
1069
|
+
invite_code: args.inviteCode,
|
|
1070
|
+
workspace_id: args.workspaceId,
|
|
1071
|
+
},
|
|
1072
|
+
requestId: requestId,
|
|
1073
|
+
});
|
|
1074
|
+
return validateJoinWorkspaceResult(response.data);
|
|
1075
|
+
}
|
|
1076
|
+
/**
|
|
1077
|
+
* Uploads or updates a workspace logo.
|
|
1078
|
+
*
|
|
1079
|
+
* @param args - Arguments including workspace ID, file, and options.
|
|
1080
|
+
* @param requestId - Optional request ID for idempotency.
|
|
1081
|
+
* @returns Logo information or null if deleted.
|
|
1082
|
+
*/
|
|
1083
|
+
async uploadWorkspaceLogo(args, requestId) {
|
|
1084
|
+
if (args.delete) {
|
|
1085
|
+
// Delete logo
|
|
1086
|
+
const data = await uploadMultipartFile({
|
|
1087
|
+
baseUrl: this.syncApiBase,
|
|
1088
|
+
authToken: this.authToken,
|
|
1089
|
+
endpoint: ENDPOINT_WORKSPACE_LOGO,
|
|
1090
|
+
file: Buffer.alloc(0), // Empty buffer for delete
|
|
1091
|
+
fileName: 'delete',
|
|
1092
|
+
additionalFields: {
|
|
1093
|
+
workspace_id: args.workspaceId,
|
|
1094
|
+
delete: true,
|
|
1095
|
+
},
|
|
1096
|
+
requestId: requestId,
|
|
1097
|
+
});
|
|
1098
|
+
return data;
|
|
1099
|
+
}
|
|
1100
|
+
if (!args.file) {
|
|
1101
|
+
throw new Error('file is required when not deleting logo');
|
|
1102
|
+
}
|
|
1103
|
+
// Validate buffer is not empty if it's a Buffer
|
|
1104
|
+
if (Buffer.isBuffer(args.file) && args.file.length === 0) {
|
|
1105
|
+
throw new Error('Cannot upload empty image file');
|
|
1106
|
+
}
|
|
1107
|
+
const additionalFields = {
|
|
1108
|
+
workspace_id: args.workspaceId,
|
|
1109
|
+
};
|
|
1110
|
+
const data = await uploadMultipartFile({
|
|
1111
|
+
baseUrl: this.syncApiBase,
|
|
1112
|
+
authToken: this.authToken,
|
|
1113
|
+
endpoint: ENDPOINT_WORKSPACE_LOGO,
|
|
1114
|
+
file: args.file,
|
|
1115
|
+
fileName: args.fileName,
|
|
1116
|
+
additionalFields: additionalFields,
|
|
1117
|
+
requestId: requestId,
|
|
1118
|
+
});
|
|
1119
|
+
return data;
|
|
1120
|
+
}
|
|
1121
|
+
/**
|
|
1122
|
+
* Gets workspace plan and billing details.
|
|
1123
|
+
*
|
|
1124
|
+
* @param args - Arguments including workspace ID.
|
|
1125
|
+
* @param requestId - Optional request ID for idempotency.
|
|
1126
|
+
* @returns Workspace plan details.
|
|
1127
|
+
*/
|
|
1128
|
+
async getWorkspacePlanDetails(args, requestId) {
|
|
1129
|
+
const response = await request({
|
|
1130
|
+
httpMethod: 'GET',
|
|
1131
|
+
baseUri: this.syncApiBase,
|
|
1132
|
+
relativePath: ENDPOINT_WORKSPACE_PLAN_DETAILS,
|
|
1133
|
+
apiToken: this.authToken,
|
|
1134
|
+
payload: { workspace_id: args.workspaceId },
|
|
1135
|
+
requestId: requestId,
|
|
1136
|
+
});
|
|
1137
|
+
return validateWorkspacePlanDetails(response.data);
|
|
1138
|
+
}
|
|
1139
|
+
/**
|
|
1140
|
+
* Gets workspace users with pagination.
|
|
1141
|
+
*
|
|
1142
|
+
* @param args - Arguments including optional workspace ID, cursor, and limit.
|
|
1143
|
+
* @param requestId - Optional request ID for idempotency.
|
|
1144
|
+
* @returns Paginated list of workspace users.
|
|
1145
|
+
*/
|
|
1146
|
+
async getWorkspaceUsers(args = {}, requestId) {
|
|
1147
|
+
const queryParams = {};
|
|
1148
|
+
if (args.workspaceId !== undefined && args.workspaceId !== null) {
|
|
1149
|
+
queryParams.workspace_id = args.workspaceId;
|
|
1150
|
+
}
|
|
1151
|
+
if (args.cursor) {
|
|
1152
|
+
queryParams.cursor = args.cursor;
|
|
1153
|
+
}
|
|
1154
|
+
if (args.limit) {
|
|
1155
|
+
queryParams.limit = args.limit;
|
|
1156
|
+
}
|
|
1157
|
+
const response = await request({
|
|
1158
|
+
httpMethod: 'GET',
|
|
1159
|
+
baseUri: this.syncApiBase,
|
|
1160
|
+
relativePath: ENDPOINT_WORKSPACE_USERS,
|
|
1161
|
+
apiToken: this.authToken,
|
|
1162
|
+
payload: queryParams,
|
|
1163
|
+
requestId: requestId,
|
|
1164
|
+
});
|
|
1165
|
+
return {
|
|
1166
|
+
hasMore: response.data.has_more || false,
|
|
1167
|
+
nextCursor: response.data.next_cursor,
|
|
1168
|
+
workspaceUsers: validateWorkspaceUserArray(response.data.workspace_users || []),
|
|
1169
|
+
};
|
|
1170
|
+
}
|
|
1171
|
+
/**
|
|
1172
|
+
* Gets active projects in a workspace with pagination.
|
|
1173
|
+
*
|
|
1174
|
+
* @param args - Arguments including workspace ID, cursor, and limit.
|
|
1175
|
+
* @param requestId - Optional request ID for idempotency.
|
|
1176
|
+
* @returns Paginated list of active workspace projects.
|
|
1177
|
+
*/
|
|
1178
|
+
async getWorkspaceActiveProjects(args, requestId) {
|
|
1179
|
+
var _a;
|
|
1180
|
+
const queryParams = {};
|
|
1181
|
+
if (args.cursor) {
|
|
1182
|
+
queryParams.cursor = args.cursor;
|
|
1183
|
+
}
|
|
1184
|
+
if (args.limit) {
|
|
1185
|
+
queryParams.limit = args.limit;
|
|
1186
|
+
}
|
|
1187
|
+
const response = await request({
|
|
1188
|
+
httpMethod: 'GET',
|
|
1189
|
+
baseUri: this.syncApiBase,
|
|
1190
|
+
relativePath: getWorkspaceActiveProjectsEndpoint(args.workspaceId),
|
|
1191
|
+
apiToken: this.authToken,
|
|
1192
|
+
payload: queryParams,
|
|
1193
|
+
requestId: requestId,
|
|
1194
|
+
});
|
|
1195
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
|
1196
|
+
const validatedProjects = (_a = response.data.results) === null || _a === void 0 ? void 0 : _a.map((project) => validateProject(project));
|
|
1197
|
+
return Object.assign(Object.assign({}, response.data), {
|
|
1198
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
1199
|
+
results: validatedProjects || [] });
|
|
1200
|
+
}
|
|
1201
|
+
/**
|
|
1202
|
+
* Gets archived projects in a workspace with pagination.
|
|
1203
|
+
*
|
|
1204
|
+
* @param args - Arguments including workspace ID, cursor, and limit.
|
|
1205
|
+
* @param requestId - Optional request ID for idempotency.
|
|
1206
|
+
* @returns Paginated list of archived workspace projects.
|
|
1207
|
+
*/
|
|
1208
|
+
async getWorkspaceArchivedProjects(args, requestId) {
|
|
1209
|
+
var _a;
|
|
1210
|
+
const queryParams = {};
|
|
1211
|
+
if (args.cursor) {
|
|
1212
|
+
queryParams.cursor = args.cursor;
|
|
1213
|
+
}
|
|
1214
|
+
if (args.limit) {
|
|
1215
|
+
queryParams.limit = args.limit;
|
|
1216
|
+
}
|
|
1217
|
+
const response = await request({
|
|
1218
|
+
httpMethod: 'GET',
|
|
1219
|
+
baseUri: this.syncApiBase,
|
|
1220
|
+
relativePath: getWorkspaceArchivedProjectsEndpoint(args.workspaceId),
|
|
1221
|
+
apiToken: this.authToken,
|
|
1222
|
+
payload: queryParams,
|
|
1223
|
+
requestId: requestId,
|
|
1224
|
+
});
|
|
1225
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
|
1226
|
+
const validatedProjects = (_a = response.data.results) === null || _a === void 0 ? void 0 : _a.map((project) => validateProject(project));
|
|
1227
|
+
return Object.assign(Object.assign({}, response.data), {
|
|
1228
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
1229
|
+
results: validatedProjects || [] });
|
|
1230
|
+
}
|
|
1231
|
+
}
|