瀏覽代碼

ensure no missing :block/parent

Tienson Qin 3 周之前
父節點
當前提交
d0813af686
共有 2 個文件被更改,包括 105 次插入2 次删除
  1. 48 2
      src/main/frontend/worker/db_sync.cljs
  2. 57 0
      src/test/frontend/worker/db_sync_test.cljs

+ 48 - 2
src/main/frontend/worker/db_sync.cljs

@@ -763,6 +763,50 @@
             (take 3 item)
             item)))))
 
+(defn- ensure-block-parents
+  "Ensure block entities don't lose :block/parent without becoming pages."
+  [db tx-data]
+  (let [last-parent-op (atom {})
+        page-retracts (atom #{})
+        name-adds (atom #{})
+        retract-entities (atom #{})]
+    (doseq [item tx-data]
+      (when (vector? item)
+        (let [op (first item)]
+          (cond
+            (= op :db/retractEntity)
+            (swap! retract-entities conj (second item))
+
+            (and (contains? #{:db/add :db/retract} op)
+                 (>= (count item) 4))
+            (let [e (second item)
+                  a (nth item 2)]
+              (cond
+                (= a :block/parent)
+                (swap! last-parent-op assoc e {:op op :v (nth item 3)})
+
+                (and (= op :db/retract) (= a :block/page))
+                (swap! page-retracts conj e)
+
+                (and (= op :db/add) (= a :block/name))
+                (swap! name-adds conj e)))))))
+    (let [candidates (->> @last-parent-op
+                          (keep (fn [[e {:keys [op]}]]
+                                  (when (= op :db/retract) e)))
+                          (remove @retract-entities)
+                          (remove @page-retracts)
+                          (remove @name-adds))
+          fixes (keep (fn [e]
+                        (when-let [ent (d/entity db e)]
+                          (when (and (:block/page ent)
+                                     (not (ldb/page? ent)))
+                            (when-let [page-uuid (:block/uuid (:block/page ent))]
+                              [:db/add e :block/parent [:block/uuid page-uuid]]))))
+                      candidates)]
+      (if (seq fixes)
+        (into tx-data fixes)
+        tx-data))))
+
 (defn- sanitize-tx-data
   [db tx-data local-deleted-ids]
   (let [sanitized-tx-data (->> tx-data
@@ -773,7 +817,8 @@
                                                   (contains? #{:block/created-at :block/updated-at :block/title}
                                                              (nth item 2)))
                                              (contains? local-deleted-ids (get-lookup-id (last item))))))
-                               keep-last-update)]
+                               keep-last-update)
+        sanitized-tx-data (ensure-block-parents db sanitized-tx-data)]
     ;; (when (not= tx-data sanitized-tx-data)
     ;;   (prn :debug :tx-data tx-data)
     ;;   (prn :debug :sanitized-tx-data sanitized-tx-data))
@@ -796,7 +841,8 @@
                       tx-data (->> txs
                                    (db-normalize/remove-retract-entity-ref @conn)
                                    keep-last-update
-                                   distinct)]
+                                   distinct)
+                      tx-data (ensure-block-parents @conn tx-data)]
                   ;; (prn :debug :before-keep-last-update txs)
                   ;; (prn :debug :upload :tx-data tx-data)
                   (when (seq txs)

+ 57 - 0
src/test/frontend/worker/db_sync_test.cljs

@@ -67,6 +67,63 @@
             (is (= (:db/id child1') (:db/id (:block/parent parent'))))
             (is (= (:db/id page') (:db/id (:block/parent child1'))))))))))
 
+(deftest ensure-block-parents-test
+  (let [{:keys [conn child1 parent]} (setup-parent-child)
+        child-uuid (:block/uuid child1)
+        parent-uuid (:block/uuid parent)
+        page-uuid (:block/uuid (:block/page child1))]
+    (testing "adds parent when a block loses parent without becoming a page"
+      (let [tx-data [[:db/retract [:block/uuid child-uuid] :block/parent [:block/uuid parent-uuid] 1]]
+            fixed (#'db-sync/ensure-block-parents @conn tx-data)]
+        (is (some (fn [item]
+                    (and (= :db/add (first item))
+                         (= [:block/uuid child-uuid] (second item))
+                         (= :block/parent (nth item 2))
+                         (= [:block/uuid page-uuid] (nth item 3))))
+                  fixed))))
+    (testing "does not add parent when converting to page"
+      (let [tx-data [[:db/retract [:block/uuid child-uuid] :block/parent [:block/uuid parent-uuid] 1]
+                     [:db/add [:block/uuid child-uuid] :block/name "page" 1]]
+            fixed (#'db-sync/ensure-block-parents @conn tx-data)]
+        (is (= tx-data fixed))))
+    (testing "does not add parent when entity is retracted"
+      (let [tx-data [[:db/retract [:block/uuid child-uuid] :block/parent [:block/uuid parent-uuid] 1]
+                     [:db/retractEntity [:block/uuid child-uuid]]]
+            fixed (#'db-sync/ensure-block-parents @conn tx-data)]
+        (is (= tx-data fixed))))))
+
+(deftest ensure-block-parents-last-op-retract-test
+  (let [{:keys [conn child1 parent]} (setup-parent-child)
+        child-uuid (:block/uuid child1)
+        parent-uuid (:block/uuid parent)
+        page-uuid (:block/uuid (:block/page child1))]
+    (testing "retract after add still reparents to page"
+      (let [tx-data [[:db/add [:block/uuid child-uuid] :block/parent [:block/uuid parent-uuid] 1]
+                     [:db/retract [:block/uuid child-uuid] :block/parent [:block/uuid parent-uuid] 2]]
+            fixed (#'db-sync/ensure-block-parents @conn tx-data)]
+        (is (some (fn [item]
+                    (and (= :db/add (first item))
+                         (= [:block/uuid child-uuid] (second item))
+                         (= :block/parent (nth item 2))
+                         (= [:block/uuid page-uuid] (nth item 3))))
+                  fixed))))))
+
+(deftest sanitize-tx-data-ensures-parent-test
+  (let [{:keys [conn child1 parent]} (setup-parent-child)
+        child-uuid (:block/uuid child1)
+        parent-uuid (:block/uuid parent)
+        page-uuid (:block/uuid (:block/page child1))]
+    (testing "sanitize-tx-data preserves parent for blocks when last op retracts parent"
+      (let [tx-data [[:db/add [:block/uuid child-uuid] :block/parent [:block/uuid parent-uuid] 1]
+                     [:db/retract [:block/uuid child-uuid] :block/parent [:block/uuid parent-uuid] 2]]
+            fixed (#'db-sync/sanitize-tx-data @conn tx-data #{})]
+        (is (some (fn [item]
+                    (and (= :db/add (first item))
+                         (= [:block/uuid child-uuid] (second item))
+                         (= :block/parent (nth item 2))
+                         (= [:block/uuid page-uuid] (nth item 3))))
+                  fixed))))))
+
 (deftest encrypt-decrypt-tx-data-test
   (async done
          (-> (p/let [aes-key (crypt/<generate-aes-key)