Skip to content

Commit

Permalink
Merge pull request #166 from takker99/deal-with-push-errors
Browse files Browse the repository at this point in the history
feat(websocket): websocketから返されるcommit errorを適切に処理する
  • Loading branch information
takker99 committed Apr 30, 2024
2 parents b7854da + b66581f commit 6aeb500
Show file tree
Hide file tree
Showing 12 changed files with 313 additions and 421 deletions.
29 changes: 0 additions & 29 deletions browser/websocket/_codeBlock.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import { Change, Socket, wrap } from "../../deps/socket.ts";
import { Page } from "../../deps/scrapbox-rest.ts";
import { TinyCodeBlock } from "../../rest/getCodeBlocks.ts";
import { getProjectId, getUserId } from "./id.ts";
import { pushWithRetry } from "./_fetch.ts";

/** コードブロックのタイトル行の情報を保持しておくためのinterface */
export interface CodeTitle {
Expand All @@ -11,31 +7,6 @@ export interface CodeTitle {
indent: number;
}

/** コミットを送信する一連の処理 */
export const applyCommit = async (
commits: Change[],
page: Page,
projectName: string,
pageTitle: string,
socket: Socket,
userId?: string,
): ReturnType<typeof pushWithRetry> => {
const [projectId, userId_] = await Promise.all([
getProjectId(projectName),
userId ?? getUserId(),
]);
const { request } = wrap(socket);
return await pushWithRetry(request, commits, {
parentId: page.commitId,
projectId: projectId,
pageId: page.id,
userId: userId_,
project: projectName,
title: pageTitle,
retry: 3,
});
};

/** コードブロックのタイトル行から各種プロパティを抽出する
*
* @param lineText {string} 行テキスト
Expand Down
97 changes: 0 additions & 97 deletions browser/websocket/_fetch.ts

This file was deleted.

52 changes: 12 additions & 40 deletions browser/websocket/deletePage.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,22 @@
import { Socket, socketIO, wrap } from "../../deps/socket.ts";
import { connect, disconnect } from "./socket.ts";
import { getProjectId, getUserId } from "./id.ts";
import { pull } from "./pull.ts";
import { pushWithRetry } from "./_fetch.ts";
import { push, PushOptions, RetryError } from "./push.ts";
import { Result } from "../../rest/util.ts";

export interface DeletePageOptions {
socket?: Socket;
}
export type DeletePageOptions = PushOptions;

/** 指定したページを削除する
*
* @param project 削除したいページのproject
* @param title 削除したいページのタイトル
* @param options 使用したいSocketがあれば指定する
* @param options
*/
export const deletePage = async (
export const deletePage = (
project: string,
title: string,
options?: DeletePageOptions,
): Promise<void> => {
const [
{ id: pageId, commitId: parentId, persistent },
projectId,
userId,
] = await Promise.all([
pull(project, title),
getProjectId(project),
getUserId(),
]);

if (!persistent) return;

const injectedSocket = options?.socket;
const socket = injectedSocket ?? await socketIO();
await connect(socket);
const { request } = wrap(socket);
try {
await pushWithRetry(request, [{ deleted: true }], {
projectId,
pageId,
parentId,
userId,
project,
title,
});
} finally {
if (!injectedSocket) await disconnect(socket);
}
};
): Promise<Result<string, RetryError>> =>
push(
project,
title,
(page) => page.persistent ? [{ deleted: true }] : [],
options,
);
5 changes: 2 additions & 3 deletions browser/websocket/makeChanges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@ import type { Change } from "../../deps/socket.ts";
import { toTitleLc } from "../../title.ts";
import { parseYoutube } from "../../parser/youtube.ts";

export interface Init {
export interface Init extends Page {
userId: string;
page: Page;
}
export function* makeChanges(
left: Pick<Line, "text" | "id">[],
right: string[],
{ userId, page }: Init,
{ userId, ...page }: Init,
): Generator<Change, void, unknown> {
// 改行文字が入るのを防ぐ
const right_ = right.flatMap((text) => text.split("\n"));
Expand Down
101 changes: 26 additions & 75 deletions browser/websocket/patch.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
import { Socket, socketIO, wrap } from "../../deps/socket.ts";
import { connect, disconnect } from "./socket.ts";
import { getProjectId, getUserId } from "./id.ts";
import { Change, DeletePageChange, PinChange } from "../../deps/socket.ts";
import { makeChanges } from "./makeChanges.ts";
import { pull } from "./pull.ts";
import { Line, Page } from "../../deps/scrapbox-rest.ts";
import { pushCommit, pushWithRetry } from "./_fetch.ts";
import { push, PushOptions, RetryError } from "./push.ts";
import { suggestUnDupTitle } from "./suggestUnDupTitle.ts";
import { Result } from "../../rest/util.ts";

export interface PatchOptions {
socket?: Socket;
}
export type PatchOptions = PushOptions;

export interface PatchMetadata extends Page {
/** 書き換えを再試行した回数
*
* 初回は`0`で、再試行するたびに増える
*/
retry: number;
attempts: number;
}

/** ページ全体を書き換える
Expand All @@ -27,77 +24,31 @@ export interface PatchMetadata extends Page {
* @param update 書き換え後の本文を作成する函数。引数には現在の本文が渡される。空配列を返すとページが削除される。undefinedを返すと書き換えを中断する
* @param options 使用したいSocketがあれば指定する
*/
export const patch = async (
export const patch = (
project: string,
title: string,
update: (
lines: Line[],
metadata: PatchMetadata,
) => string[] | undefined | Promise<string[] | undefined>,
options?: PatchOptions,
): Promise<void> => {
const [
page_,
projectId,
userId,
] = await Promise.all([
pull(project, title),
getProjectId(project),
getUserId(),
]);

let page = page_;

const injectedSocket = options?.socket;
const socket = injectedSocket ?? await socketIO();
await connect(socket);
try {
const { request } = wrap(socket);

// 3回retryする
for (let retry = 0; retry < 3; retry++) {
try {
const pending = update(page.lines, { ...page, retry });
const newLines = pending instanceof Promise ? await pending : pending;

if (!newLines) return;

if (newLines.length === 0) {
await pushWithRetry(request, [{ deleted: true }], {
projectId,
pageId: page.id,
parentId: page.commitId,
userId,
project,
title,
});
}

const changes = [
...makeChanges(page.lines, newLines, { userId, page }),
];
await pushCommit(request, changes, {
parentId: page.commitId,
projectId,
pageId: page.id,
userId,
});
break;
} catch (_e: unknown) {
if (retry === 2) {
throw Error("Faild to retry pushing.");
}
console.log(
"Faild to push a commit. Retry after pulling new commits",
);
try {
page = await pull(project, title);
} catch (e: unknown) {
throw e;
}
): Promise<Result<string, RetryError>> =>
push(
project,
title,
async (page, attempts, prev, reason) => {
if (reason === "DuplicateTitleError") {
const fallbackTitle = suggestUnDupTitle(title);
return prev.map((change) => {
if ("title" in change) change.title = fallbackTitle;
return change;
}) as Change[] | [DeletePageChange] | [PinChange];
}
}
} finally {
if (!injectedSocket) await disconnect(socket);
}
};
const pending = update(page.lines, { ...page, attempts });
const newLines = pending instanceof Promise ? await pending : pending;
if (newLines === undefined) return [];
if (newLines.length === 0) return [{ deleted: true }];
return [...makeChanges(page.lines, newLines, page)];
},
options,
);
Loading

0 comments on commit 6aeb500

Please sign in to comment.