mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-09-10 03:41:52 +02:00
fix(models): don't mint a version for row ids the editor writes
#619 got here first and got more of it right. Two things adopted from it, and one backed out. **Adopted, and this one is a bug fix.** The pair editor generates an `id` for every header and parameter row the first time it touches a request, and that write reaches the database like any other. Stripping only the top-level `id` left those in, so merely opening a request minted a version whose diff was nothing but ids. `strip_ids` drops every `id` at every depth. The regression test fails without it. **Adopted.** Hashes carry a `v1:` prefix, so a hash written by an algorithm a build doesn't understand is recognizable as unreadable rather than silently read as "different". And `sorted_keys` + `to_string` replaces the hand-rolled canonical writer — same guarantee, less code, and byte-identical to the implementation that is landing. **Backed out.** `comparable()` in import.rs goes back to its own copy. #619 rewrites that function and most of the file around it, including dropping `sortPriority` from the comparison, so pointing it at the shared module now would collide in the hottest region of that branch to save six lines. This module is shaped to be what #619 calls when it lands, and says so; the convergence is one deletion away rather than a reconciliation. That leaves `folderId` as the only real disagreement between the two callers, which PLACEMENT_KEYS now documents as such: import counts a move between folders as a change, versioning must not.
This commit is contained in:
@@ -4,7 +4,6 @@ use log::info;
|
||||
use serde_json::Value;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use yaak_models::client_db::ClientDb;
|
||||
use yaak_models::content::{IDENTITY_KEYS, without_keys};
|
||||
use yaak_models::models::{
|
||||
AnyModel, DEFAULT_REQUEST_MESSAGE_SIZE, Environment, Folder, GrpcRequest, HttpRequest,
|
||||
ImportSource, ImportSourceResource, UpsertModelInfo, WebsocketRequest, Workspace,
|
||||
@@ -842,18 +841,15 @@ fn create_only_items(plan: &ImportPlan) -> Vec<ImportPlanItem> {
|
||||
items
|
||||
}
|
||||
|
||||
/// Strip identity fields so equality means "same content in the same place".
|
||||
///
|
||||
/// Placement (`folderId`, `sortPriority`) is deliberately *kept*, which is
|
||||
/// where this parts company with request versioning: a re-import that moved a
|
||||
/// resource somewhere else is a change worth showing, while dragging a request
|
||||
/// around the sidebar is not an edit. See [`yaak_models::content`].
|
||||
///
|
||||
/// The deprecated environment `base` flag mirrors `parentModel`, which is
|
||||
/// compared already.
|
||||
fn comparable(value: Value) -> Value {
|
||||
let stripped = [IDENTITY_KEYS, &["base"]].concat();
|
||||
without_keys(value, &stripped)
|
||||
/// Strip identity and bookkeeping fields so equality means "same content in the same place".
|
||||
/// The deprecated environment `base` flag mirrors `parentModel`, which is compared already.
|
||||
fn comparable(mut value: Value) -> Value {
|
||||
if let Some(object) = value.as_object_mut() {
|
||||
for field in ["id", "model", "workspaceId", "createdAt", "updatedAt", "base"] {
|
||||
object.remove(field);
|
||||
}
|
||||
}
|
||||
value
|
||||
}
|
||||
|
||||
fn existing_model_json(
|
||||
@@ -1208,42 +1204,6 @@ mod tests {
|
||||
use serde_json::json;
|
||||
use yaak_models::models::{EnvironmentVariable, HttpRequestHeader};
|
||||
|
||||
/// Import and request versioning share the stripping mechanism but not the
|
||||
/// key list, and this is the difference. Versioning drops placement so
|
||||
/// dragging a request around the sidebar is not an edit; import keeps it so
|
||||
/// a source that moved a resource reads as a change. Unifying the two lists
|
||||
/// would silently make a re-import stop noticing moves.
|
||||
#[test]
|
||||
fn comparable_treats_a_move_as_a_change_but_ignores_identity() {
|
||||
let base = json!({
|
||||
"model": "http_request",
|
||||
"id": "rq_1",
|
||||
"workspaceId": "wk_1",
|
||||
"createdAt": "2026-01-01T00:00:00",
|
||||
"updatedAt": "2026-01-01T00:00:00",
|
||||
"folderId": "fl_1",
|
||||
"sortPriority": 1.0,
|
||||
"url": "https://example.com",
|
||||
});
|
||||
|
||||
let mut renamed_identity = base.clone();
|
||||
renamed_identity["id"] = json!("rq_2");
|
||||
renamed_identity["updatedAt"] = json!("2026-09-06T00:00:00");
|
||||
assert_eq!(
|
||||
comparable(renamed_identity),
|
||||
comparable(base.clone()),
|
||||
"identity and timestamps are never content",
|
||||
);
|
||||
|
||||
let mut moved = base.clone();
|
||||
moved["folderId"] = json!("fl_2");
|
||||
assert_ne!(comparable(moved), comparable(base.clone()), "a move is a change");
|
||||
|
||||
let mut resorted = base.clone();
|
||||
resorted["sortPriority"] = json!(99.0);
|
||||
assert_ne!(comparable(resorted), comparable(base), "a reorder is a change");
|
||||
}
|
||||
|
||||
fn destination_workspace() -> Workspace {
|
||||
Workspace {
|
||||
id: "wk_destination".to_string(),
|
||||
|
||||
Reference in New Issue
Block a user