mirror of
https://github.com/penpot/penpot.git
synced 2025-05-13 22:26:39 +02:00
🚧 Initial work on ops based page data updates.
This commit is contained in:
parent
5b96e1e9fd
commit
db768f356b
5 changed files with 94 additions and 44 deletions
|
@ -68,7 +68,7 @@ CREATE TABLE IF NOT EXISTS project_pages (
|
||||||
metadata bytea NULL DEFAULT NULL
|
metadata bytea NULL DEFAULT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS project_page_history (
|
CREATE TABLE IF NOT EXISTS project_page_snapshots (
|
||||||
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
|
||||||
user_id uuid NULL REFERENCES users(id) ON DELETE SET NULL,
|
user_id uuid NULL REFERENCES users(id) ON DELETE SET NULL,
|
||||||
|
@ -81,7 +81,8 @@ CREATE TABLE IF NOT EXISTS project_page_history (
|
||||||
pinned bool NOT NULL DEFAULT false,
|
pinned bool NOT NULL DEFAULT false,
|
||||||
label text NOT NULL DEFAULT '',
|
label text NOT NULL DEFAULT '',
|
||||||
|
|
||||||
data bytea NOT NULL
|
data bytea NOT NULL,
|
||||||
|
operations bytea NULL DEFAULT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Indexes
|
-- Indexes
|
||||||
|
@ -94,8 +95,8 @@ CREATE INDEX project_files__project_id__idx ON project_files(project_id);
|
||||||
CREATE INDEX project_pages__user_id__idx ON project_pages(user_id);
|
CREATE INDEX project_pages__user_id__idx ON project_pages(user_id);
|
||||||
CREATE INDEX project_pages__file_id__idx ON project_pages(file_id);
|
CREATE INDEX project_pages__file_id__idx ON project_pages(file_id);
|
||||||
|
|
||||||
CREATE INDEX project_page_history__page_id__idx ON project_page_history(page_id);
|
CREATE INDEX project_page_snapshots__page_id__idx ON project_page_snapshots(page_id);
|
||||||
CREATE INDEX project_page_history__user_id__idx ON project_page_history(user_id);
|
CREATE INDEX project_page_snapshots__user_id__idx ON project_page_snapshots(user_id);
|
||||||
|
|
||||||
-- Triggers
|
-- Triggers
|
||||||
|
|
||||||
|
@ -152,6 +153,6 @@ CREATE TRIGGER project_pages__modified_at__tgr
|
||||||
BEFORE UPDATE ON project_pages
|
BEFORE UPDATE ON project_pages
|
||||||
FOR EACH ROW EXECUTE PROCEDURE update_modified_at();
|
FOR EACH ROW EXECUTE PROCEDURE update_modified_at();
|
||||||
|
|
||||||
CREATE TRIGGER project_page_history__modified_at__tgr
|
CREATE TRIGGER project_page_snapshots__modified_at__tgr
|
||||||
BEFORE UPDATE ON project_page_history
|
BEFORE UPDATE ON project_page_snapshots
|
||||||
FOR EACH ROW EXECUTE PROCEDURE update_modified_at();
|
FOR EACH ROW EXECUTE PROCEDURE update_modified_at();
|
||||||
|
|
|
@ -56,11 +56,11 @@
|
||||||
(-> (db/query-one conn [sql id user file-id name ordering data mdata])
|
(-> (db/query-one conn [sql id user file-id name ordering data mdata])
|
||||||
(p/then' decode-row))))
|
(p/then' decode-row))))
|
||||||
|
|
||||||
;; --- Mutation: Update Page
|
;; --- Mutation: Update Page Data
|
||||||
|
|
||||||
(declare select-page-for-update)
|
(declare select-page-for-update)
|
||||||
(declare update-page)
|
(declare update-page-data)
|
||||||
(declare update-history)
|
(declare insert-page-snapshot)
|
||||||
|
|
||||||
(s/def ::update-project-page-data
|
(s/def ::update-project-page-data
|
||||||
(s/keys :req-un [::id ::user ::data]))
|
(s/keys :req-un [::id ::user ::data]))
|
||||||
|
@ -73,13 +73,13 @@
|
||||||
(let [data (blob/encode data)
|
(let [data (blob/encode data)
|
||||||
version (inc version)
|
version (inc version)
|
||||||
params (assoc params :id id :data data :version version)]
|
params (assoc params :id id :data data :version version)]
|
||||||
(p/do! (update-page conn params)
|
(p/do! (update-page-data conn params)
|
||||||
(update-history conn params)
|
(insert-page-snapshot conn params)
|
||||||
(select-keys params [:id :version]))))))
|
(select-keys params [:id :version]))))))
|
||||||
|
|
||||||
(defn- select-page-for-update
|
(defn- select-page-for-update
|
||||||
[conn id]
|
[conn id]
|
||||||
(let [sql "select p.id, p.version, p.file_id
|
(let [sql "select p.id, p.version, p.file_id, p.data
|
||||||
from project_pages as p
|
from project_pages as p
|
||||||
where p.id = $1
|
where p.id = $1
|
||||||
and deleted_at is null
|
and deleted_at is null
|
||||||
|
@ -87,8 +87,8 @@
|
||||||
(-> (db/query-one conn [sql id])
|
(-> (db/query-one conn [sql id])
|
||||||
(p/then' su/raise-not-found-if-nil))))
|
(p/then' su/raise-not-found-if-nil))))
|
||||||
|
|
||||||
(defn- update-page
|
(defn- update-page-data
|
||||||
[conn {:keys [id name version data metadata]}]
|
[conn {:keys [id name version data]}]
|
||||||
(let [sql "update project_pages
|
(let [sql "update project_pages
|
||||||
set version = $1,
|
set version = $1,
|
||||||
data = $2
|
data = $2
|
||||||
|
@ -96,12 +96,12 @@
|
||||||
(-> (db/query-one conn [sql version data id])
|
(-> (db/query-one conn [sql version data id])
|
||||||
(p/then' su/constantly-nil))))
|
(p/then' su/constantly-nil))))
|
||||||
|
|
||||||
(defn- update-history
|
(defn- insert-page-snapshot
|
||||||
[conn {:keys [user id version data]}]
|
[conn {:keys [user id version data operations]}]
|
||||||
(let [sql "insert into project_page_history (user_id, page_id, version, data)
|
(let [sql "insert into project_page_snapshots (user_id, page_id, version, data, operations)
|
||||||
values ($1, $2, $3, $4)"]
|
values ($1, $2, $3, $4, $5)
|
||||||
(-> (db/query-one conn [sql user id version data])
|
returning id, version, operations"]
|
||||||
(p/then' su/constantly-nil))))
|
(db/query-one conn [sql user id version data operations])))
|
||||||
|
|
||||||
;; --- Mutation: Rename Page
|
;; --- Mutation: Rename Page
|
||||||
|
|
||||||
|
@ -126,23 +126,60 @@
|
||||||
(-> (db/query-one db/pool [sql id name])
|
(-> (db/query-one db/pool [sql id name])
|
||||||
(p/then su/constantly-nil))))
|
(p/then su/constantly-nil))))
|
||||||
|
|
||||||
;; --- Mutation: Update Page Metadata
|
;; --- Mutation: Update Page
|
||||||
|
|
||||||
;; (s/def ::update-page-metadata
|
;; A generic, Ops based (granular) page update method.
|
||||||
;; (s/keys :req-un [::user ::project-id ::name ::metadata ::id]))
|
|
||||||
|
|
||||||
;; (sm/defmutation ::update-page-metadata
|
(s/def ::operations
|
||||||
;; [{:keys [id user project-id name metadata]}]
|
(s/coll-of ::cp/opeation :kind vector?))
|
||||||
;; (let [sql "update pages
|
|
||||||
;; set name = $3,
|
(s/def ::update-project-page
|
||||||
;; metadata = $4
|
(s/keys :opt-un [::id ::user ::version ::operations]))
|
||||||
;; where id = $1
|
|
||||||
;; and user_id = $2
|
(declare update-project-page)
|
||||||
;; and deleted_at is null
|
(declare retrieve-lagged-operations)
|
||||||
;; returning *"
|
|
||||||
;; mdata (blob/encode metadata)]
|
(sm/defmutation ::update-project-page
|
||||||
;; (-> (db/query-one db/pool [sql id user name mdata])
|
[{:keys [id user] :as params}]
|
||||||
;; (p/then' decode-row))))
|
(db/with-atomic [conn db/pool]
|
||||||
|
(p/let [{:keys [file-id] :as page} (select-page-for-update conn id)]
|
||||||
|
(files/check-edition-permissions! conn user file-id)
|
||||||
|
(update-project-page conn page params))))
|
||||||
|
|
||||||
|
(defn- update-project-page
|
||||||
|
[conn page params]
|
||||||
|
(when (> (:version page)
|
||||||
|
(:version params))
|
||||||
|
(ex/raise :type :validation
|
||||||
|
:code :version-conflict
|
||||||
|
:hint "The incoming version is greater that stored version."
|
||||||
|
:context {:incoming-version (:version params)
|
||||||
|
:stored-version (:version page)}))
|
||||||
|
(let [ops (:operations params)
|
||||||
|
data (-> (:data page)
|
||||||
|
(blob/decode)
|
||||||
|
(cp/process-ops ops)
|
||||||
|
(blob/encode))
|
||||||
|
page (assoc page
|
||||||
|
:data data
|
||||||
|
:version (inc (:version page))
|
||||||
|
:operations (blob/encode ops))]
|
||||||
|
(-> (update-page-data conn page)
|
||||||
|
(p/then (fn [_] (insert-page-snapshot conn page)))
|
||||||
|
(p/then (fn [s] (retrieve-lagged-operations conn s params))))))
|
||||||
|
|
||||||
|
(su/defstr sql:lagged-snapshots
|
||||||
|
"select s.id, s.version, s.operations,
|
||||||
|
s.created_at, s.modified_at, s.user_id
|
||||||
|
from project_page_snapshots as s
|
||||||
|
where s.page_id = $1
|
||||||
|
and s.version > $2")
|
||||||
|
|
||||||
|
(defn- retrieve-lagged-operations
|
||||||
|
[conn snapshot params]
|
||||||
|
(let [sql sql:lagged-snapshots]
|
||||||
|
(-> (db/query conn [sql (:id params) (:version params)])
|
||||||
|
(p/then (partial mapv decode-row)))))
|
||||||
|
|
||||||
;; --- Mutation: Delete Page
|
;; --- Mutation: Delete Page
|
||||||
|
|
||||||
|
@ -158,12 +195,15 @@
|
||||||
(files/check-edition-permissions! conn user (:file-id page))
|
(files/check-edition-permissions! conn user (:file-id page))
|
||||||
(delete-page conn id))))
|
(delete-page conn id))))
|
||||||
|
|
||||||
(defn- delete-page
|
(su/defstr sql:delete-page
|
||||||
[conn id]
|
"update project_pages
|
||||||
(let [sql "update project_pages
|
|
||||||
set deleted_at = clock_timestamp()
|
set deleted_at = clock_timestamp()
|
||||||
where id = $1
|
where id = $1
|
||||||
and deleted_at is null"]
|
and deleted_at is null")
|
||||||
|
|
||||||
|
(defn- delete-page
|
||||||
|
[conn id]
|
||||||
|
(let [sql sql:delete-page]
|
||||||
(-> (db/query-one conn [sql id])
|
(-> (db/query-one conn [sql id])
|
||||||
(p/then su/constantly-nil))))
|
(p/then su/constantly-nil))))
|
||||||
|
|
||||||
|
@ -178,7 +218,7 @@
|
||||||
;; (some-> (db/fetch-one conn sqlv)
|
;; (some-> (db/fetch-one conn sqlv)
|
||||||
;; (decode-row))))
|
;; (decode-row))))
|
||||||
|
|
||||||
;; (s/def ::label ::us/string)
|
;; (s/def ::label ::cs/string)
|
||||||
;; (s/def ::update-page-history
|
;; (s/def ::update-page-history
|
||||||
;; (s/keys :req-un [::user ::id ::pinned ::label]))
|
;; (s/keys :req-un [::user ::id ::pinned ::label]))
|
||||||
|
|
||||||
|
|
|
@ -139,8 +139,9 @@
|
||||||
;; --- Helpers
|
;; --- Helpers
|
||||||
|
|
||||||
(defn decode-row
|
(defn decode-row
|
||||||
[{:keys [data metadata] :as row}]
|
[{:keys [data metadata operations] :as row}]
|
||||||
(when row
|
(when row
|
||||||
(cond-> row
|
(cond-> row
|
||||||
data (assoc :data (blob/decode data))
|
data (assoc :data (blob/decode data))
|
||||||
metadata (assoc :metadata (blob/decode metadata)))))
|
metadata (assoc :metadata (blob/decode metadata))
|
||||||
|
operations (assoc :operations (blob/decode operations)))))
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
[uxbox.http :as http]
|
[uxbox.http :as http]
|
||||||
[uxbox.services.mutations :as sm]
|
[uxbox.services.mutations :as sm]
|
||||||
[uxbox.services.queries :as sq]
|
[uxbox.services.queries :as sq]
|
||||||
|
[uxbox.util.uuid :as uuid]
|
||||||
[uxbox.tests.helpers :as th]))
|
[uxbox.tests.helpers :as th]))
|
||||||
|
|
||||||
(t/use-fixtures :once th/state-init)
|
(t/use-fixtures :once th/state-init)
|
||||||
|
@ -34,7 +35,9 @@
|
||||||
pf @(th/create-project-file db/pool (:id user) (:id proj) 1)
|
pf @(th/create-project-file db/pool (:id user) (:id proj) 1)
|
||||||
|
|
||||||
data {::sm/type :create-project-page
|
data {::sm/type :create-project-page
|
||||||
:data {}
|
:data {:canvas []
|
||||||
|
:shapes []
|
||||||
|
:shapes-by-id {}}
|
||||||
:metadata {}
|
:metadata {}
|
||||||
:file-id (:id pf)
|
:file-id (:id pf)
|
||||||
:ordering 1
|
:ordering 1
|
||||||
|
@ -57,7 +60,9 @@
|
||||||
page @(th/create-project-page db/pool (:id user) (:id file) 1)
|
page @(th/create-project-page db/pool (:id user) (:id file) 1)
|
||||||
data {::sm/type :update-project-page-data
|
data {::sm/type :update-project-page-data
|
||||||
:id (:id page)
|
:id (:id page)
|
||||||
:data {:shapes [1 2 3]}
|
:data {:shapes [(uuid/next)]
|
||||||
|
:canvas []
|
||||||
|
:shapes-by-id {}}
|
||||||
:file-id (:id file)
|
:file-id (:id file)
|
||||||
:user (:id user)}
|
:user (:id user)}
|
||||||
out (th/try-on! (sm/handle data))]
|
out (th/try-on! (sm/handle data))]
|
||||||
|
|
|
@ -82,6 +82,9 @@
|
||||||
"A marker protocol for mark events that alters the
|
"A marker protocol for mark events that alters the
|
||||||
page and is subject to perform a backend synchronization.")
|
page and is subject to perform a backend synchronization.")
|
||||||
|
|
||||||
|
(defprotocol IPageOps
|
||||||
|
(-ops [_] "Get a list of ops for the event."))
|
||||||
|
|
||||||
(defn page-update?
|
(defn page-update?
|
||||||
[o]
|
[o]
|
||||||
(or (satisfies? IPageDataUpdate o)
|
(or (satisfies? IPageDataUpdate o)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue