Skip to content

Commit 88bad85

Browse files
committed
feat(api): Implement the /api/pages/:project endpoint
1 parent e2ec5dd commit 88bad85

File tree

1 file changed

+126
-0
lines changed

1 file changed

+126
-0
lines changed

api/pages/project.ts

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,127 @@
1+
import type {
2+
BasePage,
3+
NotFoundError,
4+
NotLoggedInError,
5+
NotMemberError,
6+
PageList,
7+
} from "@cosense/types/rest";
8+
import { type BaseOptions, setDefaults } from "../../util.ts";
9+
import { cookie } from "../../rest/auth.ts";
10+
import type { ResponseOfEndpoint } from "../../targeted_response.ts";
11+
12+
/** Options for {@linkcode listPages} */
13+
export interface ListPagesOption<R extends Response | undefined>
14+
extends BaseOptions<R> {
15+
/** the sort of page list to return
16+
*
17+
* @default {"updated"}
18+
*/
19+
sort?:
20+
| "updatedWithMe"
21+
| "updated"
22+
| "created"
23+
| "accessed"
24+
| "pageRank"
25+
| "linked"
26+
| "views"
27+
| "title";
28+
/** the index getting page list from
29+
*
30+
* @default {0}
31+
*/
32+
skip?: number;
33+
/** threshold of the length of page list
34+
*
35+
* @default {100}
36+
*/
37+
limit?: number;
38+
}
39+
40+
/** Constructs a request for the `/api/pages/:project` endpoint
41+
*
42+
* @param project The project name to list pages from
43+
* @param options - Additional configuration options (sorting, pagination, etc.)
44+
* @returns A {@linkcode Request} object for fetching pages data
45+
*/
46+
export const makeGetRequest = <R extends Response | undefined>(
47+
project: string,
48+
options?: ListPagesOption<R>,
49+
): Request => {
50+
const { sid, hostName, sort, limit, skip } = setDefaults(
51+
options ?? {},
52+
);
53+
const params = new URLSearchParams();
54+
if (sort !== undefined) params.append("sort", sort);
55+
if (limit !== undefined) params.append("limit", `${limit}`);
56+
if (skip !== undefined) params.append("skip", `${skip}`);
57+
58+
return new Request(
59+
`https://${hostName}/api/pages/${project}?${params}`,
60+
sid ? { headers: { Cookie: cookie(sid) } } : undefined,
61+
);
62+
};
63+
64+
/** Lists pages from a specified project
65+
*
66+
* @param project The project name to list pages from
67+
* @param options Configuration options for pagination and sorting
68+
* @returns A {@linkcode Result}<{@linkcode unknown}, {@linkcode Error}> containing:
69+
* - Success: The page data in JSON format
70+
* - Error: One of several possible errors:
71+
* - {@linkcode NotFoundError}: Page not found
72+
* - {@linkcode NotLoggedInError}: Authentication required
73+
* - {@linkcode NotMemberError}: User lacks access
74+
*/
75+
export const get = <R extends Response | undefined = Response>(
76+
project: string,
77+
options?: ListPagesOption<R>,
78+
): Promise<
79+
| ResponseOfEndpoint<{
80+
200: PageList;
81+
404: NotFoundError;
82+
401: NotLoggedInError;
83+
403: NotMemberError;
84+
}>
85+
| (undefined extends R ? undefined : never)
86+
> =>
87+
setDefaults(options ?? {}).fetch(
88+
makeGetRequest(project, options),
89+
) as Promise<
90+
| ResponseOfEndpoint<{
91+
200: PageList;
92+
404: NotFoundError;
93+
401: NotLoggedInError;
94+
403: NotMemberError;
95+
}>
96+
| (undefined extends R ? undefined : never)
97+
>;
98+
99+
/**
100+
* Lists pages from a given `project` with pagination
101+
*
102+
* @param project The project name to list pages from
103+
* @param options Configuration options for pagination and sorting
104+
* @throws {HTTPError} If any requests in the pagination sequence fail
105+
*/
106+
export async function* list(
107+
project: string,
108+
options?: ListPagesOption<Response>,
109+
): AsyncGenerator<BasePage, void, unknown> {
110+
const props = { ...(options ?? {}), skip: options?.skip ?? 0 };
111+
while (true) {
112+
const response = await get(project, props);
113+
if (response.status !== 200) {
114+
throw new Error(response.statusText, { cause: response }) as HTTPError;
115+
}
116+
const list = await response.json();
117+
yield* list.pages;
118+
props.skip += props.limit ?? 100;
119+
if (list.skip + list.limit >= list.count) break;
120+
}
121+
}
122+
1123
export * as title from "./project/title.ts";
124+
125+
export interface HTTPError extends Error {
126+
readonly cause: Response;
127+
}

0 commit comments

Comments
 (0)