mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-11-13 12:13:42 +01:00
Version the browser send from the read it was prepared from
The browser host snapshotted the request through its own RPC, then prepare_http_send read the row again to build what was sent. An edit committed between the two left the response pointing at a version it never sent. prepare_http_send now versions the stored row it has just read, in the same call, and hands the id back with the prepared send; the response takes it from there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4686d6b4a5
commit
9eac9d3da2
2 files changed
+23
-34
No files matched your search
@@ -35,6 +35,7 @@ use yaak_models::models::{
|
||||
HttpSendSettings, ModelVersionReason, RequestVersionComparison,
|
||||
};
|
||||
use yaak_models::models_ops;
|
||||
use yaak_models::queries::any_request::AnyRequest;
|
||||
use yaak_models::query_manager::QueryManager;
|
||||
use yaak_models::render::render_http_request;
|
||||
use yaak_models::util::{ModelPayload, UpdateSource};
|
||||
@@ -542,6 +543,9 @@ struct PreparedHttpSend {
|
||||
setting_events: Vec<HttpResponseEventData>,
|
||||
/// The jar the send starts with, so the tab can write it back with the proxy's changes.
|
||||
cookie_jar: Option<CookieJar>,
|
||||
/// The version of the stored row this send was prepared from, taken from the same
|
||||
/// read, so the response names exactly what was sent. `None` if it couldn't be written.
|
||||
version_id: Option<String>,
|
||||
}
|
||||
|
||||
/// A template callback for a host with no plugins. Variables render; a function is a clear
|
||||
@@ -584,9 +588,19 @@ pub async fn prepare_http_send(payload: JsValue) -> Result<JsValue> {
|
||||
let req: PrepareHttpSendReq = from_js(payload)?;
|
||||
|
||||
// Everything from the database first, then release the host borrow before rendering.
|
||||
let (request, environment_chain, settings, cookie_jar) = with_host(|host| {
|
||||
let (request, environment_chain, settings, cookie_jar, version_id) = with_host(|host| {
|
||||
let db = host.queries.connect();
|
||||
let request = db.get_http_request(&req.request_id).map_err(js_error)?;
|
||||
// Version this read of the row, the one being sent, rather than reading it again
|
||||
// where an edit could land in between. Failing to write history fails nothing.
|
||||
let version_id = host
|
||||
.queries
|
||||
.with_tx(|tx| {
|
||||
Ok::<_, yaak_models::error::Error>(
|
||||
tx.snapshot_request_for_send(&AnyRequest::HttpRequest(request.clone())),
|
||||
)
|
||||
})
|
||||
.unwrap_or(None);
|
||||
let environment_chain = db
|
||||
.resolve_environments(
|
||||
&request.workspace_id,
|
||||
@@ -603,7 +617,7 @@ pub async fn prepare_http_send(payload: JsValue) -> Result<JsValue> {
|
||||
None => None,
|
||||
};
|
||||
let request = HttpRequest { authentication_type, authentication, headers, ..request };
|
||||
Ok((request, environment_chain, settings, cookie_jar))
|
||||
Ok((request, environment_chain, settings, cookie_jar, version_id))
|
||||
})?;
|
||||
|
||||
let rendered = render_http_request(
|
||||
@@ -634,6 +648,7 @@ pub async fn prepare_http_send(payload: JsValue) -> Result<JsValue> {
|
||||
settings: HttpSendSettings::from(&settings),
|
||||
setting_events: settings.timeline_events(),
|
||||
cookie_jar,
|
||||
version_id,
|
||||
};
|
||||
// JSON-compatible, as `rpc` does: the tab posts this to the proxy with `JSON.stringify`,
|
||||
// and the default serializer's `Map` for the request body would stringify to `{}`.
|
||||
|
||||
@@ -30,7 +30,6 @@ import type {
|
||||
HttpResponse,
|
||||
HttpResponseEventData,
|
||||
HttpSendSettings,
|
||||
ModelVersion,
|
||||
} from "@yaakapp-internal/models";
|
||||
import type { Frame, SendRequest } from "@yaakapp-internal/web";
|
||||
import type { WorkerConnection } from "./connection";
|
||||
@@ -55,6 +54,8 @@ interface PreparedHttpSend {
|
||||
settings: HttpSendSettings;
|
||||
settingEvents: HttpResponseEventData[];
|
||||
cookieJar: CookieJar | null;
|
||||
/** The version of the stored request this send was prepared from, if one was written. */
|
||||
versionId: string | null;
|
||||
}
|
||||
|
||||
/** The desktop writes progress at most this often while a body streams in. */
|
||||
@@ -72,13 +73,7 @@ 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 versionId = await snapshotRequestVersion(db, requestId);
|
||||
const response = new ResponseWriter(db, {
|
||||
model: "http_response",
|
||||
requestId,
|
||||
workspaceId,
|
||||
versionId,
|
||||
});
|
||||
const response = new ResponseWriter(db, { model: "http_response", requestId, workspaceId });
|
||||
await response.create();
|
||||
|
||||
const cancel = new AbortController();
|
||||
@@ -95,29 +90,6 @@ 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,
|
||||
@@ -131,7 +103,9 @@ async function runSend(
|
||||
environmentId,
|
||||
cookieJarId,
|
||||
});
|
||||
await response.patch({ url: prepared.request.url });
|
||||
// The version comes from the same read of the request that was prepared, so the
|
||||
// response can offer back exactly what it sent
|
||||
await response.patch({ url: prepared.request.url, versionId: prepared.versionId });
|
||||
|
||||
// The first line of the timeline says what did the sending and where. A
|
||||
// request through a proxy shows a different origin to the server than the
|
||||
|
||||
Reference in new issue
Block a user