mirror of
https://github.com/logseq/logseq.git
synced 2026-06-01 19:01:22 +00:00
fix(cli): upsert page on deleted/recycled page
restores it. Also disable editing of deleted page to keep consistent with app
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
[logseq.cli.transport :as transport]
|
||||
[logseq.common.graph :as common-graph]
|
||||
[logseq.common.util :as common-util]
|
||||
[logseq.db :as ldb]
|
||||
[logseq.db.frontend.property.type :as db-property-type]
|
||||
[promesa.core :as p]
|
||||
[logseq.db.frontend.property :as db-property]))
|
||||
@@ -596,9 +597,14 @@
|
||||
|
||||
(defn- ensure-page-entity!
|
||||
[config repo page-name]
|
||||
(p/let [existing (pull-page-by-name config repo page-name [:db/id :block/uuid])]
|
||||
(if (:db/id existing)
|
||||
(p/let [existing (pull-page-by-name config repo page-name
|
||||
[:db/id :block/uuid :logseq.property/deleted-at])]
|
||||
(if (and (:db/id existing) (not (ldb/recycled? existing)))
|
||||
existing
|
||||
;; Either no page exists, or only a recycled one does. Calling
|
||||
;; :create-page in both cases is correct: outliner-page/create has a
|
||||
;; (ldb/recycled? existing-page) branch that restores the recycled page
|
||||
;; instead of creating a duplicate.
|
||||
(p/let [result (transport/invoke config :thread-api/apply-outliner-ops false
|
||||
[repo [[:create-page [page-name {}]]] {}])
|
||||
;; create-page returns [title' page-uuid]; use uuid to find
|
||||
@@ -620,7 +626,7 @@
|
||||
:upsert-id-type-mismatch)
|
||||
|
||||
(def ^:private page-selector
|
||||
[:db/id :block/uuid :block/name :block/title])
|
||||
[:db/id :block/uuid :block/name :block/title :logseq.property/deleted-at])
|
||||
|
||||
(def ^:private tag-selector
|
||||
[:db/id :block/uuid :block/name :block/title
|
||||
@@ -665,7 +671,7 @@
|
||||
[config repo id]
|
||||
(p/let [entity (pull-entity-by-id config repo page-selector id)]
|
||||
(cond
|
||||
(not (:db/id entity))
|
||||
(or (not (:db/id entity)) (ldb/recycled? entity))
|
||||
(throw-upsert-id-not-found! "page" id)
|
||||
|
||||
(not (page-entity? entity))
|
||||
|
||||
@@ -3155,6 +3155,68 @@
|
||||
(p/catch (fn [e] (is false (str "unexpected error: " e))))
|
||||
(p/finally done)))))
|
||||
|
||||
(deftest test-execute-upsert-page-restores-recycled-page
|
||||
;; A recycled page with the same name must be treated as "not existing" so
|
||||
;; the create-page outliner op runs. The outliner's `create` already has a
|
||||
;; (ldb/recycled? existing-page) branch that restores the page in place,
|
||||
;; preventing duplicate :block/name entries.
|
||||
(async done
|
||||
(let [batches* (atom [])
|
||||
recycled-uuid (uuid "00000000-0000-0000-0000-0000000000ec")
|
||||
action {:type :upsert-page :repo "demo" :page "Home"
|
||||
:update-properties {:logseq.property/publishing-public? true}}]
|
||||
(-> (p/with-redefs [cli-server/list-graphs (fn [_] ["demo"])
|
||||
cli-server/ensure-server! (fn [_ _] {:base-url "http://example"})
|
||||
add-command/resolve-tags (fn [_ _ _] (p/resolved nil))
|
||||
add-command/resolve-properties (fn [_ _ properties & _] (p/resolved properties))
|
||||
add-command/resolve-property-identifiers (fn [_ _ properties & _] (p/resolved properties))
|
||||
transport/invoke (fn [_ method _ args]
|
||||
(case method
|
||||
:thread-api/pull (let [[_ _ lookup] args]
|
||||
(cond
|
||||
(= lookup [:block/name "home"])
|
||||
{:db/id 50
|
||||
:block/uuid recycled-uuid
|
||||
:logseq.property/deleted-at 1712000000000}
|
||||
(= lookup [:block/uuid recycled-uuid])
|
||||
{:db/id 50 :block/uuid recycled-uuid}
|
||||
(and (vector? lookup) (= :db/ident (first lookup)))
|
||||
{:db/id 999}
|
||||
:else {}))
|
||||
:thread-api/apply-outliner-ops (let [[_ ops _] args]
|
||||
(swap! batches* conj ops)
|
||||
["Home" recycled-uuid])
|
||||
(throw (ex-info "unexpected invoke" {:method method :args args}))))]
|
||||
(p/let [result (commands/execute action {})]
|
||||
(is (= :ok (:status result)))
|
||||
(is (some (fn [batch]
|
||||
(some #(= [:create-page ["Home" {}]] %) batch))
|
||||
@batches*)
|
||||
"create-page op is invoked, which restores the recycled page in the outliner")))
|
||||
(p/catch (fn [e] (is false (str "unexpected error: " e))))
|
||||
(p/finally done)))))
|
||||
|
||||
(deftest test-execute-upsert-page-by-id-rejects-recycled-page
|
||||
(async done
|
||||
(let [action {:type :upsert-page :mode :update :repo "demo" :id 50}]
|
||||
(-> (p/with-redefs [cli-server/list-graphs (fn [_] ["demo"])
|
||||
cli-server/ensure-server! (fn [_ _] {:base-url "http://example"})
|
||||
transport/invoke (fn [_ method _ _]
|
||||
(case method
|
||||
:thread-api/pull {:db/id 50
|
||||
:block/name "home"
|
||||
:block/title "Home"
|
||||
:block/uuid (uuid "00000000-0000-0000-0000-0000000000ed")
|
||||
:logseq.property/deleted-at 1712000000000}
|
||||
:thread-api/apply-outliner-ops
|
||||
(throw (ex-info "should not apply ops on recycled page" {:method method}))
|
||||
(throw (ex-info "unexpected invoke" {:method method}))))]
|
||||
(p/let [result (commands/execute action {})]
|
||||
(is (= :error (:status result)))
|
||||
(is (= :upsert-id-not-found (get-in result [:error :code])))))
|
||||
(p/catch (fn [e] (is false (str "unexpected error: " e))))
|
||||
(p/finally done)))))
|
||||
|
||||
(deftest test-execute-show-page-skips-recycled-page
|
||||
;; `execute-show` throws `ex-info` with `:code :page-not-found` rather than
|
||||
;; returning `:status :error`; the top-level CLI catches it. The test
|
||||
|
||||
Reference in New Issue
Block a user