add request batching and drawing module

master
John Montagu, the 4th Earl of Sandvich 2024-05-07 23:31:16 -07:00
parent cca7ca2cd7
commit 9e638d94f1
Signed by: sandvich
GPG Key ID: 9A39BE37E602B22D
3 changed files with 157 additions and 4 deletions

View File

@ -1,7 +1,15 @@
import { MethodCall, TCJSONResponse, TCResponse, TCResponseRaw } from "./types";
import { AskModule } from "./ask"; import { AskModule } from "./ask";
import { MessagesModule } from "./messages"; import { MessagesModule } from "./messages";
import { ForumModule } from "./forum"; import { ForumModule } from "./forum";
import { MethodCall, TCJSONResponse, TCResponse, TCResponseRaw } from "./types"; import { DrawingModule } from "./drawing";
interface QueuedCall {
methodCall: MethodCall;
creator: new(r: TCResponseRaw) => TCResponse;
resolve: (value: any) => void;
reject: (reason: any) => void;
}
/** /**
* Client for Two Cans & String API. * Client for Two Cans & String API.
@ -13,6 +21,10 @@ export class Client {
#messages: MessagesModule; #messages: MessagesModule;
#ask: AskModule; #ask: AskModule;
#forum: ForumModule; #forum: ForumModule;
#drawing: DrawingModule;
#requestQueue: QueuedCall[] = [];
#isBatching: boolean = false;
/** @internal */ /** @internal */
public _cache: { [key: string]: any; } = { }; public _cache: { [key: string]: any; } = { };
@ -33,6 +45,22 @@ export class Client {
return this.#forum; return this.#forum;
} }
public get drawing(): DrawingModule {
return this.#drawing;
}
public get isBatching(): boolean {
return this.#isBatching;
}
public set isBatching(value: boolean) {
if (value) {
this.beginBatch();
} else {
this.endBatch();
}
}
public constructor(auth?: string) { public constructor(auth?: string) {
this.auth = auth; this.auth = auth;
@ -40,6 +68,7 @@ export class Client {
this.#messages = new MessagesModule(this); this.#messages = new MessagesModule(this);
this.#ask = new AskModule(this); this.#ask = new AskModule(this);
this.#forum = new ForumModule(this); this.#forum = new ForumModule(this);
this.#drawing = new DrawingModule(this);
} }
//public login(username: string, password: string) { //public login(username: string, password: string) {
@ -53,7 +82,7 @@ export class Client {
* @param args The arguments to pass to the API method. * @param args The arguments to pass to the API method.
* @internal * @internal
*/ */
public async _call<T extends TCResponse>( public _call<T extends TCResponse>(
creator: new(r: TCResponseRaw) => T, creator: new(r: TCResponseRaw) => T,
methodName: string, methodName: string,
args: { [key: string]: any } args: { [key: string]: any }
@ -64,19 +93,82 @@ export class Client {
payload: args, payload: args,
}; };
if (this.#isBatching) {
return new Promise((resolve, reject) => {
this.#requestQueue.push({
methodCall,
creator,
resolve,
reject,
});
});
}
const body = { const body = {
auth: this.auth, auth: this.auth,
requests: [methodCall], requests: [methodCall],
}; };
return new Promise((resolve, reject) => {
this.fetch(body)
.then((res) => {
const rawResponse: TCResponseRaw = {
ok: res?.ok ?? false,
...res?.responses[0],
};
resolve(new creator(rawResponse));
})
.catch((reason) => reject(reason));
});
/*
const res = await this.fetch(body); const res = await this.fetch(body);
const rawResponse: TCResponseRaw = { const rawResponse: TCResponseRaw = {
ok: res?.ok ?? false, ok: res?.ok ?? false,
...res?.responses[0], ...res?.responses[0],
} as T; } as T;
return new creator(rawResponse); return new creator(rawResponse);
*/
}
public beginBatch(): void {
this.#isBatching = true;
}
public endBatch(process = true): void {
this.#isBatching = false;
if (process) {
this.processBatch();
}
}
public processBatch(): void {
const methodCalls = this.#requestQueue.map((c) => c.methodCall);
const body = {
auth: this.auth,
requests: methodCalls,
};
this.fetch(body)
.then((res) => {
this.#requestQueue.forEach((c, index) => {
const individualResponse: TCResponseRaw = {
ok: res?.ok ?? false,
...res?.responses[index],
};
c.resolve(new c.creator(individualResponse));
});
})
.catch((reason) => {
this.#requestQueue.forEach((c) => c.reject(reason));
})
.finally(() => {
this.#requestQueue.length = 0;
});
} }
private async fetch(body: any): Promise<TCJSONResponse | undefined> { private async fetch(body: any): Promise<TCJSONResponse | undefined> {
@ -93,7 +185,7 @@ export class Client {
const res = await fetch(Client.BASE_URI, req); const res = await fetch(Client.BASE_URI, req);
if (!res.ok) { if (!res.ok) {
return; return Promise.reject();
} }
return await res.json() as TCJSONResponse; return await res.json() as TCJSONResponse;

View File

@ -0,0 +1,42 @@
import { Module } from "../module";
import { TCResponse } from "../types";
import { SaveDrawingResponse, ViewDrawingDataResponse } from "./types";
export class DrawingModule extends Module {
public async saveDrawing(data: string, makeActive = false) {
this.client._call(
// newImageId: string
SaveDrawingResponse,
"legacy.drawing",
{
action: "save",
data,
makeActive,
}
);
}
public async viewDrawingData(imageId: string) {
// response { ok: boolean , pixelData: string }
this.client._call(
// newImageId: string
ViewDrawingDataResponse,
"legacy.drawing",
{
action: "initial-image-data",
imageId,
}
);
}
public async sendDrawing(imageId: string, receiver: string) {
this.client._call(
TCResponse,
"drawing.send",
{
imageId,
receiver,
}
);
}
}

View File

@ -0,0 +1,19 @@
import { TCResponse, TCResponseRaw } from "../types";
export class SaveDrawingResponse extends TCResponse {
public newImageId: string;
public constructor(res: TCResponseRaw) {
super(res);
this.newImageId = res["newImageId"];
}
}
export class ViewDrawingDataResponse extends TCResponse {
public data: string;
public constructor(res: TCResponseRaw) {
super(res);
this.data = res["data"];
}
}