@@ -8,6 +8,11 @@ import type { CreateTaskParams, Task, TaskId } from "@/api/domain/task";
8
8
import type { RequestParams , WebFetcher , WebResponse } from "@/api/fetcher" ;
9
9
import debug from "@/log" ;
10
10
11
+ type PaginatedResponse < T > = {
12
+ results : T [ ] ;
13
+ nextCursor : string | null ;
14
+ } ;
15
+
11
16
export class TodoistApiClient {
12
17
private token : string ;
13
18
private fetcher : WebFetcher ;
@@ -18,15 +23,11 @@ export class TodoistApiClient {
18
23
}
19
24
20
25
public async getTasks ( filter ?: string ) : Promise < Task [ ] > {
21
- let path = "/tasks" ;
22
-
23
26
if ( filter !== undefined ) {
24
- path += `?filter=${ encodeURIComponent ( filter ) } ` ;
27
+ return await this . doPaginated < Task > ( "/tasks/filter" , { query : filter } ) ;
28
+ } else {
29
+ return await this . doPaginated < Task > ( "/tasks" ) ;
25
30
}
26
-
27
- const response = await this . do ( path , "GET" ) ;
28
-
29
- return camelize ( JSON . parse ( response . body ) ) as Task [ ] ;
30
31
}
31
32
32
33
public async createTask ( content : string , options ?: CreateTaskParams ) : Promise < void > {
@@ -42,23 +43,43 @@ export class TodoistApiClient {
42
43
}
43
44
44
45
public async getProjects ( ) : Promise < Project [ ] > {
45
- const response = await this . do ( "/projects" , "GET" ) ;
46
- return camelize ( JSON . parse ( response . body ) ) as Project [ ] ;
46
+ return await this . doPaginated < Project > ( "/projects" ) ;
47
47
}
48
48
49
49
public async getSections ( ) : Promise < Section [ ] > {
50
- const response = await this . do ( "/sections" , "GET" ) ;
51
- return camelize ( JSON . parse ( response . body ) ) as Section [ ] ;
50
+ return await this . doPaginated < Section > ( "/sections" ) ;
52
51
}
53
52
54
53
public async getLabels ( ) : Promise < Label [ ] > {
55
- const response = await this . do ( "/labels" , "GET" ) ;
56
- return camelize ( JSON . parse ( response . body ) ) as Label [ ] ;
54
+ return await this . doPaginated < Label > ( "/labels" ) ;
55
+ }
56
+
57
+ private async doPaginated < T > ( path : string , params ?: Record < string , string > ) : Promise < T [ ] > {
58
+ const allResults : T [ ] = [ ] ;
59
+ let cursor : string | null = null ;
60
+
61
+ do {
62
+ const queryParams = new URLSearchParams ( params ) ;
63
+ if ( cursor ) {
64
+ queryParams . set ( "cursor" , cursor ) ;
65
+ }
66
+
67
+ const queryString = queryParams . toString ( ) ;
68
+ const fullPath = queryString ? `${ path } ?${ queryString } ` : path ;
69
+
70
+ const response = await this . do ( fullPath , "GET" ) ;
71
+ const paginatedResponse = camelize ( JSON . parse ( response . body ) ) as PaginatedResponse < T > ;
72
+
73
+ allResults . push ( ...paginatedResponse . results ) ;
74
+ cursor = paginatedResponse . nextCursor ;
75
+ } while ( cursor ) ;
76
+
77
+ return allResults ;
57
78
}
58
79
59
80
private async do ( path : string , method : string , json ?: object ) : Promise < WebResponse > {
60
81
const params : RequestParams = {
61
- url : `https://api.todoist.com/rest/v2 ${ path } ` ,
82
+ url : `https://api.todoist.com/api/v1 ${ path } ` ,
62
83
method : method ,
63
84
headers : {
64
85
Authorization : `Bearer ${ this . token } ` ,
0 commit comments