mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-09-10 03:41:52 +02:00
feat(rpc): expose snapshot, compare and restore for request versions
Three commands, all host-independent so the browser build answers them from the same model layer as the desktop: - models_snapshot_request, for the edit-session boundaries only the frontend can see. It takes a reason and nothing else — the caller does not decide whether anything changed, because content addressing already has. - models_request_version, which returns a version and the live request's content together so they are guaranteed comparable, plus the same content-hash verdict the backend uses rather than a second opinion formed in TypeScript. - models_restore_request_version. The browser host also snapshots before its own send, since its send pipeline is in TypeScript rather than in the shared crate. Bindings regenerated with CI's `cargo test --all --features yaak-app-client/wry`, which also drops a stale ImportSourceResource from the rpc-schema copy and adds a missing ImportSource to the plugins copy.
This commit is contained in:
@@ -63,6 +63,10 @@ const HANDLERS: Partial<Record<AppCmd, Handler>> = {
|
||||
db.rpc("models_get_graphql_introspection", payload),
|
||||
models_upsert_graphql_introspection: (payload, db) =>
|
||||
db.rpc("models_upsert_graphql_introspection", payload),
|
||||
models_snapshot_request: (payload, db) => db.rpc("models_snapshot_request", payload),
|
||||
models_request_version: (payload, db) => db.rpc("models_request_version", payload),
|
||||
models_restore_request_version: (payload, db) =>
|
||||
db.rpc("models_restore_request_version", payload),
|
||||
models_grpc_events: (payload, db) => db.rpc("models_grpc_events", payload),
|
||||
models_websocket_events: (payload, db) => db.rpc("models_websocket_events", payload),
|
||||
cmd_get_workspace_meta: (payload, db) => db.rpc("cmd_get_workspace_meta", payload),
|
||||
@@ -76,7 +80,12 @@ const HANDLERS: Partial<Record<AppCmd, Handler>> = {
|
||||
cmd_send_http_request: (payload, db) => {
|
||||
const requestId = str(payload, "requestId");
|
||||
if (requestId == null) throw new Error("cmd_send_http_request needs a requestId");
|
||||
return sendHttpRequest(db, requestId, str(payload, "environmentId"), str(payload, "cookieJarId"));
|
||||
return sendHttpRequest(
|
||||
db,
|
||||
requestId,
|
||||
str(payload, "environmentId"),
|
||||
str(payload, "cookieJarId"),
|
||||
);
|
||||
},
|
||||
|
||||
/* -------------------------------- app ---------------------------------- */
|
||||
@@ -262,10 +271,16 @@ const DECLINED: Partial<Record<AppCmd, [reason: string, capability: CapabilityNa
|
||||
cmd_ws_connect: ["WebSocket requests aren't available in the browser yet", "websocket"],
|
||||
cmd_ws_send: ["WebSocket requests aren't available in the browser yet", "websocket"],
|
||||
cmd_ws_close: ["WebSocket requests aren't available in the browser yet", "websocket"],
|
||||
cmd_ws_delete_connections: ["WebSocket requests aren't available in the browser yet", "websocket"],
|
||||
cmd_ws_delete_connections: [
|
||||
"WebSocket requests aren't available in the browser yet",
|
||||
"websocket",
|
||||
],
|
||||
|
||||
// Anything that needs files the page can't reach.
|
||||
cmd_import_data: ["Importing from a file needs a filesystem, which a browser tab has no", "localFiles"],
|
||||
cmd_import_data: [
|
||||
"Importing from a file needs a filesystem, which a browser tab has no",
|
||||
"localFiles",
|
||||
],
|
||||
cmd_import_url: ["Importing from a URL needs the Yaak server, which isn't available yet", null],
|
||||
cmd_commit_import: ["Importing needs a plugin, which this host doesn't run", null],
|
||||
cmd_list_import_sources: ["Importing isn't available in the browser yet", null],
|
||||
@@ -298,8 +313,14 @@ const DECLINED: Partial<Record<AppCmd, [reason: string, capability: CapabilityNa
|
||||
cmd_plugins_uninstall: ["Plugins aren't available in the browser yet", "plugins"],
|
||||
cmd_plugins_updates: ["Plugins aren't available in the browser yet", "plugins"],
|
||||
cmd_plugins_update_all: ["Plugins aren't available in the browser yet", "plugins"],
|
||||
cmd_template_function_config: ["Template functions come from plugins, which this host doesn't run", "plugins"],
|
||||
cmd_template_tokens_to_string: ["Template functions come from plugins, which this host doesn't run", "plugins"],
|
||||
cmd_template_function_config: [
|
||||
"Template functions come from plugins, which this host doesn't run",
|
||||
"plugins",
|
||||
],
|
||||
cmd_template_tokens_to_string: [
|
||||
"Template functions come from plugins, which this host doesn't run",
|
||||
"plugins",
|
||||
],
|
||||
cmd_call_http_request_action: ["Plugins aren't available in the browser yet", "plugins"],
|
||||
cmd_call_websocket_request_action: ["Plugins aren't available in the browser yet", "plugins"],
|
||||
cmd_call_grpc_request_action: ["Plugins aren't available in the browser yet", "plugins"],
|
||||
|
||||
@@ -30,6 +30,7 @@ import type {
|
||||
HttpResponse,
|
||||
HttpResponseEventData,
|
||||
HttpSendSettings,
|
||||
ModelVersion,
|
||||
} from "@yaakapp-internal/models";
|
||||
import type { Frame, SendRequest } from "@yaakapp-internal/web";
|
||||
import type { WorkerConnection } from "./connection";
|
||||
@@ -71,7 +72,13 @@ export async function sendHttpRequest(
|
||||
// a failure to render or to reach the server lands in the response pane as
|
||||
// that response's error rather than as a toast that names no request.
|
||||
const workspaceId = await workspaceIdOfRequest(db, requestId);
|
||||
const response = new ResponseWriter(db, { model: "http_response", requestId, workspaceId });
|
||||
const versionId = await snapshotRequestVersion(db, requestId);
|
||||
const response = new ResponseWriter(db, {
|
||||
model: "http_response",
|
||||
requestId,
|
||||
workspaceId,
|
||||
versionId,
|
||||
});
|
||||
await response.create();
|
||||
|
||||
const cancel = new AbortController();
|
||||
@@ -88,6 +95,29 @@ export async function sendHttpRequest(
|
||||
return response.current();
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture what is about to be sent, so the response can offer it back later.
|
||||
* The desktop does this inside its send pipeline; this host's pipeline is here,
|
||||
* so this is where it goes. Versions are content-addressed, so repeated sends
|
||||
* of an unchanged request all point at the same one.
|
||||
*/
|
||||
async function snapshotRequestVersion(
|
||||
db: WorkerConnection,
|
||||
requestId: string,
|
||||
): Promise<string | undefined> {
|
||||
try {
|
||||
const version = await db.rpc<ModelVersion>("models_snapshot_request", {
|
||||
requestId,
|
||||
reason: "send",
|
||||
});
|
||||
return version.id;
|
||||
} catch (err) {
|
||||
// History is not worth failing a send over
|
||||
console.warn("Failed to snapshot request version", err);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
async function runSend(
|
||||
db: WorkerConnection,
|
||||
response: ResponseWriter,
|
||||
@@ -315,8 +345,16 @@ class TimelineWriter {
|
||||
* yaak-models), so an edit made while the send was in flight survives rather
|
||||
* than being written over by the send's stale snapshot.
|
||||
*/
|
||||
async function persistCookies(db: WorkerConnection, jar: CookieJar, cookies: Cookie[]): Promise<void> {
|
||||
await db.rpc("web_persist_send_cookies", { cookieJarId: jar.id, before: jar.cookies, after: cookies });
|
||||
async function persistCookies(
|
||||
db: WorkerConnection,
|
||||
jar: CookieJar,
|
||||
cookies: Cookie[],
|
||||
): Promise<void> {
|
||||
await db.rpc("web_persist_send_cookies", {
|
||||
cookieJarId: jar.id,
|
||||
before: jar.cookies,
|
||||
after: cookies,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user