Browse Source

enhance: dev commands for exporting and importing

a block data. Works within same graph currently
Gabriel Horner 9 months ago
parent
commit
085872ea64

+ 1 - 0
deps/db/.carve/config.edn

@@ -2,6 +2,7 @@
  :api-namespaces [logseq.db.sqlite.common-db
                   logseq.db.sqlite.util
                   logseq.db.sqlite.cli
+                  logseq.db.sqlite.export
                   logseq.db.frontend.property
                   logseq.db.frontend.property.build
                   logseq.db.frontend.property.util

+ 44 - 0
deps/db/src/logseq/db/sqlite/export.cljs

@@ -0,0 +1,44 @@
+(ns logseq.db.sqlite.export
+  "Builds sqlite.build EDN to represent nodes in a graph-agnostic way.
+   Useful for exporting and importing across DB graphs"
+  (:require [datascript.core :as d]
+            [datascript.impl.entity :as de]
+            [logseq.db.frontend.property :as db-property]
+            [logseq.db.sqlite.build :as sqlite-build]))
+
+(defn build-entity-export
+  "Given entity id, build an EDN map"
+  [db eid]
+  (let [entity (d/entity db eid)
+        properties (dissoc (:block/properties entity) :block/tags)
+        result (cond-> (select-keys entity [:block/title])
+                 (seq (:block/tags entity))
+                 (assoc :build/tags
+                        (mapv :db/ident (:block/tags entity)))
+                 (seq properties)
+                 (assoc :build/properties
+                        (->> properties
+                             (map (fn [[k v]]
+                                    [k
+                                     ;; Copied from readable-properties
+                                     (cond
+                                       (de/entity? v)
+                                       (or (:db/ident v) (db-property/property-value-content v))
+                                       (and (set? v) (every? de/entity? v))
+                                       (set (map db-property/property-value-content v))
+                                       :else
+                                       v)]))
+                             (into {}))))]
+    result))
+
+(defn build-entity-import
+  "Given an entity's export map, build the import tx to create it"
+  [db block]
+  (let [opts (cond-> {:pages-and-blocks [{:page (select-keys (:block/page block) [:block/title :block/uuid])
+                                          :blocks [block]}]
+                      :build-existing-tx? true}
+               (seq (:build/properties block))
+               (assoc :properties (into {}
+                                        (map #(vector % (select-keys (d/entity db %) [:logseq.property/type]))
+                                             (keys (:build/properties block))))))]
+    (sqlite-build/build-blocks-tx opts)))

+ 4 - 2
src/main/frontend/components/cmdk/core.cljs

@@ -482,10 +482,12 @@
       (reset! (::input state) search-query))))
 
 (defmethod handle-action :trigger [_ state _event]
-  (let [command (some-> state state->highlighted-item :source-command)]
+  (let [command (some-> state state->highlighted-item :source-command)
+        dont-close-commands #{:graph/open :graph/remove :dev/replace-graph-with-db-file
+                              :ui/toggle-settings :go/flashcards :dev/import-block-data}]
     (when-let [action (:action command)]
       (action)
-      (when-not (contains? #{:graph/open :graph/remove :dev/replace-graph-with-db-file :ui/toggle-settings :go/flashcards} (:id command))
+      (when-not (contains? dont-close-commands (:id command))
         (shui/dialog-close! :ls-dialog-cmdk)))))
 
 (defmethod handle-action :create [_ state _event]

+ 53 - 0
src/main/frontend/handler/common/developer.cljs

@@ -1,6 +1,7 @@
 (ns frontend.handler.common.developer
   "Common fns for developer related functionality"
   (:require [cljs.pprint :as pprint]
+            [clojure.edn :as edn]
             [datascript.impl.entity :as de]
             [frontend.config :as config]
             [frontend.db :as db]
@@ -9,8 +10,11 @@
             [frontend.persist-db :as persist-db]
             [frontend.state :as state]
             [frontend.ui :as ui]
+            [frontend.util :as util]
             [frontend.util.page :as page-util]
             [logseq.db.frontend.property :as db-property]
+            [logseq.db.sqlite.export :as sqlite-export]
+            [logseq.shui.ui :as shui]
             [promesa.core :as p]))
 
 ;; Fns used between menus and commands
@@ -62,6 +66,43 @@
      :success
      false)))
 
