0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-23 23:18:48 -05:00

Add better file feature handling on file retrieval

This commit is contained in:
Andrey Antukh 2023-07-06 17:48:39 +02:00 committed by Alejandro Alonso
parent 90f5b4b631
commit 09d28d8583
3 changed files with 75 additions and 48 deletions

View file

@ -189,6 +189,8 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn check-features-compatibility!
"Function responsible to check if provided features are supported by
the current backend"
[features]
(let [not-supported (set/difference features supported-features)]
(when (seq not-supported)
@ -248,47 +250,59 @@
(into #{} (comp (filter pmap/pointer-map?)
(map pmap/get-id)))))
;; FIXME: file locking
(defn- process-components-v2-feature
"A special case handling of the components/v2 feature."
[conn {:keys [id features data] :as file}]
(binding [pmap/*tracked* (atom {})]
(let [data (ctf/migrate-to-components-v2 data)
features (conj features "components/v2")
features' (db/create-array conn "text" features)]
(db/update! conn :file
{:data (blob/encode data)
:features features'}
{:id id})
(persist-pointers! conn id)
(-> file
(assoc :features features)
(assoc :data data)))))
(defn handle-file-features!
[conn {:keys [features] :as file} client-features]
;; Check features compatibility between the currently supported features on
;; the current backend instance and the file retrieved from the database
(check-features-compatibility! features)
(cond-> file
(and (contains? features "components/v2")
(not (contains? client-features "components/v2")))
(as-> file (ex/raise :type :restriction
:code :feature-mismatch
:feature "components/v2"
:hint "file has 'components/v2' feature enabled but frontend didn't specifies it"
:file-id (:id file)))
;; This operation is needed because the components migration generates a new
;; page with random id which is returned to the client; without persisting
;; the migration this can cause that two simultaneous clients can have a
;; different view of the file data and end persisting two pages with main
;; components and breaking the whole file."
(and (contains? client-features "components/v2")
(not (contains? features "components/v2")))
(as-> file (process-components-v2-feature conn file))
;; This operation is needed for backward comapatibility with frontends that
;; does not support pointer-map resolution mechanism; this just resolves the
;; pointers on backend and return a complete file.
(and (contains? features "storage/pointer-map")
(not (contains? client-features "storage/pointer-map")))
(process-pointers deref)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; QUERY COMMANDS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn handle-file-features!
[conn {:keys [id features data] :as file} client-features]
(when (and (contains? features "components/v2")
(not (contains? client-features "components/v2")))
(ex/raise :type :restriction
:code :feature-mismatch
:feature "components/v2"
:hint "file has 'components/v2' feature enabled but frontend didn't specifies it"))
;; NOTE: this operation is needed because the components migration
;; generates a new page with random id which is returned to the
;; client; without persisting the migration this can cause that two
;; simultaneous clients can have a different view of the file data
;; and end persisting two pages with main components and breaking
;; the whole file
(let [file (if (and (contains? client-features "components/v2")
(not (contains? features "components/v2")))
(binding [pmap/*tracked* (atom {})]
(let [data (ctf/migrate-to-components-v2 data)
features (conj features "components/v2")
features' (db/create-array conn "text" features)]
(db/update! conn :file
{:data (blob/encode data)
:features features'}
{:id id})
(persist-pointers! conn id)
(-> file
(assoc :features features)
(assoc :data data))))
file)]
(cond-> file
(and (contains? features "storage/pointer-map")
(not (contains? client-features "storage/pointer-map")))
(process-pointers deref))))
;; --- COMMAND QUERY: get-file (by id)
(sm/def! ::features
@ -331,7 +345,7 @@
([conn id client-features]
(get-file conn id client-features nil))
([conn id client-features project-id]
;; here we check if client requested features are supported
;; here we check if client requested features are supported
(check-features-compatibility! client-features)
(binding [pmap/*load-fn* (partial load-pointer conn id)]
(let [params (merge {:id id}

View file

@ -323,3 +323,13 @@
:rfn (fn [^Reader rdr]
(let [^List x (read-object! rdr)]
(Matrix. (.get x 0) (.get x 1) (.get x 2) (.get x 3) (.get x 4) (.get x 5))))})
;; Backward compatibility for 1.19 with v1.20;
(add-handlers!
{:name "penpot/geom/rect"
:rfn read-map-like}
{:name "penpot/shape"
:rfn read-map-like})

View file

@ -96,22 +96,25 @@
"Get the parent shape linked to a component for this shape, if any"
([objects shape] (get-component-shape objects shape nil))
([objects shape {:keys [allow-main?] :or {allow-main? false} :as options}]
(cond
(nil? shape)
nil
(cond
(nil? shape)
nil
(and (not (ctk/in-component-copy? shape)) (not allow-main?))
nil
(= uuid/zero (:id shape))
nil
(ctk/instance-root? shape)
shape
(and (not (ctk/in-component-copy? shape)) (not allow-main?))
nil
:else
(get-component-shape objects (get objects (:parent-id shape)) options))))
(ctk/instance-root? shape)
shape
:else
(get-component-shape objects (get objects (:parent-id shape)) options))))
(defn in-component-main?
"Check if the shape is inside a component non-main instance.
Note that we must iterate on the parents because non-root shapes in
a main component have not any discriminating attribute."
[objects shape]