@@ -8,6 +8,11 @@ import type { CreateTaskParams, Task, TaskId } from "@/api/domain/task";
88import type { RequestParams , WebFetcher , WebResponse } from "@/api/fetcher" ;
99import debug from "@/log" ;
1010
11+ type PaginatedResponse < T > = {
12+ results : T [ ] ;
13+ nextCursor : string | null ;
14+ } ;
15+
1116export class TodoistApiClient {
1217 private token : string ;
1318 private fetcher : WebFetcher ;
@@ -18,15 +23,11 @@ export class TodoistApiClient {
1823 }
1924
2025 public async getTasks ( filter ?: string ) : Promise < Task [ ] > {
21- let path = "/tasks" ;
22-
2326 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" ) ;
2530 }
26-
27- const response = await this . do ( path , "GET" ) ;
28-
29- return camelize ( JSON . parse ( response . body ) ) as Task [ ] ;
3031 }
3132
3233 public async createTask ( content : string , options ?: CreateTaskParams ) : Promise < void > {
@@ -42,23 +43,43 @@ export class TodoistApiClient {
4243 }
4344
4445 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" ) ;
4747 }
4848
4949 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" ) ;
5251 }
5352
5453 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 ;
5778 }
5879
5980 private async do ( path : string , method : string , json ?: object ) : Promise < WebResponse > {
6081 const params : RequestParams = {
61- url : `https://api.todoist.com/rest/v2 ${ path } ` ,
82+ url : `https://api.todoist.com/api/v1 ${ path } ` ,
6283 method : method ,
6384 headers : {
6485 Authorization : `Bearer ${ this . token } ` ,
0 commit comments