+(defn- export-entity-data
+  [eid]
+  (let [result (sqlite-export/build-entity-export (db/get-db) eid)
+        pull-data (with-out-str (pprint/pprint result))]
+    (.writeText js/navigator.clipboard pull-data)
+    (println pull-data)
+    (notification/show! "Copied block's data!" :success)))
+
+(defn- import-submit [block import-input _]
+  (let [new-block (edn/read-string @import-input)
+        updated-block (merge (select-keys block [:block/uuid])
+                             {:block/page (select-keys (:block/page block) [:block/title :block/uuid])}
+                             new-block)
+        {:keys [init-tx]} (sqlite-export/build-entity-import (db/get-db) updated-block)]
+    (pprint/pprint init-tx)
+    (db/transact! (state/get-current-repo) init-tx {:save-block true})
+    ;; Also close cmd-k
+    (shui/dialog-close-all!)))
+
+(defn- import-entity-data
+  [eid]
+  (let [import-input (atom nil)
+        block (db/entity eid)]
+    (shui/dialog-open!
+     [:div
+      [:label.flex.my-2 "Import into block with text " (pr-str (:block/title block))]
+      (shui/textarea {:placeholder "Import EDN"
+                      :auto-focus true
+                      :on-key-down (fn [e]
+                                     (when (= "Enter" (util/ekey e))
+                                       (import-submit block import-input e)
+                                       (util/stop e)))
+                      :on-change (fn [^js e] (reset! import-input (util/evalue e)))})
+      (shui/button {:class "mt-3"
+                    :on-click (partial import-submit block import-input)}
+                   "Import")])))
+
 ;; Public Commands
 (defn ^:export show-block-data []
   ;; Use editor state to locate most recent block
@@ -89,6 +130,18 @@
                           (get page-data :block/format :markdown))
         (notification/show! "No page found" :warning)))))
 
+(defn ^:export export-block-data []
+  ;; Use editor state to locate most recent block
+  (if-let [block-uuid (:block-id (first (state/get-editor-args)))]
+    (export-entity-data [:block/uuid block-uuid])
+    (notification/show! "No block found" :warning)))
+
+(defn ^:export import-block-data []
+  ;; Use editor state to locate most recent block
+  (if-let [block-uuid (:block-id (first (state/get-editor-args)))]
+    (import-entity-data [:block/uuid block-uuid])
+    (notification/show! "No block found" :warning)))
+
 (defn ^:export validate-db []
   (when-let [^Object worker @state/*db-worker]
     (.validate-db worker (state/get-current-repo))))

+ 12 - 0
src/main/frontend/modules/shortcut/config.cljs

@@ -609,6 +609,14 @@
                        :inactive (not (state/developer-mode?))
                        :fn :frontend.handler.common.developer/show-page-ast}
 
+   :dev/export-block-data {:binding []
+                           :inactive (not (state/developer-mode?))
+                           :fn :frontend.handler.common.developer/export-block-data}
+
+   :dev/import-block-data {:binding []
+                           :inactive (not (state/developer-mode?))
+                           :fn :frontend.handler.common.developer/import-block-data}
+
    :dev/validate-db   {:binding []
                        :inactive (not (state/developer-mode?))
                        :fn :frontend.handler.common.developer/validate-db}})
@@ -817,6 +825,8 @@
           :dev/show-block-ast
           :dev/show-page-data
           :dev/show-page-ast
+          :dev/export-block-data
+          :dev/import-block-data
           :dev/replace-graph-with-db-file
           :dev/validate-db
           :ui/customize-appearance])
@@ -1005,6 +1015,8 @@
      :dev/show-block-ast
      :dev/show-page-data
      :dev/show-page-ast
+     :dev/export-block-data
+     :dev/import-block-data
      :dev/replace-graph-with-db-file
      :dev/validate-db
      :ui/clear-all-notifications]

+ 2 - 0
src/resources/dicts/en.edn

@@ -798,6 +798,8 @@
   :dev/show-block-ast "(Dev) Show block AST"
   :dev/show-page-data "(Dev) Show page data"
   :dev/show-page-ast "(Dev) Show page AST"
+  :dev/export-block-data "(Dev) Export block data as EDN"
+  :dev/import-block-data "(Dev) Import block data as EDN"
   :dev/replace-graph-with-db-file "(Dev) Replace graph with its db.sqlite file"
   :dev/validate-db "(Dev) Validate current graph"
   :window/close "Close window"}}