mirror of
https://github.com/penpot/penpot.git
synced 2026-10-31 23:10:31 +00:00
🐛 Keep the conflict refusal's error page and fix the guide
The backend answers a :conflict refusal (:file-modified, :file-locked) with HTTP 409 since the previous commit. On the frontend that type had no ptk/handle-error method, so the version-restore flow would have fallen to the generic toast instead of the error page the old 500 reached. errors.cljs now maps :conflict to the same exceptional-state handler. The technical guide's audit paragraph said rename, archive and delete report nothing; they report a duration and an outcome. The merge step list now records the re-validation after the repair and its refusal, and the update section names the same refusal. AI-assisted-by: mimo-v2.6-pro, claude-opus-5-5
This commit is contained in:
parent
ac3abfaf38
commit
0e51e5b3b8
@ -437,7 +437,11 @@ The flow:
|
||||
version history;
|
||||
* applies the change vector through `cpc/process-changes`, bumping `revn`;
|
||||
* **validates** the merged file (`cfv/validate-file`) and, if it finds
|
||||
problems, runs `cfr/repair-file` and applies the repair changes;
|
||||
problems, runs `cfr/repair-file` and applies the repair changes, then
|
||||
re-validates: a result that is still invalid refuses with
|
||||
`:merge-result-invalid`, so a bad repair never reaches main (the update
|
||||
refuses `:update-result-invalid` the same way, and materialise logs the
|
||||
result instead of refusing);
|
||||
* copies the `file_media_object` rows of media added on the branch into
|
||||
main, under the pre-allocated ids the changes reference;
|
||||
* writes a `file_change` (xlog) row, persists the file
|
||||
@ -536,8 +540,9 @@ permissions on the *branch* file, locks the *branch* file id, and applies main's
|
||||
changes (plus any conflicts resolved to `:main`) into the branch.
|
||||
|
||||
It works like a merge with `compute-merge`/`compute-changes` run with the sides
|
||||
swapped, the same `:conflicts` / `:unsupported` gates, and the same safety
|
||||
snapshot. Two extra steps at the end keep the op log sound:
|
||||
swapped, the same `:conflicts` / `:unsupported` gates, the same re-validation
|
||||
refusal (`:update-result-invalid`), and the same safety snapshot. Two extra
|
||||
steps at the end keep the op log sound:
|
||||
|
||||
* it **squashes the op log**: the repositioned base already carries
|
||||
everything main contributed, so the log is replaced with the net
|
||||
@ -616,19 +621,19 @@ branch's op log, attaches `:branch-operation`, `:branch-outcome` and
|
||||
`:branch-duration-ms` to its own audit event, because the generic RPC event
|
||||
records who called what and not how long it took, and duration is the number
|
||||
the first enterprise trial will be asked about. Creation, the listing,
|
||||
compare, merge, update-from-main, and materialise attach them through
|
||||
`audited`, and the listing adds `:branches-compared` and `:branches-cached`,
|
||||
the two numbers that explain its duration. The save attaches them on the
|
||||
`update-file` event, whose props it replaces. Every outcome these commands
|
||||
return carries them, the no-op merge, the idempotent materialise, and the
|
||||
`:conflicts` and `:unsupported` refusals included. A refusal raised as an
|
||||
error, such as a size gate's `:restriction` or `:file-modified`, carries
|
||||
none of them: the audit middleware records a call only when it returns, so
|
||||
the error writes no audit event at all.
|
||||
compare, merge, update-from-main, materialise, and the three small
|
||||
mutations (`update-file-branch`, `archive-file-branch`,
|
||||
`delete-file-branch`) attach them through `audited`, and the listing adds
|
||||
`:branches-compared` and `:branches-cached`, the two numbers that explain
|
||||
its duration. The save attaches them on the `update-file` event, whose
|
||||
props it replaces. Every outcome these commands return carries them, the
|
||||
no-op merge, the idempotent materialise, and the `:conflicts` and
|
||||
`:unsupported` refusals included. A refusal raised as an error, such as a
|
||||
size gate's `:restriction` or `:file-modified`, carries none of them: the
|
||||
audit middleware records a call only when it returns, so the error writes
|
||||
no audit event at all.
|
||||
|
||||
Three mutations are the exception: `update-file-branch`,
|
||||
`archive-file-branch`, and `delete-file-branch` do not run through `audited`
|
||||
and carry only the generic event. Two reads are the same: the branch
|
||||
Two reads are the exception and carry only the generic event: the branch
|
||||
context, which the frontend asks for on every file open, and the limits.
|
||||
|
||||
## RPC API summary
|
||||
|
||||
@ -795,6 +795,10 @@
|
||||
|
||||
(defmethod ptk/handle-error :not-found [error] (handle-exceptional-state error))
|
||||
(defmethod ptk/handle-error :bad-gateway [error] (handle-exceptional-state error))
|
||||
;; Refused optimistic-concurrency checks (:file-modified, :file-locked)
|
||||
;; got this surface as 500s. Keep it now the backend answers 409. Callers
|
||||
;; matching :code still find it in the error.
|
||||
(defmethod ptk/handle-error :conflict [error] (handle-exceptional-state error))
|
||||
(defmethod ptk/handle-error :service-unavailable [error] (handle-exceptional-state error))
|
||||
(defmethod ptk/handle-error :nitrate-unavailable [error] (handle-exceptional-state error))
|
||||
(defmethod ptk/handle-error :nitrate-not-configured [error] (handle-exceptional-state error))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user