Flush model writes before sending HTTP requests

This commit is contained in:
Gregory Schier
2026-06-29 10:25:15 -07:00
parent 55d0066efd
commit 9ffd8d4810
3 changed files with 44 additions and 34 deletions
+17 -25
View File
@@ -1,40 +1,32 @@
import type { HttpResponse } from "@yaakapp-internal/models";
import { getModel } from "@yaakapp-internal/models";
import { flushAllModelWrites } from "@yaakapp-internal/models";
import { invokeCmd } from "../lib/tauri";
import { getActiveCookieJar } from "./useActiveCookieJar";
import { getActiveEnvironment } from "./useActiveEnvironment";
import { createFastMutation, useFastMutation } from "./useFastMutation";
async function sendAnyHttpRequestById(id: string | null): Promise<HttpResponse | null> {
if (id == null) {
return null;
}
await flushAllModelWrites();
return invokeCmd("cmd_send_http_request", {
requestId: id,
environmentId: getActiveEnvironment()?.id,
cookieJarId: getActiveCookieJar()?.id,
});
}
export function useSendAnyHttpRequest() {
return useFastMutation<HttpResponse | null, string, string | null>({
mutationKey: ["send_any_request"],
mutationFn: async (id) => {
const request = getModel("http_request", id ?? "n/a");
if (request == null) {
return null;
}
return invokeCmd("cmd_send_http_request", {
request,
environmentId: getActiveEnvironment()?.id,
cookieJarId: getActiveCookieJar()?.id,
});
},
mutationFn: sendAnyHttpRequestById,
});
}
export const sendAnyHttpRequest = createFastMutation<HttpResponse | null, string, string | null>({
mutationKey: ["send_any_request"],
mutationFn: async (id) => {
const request = getModel("http_request", id ?? "n/a");
if (request == null) {
return null;
}
return invokeCmd("cmd_send_http_request", {
request,
environmentId: getActiveEnvironment()?.id,
cookieJarId: getActiveCookieJar()?.id,
});
},
mutationFn: sendAnyHttpRequestById,
});
+3 -4
View File
@@ -1425,11 +1425,10 @@ async fn cmd_send_http_request<R: Runtime>(
window: WebviewWindow<R>,
environment_id: Option<&str>,
cookie_jar_id: Option<&str>,
// NOTE: We receive the entire request because to account for the race
// condition where the user may have just edited a field before sending
// that has not yet been saved in the DB.
request: HttpRequest,
request_id: String,
) -> YaakResult<HttpResponse> {
let request = app_handle.db().get_http_request(&request_id)?;
let blobs = app_handle.blob_manager();
let response = app_handle.db().upsert_http_response(
&HttpResponse {
+24 -5
View File
@@ -8,6 +8,8 @@ import { newStoreData } from "./util";
let _store: JotaiStore | null = null;
const pendingModelWrites = new Set<Promise<unknown>>();
export function initModelStore(store: JotaiStore) {
_store = store;
@@ -42,6 +44,23 @@ function mustStore(): JotaiStore {
return _store;
}
function trackModelWrite<T>(write: Promise<T>): Promise<T> {
const tracked = write.finally(() => {
pendingModelWrites.delete(tracked);
});
pendingModelWrites.add(tracked);
return tracked;
}
export async function flushAllModelWrites(): Promise<void> {
const results = await Promise.allSettled([...pendingModelWrites]);
const rejected = results.find((result) => result.status === "rejected");
if (rejected?.status === "rejected") {
throw rejected.reason;
}
}
let _activeWorkspaceId: string | null = null;
export async function changeModelStoreWorkspace(workspaceId: string | null) {
@@ -117,7 +136,7 @@ export async function patchModel<M extends AnyModel["model"], T extends ExtractM
export async function updateModel<M extends AnyModel["model"], T extends ExtractModel<AnyModel, M>>(
model: T,
): Promise<string> {
return invoke<string>("models_upsert", { model });
return trackModelWrite(invoke<string>("models_upsert", { model }));
}
export async function deleteModelById<
@@ -134,7 +153,7 @@ export async function deleteModel<M extends AnyModel["model"], T extends Extract
if (model == null) {
throw new Error("Failed to delete null model");
}
await invoke<string>("models_delete", { model });
await trackModelWrite(invoke<string>("models_delete", { model }));
}
export function duplicateModel<M extends AnyModel["model"], T extends ExtractModel<AnyModel, M>>(
@@ -174,19 +193,19 @@ export function duplicateModel<M extends AnyModel["model"], T extends ExtractMod
}
}
return invoke<string>("models_duplicate", { model: { ...model, name } });
return trackModelWrite(invoke<string>("models_duplicate", { model: { ...model, name } }));
}
export async function createGlobalModel<T extends Exclude<AnyModel, { workspaceId: string }>>(
patch: Partial<T> & Pick<T, "model">,
): Promise<string> {
return invoke<string>("models_upsert", { model: patch });
return trackModelWrite(invoke<string>("models_upsert", { model: patch }));
}
export async function createWorkspaceModel<T extends Extract<AnyModel, { workspaceId: string }>>(
patch: Partial<T> & Pick<T, "model" | "workspaceId">,
): Promise<string> {
return invoke<string>("models_upsert", { model: patch });
return trackModelWrite(invoke<string>("models_upsert", { model: patch }));
}
export function replaceModelsInStore<