enhance(cli): show command displays public built-in props

Adds support for show to display several public built-in props
that weren't visible for. Confirmed this works for scheduled,
deadline, priority, description and user* properties
This commit is contained in:
Gabriel Horner
2026-04-06 11:59:04 -04:00
parent 1f576f1f13
commit a0fb6722aa
3 changed files with 70 additions and 12 deletions

View File

@@ -26,7 +26,7 @@
* :cardinality - property cardinality. Default to one/single cardinality if not set
* :hide? - Boolean which hides property when set on a block or exported e.g. slides
* :public? - Boolean which allows property to be used by user: add and remove property to blocks/pages
and queryable via property and has-property rules
and queryable via property and has-property rules. When it's not set, it's the same as false
* :view-context - Keyword to indicate which view contexts a property can be
seen in when :public? is set. Valid values are :page, :block and :never. Property can
be viewed in any context if not set
@@ -662,6 +662,11 @@
:public? false
:hide? true}})))
(def public-built-in-properties
(->> built-in-properties
(keep (fn [[k v]] (when (get-in v [:schema :public?]) k)))
set))
(def db-attribute-properties
"Internal properties that are also db schema attributes"
#{:block/alias :block/tags :block/parent

View File

@@ -9,7 +9,9 @@
[logseq.cli.style :as style]
[logseq.cli.transport :as transport]
[logseq.common.util :as common-util]
[promesa.core :as p]))
[promesa.core :as p]
[clojure.set :as set]
[logseq.db.frontend.property :as db-property]))
(def ^:private show-spec
{:id {:desc "Block db/id or EDN vector of ids"}
@@ -154,12 +156,21 @@
(when (seq labels)
(string/join " " (map #(style/bold (str "#" %)) labels)))))
(def ^:private user-property-namespace "user.property")
(def ^:private displayable-built-in-properties
"Built-in properties that are displayed alongside user properties."
(set/difference db-property/public-built-in-properties
;; Exclude built-in properties handled elsewhere in this command
#{:block/tags :logseq.property/status}))
(defn- user-property-key?
[k]
(and (qualified-keyword? k)
(= user-property-namespace (namespace k))))
(= db-property/default-user-namespace (namespace k))))
(defn- displayable-property-key?
[k]
(or (user-property-key? k)
(contains? displayable-built-in-properties k)))
(defn- nonblank-string
[value]
@@ -223,7 +234,7 @@
([node] (node-user-property-entries node nil))
([node labels]
(->> node
(filter (fn [[k _]] (user-property-key? k)))
(filter (fn [[k _]] (displayable-property-key? k)))
(map (fn [[k v]] [k (normalize-property-values v labels)]))
(remove (fn [[_ values]] (empty? values)))
vec)))
@@ -488,7 +499,7 @@
[{:keys [root linked-references]}]
(letfn [(collect-node [node]
(let [node-keys (->> (keys node)
(filter user-property-key?))]
(filter displayable-property-key?))]
(reduce (fn [acc child]
(into acc (collect-node child)))
(set node-keys)
@@ -542,7 +553,7 @@
:else acc))
(collect-node [acc node]
(let [acc (reduce (fn [acc [k v]]
(if (user-property-key? k)
(if (displayable-property-key? k)
(collect-value acc v)
acc))
acc
@@ -633,12 +644,21 @@
[(namespace ?a) ?ns]
[(= "user.property" ?ns)]
[(get-else $ ?e :logseq.property/type :default) ?type]]
built-in-query '[:find ?a ?type
:in $ [?a ...]
:where
[?e :db/ident ?a]
[(get-else $ ?e :logseq.property/type :default) ?type]]
props-query '[:find ?b ?a ?v
:in $ [?b ...] [?a ...]
:where
[?b ?a ?v]]
ids (vec block-ids)]
(p/let [ident-type-pairs (transport/invoke config :thread-api/q false [repo [idents-query]])
ids (vec block-ids)
built-in-idents (vec displayable-built-in-properties)]
(p/let [user-pairs (transport/invoke config :thread-api/q false [repo [idents-query]])
built-in-pairs (transport/invoke config :thread-api/q false
[repo [built-in-query built-in-idents]])
ident-type-pairs (into (vec user-pairs) built-in-pairs)
datetime-idents (set (keep (fn [[a type]] (when (= :datetime type) a)) ident-type-pairs))
property-idents (vec (map first ident-type-pairs))]
(if (seq property-idents)

View File

@@ -101,12 +101,14 @@
(let [call-idx (swap! call-count inc)]
(p/resolved
(case call-idx
;; First call: idents-query returns property idents with types
;; First call: user idents-query returns property idents with types
1 [[:user.property/title :default]
[:user.property/due :datetime]
[:user.property/count :number]]
;; Second call: props-query returns raw values
2 [[10 :user.property/title "hello"]
;; Second call: built-in idents-query returns built-in property types
2 []
;; Third call: props-query returns raw values
3 [[10 :user.property/title "hello"]
[10 :user.property/due 1774267200000]
[10 :user.property/count 42]]
[]))))]
@@ -123,6 +125,37 @@
(p/catch (fn [e] (is false (str "unexpected error: " e))))
(p/finally done)))))
(deftest test-fetch-user-properties-includes-built-in-datetime
(let [fetch #'show-command/fetch-user-properties
call-count (atom 0)
mock-invoke (fn [_ _method _ _args]
(let [call-idx (swap! call-count inc)]
(p/resolved
(case call-idx
;; First call: user idents-query
1 [[:user.property/status :default]]
;; Second call: built-in idents-query returns deadline and scheduled
2 [[:logseq.property/deadline :datetime]
[:logseq.property/scheduled :datetime]]
;; Third call: props-query returns raw values
3 [[10 :user.property/status "todo"]
[10 :logseq.property/deadline 1774267200000]
[10 :logseq.property/scheduled 1774180800000]]
[]))))]
(async done
(-> (p/with-redefs [transport/invoke mock-invoke]
(p/let [result (fetch {} "demo" [10])]
(testing "built-in deadline is converted to ISO string"
(is (string? (get-in result [10 :logseq.property/deadline])))
(is (string/includes? (get-in result [10 :logseq.property/deadline]) "2026")))
(testing "built-in scheduled is converted to ISO string"
(is (string? (get-in result [10 :logseq.property/scheduled])))
(is (string/includes? (get-in result [10 :logseq.property/scheduled]) "2026")))
(testing "user property is unaffected"
(is (= "todo" (get-in result [10 :user.property/status]))))))
(p/catch (fn [e] (is false (str "unexpected error: " e))))
(p/finally done)))))
(defn- call-private
[sym & args]
(when-let [v (get (ns-interns 'logseq.cli.command.show) sym)]