mirror of
https://github.com/penpot/penpot.git
synced 2025-01-24 07:29:08 -05:00
✨ Add better file feature handling on file retrieval
This commit is contained in:
parent
90f5b4b631
commit
09d28d8583
3 changed files with 75 additions and 48 deletions
|
@ -189,6 +189,8 @@
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
(defn check-features-compatibility!
|
(defn check-features-compatibility!
|
||||||
|
"Function responsible to check if provided features are supported by
|
||||||
|
the current backend"
|
||||||
[features]
|
[features]
|
||||||
(let [not-supported (set/difference features supported-features)]
|
(let [not-supported (set/difference features supported-features)]
|
||||||
(when (seq not-supported)
|
(when (seq not-supported)
|
||||||
|
@ -248,47 +250,59 @@
|
||||||
(into #{} (comp (filter pmap/pointer-map?)
|
(into #{} (comp (filter pmap/pointer-map?)
|
||||||
(map pmap/get-id)))))
|
(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
|
;; 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)
|
;; --- COMMAND QUERY: get-file (by id)
|
||||||
|
|
||||||
(sm/def! ::features
|
(sm/def! ::features
|
||||||
|
@ -331,7 +345,7 @@
|
||||||
([conn id client-features]
|
([conn id client-features]
|
||||||
(get-file conn id client-features nil))
|
(get-file conn id client-features nil))
|
||||||
([conn id client-features project-id]
|
([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)
|
(check-features-compatibility! client-features)
|
||||||
(binding [pmap/*load-fn* (partial load-pointer conn id)]
|
(binding [pmap/*load-fn* (partial load-pointer conn id)]
|
||||||
(let [params (merge {:id id}
|
(let [params (merge {:id id}
|
||||||
|
|
|
@ -323,3 +323,13 @@
|
||||||
:rfn (fn [^Reader rdr]
|
:rfn (fn [^Reader rdr]
|
||||||
(let [^List x (read-object! 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))))})
|
(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})
|
||||||
|
|
||||||
|
|
|
@ -96,22 +96,25 @@
|
||||||
"Get the parent shape linked to a component for this shape, if any"
|
"Get the parent shape linked to a component for this shape, if any"
|
||||||
([objects shape] (get-component-shape objects shape nil))
|
([objects shape] (get-component-shape objects shape nil))
|
||||||
([objects shape {:keys [allow-main?] :or {allow-main? false} :as options}]
|
([objects shape {:keys [allow-main?] :or {allow-main? false} :as options}]
|
||||||
(cond
|
(cond
|
||||||
(nil? shape)
|
(nil? shape)
|
||||||
nil
|
nil
|
||||||
|
|
||||||
(and (not (ctk/in-component-copy? shape)) (not allow-main?))
|
(= uuid/zero (:id shape))
|
||||||
nil
|
nil
|
||||||
|
|
||||||
(ctk/instance-root? shape)
|
(and (not (ctk/in-component-copy? shape)) (not allow-main?))
|
||||||
shape
|
nil
|
||||||
|
|
||||||
:else
|
(ctk/instance-root? shape)
|
||||||
(get-component-shape objects (get objects (:parent-id shape)) options))))
|
shape
|
||||||
|
|
||||||
|
:else
|
||||||
|
(get-component-shape objects (get objects (:parent-id shape)) options))))
|
||||||
|
|
||||||
(defn in-component-main?
|
(defn in-component-main?
|
||||||
"Check if the shape is inside a component non-main instance.
|
"Check if the shape is inside a component non-main instance.
|
||||||
|
|
||||||
Note that we must iterate on the parents because non-root shapes in
|
Note that we must iterate on the parents because non-root shapes in
|
||||||
a main component have not any discriminating attribute."
|
a main component have not any discriminating attribute."
|
||||||
[objects shape]
|
[objects shape]
|
||||||
|
|
Loading…
Add table
Reference in a new issue