Skip to content

Commit bf9e927

Browse files
authored
Merge pull request #66 from takker99/add-snapshot
✨ Implement wrappers for /api/page-snapshots/
2 parents c849832 + c52e7d9 commit bf9e927

File tree

2 files changed

+152
-0
lines changed

2 files changed

+152
-0
lines changed

deps/scrapbox-rest.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@ export type {
1616
NotPrivilegeError,
1717
Page,
1818
PageList,
19+
PageSnapshot,
1920
ProjectId,
2021
ProjectResponse,
2122
ProjectSearchResult,
2223
SearchedTitle,
2324
SearchResult,
2425
SessionError,
26+
Snapshot,
2527
TweetInfo,
2628
} from "https://raw.githubusercontent.com/scrapbox-jp/types/0.3.5/rest.ts";

rest/getSnapshots.ts

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import type {
2+
ErrorLike,
3+
NotFoundError,
4+
NotLoggedInError,
5+
NotMemberError,
6+
PageSnapshot,
7+
Snapshot,
8+
} from "../deps/scrapbox-rest.ts";
9+
import { tryToErrorLike } from "../is.ts";
10+
import { cookie } from "./auth.ts";
11+
import { BaseOptions, Result, setDefaults } from "./util.ts";
12+
import { UnexpectedResponseError } from "./error.ts";
13+
14+
/** 不正なfollowingIdを渡されたときに発生するエラー */
15+
export interface InvalidPageSnapshotIdError extends ErrorLike {
16+
name: "InvalidPageSnapshotIdError";
17+
}
18+
19+
export interface GetSnapshotsOptions extends BaseOptions {
20+
/** 次のsnapshots listを示すID */
21+
followingId?: string;
22+
}
23+
24+
/** get page snapshots
25+
*
26+
* @param options connect.sid etc.
27+
*/
28+
export const getSnapshots = async (
29+
project: string,
30+
pageId: string,
31+
options?: GetSnapshotsOptions,
32+
): Promise<
33+
Result<
34+
(PageSnapshot & { followingId: string }),
35+
| NotFoundError
36+
| NotLoggedInError
37+
| NotMemberError
38+
| InvalidPageSnapshotIdError
39+
>
40+
> => {
41+
const { sid, hostName, fetch, followingId } = setDefaults(options ?? {});
42+
const path = `https://${hostName}/api/page-snapshots/${project}/${pageId}/${
43+
followingId ? `?followingId=${followingId}` : ""
44+
}`;
45+
46+
const res = await fetch(
47+
path,
48+
sid ? { headers: { Cookie: cookie(sid) } } : undefined,
49+
);
50+
51+
if (!res.ok) {
52+
if (res.status === 422) {
53+
return {
54+
ok: false,
55+
value: {
56+
name: "InvalidPageSnapshotIdError",
57+
message: await res.text(),
58+
},
59+
};
60+
}
61+
const text = await res.text();
62+
const value = tryToErrorLike(text);
63+
if (!value) {
64+
throw new UnexpectedResponseError({
65+
path: new URL(path),
66+
...res,
67+
body: text,
68+
});
69+
}
70+
return {
71+
ok: false,
72+
value: value as (NotFoundError | NotLoggedInError | NotMemberError),
73+
};
74+
}
75+
76+
const data = (await res.json()) as PageSnapshot;
77+
return {
78+
ok: true,
79+
value: { ...data, followingId: res.headers.get("X-following-id") ?? "" },
80+
};
81+
};
82+
83+
/** 指定したページのsnapshotsを、responseに入っている塊ごとに全て返す
84+
*
85+
* @param project ページのproject name
86+
* @param pageId page id
87+
* @return 認証が通らなかったらエラーを、通ったらasync generatorを返す
88+
*/
89+
export const readSnapshotsBulk = async (
90+
project: string,
91+
pageId: string,
92+
options?: BaseOptions,
93+
): Promise<
94+
| NotFoundError
95+
| NotLoggedInError
96+
| NotMemberError
97+
| InvalidPageSnapshotIdError
98+
| AsyncGenerator<Snapshot[], void, unknown>
99+
> => {
100+
const first = await getSnapshots(project, pageId, options);
101+
if (!first.ok) return first.value;
102+
103+
return async function* () {
104+
yield first.value.snapshots;
105+
let followingId = first.value.followingId;
106+
107+
while (followingId) {
108+
const result = await getSnapshots(project, pageId, {
109+
followingId,
110+
...options,
111+
});
112+
113+
// すでに認証は通っているので、ここでエラーになるはずがない
114+
if (!result.ok) {
115+
throw new Error("The authorization cannot be unavailable");
116+
}
117+
yield result.value.snapshots;
118+
followingId = result.value.followingId;
119+
}
120+
}();
121+
};
122+
123+
/** 指定したページの全てのsnapshotsを取得し、一つづつ返す
124+
*
125+
* @param project ページのproject name
126+
* @param pageId page id
127+
* @return 認証が通らなかったらエラーを、通ったらasync generatorを返す
128+
*/
129+
export const readSnapshots = async (
130+
project: string,
131+
pageId: string,
132+
options?: BaseOptions,
133+
): Promise<
134+
| NotFoundError
135+
| NotLoggedInError
136+
| NotMemberError
137+
| InvalidPageSnapshotIdError
138+
| AsyncGenerator<Snapshot, void, unknown>
139+
> => {
140+
const reader = await readSnapshotsBulk(project, pageId, options);
141+
if ("name" in reader) return reader;
142+
143+
return async function* () {
144+
for await (const titles of reader) {
145+
for (const title of titles) {
146+
yield title;
147+
}
148+
}
149+
}();
150+
};

0 commit comments

Comments
 (0)