fix(cli): merge partial scenario updates - #769
Conversation
Greptile SummaryThe PATCH scenario endpoint now merges partial updates into existing scenarios while retaining full-document upsert compatibility.
Confidence Score: 5/5The PR appears safe to merge. The recursive supplied-versus-normalized field validation now rejects nested unknown override fields before stored scenarios are mutated, so no blocking failure remains. Important Files Changed
Reviews (2): Last reviewed commit: "fix(cli): reject nested unknown scenario..." | Re-trigger Greptile |
| if key != "id" && !obj.contains_key(key) { | ||
| return Err(format!("Unknown scenario field '{key}'")); | ||
| } |
There was a problem hiding this comment.
Nested unknown fields bypass validation
When a PATCH contains a misspelled or unsupported field inside an overrides element, the top-level allowlist accepts overrides and permissive deserialization discards the nested field, causing the endpoint to return success even though the requested property was not stored.
There was a problem hiding this comment.
@greptileai check commit 66e589a for a solution, review it
There was a problem hiding this comment.
The solution in this commit effectively addresses the original concern. Here's what was added:
deserialize_scenario_strictly round-trips the value through serde (from_value → Scenario → to_value) to get a normalized reference, then calls validate_json_fields to recursively compare the supplied JSON against it.
validate_json_fields walks both trees in parallel — if a key exists in supplied but is absent from normalized (because serde dropped it as unknown), it returns an error with the full dotted path like "Unknown field 'scenario.overrides[0].fetchBeforeUes'".
The test a_nested_unknown_override_field_is_rejected_without_mutating specifically covers the case from my original comment (a misspelled fetchBeforeUes inside an override element), and the arbitrary_nested_override_values_remain_valid test confirms that the open-ended values map still accepts arbitrary nested content — which is the right carve-out since values is typed as serde_json::Value.
One minor edge case to be aware of: the array comparison uses .zip(), which silently truncates if supplied has more elements than normalized. In practice this can't happen here because the supplied array passes through serde unchanged, but it's worth noting if the logic is ever reused in a context where serde could produce a shorter array (e.g., deduplication).
Overall this looks good — the recursive field validation is the right approach and the tests cover the key scenarios.
PATCH /v1/scenarios/{id} currently replaces the complete stored scenario. A client that sends only a changed name or description can therefore erase overrides, tags, and backend-only data.
PATCH now merges supplied fields into an existing scenario, rejects unknown fields, and keeps the path id authoritative. Complete scenario documents retain the existing upsert behavior for compatibility.