Skip to content

Commit 6d2f5cc

Browse files
Batch network requests
1 parent 5bc25fc commit 6d2f5cc

File tree

4 files changed

+312
-86
lines changed

4 files changed

+312
-86
lines changed

src/anki-connect/client.ts

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import { requestUrl, RequestUrlParam, RequestUrlResponse } from "obsidian";
2-
import { AnkiActionResponse, CreateModelInput, Note, NoteFields } from "./types";
2+
import { AnkiActionResponse, AnkiMultiAction, CreateModelInput, Note, NoteFields } from "./types";
33

44
const DEFAULT_ANKI_CONNECT_URL = "http://localhost:8765";
55
const DEFAULT_ANKI_CONNECT_VERSION = 6;
6+
const REQUEST_LOG_PREFIX = "[anki-link][network]";
67

78
type AnkiAction =
89
| "createDeck"
910
| "createModel"
1011
| "addNote"
12+
| "addNotes"
1113
| "addTags"
1214
| "changeDeck"
1315
| "deckNames"
@@ -17,7 +19,8 @@ type AnkiAction =
1719
| "findNotes"
1820
| "deleteNotes"
1921
| "notesInfo"
20-
| "updateNoteFields";
22+
| "updateNoteFields"
23+
| "multi";
2124

2225
interface AnkiConnectClientOptions {
2326
url?: string;
@@ -42,8 +45,24 @@ export class AnkiConnectClient {
4245
}
4346

4447
private async send<T>(action: AnkiAction, params?: unknown): Promise<AnkiActionResponse<T>> {
45-
const response: RequestUrlResponse = await requestUrl(this.buildRequest(action, params));
46-
return response.json as AnkiActionResponse<T>;
48+
const request = this.buildRequest(action, params);
49+
const startedAt = Date.now();
50+
console.debug(`${REQUEST_LOG_PREFIX} -> ${request.method} ${request.url} action=${action}`);
51+
try {
52+
const response: RequestUrlResponse = await requestUrl(request);
53+
const elapsedMs = Date.now() - startedAt;
54+
const result = response.json as AnkiActionResponse<T>;
55+
const status = result.error ? "error" : "ok";
56+
console.debug(`${REQUEST_LOG_PREFIX} <- action=${action} status=${status} elapsedMs=${elapsedMs}`);
57+
if (result.error) {
58+
console.debug(`${REQUEST_LOG_PREFIX} !! action=${action} error="${result.error}"`);
59+
}
60+
return result;
61+
} catch (error) {
62+
const elapsedMs = Date.now() - startedAt;
63+
console.debug(`${REQUEST_LOG_PREFIX} xx action=${action} threw elapsedMs=${elapsedMs}`);
64+
throw error;
65+
}
4766
}
4867

4968
async deckNames(): Promise<AnkiActionResponse<string[]>> {
@@ -81,6 +100,10 @@ export class AnkiConnectClient {
81100
return this.send<number>("addNote", { note });
82101
}
83102

103+
async addNotes(notes: Note[]): Promise<AnkiActionResponse<(number | null)[]>> {
104+
return this.send<(number | null)[]>("addNotes", { notes });
105+
}
106+
84107
async addTags(notes: number[], tags: string): Promise<AnkiActionResponse<null>> {
85108
return this.send<null>("addTags", { notes, tags });
86109
}
@@ -104,6 +127,10 @@ export class AnkiConnectClient {
104127
async changeDeck(cards: number[], deck: string): Promise<AnkiActionResponse<null>> {
105128
return this.send<null>("changeDeck", { cards, deck });
106129
}
130+
131+
async multi(actions: AnkiMultiAction[]): Promise<AnkiActionResponse<AnkiActionResponse<unknown>[]>> {
132+
return this.send<AnkiActionResponse<unknown>[]>("multi", { actions });
133+
}
107134
}
108135

109136
export const defaultAnkiConnectClient = new AnkiConnectClient();

src/anki-connect/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ export interface AnkiActionResponse<T> {
33
result: T;
44
}
55

6+
export interface AnkiMultiAction {
7+
action: string;
8+
params?: unknown;
9+
}
10+
611
export interface NoteFields {
712
Front: string;
813
Back: string;

src/ankiConnectUtil.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { RequestUrlParam } from "obsidian";
22
import { defaultAnkiConnectClient } from "./anki-connect/client";
3-
import { AnkiActionResponse, Note, NoteFields, NoteInfo } from "./anki-connect/types";
3+
import { AnkiActionResponse, AnkiMultiAction, Note, NoteFields, NoteInfo } from "./anki-connect/types";
44

5-
export type { Note, NoteFields, NoteInfo } from "./anki-connect/types";
5+
export type { AnkiMultiAction, Note, NoteFields, NoteInfo } from "./anki-connect/types";
66

77
export enum DeckTypes {
88
BASIC = "basic",
@@ -48,6 +48,10 @@ export interface AddNoteResult extends ConnResult {
4848
result: number;
4949
}
5050

51+
export interface AddNotesResult extends ConnResult {
52+
result: (number | null)[];
53+
}
54+
5155
export interface AddTagsResult extends ConnResult {
5256
result: null;
5357
}
@@ -72,6 +76,10 @@ export interface UpdateNoteFieldsResult extends ConnResult {
7276
result: null;
7377
}
7478

79+
export interface MultiResult extends ConnResult {
80+
result: AnkiActionResponse<unknown>[];
81+
}
82+
7583
/**
7684
* Check if a value from notesInfo is a valid note (and not an empty object
7785
* returned when the note was deleted in Anki).
@@ -219,6 +227,10 @@ export async function sendAddNoteRequest(note: Note): Promise<AddNoteResult> {
219227
return toResult(defaultAnkiConnectClient.addNote(note));
220228
}
221229

230+
export async function sendAddNotesRequest(notes: Note[]): Promise<AddNotesResult> {
231+
return toResult(defaultAnkiConnectClient.addNotes(notes));
232+
}
233+
222234
export async function sendNotesInfoRequest(notes: number[]): Promise<NotesInfoResult> {
223235
return toResult(defaultAnkiConnectClient.notesInfo(notes));
224236
}
@@ -227,6 +239,10 @@ export async function sendUpdateNoteFieldsRequest(id: number, fields: NoteFields
227239
return toResult(defaultAnkiConnectClient.updateNoteFields(id, fields));
228240
}
229241

242+
export async function sendMultiRequest(actions: AnkiMultiAction[]): Promise<MultiResult> {
243+
return toResult(defaultAnkiConnectClient.multi(actions));
244+
}
245+
230246
export async function sendDeckNamesRequest(): Promise<DeckNamesResult> {
231247
return toResult(defaultAnkiConnectClient.deckNames());
232248
}

0 commit comments

Comments
 (0)