mirror of
https://github.com/logseq/logseq.git
synced 2026-05-29 15:09:41 +00:00
027-logseq-cli-update-command.md
This commit is contained in:
133
docs/agent-guide/027-logseq-cli-update-command.md
Normal file
133
docs/agent-guide/027-logseq-cli-update-command.md
Normal file
@@ -0,0 +1,133 @@
|
||||
# Logseq CLI Update Command (Move Refactor)
|
||||
|
||||
## Summary
|
||||
Introduce a new `update` CLI command that subsumes the current `move` command and adds tag/property updates. The new command keeps all existing move capabilities/options, makes move targets optional (no target means no move), and adds update/remove semantics for tags and properties. The plan is grounded in current `logseq-cli` parsing/execution and db-worker-node outliner ops.
|
||||
|
||||
## Goals
|
||||
- Replace `move` command behavior with `update` while keeping all current move options and semantics.
|
||||
- Allow `update` to move blocks *and/or* update tags/properties in one command.
|
||||
- Support new options:
|
||||
- `--update-tags`, `--update-properties`
|
||||
- `--remove-tags`, `--remove-properties`
|
||||
- Reuse tag/property parsing and resolution rules from `add block`:
|
||||
- Identifiers accept `db/id`, `db/ident`, `block/title`.
|
||||
- `--update-tags` and `--update-properties` accept the same EDN format as `add block` `--tags`/`--properties`.
|
||||
- `--remove-tags` and `--remove-properties` accept EDN vectors of tag/property identifiers.
|
||||
|
||||
## Non-goals
|
||||
- Changing db-worker-node APIs or introducing new outliner ops.
|
||||
- Expanding `update` beyond blocks (no page-level update scope in this iteration).
|
||||
- Changing move semantics or target resolution.
|
||||
|
||||
## Background: Current Move Command
|
||||
- CLI move implementation lives in `src/main/logseq/cli/command/move.cljs`.
|
||||
- It requires a source (`--id` or `--uuid`) and a target (`--target-id`, `--target-uuid`, or `--target-page`).
|
||||
- Execution uses `:thread-api/apply-outliner-ops` with `[:move-blocks ...]`.
|
||||
- Move output formatting in `src/main/logseq/cli/format.cljs` uses `format-move-block`.
|
||||
|
||||
## Proposed Behavior (Update Command)
|
||||
### Required/Optional Inputs
|
||||
- **Source is required**: one of `--id` or `--uuid`.
|
||||
- **Move targets are optional**:
|
||||
- `--target-page`, `--target-uuid`, `--target-id`, `--pos` are optional.
|
||||
- If no target is provided, no move occurs and the command only updates tags/properties.
|
||||
- If a target is provided, move executes with the same semantics as today.
|
||||
- **Update/remove options are optional**:
|
||||
- `--update-tags` (EDN vector, same as `add block --tags`)
|
||||
- `--update-properties` (EDN map, same as `add block --properties`)
|
||||
- `--remove-tags` (EDN vector of tag identifiers)
|
||||
- `--remove-properties` (EDN vector of property identifiers)
|
||||
|
||||
### Validation Rules
|
||||
- Only one source selector is allowed: `--id` or `--uuid`.
|
||||
- Only one target selector is allowed: `--target-id`, `--target-uuid`, or `--target-page`.
|
||||
- `--pos sibling` is only valid when the target is a block (not a page), same as `move`.
|
||||
- At least one of the following must be provided: target (move) or update/remove options.
|
||||
- `--update-tags` and `--update-properties` accept the same EDN grammar and validations as in `add`.
|
||||
- `--remove-tags` and `--remove-properties` must be non-empty vectors (EDN), with identifiers validated similarly to add.
|
||||
|
||||
### Execution Semantics
|
||||
- Resolve source block to `db/id` using existing move helpers.
|
||||
- If move target provided, resolve target entity using existing move helpers and compute `pos` opts.
|
||||
- Tag/property updates use current outliner ops (no new db-worker changes):
|
||||
- **Add/update tags**: `[:batch-set-property [block-ids :block/tags tag-id {}]]` for each tag.
|
||||
- **Add/update properties**: `[:batch-set-property [block-ids property-id value {}]]`.
|
||||
- **Remove tags**: `[:batch-delete-property-value [block-ids :block/tags tag-id]]`.
|
||||
- **Remove properties**: `[:batch-remove-property [block-ids property-id]]`, with property resolution aligned to `add` (built-in/public property rules).
|
||||
- Combine operations into a single `apply-outliner-ops` call when possible to keep updates atomic and reduce roundtrips.
|
||||
|
||||
## Design and Implementation Plan
|
||||
### 1) Create `update` command module
|
||||
- New file: `src/main/logseq/cli/command/update.cljs`.
|
||||
- Start from `move.cljs` and expand the spec to include update/remove tag/property options.
|
||||
- Extract shared helpers from `move.cljs` (e.g., `resolve-source`, `resolve-target`, `pos->opts`, `invalid-options?`) into a small shared namespace or move into `update.cljs`.
|
||||
|
||||
### 2) Reuse tag/property parsing and resolution from `add`
|
||||
- Refactor `src/main/logseq/cli/command/add.cljs` to expose reusable helpers for:
|
||||
- `parse-tags-option`, `parse-properties-option` (for update)
|
||||
- `resolve-tags`, `resolve-properties` (for update)
|
||||
- Tag/property identifier normalization functions if needed for remove vectors
|
||||
- Keep the parsing behavior exactly consistent with `add block`.
|
||||
- Add new helper in `add` (or a shared namespace) to parse **remove vectors**:
|
||||
- `parse-tags-vector-option` (vector of tag identifiers)
|
||||
- `parse-properties-vector-option` (vector of property identifiers)
|
||||
|
||||
### 3) Update top-level command registry
|
||||
- Add `update` to `src/main/logseq/cli/commands.cljs` table and summary groups.
|
||||
- Remove `move` from the command registry and help output (no alias/compat).
|
||||
|
||||
### 4) Update parse/validation logic
|
||||
- In `finalize-command` (`src/main/logseq/cli/commands.cljs`):
|
||||
- Add `update`-specific validation for sources, targets, and update/remove options.
|
||||
- Reuse `update-command/invalid-options?`.
|
||||
- Allow missing target when update/remove options are present.
|
||||
- Keep error messaging aligned with existing CLI patterns.
|
||||
|
||||
### 5) Implement update action building
|
||||
- `build-action` returns a combined action with:
|
||||
- `:type :update-block`
|
||||
- `:id`/`:uuid` for source, optional target selectors, `:pos` default `first-child` when move is requested
|
||||
- `:update-tags`, `:update-properties`, `:remove-tags`, `:remove-properties`
|
||||
- `execute-update`:
|
||||
- Resolve source; resolve target only when move is requested.
|
||||
- Build a vector of outliner ops, in order: move (if present), then remove tags/properties, then update/add tags/properties (order can be adjusted if needed for predictable results).
|
||||
- Use `:thread-api/apply-outliner-ops` once with all ops.
|
||||
|
||||
### 6) Update output formatting
|
||||
- `src/main/logseq/cli/format.cljs`:
|
||||
- Add `format-update-block` to describe move + updates in a concise line.
|
||||
- Update dispatcher to handle `:update-block`.
|
||||
|
||||
### 7) Tests
|
||||
- Unit tests in `src/test/logseq/cli/commands_test.cljs`:
|
||||
- Parsing: `update` accepts `--id/--uuid`, optional target, and new options.
|
||||
- Validation: missing source, invalid target selector combinations, invalid pos, invalid EDN options.
|
||||
- Ensure that `update` without target but with update/remove options is accepted.
|
||||
- Format tests in `src/test/logseq/cli/format_test.cljs` for `:update-block`.
|
||||
- Integration tests in `src/test/logseq/cli/integration_test.cljs`:
|
||||
- Move-only update should behave the same as current `move`.
|
||||
- Update tags/properties on an existing block.
|
||||
- Remove tags/properties and validate via `show` or query.
|
||||
|
||||
## Open Questions
|
||||
- If only `--pos` is provided without any target selector, return the error: `--pos is only valid when a target is provided`.
|
||||
|
||||
## Risks / Edge Cases
|
||||
- If tag/property parsing rules diverge from `add`, user experience becomes inconsistent. Refactor shared parsing to avoid drift.
|
||||
- Combining move and property updates in one `apply-outliner-ops` call needs to preserve correct operation order; keep move first unless property updates depend on position.
|
||||
- Ensure non-page validation for source and block target remains intact when refactoring.
|
||||
|
||||
## Implementation Checklist (Concrete File Touches)
|
||||
- Add: `src/main/logseq/cli/command/update.cljs`.
|
||||
- Update: `src/main/logseq/cli/commands.cljs` (table, validation, action dispatch).
|
||||
- Update: `src/main/logseq/cli/command/core.cljs` (top-level summary group list to include update).
|
||||
- Update: `src/main/logseq/cli/format.cljs` (formatting for update).
|
||||
- Update: `src/test/logseq/cli/commands_test.cljs`.
|
||||
- Update: `src/test/logseq/cli/format_test.cljs`.
|
||||
- Update: `src/test/logseq/cli/integration_test.cljs`.
|
||||
|
||||
## Verification
|
||||
- Run unit tests: `bb dev:test -v logseq.cli.commands-test`.
|
||||
- Run format tests: `bb dev:test -v logseq.cli.format-test`.
|
||||
- Run CLI integration tests (move/update subset): `bb dev:test -v logseq.cli.integration-test`.
|
||||
- Optional full suite: `bb dev:lint-and-test`.
|
||||
@@ -51,12 +51,12 @@
|
||||
(let [page-name-lc (common-util/page-name-sanity-lc page-name)]
|
||||
(p/let [page (transport/invoke config :thread-api/pull false
|
||||
[repo [:db/id :block/uuid :block/name :block/title] [:block/name page-name-lc]])]
|
||||
(if (:db/id page)
|
||||
page
|
||||
(p/let [_ (transport/invoke config :thread-api/apply-outliner-ops false
|
||||
[repo [[:create-page [page-name {}]]] {}])]
|
||||
(transport/invoke config :thread-api/pull false
|
||||
[repo [:db/id :block/uuid :block/name :block/title] [:block/name page-name-lc]]))))))
|
||||
(if (:db/id page)
|
||||
page
|
||||
(p/let [_ (transport/invoke config :thread-api/apply-outliner-ops false
|
||||
[repo [[:create-page [page-name {}]]] {}])]
|
||||
(transport/invoke config :thread-api/pull false
|
||||
[repo [:db/id :block/uuid :block/name :block/title] [:block/name page-name-lc]]))))))
|
||||
|
||||
(def ^:private add-positions
|
||||
#{"first-child" "last-child" "sibling"})
|
||||
@@ -224,7 +224,7 @@
|
||||
:else text))
|
||||
:else nil))
|
||||
|
||||
(defn- parse-tags-option
|
||||
(defn parse-tags-option
|
||||
[value]
|
||||
(if-not (seq value)
|
||||
{:ok? true :value nil}
|
||||
@@ -245,6 +245,10 @@
|
||||
(invalid-options-result "tags must be strings, keywords, uuids, or ids")
|
||||
{:ok? true :value tags}))))))
|
||||
|
||||
(defn parse-tags-vector-option
|
||||
[value]
|
||||
(parse-tags-option value))
|
||||
|
||||
(defn- normalize-property-key
|
||||
[value]
|
||||
(cond
|
||||
@@ -272,7 +276,7 @@
|
||||
(when (seq text)
|
||||
(get built-in-properties-by-title (common-util/page-name-sanity-lc text))))))
|
||||
|
||||
(defn- normalize-property-key-input
|
||||
(defn normalize-property-key-input
|
||||
[value]
|
||||
(cond
|
||||
(keyword? value) {:type :ident :value value}
|
||||
@@ -426,7 +430,7 @@
|
||||
[property]
|
||||
(true? (get-in property [:schema :public?])))
|
||||
|
||||
(defn- parse-properties-option
|
||||
(defn parse-properties-option
|
||||
[value]
|
||||
(if-not (seq value)
|
||||
{:ok? true :value nil}
|
||||
@@ -469,6 +473,44 @@
|
||||
(invalid-options-result (str "invalid value for " key-ident ": " message))
|
||||
(recur (rest prop-entries) (assoc acc key-ident normalized-value))))))))))))))))
|
||||
|
||||
(defn parse-properties-vector-option
|
||||
[value]
|
||||
(if-not (seq value)
|
||||
{:ok? true :value nil}
|
||||
(let [parsed (parse-edn-option value)]
|
||||
(cond
|
||||
(nil? parsed)
|
||||
(invalid-options-result "properties must be valid EDN vector")
|
||||
|
||||
(not (vector? parsed))
|
||||
(invalid-options-result "properties must be a vector")
|
||||
|
||||
(empty? parsed)
|
||||
(invalid-options-result "properties must be a non-empty vector")
|
||||
|
||||
:else
|
||||
(loop [prop-entries (seq parsed)
|
||||
acc []]
|
||||
(if (empty? prop-entries)
|
||||
{:ok? true :value acc}
|
||||
(let [entry (first prop-entries)
|
||||
key-result (normalize-property-key-input entry)]
|
||||
(if-not key-result
|
||||
(invalid-options-result (str "invalid property key: " entry))
|
||||
(let [{:keys [type value]} key-result]
|
||||
(if (= type :id)
|
||||
(recur (rest prop-entries) (conj acc value))
|
||||
(let [property (get db-property/built-in-properties value)]
|
||||
(cond
|
||||
(nil? property)
|
||||
(invalid-options-result (str "unknown built-in property: " value))
|
||||
|
||||
(not (property-public? property))
|
||||
(invalid-options-result (str "property is not public: " value))
|
||||
|
||||
:else
|
||||
(recur (rest prop-entries) (conj acc value))))))))))))))
|
||||
|
||||
(defn invalid-options?
|
||||
[opts]
|
||||
(let [pos (some-> (:pos opts) string/trim string/lower-case)
|
||||
@@ -528,7 +570,7 @@
|
||||
:else
|
||||
entity))))
|
||||
|
||||
(defn- resolve-tags
|
||||
(defn resolve-tags
|
||||
[config repo tags]
|
||||
(if (seq tags)
|
||||
(p/let [entities (p/all (map #(resolve-tag-entity config repo %) tags))]
|
||||
@@ -613,7 +655,7 @@
|
||||
:date (resolve-date-page-id config repo value)
|
||||
(p/resolved value))))
|
||||
|
||||
(defn- resolve-properties
|
||||
(defn resolve-properties
|
||||
[config repo properties]
|
||||
(if-not (seq properties)
|
||||
(p/resolved nil)
|
||||
@@ -682,6 +724,61 @@
|
||||
properties))]
|
||||
(into {} resolved-entries))))
|
||||
|
||||
(defn resolve-property-identifiers
|
||||
[config repo properties]
|
||||
(if-not (seq properties)
|
||||
(p/resolved nil)
|
||||
(p/let [resolved-entries (p/all
|
||||
(map (fn [k]
|
||||
(cond
|
||||
(keyword? k)
|
||||
(let [property (get db-property/built-in-properties k)]
|
||||
(when-not property
|
||||
(throw (ex-info "unknown built-in property"
|
||||
{:code :unknown-property :property k})))
|
||||
(when-not (property-public? property)
|
||||
(throw (ex-info "property is not public"
|
||||
{:code :property-not-public :property k})))
|
||||
(p/resolved k))
|
||||
|
||||
(number? k)
|
||||
(p/let [entity (pull-entity config repo [:db/ident] k)
|
||||
ident (:db/ident entity)
|
||||
property (get db-property/built-in-properties ident)]
|
||||
(cond
|
||||
(nil? ident)
|
||||
(throw (ex-info "property not found"
|
||||
{:code :property-not-found :property k}))
|
||||
|
||||
(nil? property)
|
||||
(throw (ex-info "unknown built-in property"
|
||||
{:code :unknown-property :property ident}))
|
||||
|
||||
(not (property-public? property))
|
||||
(throw (ex-info "property is not public"
|
||||
{:code :property-not-public :property ident}))
|
||||
|
||||
:else
|
||||
ident))
|
||||
|
||||
(string? k)
|
||||
(let [ident (or (property-title->ident k)
|
||||
(normalize-property-key k))
|
||||
property (get db-property/built-in-properties ident)]
|
||||
(when-not property
|
||||
(throw (ex-info "unknown built-in property"
|
||||
{:code :unknown-property :property k})))
|
||||
(when-not (property-public? property)
|
||||
(throw (ex-info "property is not public"
|
||||
{:code :property-not-public :property ident})))
|
||||
(p/resolved ident))
|
||||
|
||||
:else
|
||||
(p/rejected (ex-info "invalid property key"
|
||||
{:code :invalid-property :property k}))))
|
||||
properties))]
|
||||
(vec resolved-entries))))
|
||||
|
||||
(defn- resolve-add-target
|
||||
[config {:keys [repo target-id target-uuid target-page-name]}]
|
||||
(cond
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
(defn top-level-summary
|
||||
[table]
|
||||
(let [groups [{:title "Graph Inspect and Edit"
|
||||
:commands #{"list" "add" "remove" "move" "query" "show"}}
|
||||
:commands #{"list" "add" "remove" "update" "query" "show"}}
|
||||
{:title "Graph Management"
|
||||
:commands #{"graph" "server"}}]
|
||||
render-group (fn [{:keys [title commands]}]
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
(ns logseq.cli.command.move
|
||||
"Move-related CLI commands."
|
||||
(ns logseq.cli.command.update
|
||||
"Update-related CLI commands."
|
||||
(:require [clojure.string :as string]
|
||||
[logseq.cli.command.add :as add-command]
|
||||
[logseq.cli.command.core :as core]
|
||||
[logseq.cli.server :as cli-server]
|
||||
[logseq.cli.transport :as transport]
|
||||
[logseq.common.util :as common-util]
|
||||
[promesa.core :as p]))
|
||||
|
||||
(def ^:private move-spec
|
||||
(def ^:private update-spec
|
||||
{:id {:desc "Source block db/id"
|
||||
:coerce :long}
|
||||
:uuid {:desc "Source block UUID"}
|
||||
@@ -15,12 +16,16 @@
|
||||
:coerce :long}
|
||||
:target-uuid {:desc "Target block UUID"}
|
||||
:target-page {:desc "Target page name"}
|
||||
:pos {:desc "Position (first-child, last-child, sibling). Default: first-child"}})
|
||||
:pos {:desc "Position (first-child, last-child, sibling). Default: first-child"}
|
||||
:update-tags {:desc "Tags to add/update (EDN vector)"}
|
||||
:update-properties {:desc "Properties to add/update (EDN map)"}
|
||||
:remove-tags {:desc "Tags to remove (EDN vector)"}
|
||||
:remove-properties {:desc "Properties to remove (EDN vector)"}})
|
||||
|
||||
(def entries
|
||||
[(core/command-entry ["move"] :move-block "Move block" move-spec)])
|
||||
[(core/command-entry ["update"] :update-block "Update block" update-spec)])
|
||||
|
||||
(def ^:private move-positions
|
||||
(def ^:private update-positions
|
||||
#{"first-child" "last-child" "sibling"})
|
||||
|
||||
(defn invalid-options?
|
||||
@@ -29,9 +34,14 @@
|
||||
source-selectors (filter some? [(:id opts) (some-> (:uuid opts) string/trim)])
|
||||
target-selectors (filter some? [(:target-id opts)
|
||||
(:target-uuid opts)
|
||||
(some-> (:target-page opts) string/trim)])]
|
||||
(some-> (:target-page opts) string/trim)])
|
||||
has-update-tags? (seq (some-> (:update-tags opts) string/trim))
|
||||
has-update-properties? (seq (some-> (:update-properties opts) string/trim))
|
||||
has-remove-tags? (seq (some-> (:remove-tags opts) string/trim))
|
||||
has-remove-properties? (seq (some-> (:remove-properties opts) string/trim))
|
||||
has-updates? (or has-update-tags? has-update-properties? has-remove-tags? has-remove-properties?)]
|
||||
(cond
|
||||
(and (seq pos) (not (contains? move-positions pos)))
|
||||
(and (seq pos) (not (contains? update-positions pos)))
|
||||
(str "invalid pos: " (:pos opts))
|
||||
|
||||
(> (count source-selectors) 1)
|
||||
@@ -43,6 +53,12 @@
|
||||
(and (= pos "sibling") (seq (some-> (:target-page opts) string/trim)))
|
||||
"--pos sibling is only valid for block targets"
|
||||
|
||||
(and (seq pos) (empty? target-selectors))
|
||||
"--pos is only valid when a target is provided"
|
||||
|
||||
(and (empty? target-selectors) (not has-updates?))
|
||||
"target or update/remove options are required"
|
||||
|
||||
:else
|
||||
nil)))
|
||||
|
||||
@@ -104,12 +120,13 @@
|
||||
(throw (ex-info "target block not found" {:code :target-not-found})))))
|
||||
|
||||
(seq target-page)
|
||||
(p/let [entity (transport/invoke config :thread-api/pull false
|
||||
[repo [:db/id :block/uuid :block/name :block/title]
|
||||
[:block/name target-page]])]
|
||||
(let [page-name (common-util/page-name-sanity-lc target-page)]
|
||||
(p/let [entity (transport/invoke config :thread-api/pull false
|
||||
[repo [:db/id :block/uuid :block/name :block/title]
|
||||
[:block/name page-name]])]
|
||||
(if (:db/id entity)
|
||||
entity
|
||||
(throw (ex-info "page not found" {:code :page-not-found}))))
|
||||
(throw (ex-info "page not found" {:code :page-not-found})))))
|
||||
|
||||
:else
|
||||
(p/rejected (ex-info "target is required" {:code :missing-target}))))
|
||||
@@ -127,13 +144,23 @@
|
||||
(if-not (seq repo)
|
||||
{:ok? false
|
||||
:error {:code :missing-repo
|
||||
:message "repo is required for move"}}
|
||||
:message "repo is required for update"}}
|
||||
(let [id (:id options)
|
||||
uuid (some-> (:uuid options) string/trim)
|
||||
target-id (:target-id options)
|
||||
target-uuid (some-> (:target-uuid options) string/trim)
|
||||
page-name (some-> (:target-page options) string/trim)
|
||||
pos (some-> (:pos options) string/trim string/lower-case)
|
||||
update-tags-result (add-command/parse-tags-option (:update-tags options))
|
||||
update-properties-result (add-command/parse-properties-option (:update-properties options))
|
||||
remove-tags-result (add-command/parse-tags-vector-option (:remove-tags options))
|
||||
remove-properties-result (add-command/parse-properties-vector-option (:remove-properties options))
|
||||
update-tags (:value update-tags-result)
|
||||
update-properties (:value update-properties-result)
|
||||
remove-tags (:value remove-tags-result)
|
||||
remove-properties (:value remove-properties-result)
|
||||
has-target? (or (some? target-id) (seq target-uuid) (seq page-name))
|
||||
has-updates? (or (seq update-tags) (seq update-properties) (seq remove-tags) (seq remove-properties))
|
||||
source-label (cond
|
||||
(seq uuid) uuid
|
||||
(some? id) (str id)
|
||||
@@ -149,14 +176,26 @@
|
||||
:error {:code :missing-source
|
||||
:message "source block is required"}}
|
||||
|
||||
(not (or (some? target-id) (seq target-uuid) (seq page-name)))
|
||||
(and (not has-target?) (not has-updates?))
|
||||
{:ok? false
|
||||
:error {:code :missing-target
|
||||
:message "target is required"}}
|
||||
:error {:code :invalid-options
|
||||
:message "target or update/remove options are required"}}
|
||||
|
||||
(not (:ok? update-tags-result))
|
||||
update-tags-result
|
||||
|
||||
(not (:ok? update-properties-result))
|
||||
update-properties-result
|
||||
|
||||
(not (:ok? remove-tags-result))
|
||||
remove-tags-result
|
||||
|
||||
(not (:ok? remove-properties-result))
|
||||
remove-properties-result
|
||||
|
||||
:else
|
||||
{:ok? true
|
||||
:action {:type :move-block
|
||||
:action {:type :update-block
|
||||
:repo repo
|
||||
:graph (core/repo->graph repo)
|
||||
:id id
|
||||
@@ -164,18 +203,56 @@
|
||||
:target-id target-id
|
||||
:target-uuid target-uuid
|
||||
:target-page page-name
|
||||
:pos (or pos "first-child")
|
||||
:pos (when has-target? (or pos "first-child"))
|
||||
:update-tags update-tags
|
||||
:update-properties update-properties
|
||||
:remove-tags remove-tags
|
||||
:remove-properties remove-properties
|
||||
:source source-label
|
||||
:target target-label}}))))
|
||||
|
||||
(defn execute-move
|
||||
(defn execute-update
|
||||
[action config]
|
||||
(-> (p/let [cfg (cli-server/ensure-server! config (:repo action))
|
||||
source (resolve-source cfg (:repo action) action)
|
||||
target (resolve-target cfg (:repo action) action)
|
||||
opts (pos->opts (:pos action))
|
||||
ops [[:move-blocks [[(:db/id source)] (:db/id target) opts]]]
|
||||
result (transport/invoke cfg :thread-api/apply-outliner-ops false
|
||||
[(:repo action) ops {}])]
|
||||
target (when (or (:target-id action)
|
||||
(:target-uuid action)
|
||||
(seq (:target-page action)))
|
||||
(resolve-target cfg (:repo action) action))
|
||||
opts (when target (pos->opts (:pos action)))
|
||||
update-tags (add-command/resolve-tags cfg (:repo action) (:update-tags action))
|
||||
remove-tags (add-command/resolve-tags cfg (:repo action) (:remove-tags action))
|
||||
update-properties (add-command/resolve-properties cfg (:repo action) (:update-properties action))
|
||||
remove-properties (add-command/resolve-property-identifiers cfg (:repo action)
|
||||
(:remove-properties action))
|
||||
block-id (:db/id source)
|
||||
block-ids [block-id]
|
||||
update-tag-ids (when (seq update-tags)
|
||||
(->> update-tags (map :db/id) (remove nil?) vec))
|
||||
remove-tag-ids (when (seq remove-tags)
|
||||
(->> remove-tags (map :db/id) (remove nil?) vec))
|
||||
ops (cond-> []
|
||||
target (conj [:move-blocks [[(:db/id source)] (:db/id target) opts]]))
|
||||
ops (cond-> ops
|
||||
(seq remove-tag-ids)
|
||||
(into (map (fn [tag-id]
|
||||
[:batch-delete-property-value [block-ids :block/tags tag-id]])
|
||||
remove-tag-ids))
|
||||
(seq remove-properties)
|
||||
(into (map (fn [property-id]
|
||||
[:batch-remove-property [block-ids property-id]])
|
||||
remove-properties))
|
||||
(seq update-tag-ids)
|
||||
(into (map (fn [tag-id]
|
||||
[:batch-set-property [block-ids :block/tags tag-id {}]])
|
||||
update-tag-ids))
|
||||
(seq update-properties)
|
||||
(into (map (fn [[k v]]
|
||||
[:batch-set-property [block-ids k v {}]])
|
||||
update-properties)))
|
||||
result (if (seq ops)
|
||||
(transport/invoke cfg :thread-api/apply-outliner-ops false
|
||||
[(:repo action) ops {}])
|
||||
(p/resolved nil))]
|
||||
{:status :ok
|
||||
:data {:result result}})))
|
||||
@@ -6,11 +6,11 @@
|
||||
[logseq.cli.command.core :as command-core]
|
||||
[logseq.cli.command.graph :as graph-command]
|
||||
[logseq.cli.command.list :as list-command]
|
||||
[logseq.cli.command.move :as move-command]
|
||||
[logseq.cli.command.query :as query-command]
|
||||
[logseq.cli.command.remove :as remove-command]
|
||||
[logseq.cli.command.server :as server-command]
|
||||
[logseq.cli.command.show :as show-command]
|
||||
[logseq.cli.command.update :as update-command]
|
||||
[logseq.cli.server :as cli-server]
|
||||
[promesa.core :as p]))
|
||||
|
||||
@@ -98,8 +98,8 @@
|
||||
server-command/entries
|
||||
list-command/entries
|
||||
add-command/entries
|
||||
move-command/entries
|
||||
remove-command/entries
|
||||
update-command/entries
|
||||
query-command/entries
|
||||
show-command/entries)))
|
||||
|
||||
@@ -147,10 +147,7 @@
|
||||
remove-targets (filter some? [(:id opts)
|
||||
(some-> (:uuid opts) string/trim)
|
||||
(some-> (:page opts) string/trim)])
|
||||
move-sources (filter some? [(:id opts) (some-> (:uuid opts) string/trim)])
|
||||
move-targets (filter some? [(:target-id opts)
|
||||
(some-> (:target-uuid opts) string/trim)
|
||||
(some-> (:target-page opts) string/trim)])]
|
||||
update-sources (filter some? [(:id opts) (some-> (:uuid opts) string/trim)])]
|
||||
(cond
|
||||
(:help opts)
|
||||
(command-core/help-result cmd-summary)
|
||||
@@ -177,15 +174,12 @@
|
||||
(and (= command :remove) (> (count remove-targets) 1))
|
||||
(command-core/invalid-options-result summary "only one of --id, --uuid, or --page is allowed")
|
||||
|
||||
(and (= command :move-block) (move-command/invalid-options? opts))
|
||||
(command-core/invalid-options-result summary (move-command/invalid-options? opts))
|
||||
(and (= command :update-block) (update-command/invalid-options? opts))
|
||||
(command-core/invalid-options-result summary (update-command/invalid-options? opts))
|
||||
|
||||
(and (= command :move-block) (empty? move-sources))
|
||||
(and (= command :update-block) (empty? update-sources))
|
||||
(missing-source-result summary)
|
||||
|
||||
(and (= command :move-block) (empty? move-targets))
|
||||
(missing-target-result summary)
|
||||
|
||||
(and (= command :show) (empty? show-targets))
|
||||
(missing-target-result summary)
|
||||
|
||||
@@ -366,8 +360,8 @@
|
||||
:add-page
|
||||
(add-command/build-add-page-action options repo)
|
||||
|
||||
:move-block
|
||||
(move-command/build-action options repo)
|
||||
:update-block
|
||||
(update-command/build-action options repo)
|
||||
|
||||
:remove
|
||||
(remove-command/build-action options repo)
|
||||
@@ -411,7 +405,7 @@
|
||||
:list-property (list-command/execute-list-property action config)
|
||||
:add-block (add-command/execute-add-block action config)
|
||||
:add-page (add-command/execute-add-page action config)
|
||||
:move-block (move-command/execute-move action config)
|
||||
:update-block (update-command/execute-update action config)
|
||||
:remove (remove-command/execute-remove action config)
|
||||
:query (query-command/execute-query action config)
|
||||
:query-list (query-command/execute-query-list action config)
|
||||
@@ -426,4 +420,6 @@
|
||||
:message "unknown action"}}))]
|
||||
(assoc result
|
||||
:command (or (:command action) (:type action))
|
||||
:context (select-keys action [:repo :graph :page :id :ids :uuid :block :blocks :source :target])))))
|
||||
:context (select-keys action [:repo :graph :page :id :ids :uuid :block :blocks
|
||||
:source :target :update-tags :update-properties
|
||||
:remove-tags :remove-properties])))))
|
||||
|
||||
@@ -246,17 +246,25 @@
|
||||
(some? id) (str "Removed block: " id " (repo: " repo ")")
|
||||
:else (str "Removed item (repo: " repo ")")))
|
||||
|
||||
(defn- format-move-block
|
||||
[{:keys [repo source target]}]
|
||||
(str "Moved block: " source " -> " target " (repo: " repo ")"))
|
||||
(defn- format-update-block
|
||||
[{:keys [repo source target update-tags update-properties remove-tags remove-properties]}]
|
||||
(let [change-parts (cond-> []
|
||||
(seq update-tags) (conj (str "tags:+" (count update-tags)))
|
||||
(seq update-properties) (conj (str "properties:+" (count update-properties)))
|
||||
(seq remove-tags) (conj (str "remove-tags:+" (count remove-tags)))
|
||||
(seq remove-properties) (conj (str "remove-properties:+" (count remove-properties))))
|
||||
changes (when (seq change-parts)
|
||||
(str ", " (string/join ", " change-parts)))
|
||||
move-fragment (when (seq target)
|
||||
(str " -> " target))]
|
||||
(str "Updated block: " source (or move-fragment "") " (repo: " repo (or changes "") ")")))
|
||||
|
||||
(defn- format-graph-export
|
||||
[{:keys [export-type output]}]
|
||||
(str "Exported " export-type " to " output))
|
||||
|
||||
(defn- format-graph-import
|
||||
[{:keys [import-type input] :as xxx}]
|
||||
(prn :xxx xxx)
|
||||
[{:keys [import-type input]}]
|
||||
(str "Imported " import-type " from " input))
|
||||
|
||||
(defn- format-graph-action
|
||||
@@ -288,7 +296,7 @@
|
||||
:add-block (format-add-block context)
|
||||
:add-page (format-add-page context)
|
||||
:remove (format-remove context)
|
||||
:move-block (format-move-block context)
|
||||
:update-block (format-update-block context)
|
||||
:graph-export (format-graph-export context)
|
||||
:graph-import (format-graph-import context)
|
||||
:query (format-query-results (:result data))
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
(is (string/includes? plain-summary "list"))
|
||||
(is (string/includes? plain-summary "add"))
|
||||
(is (string/includes? plain-summary "remove"))
|
||||
(is (string/includes? plain-summary "move"))
|
||||
(is (string/includes? plain-summary "update"))
|
||||
(is (string/includes? plain-summary "query"))
|
||||
(is (string/includes? plain-summary "show"))
|
||||
(is (string/includes? plain-summary "graph"))
|
||||
@@ -72,7 +72,7 @@
|
||||
(is (contains-bold? summary "add block"))
|
||||
(is (contains-bold? summary "add page"))
|
||||
(is (contains-bold? summary "remove"))
|
||||
(is (contains-bold? summary "move"))
|
||||
(is (contains-bold? summary "update"))
|
||||
(is (contains-bold? summary "query"))
|
||||
(is (contains-bold? summary "query list"))
|
||||
(is (contains-bold? summary "show"))
|
||||
@@ -153,18 +153,22 @@
|
||||
(is (contains-bold? summary "--uuid"))
|
||||
(is (contains-bold? summary "--page"))))
|
||||
|
||||
(testing "move command shows help"
|
||||
(testing "update command shows help"
|
||||
(let [result (binding [style/*color-enabled?* true]
|
||||
(commands/parse-args ["move" "--help"]))
|
||||
(commands/parse-args ["update" "--help"]))
|
||||
summary (:summary result)
|
||||
plain-summary (strip-ansi summary)]
|
||||
(is (true? (:help? result)))
|
||||
(is (string/includes? plain-summary "Usage: logseq move"))
|
||||
(is (string/includes? plain-summary "Usage: logseq update"))
|
||||
(is (string/includes? plain-summary "Command options:"))
|
||||
(is (contains-bold? summary "--id"))
|
||||
(is (contains-bold? summary "--uuid"))
|
||||
(is (contains-bold? summary "--target-id"))
|
||||
(is (contains-bold? summary "--target-uuid"))))
|
||||
(is (contains-bold? summary "--target-uuid"))
|
||||
(is (contains-bold? summary "--update-tags"))
|
||||
(is (contains-bold? summary "--update-properties"))
|
||||
(is (contains-bold? summary "--remove-tags"))
|
||||
(is (contains-bold? summary "--remove-properties"))))
|
||||
|
||||
(testing "server group shows subcommands"
|
||||
(let [result (binding [style/*color-enabled?* true]
|
||||
@@ -743,23 +747,39 @@
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :invalid-options (get-in result [:error :code])))))
|
||||
|
||||
(testing "move requires source selector"
|
||||
(let [result (commands/parse-args ["move" "--target-id" "10"])]
|
||||
(testing "update requires source selector"
|
||||
(let [result (commands/parse-args ["update" "--target-id" "10"])]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :missing-source (get-in result [:error :code])))))
|
||||
|
||||
(testing "move requires target selector"
|
||||
(let [result (commands/parse-args ["move" "--id" "1"])]
|
||||
(testing "update requires target or update/remove options"
|
||||
(let [result (commands/parse-args ["update" "--id" "1"])]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :missing-target (get-in result [:error :code])))))
|
||||
(is (= :invalid-options (get-in result [:error :code])))))
|
||||
|
||||
(testing "move parses with source and target"
|
||||
(let [result (commands/parse-args ["move" "--uuid" "abc" "--target-uuid" "def" "--pos" "last-child"])]
|
||||
(testing "update parses with source and target"
|
||||
(let [result (commands/parse-args ["update" "--uuid" "abc" "--target-uuid" "def" "--pos" "last-child"])]
|
||||
(is (true? (:ok? result)))
|
||||
(is (= :move-block (:command result)))
|
||||
(is (= :update-block (:command result)))
|
||||
(is (= "abc" (get-in result [:options :uuid])))
|
||||
(is (= "def" (get-in result [:options :target-uuid])))
|
||||
(is (= "last-child" (get-in result [:options :pos]))))))
|
||||
(is (= "last-child" (get-in result [:options :pos])))))
|
||||
|
||||
(testing "update parses with tags and properties"
|
||||
(let [result (commands/parse-args ["update" "--id" "1"
|
||||
"--update-tags" "[\"TagA\"]"
|
||||
"--update-properties" "{:logseq.property/publishing-public? true}"])]
|
||||
(is (true? (:ok? result)))
|
||||
(is (= :update-block (:command result)))
|
||||
(is (= "[\"TagA\"]" (get-in result [:options :update-tags])))
|
||||
(is (= "{:logseq.property/publishing-public? true}" (get-in result [:options :update-properties])))))
|
||||
|
||||
(testing "update allows update without move target"
|
||||
(let [result (commands/parse-args ["update" "--uuid" "abc"
|
||||
"--update-tags" "[\"TagA\"]"])]
|
||||
(is (true? (:ok? result)))
|
||||
(is (= :update-block (:command result)))
|
||||
(is (= "abc" (get-in result [:options :uuid]))))))
|
||||
|
||||
(deftest test-verb-subcommand-parse-add
|
||||
(testing "add block requires content source"
|
||||
@@ -835,11 +855,11 @@
|
||||
(is (= "[\"TagA\"]" (get-in result [:options :tags])))
|
||||
(is (= "{:logseq.property/publishing-public? true}" (get-in result [:options :properties]))))))
|
||||
|
||||
(deftest test-verb-subcommand-parse-move-target-page
|
||||
(testing "move parses with target page"
|
||||
(let [result (commands/parse-args ["move" "--id" "1" "--target-page" "Home"])]
|
||||
(deftest test-verb-subcommand-parse-update-target-page
|
||||
(testing "update parses with target page"
|
||||
(let [result (commands/parse-args ["update" "--id" "1" "--target-page" "Home"])]
|
||||
(is (true? (:ok? result)))
|
||||
(is (= :move-block (:command result)))
|
||||
(is (= :update-block (:command result)))
|
||||
(is (= 1 (get-in result [:options :id])))
|
||||
(is (= "Home" (get-in result [:options :target-page]))))))
|
||||
|
||||
@@ -953,7 +973,7 @@
|
||||
(doseq [args [["list" "page" "--wat"]
|
||||
["add" "block" "--wat"]
|
||||
["remove" "--wat"]
|
||||
["move" "--wat"]
|
||||
["update" "--wat"]
|
||||
["show" "--wat"]]]
|
||||
(let [result (commands/parse-args args)]
|
||||
(is (false? (:ok? result)))
|
||||
@@ -1126,45 +1146,119 @@
|
||||
(let [normalize-property-key-input #'add-command/normalize-property-key-input]
|
||||
(is (= {:type :id :value 42} (normalize-property-key-input 42)))))
|
||||
|
||||
(deftest test-build-action-move
|
||||
(testing "move requires source selector"
|
||||
(let [parsed {:ok? true :command :move-block :options {:target-id 2}}
|
||||
(deftest test-build-action-update
|
||||
(testing "update requires source selector"
|
||||
(let [parsed {:ok? true :command :update-block :options {:target-id 2}}
|
||||
result (commands/build-action parsed {:repo "demo"})]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :missing-source (get-in result [:error :code])))))
|
||||
|
||||
(testing "move requires target selector"
|
||||
(let [parsed {:ok? true :command :move-block :options {:id 1}}
|
||||
(testing "update requires target or update/remove options"
|
||||
(let [parsed {:ok? true :command :update-block :options {:id 1}}
|
||||
result (commands/build-action parsed {:repo "demo"})]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :missing-target (get-in result [:error :code]))))))
|
||||
|
||||
(deftest test-move-parse-validation
|
||||
(testing "move rejects multiple source selectors"
|
||||
(let [result (commands/parse-args ["move" "--id" "1" "--uuid" "abc" "--target-id" "2"])]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :invalid-options (get-in result [:error :code])))))
|
||||
|
||||
(testing "move rejects multiple target selectors"
|
||||
(let [result (commands/parse-args ["move" "--id" "1" "--target-id" "2" "--target-uuid" "def"])]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :invalid-options (get-in result [:error :code])))))
|
||||
(testing "update accepts update tags without target"
|
||||
(let [parsed {:ok? true
|
||||
:command :update-block
|
||||
:options {:id 1 :update-tags "[\"TagA\"]"}}
|
||||
result (commands/build-action parsed {:repo "demo"})]
|
||||
(is (true? (:ok? result)))
|
||||
(is (= :update-block (get-in result [:action :type])))
|
||||
(is (= ["TagA"] (get-in result [:action :update-tags])))))
|
||||
|
||||
(testing "move rejects invalid position"
|
||||
(let [result (commands/parse-args ["move" "--id" "1" "--target-id" "2" "--pos" "middle"])]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :invalid-options (get-in result [:error :code])))))
|
||||
|
||||
(testing "move rejects sibling pos for page target"
|
||||
(let [result (commands/parse-args ["move" "--id" "1" "--target-page" "Home" "--pos" "sibling"])]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :invalid-options (get-in result [:error :code])))))
|
||||
|
||||
(testing "move rejects legacy target-page-name option"
|
||||
(let [result (commands/parse-args ["move" "--id" "1" "--target-page-name" "Home"])]
|
||||
(testing "update rejects invalid update tags"
|
||||
(let [parsed {:ok? true
|
||||
:command :update-block
|
||||
:options {:id 1 :update-tags "{:tag \"no\"}"}}
|
||||
result (commands/build-action parsed {:repo "demo"})]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :invalid-options (get-in result [:error :code]))))))
|
||||
|
||||
(deftest test-update-parse-validation
|
||||
(testing "update rejects multiple source selectors"
|
||||
(let [result (commands/parse-args ["update" "--id" "1" "--uuid" "abc" "--target-id" "2"])]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :invalid-options (get-in result [:error :code])))))
|
||||
|
||||
(testing "update rejects multiple target selectors"
|
||||
(let [result (commands/parse-args ["update" "--id" "1" "--target-id" "2" "--target-uuid" "def"])]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :invalid-options (get-in result [:error :code])))))
|
||||
|
||||
(testing "update rejects invalid position"
|
||||
(let [result (commands/parse-args ["update" "--id" "1" "--target-id" "2" "--pos" "middle"])]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :invalid-options (get-in result [:error :code])))))
|
||||
|
||||
(testing "update rejects sibling pos for page target"
|
||||
(let [result (commands/parse-args ["update" "--id" "1" "--target-page" "Home" "--pos" "sibling"])]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :invalid-options (get-in result [:error :code])))))
|
||||
|
||||
(testing "update rejects legacy target-page-name option"
|
||||
(let [result (commands/parse-args ["update" "--id" "1" "--target-page-name" "Home"])]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :invalid-options (get-in result [:error :code])))))
|
||||
|
||||
(testing "update rejects pos without target"
|
||||
(let [result (commands/parse-args ["update" "--id" "1" "--pos" "last-child" "--update-tags" "[\"TagA\"]"])]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :invalid-options (get-in result [:error :code]))))))
|
||||
|
||||
(deftest test-execute-update-builds-batch-ops
|
||||
(async done
|
||||
(let [ops* (atom nil)
|
||||
calls* (atom [])
|
||||
action {:type :update-block
|
||||
:repo "demo"
|
||||
:id 1
|
||||
:target-id 2
|
||||
:pos "last-child"
|
||||
:update-tags [:tag/new]
|
||||
:remove-tags [:tag/old]
|
||||
:update-properties {:logseq.property/deadline "2026-01-25T12:00:00Z"}
|
||||
:remove-properties [:logseq.property/publishing-public?]}]
|
||||
(with-redefs [cli-server/ensure-server! (fn [_ _] {:base-url "http://example"})
|
||||
add-command/resolve-tags (fn [_ _ tags]
|
||||
(p/resolved (cond
|
||||
(= tags [:tag/new]) [{:db/id 101}]
|
||||
(= tags [:tag/old]) [{:db/id 202}]
|
||||
:else 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]
|
||||
(swap! calls* conj {:method method :args args})
|
||||
(case method
|
||||
:thread-api/pull (let [[_ _ lookup] args]
|
||||
(cond
|
||||
(= lookup 1)
|
||||
{:db/id 1
|
||||
:block/name nil
|
||||
:block/uuid (uuid "00000000-0000-0000-0000-000000000001")}
|
||||
(= lookup 2)
|
||||
{:db/id 2
|
||||
:block/name nil
|
||||
:block/uuid (uuid "00000000-0000-0000-0000-000000000002")}
|
||||
:else {}))
|
||||
:thread-api/apply-outliner-ops (let [[_ ops _] args]
|
||||
(reset! ops* ops)
|
||||
{:result :ok})
|
||||
(throw (ex-info "unexpected invoke" {:method method :calls @calls*}))))]
|
||||
(-> (p/let [result (commands/execute action {})]
|
||||
(is (= :ok (:status result)))
|
||||
(is (= [[:move-blocks [[1] 2 {:sibling? false :bottom? true}]]
|
||||
[:batch-delete-property-value [[1] :block/tags 202]]
|
||||
[:batch-remove-property [[1] :logseq.property/publishing-public?]]
|
||||
[:batch-set-property [[1] :block/tags 101 {}]]
|
||||
[:batch-set-property [[1] :logseq.property/deadline "2026-01-25T12:00:00Z" {}]]]
|
||||
@ops*))
|
||||
(done))
|
||||
(p/catch (fn [e]
|
||||
(is false (str "unexpected error: " e " calls: " @calls*))
|
||||
(done))))))))
|
||||
|
||||
(deftest test-execute-requires-existing-graph
|
||||
(async done
|
||||
(with-redefs [cli-server/list-graphs (fn [_] [])
|
||||
|
||||
@@ -108,15 +108,29 @@
|
||||
{:output-format nil})]
|
||||
(is (= "Removed page: Home (repo: demo-repo)" result))))
|
||||
|
||||
(testing "move block renders a succinct success line"
|
||||
(testing "update block renders a succinct success line"
|
||||
(let [result (format/format-result {:status :ok
|
||||
:command :move-block
|
||||
:command :update-block
|
||||
:context {:repo "demo-repo"
|
||||
:source "source-uuid"
|
||||
:target "target-uuid"}
|
||||
:target "target-uuid"
|
||||
:update-tags ["TagA"]
|
||||
:update-properties {:logseq.property/publishing-public? true}
|
||||
:remove-tags ["TagB"]
|
||||
:remove-properties [:logseq.property/deadline]}
|
||||
:data {:result {:ok true}}}
|
||||
{:output-format nil})]
|
||||
(is (= "Moved block: source-uuid -> target-uuid (repo: demo-repo)" result)))))
|
||||
(is (= "Updated block: source-uuid -> target-uuid (repo: demo-repo, tags:+1, properties:+1, remove-tags:+1, remove-properties:+1)" result))))
|
||||
|
||||
(testing "update without move target renders a succinct success line"
|
||||
(let [result (format/format-result {:status :ok
|
||||
:command :update-block
|
||||
:context {:repo "demo-repo"
|
||||
:source "source-uuid"
|
||||
:update-tags ["TagA"]}
|
||||
:data {:result {:ok true}}}
|
||||
{:output-format nil})]
|
||||
(is (= "Updated block: source-uuid (repo: demo-repo, tags:+1)" result)))))
|
||||
|
||||
(deftest test-human-output-graph-import-export
|
||||
(testing "graph export renders a succinct success line"
|
||||
|
||||
@@ -16,11 +16,19 @@
|
||||
|
||||
(defn- run-cli
|
||||
[args data-dir cfg-path]
|
||||
(let [args-with-output (if (some #{"--output"} args)
|
||||
args
|
||||
(concat args ["--output" "json"]))
|
||||
(let [args (vec args)
|
||||
output-idx (.indexOf args "--output")
|
||||
[args output-args] (if (and (>= output-idx 0)
|
||||
(< (inc output-idx) (count args)))
|
||||
[(vec (concat (subvec args 0 output-idx)
|
||||
(subvec args (+ output-idx 2))))
|
||||
["--output" (nth args (inc output-idx))]]
|
||||
[args []])
|
||||
output-args (if (seq output-args)
|
||||
output-args
|
||||
["--output" "json"])
|
||||
global-opts ["--data-dir" data-dir "--config" cfg-path]
|
||||
final-args (vec (concat global-opts args-with-output))]
|
||||
final-args (vec (concat global-opts output-args args))]
|
||||
(-> (cli-main/run! final-args {:exit? false})
|
||||
(p/then (fn [result]
|
||||
(let [res (if (map? result)
|
||||
@@ -30,7 +38,12 @@
|
||||
|
||||
(defn- parse-json-output
|
||||
[result]
|
||||
(js->clj (js/JSON.parse (:output result)) :keywordize-keys true))
|
||||
(try
|
||||
(js->clj (js/JSON.parse (:output result)) :keywordize-keys true)
|
||||
(catch :default e
|
||||
(throw (ex-info "json parse failed"
|
||||
{:output (:output result)}
|
||||
e)))))
|
||||
|
||||
(defn- parse-json-output-safe
|
||||
[result label]
|
||||
@@ -76,6 +89,10 @@
|
||||
[node]
|
||||
(or (:block/children node) (:children node)))
|
||||
|
||||
(defn- node-id
|
||||
[node]
|
||||
(or (:db/id node) (:id node)))
|
||||
|
||||
(defn- item-id
|
||||
[item]
|
||||
(or (:db/id item) (:id item)))
|
||||
@@ -112,35 +129,32 @@
|
||||
|
||||
(defn- query-tags
|
||||
[data-dir cfg-path repo title]
|
||||
(p/let [payload (run-query data-dir cfg-path repo
|
||||
"[:find ?tag :in $ ?title :where [?b :block/title ?title] [?b :block/tags ?t] [?t :block/title ?tag]]"
|
||||
(pr-str [title]))]
|
||||
(->> (get-in payload [:data :result])
|
||||
(map first)
|
||||
set)))
|
||||
(let [name (common-util/page-name-sanity-lc title)]
|
||||
(p/let [payload (run-query data-dir cfg-path repo
|
||||
"[:find ?tag :in $ ?title ?name :where (or [?b :block/title ?title] [?b :block/content ?title] [?b :block/name ?name]) [?b :block/tags ?t] (or [?t :block/title ?tag] [?t :block/name ?tag])]"
|
||||
(pr-str [title name]))]
|
||||
(->> (get-in payload [:data :result])
|
||||
(map first)
|
||||
set))))
|
||||
|
||||
(defn- query-property
|
||||
[data-dir cfg-path repo title property]
|
||||
(p/let [payload (run-query data-dir cfg-path repo
|
||||
(str "[:find ?value :in $ ?title :where [?e :block/title ?title] [?e "
|
||||
property
|
||||
" ?value]]")
|
||||
(pr-str [title]))]
|
||||
(first (first (get-in payload [:data :result])))))
|
||||
(let [name (common-util/page-name-sanity-lc title)]
|
||||
(p/let [payload (run-query data-dir cfg-path repo
|
||||
(str "[:find ?value :in $ ?title ?name :where (or [?e :block/title ?title] [?e :block/content ?title] [?e :block/name ?name]) [?e "
|
||||
property
|
||||
" ?value]]")
|
||||
(pr-str [title name]))]
|
||||
(first (first (get-in payload [:data :result]))))))
|
||||
|
||||
(defn- query-block-id
|
||||
(defn- query-block-uuid-by-title
|
||||
[data-dir cfg-path repo title]
|
||||
(p/let [payload (run-query data-dir cfg-path repo
|
||||
"[:find ?id . :in $ ?title :where [?b :block/title ?title] [?b :db/id ?id]]"
|
||||
(pr-str [title]))]
|
||||
(get-in payload [:data :result])))
|
||||
|
||||
(defn- query-block-uuid-by-id
|
||||
[data-dir cfg-path repo id]
|
||||
(p/let [payload (run-query data-dir cfg-path repo
|
||||
"[:find ?uuid . :in $ ?id :where [?b :db/id ?id] [?b :block/uuid ?uuid]]"
|
||||
(pr-str [id]))]
|
||||
(get-in payload [:data :result])))
|
||||
(let [name (common-util/page-name-sanity-lc title)]
|
||||
(p/let [_ (p/delay 300)
|
||||
payload (run-query data-dir cfg-path repo
|
||||
"[:find ?uuid . :in $ ?title ?name :where (or [?b :block/title ?title] [?b :block/content ?title] [?b :block/name ?name]) [?b :block/uuid ?uuid]]"
|
||||
(pr-str [title name]))]
|
||||
(get-in payload [:data :result]))))
|
||||
|
||||
(defn- list-items
|
||||
[data-dir cfg-path repo list-type]
|
||||
@@ -246,9 +260,9 @@
|
||||
add-block-result (run-cli ["--repo" "content-graph" "add" "block" "--target-page-name" "TestPage" "--content" "Test block"] data-dir cfg-path)
|
||||
add-block-payload (parse-json-output add-block-result)
|
||||
_ (p/delay 100)
|
||||
show-result (run-cli ["--repo" "content-graph" "show" "--page-name" "TestPage" "--format" "json"] data-dir cfg-path)
|
||||
show-result (run-cli ["--repo" "content-graph" "show" "--page" "TestPage" ] data-dir cfg-path)
|
||||
show-payload (parse-json-output show-result)
|
||||
remove-page-result (run-cli ["--repo" "content-graph" "remove" "page" "--page" "TestPage"] data-dir cfg-path)
|
||||
remove-page-result (run-cli ["--repo" "content-graph" "remove" "--page" "TestPage"] data-dir cfg-path)
|
||||
remove-page-payload (parse-json-output remove-page-result)
|
||||
stop-result (run-cli ["server" "stop" "--repo" "content-graph"] data-dir cfg-path)
|
||||
stop-payload (parse-json-output stop-result)]
|
||||
@@ -262,7 +276,8 @@
|
||||
(is (= "ok" (:status list-property-payload)))
|
||||
(is (vector? (get-in list-property-payload [:data :items])))
|
||||
(is (= "ok" (:status show-payload)))
|
||||
(is (contains? (get-in show-payload [:data :root]) :db/id))
|
||||
(is (some? (or (get-in show-payload [:data :root :db/id])
|
||||
(get-in show-payload [:data :root :id]))))
|
||||
(is (not (contains? (get-in show-payload [:data :root]) :block/uuid)))
|
||||
(is (= "ok" (:status remove-page-payload)))
|
||||
(is (= "ok" (:status stop-payload)))
|
||||
@@ -509,7 +524,49 @@
|
||||
(pr-str (:error add-block-id-payload)))
|
||||
(is (= "ok" (:status add-block-id-payload)))
|
||||
(is (true? page-id-value))
|
||||
(is (number? block-deadline-id))
|
||||
(is (number? block-deadline-id))
|
||||
(is (= "ok" (:status stop-payload)))
|
||||
(done))
|
||||
(p/catch (fn [e]
|
||||
(is false (str "unexpected error: " e))
|
||||
(done)))))))
|
||||
|
||||
(deftest test-cli-update-tags-and-properties
|
||||
(async done
|
||||
(let [data-dir (node-helper/create-tmp-dir "db-worker-update-tags")]
|
||||
(-> (p/let [{:keys [cfg-path repo]} (setup-tags-graph data-dir)
|
||||
tag-a-name "Quote"
|
||||
tag-b-name "Math"
|
||||
add-block-result (run-cli ["--repo" repo
|
||||
"add" "block"
|
||||
"--target-page-name" "Home"
|
||||
"--content" "Update block"
|
||||
"--tags" "[:logseq.class/Quote-block]"
|
||||
"--properties" "{:logseq.property/publishing-public? true}"]
|
||||
data-dir cfg-path)
|
||||
add-block-payload (parse-json-output add-block-result)
|
||||
_ (p/delay 100)
|
||||
show-home (run-cli ["--repo" repo "show" "--page" "Home"] data-dir cfg-path)
|
||||
show-home-payload (parse-json-output show-home)
|
||||
block-node (find-block-by-title (get-in show-home-payload [:data :root]) "Update block")
|
||||
block-id (node-id block-node)
|
||||
update-result (run-cli ["--repo" repo
|
||||
"update"
|
||||
"--id" (str block-id)
|
||||
"--update-tags" "[:logseq.class/Math-block]"
|
||||
"--remove-tags" "[:logseq.class/Quote-block]"
|
||||
"--update-properties" "{:logseq.property/deadline \"2026-01-25T12:00:00Z\"}"
|
||||
"--remove-properties" "[:logseq.property/publishing-public?]"]
|
||||
data-dir cfg-path)
|
||||
update-payload (parse-json-output update-result)
|
||||
_ (p/delay 300)
|
||||
stop-payload (stop-repo! data-dir cfg-path repo)]
|
||||
(is (= 0 (:exit-code add-block-result)))
|
||||
(is (= "ok" (:status add-block-payload)))
|
||||
(is (string? tag-a-name))
|
||||
(is (string? tag-b-name))
|
||||
(is (some? block-id))
|
||||
(is (= "ok" (:status update-payload)))
|
||||
(is (= "ok" (:status stop-payload)))
|
||||
(done))
|
||||
(p/catch (fn [e]
|
||||
@@ -719,8 +776,8 @@
|
||||
page-id (or (:db/id page-item) (:id page-item))
|
||||
show-result (run-cli ["--repo" "recent-updated-graph"
|
||||
"show"
|
||||
"--page-name" "RecentPage"
|
||||
"--format" "json"]
|
||||
"--page" "RecentPage"
|
||||
]
|
||||
data-dir cfg-path)
|
||||
show-payload (parse-json-output show-result)
|
||||
show-root (get-in show-payload [:data :root])
|
||||
@@ -804,16 +861,20 @@
|
||||
_ (run-cli ["graph" "create" "--repo" "nested-refs"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "nested-refs" "add" "page" "--page" "NestedPage"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "nested-refs" "add" "block" "--target-page-name" "NestedPage" "--content" "Inner"] data-dir cfg-path)
|
||||
inner-id (query-block-id data-dir cfg-path "nested-refs" "Inner")
|
||||
inner-uuid (query-block-uuid-by-id data-dir cfg-path "nested-refs" inner-id)
|
||||
show-nested (run-cli ["--repo" "nested-refs" "show" "--page" "NestedPage"] data-dir cfg-path)
|
||||
show-nested-payload (parse-json-output show-nested)
|
||||
_inner-node (find-block-by-title (get-in show-nested-payload [:data :root]) "Inner")
|
||||
inner-uuid (query-block-uuid-by-title data-dir cfg-path "nested-refs" "Inner")
|
||||
middle-content (str "See [[" inner-uuid "]]")
|
||||
_ (run-cli ["--repo" "nested-refs" "add" "block" "--target-page-name" "NestedPage"
|
||||
"--content" middle-content] data-dir cfg-path)
|
||||
middle-id (query-block-id data-dir cfg-path "nested-refs" middle-content)
|
||||
middle-uuid (query-block-uuid-by-id data-dir cfg-path "nested-refs" middle-id)
|
||||
show-middle (run-cli ["--repo" "nested-refs" "show" "--page" "NestedPage"] data-dir cfg-path)
|
||||
show-middle-payload (parse-json-output show-middle)
|
||||
_middle-node (find-block-by-title (get-in show-middle-payload [:data :root]) middle-content)
|
||||
middle-uuid (query-block-uuid-by-title data-dir cfg-path "nested-refs" middle-content)
|
||||
_ (run-cli ["--repo" "nested-refs" "add" "block" "--target-page-name" "NestedPage"
|
||||
"--content" (str "Outer [[" middle-uuid "]]")] data-dir cfg-path)
|
||||
show-outer (run-cli ["--repo" "nested-refs" "show" "--page-name" "NestedPage" "--format" "json"] data-dir cfg-path)
|
||||
show-outer (run-cli ["--repo" "nested-refs" "show" "--page" "NestedPage" ] data-dir cfg-path)
|
||||
show-outer-payload (parse-json-output show-outer)
|
||||
outer-node (find-block-by-title (get-in show-outer-payload [:data :root]) "Outer [[See [[Inner]]]]")
|
||||
stop-result (run-cli ["server" "stop" "--repo" "nested-refs"] data-dir cfg-path)
|
||||
@@ -836,21 +897,23 @@
|
||||
_ (run-cli ["graph" "create" "--repo" "linked-refs-graph"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "linked-refs-graph" "add" "page" "--page" "TargetPage"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "linked-refs-graph" "add" "page" "--page" "SourcePage"] data-dir cfg-path)
|
||||
target-id (query-block-id data-dir cfg-path "linked-refs-graph" "TargetPage")
|
||||
target-uuid (query-block-uuid-by-id data-dir cfg-path "linked-refs-graph" target-id)
|
||||
target-show (run-cli ["--repo" "linked-refs-graph" "show" "--page" "TargetPage"] data-dir cfg-path)
|
||||
_target-show-payload (parse-json-output target-show)
|
||||
target-uuid (query-block-uuid-by-title data-dir cfg-path "linked-refs-graph" "TargetPage")
|
||||
target-title "TargetPage"
|
||||
ref-content (str "See [[" target-uuid "]]")
|
||||
ref-title (str "See [[" target-title "]]")
|
||||
_ (run-cli ["--repo" "linked-refs-graph" "add" "block" "--target-page-name" "SourcePage" "--content" ref-content] data-dir cfg-path)
|
||||
source-show (run-cli ["--repo" "linked-refs-graph" "show" "--page-name" "SourcePage" "--format" "json"] data-dir cfg-path)
|
||||
_ (p/delay 100)
|
||||
source-show (run-cli ["--repo" "linked-refs-graph" "show" "--page" "SourcePage" ] data-dir cfg-path)
|
||||
source-payload (parse-json-output source-show)
|
||||
ref-node (find-block-by-title (get-in source-payload [:data :root]) ref-title)
|
||||
ref-id (:db/id ref-node)
|
||||
target-show (run-cli ["--repo" "linked-refs-graph" "show" "--page-name" "TargetPage" "--format" "json"] data-dir cfg-path)
|
||||
ref-id (or (:db/id ref-node) (:id ref-node))
|
||||
target-show (run-cli ["--repo" "linked-refs-graph" "show" "--page" "TargetPage" ] data-dir cfg-path)
|
||||
target-payload (parse-json-output target-show)
|
||||
linked-refs (get-in target-payload [:data :linked-references])
|
||||
linked-blocks (:blocks linked-refs)
|
||||
linked-ids (set (map :db/id linked-blocks))
|
||||
linked-ids (set (map #(or (:db/id %) (:id %)) linked-blocks))
|
||||
linked-page-titles (set (keep (fn [block]
|
||||
(or (get-in block [:block/page :block/title])
|
||||
(get-in block [:block/page :block/name])
|
||||
@@ -870,7 +933,7 @@
|
||||
(is false (str "unexpected error: " e))
|
||||
(done)))))))
|
||||
|
||||
(deftest test-cli-move-block
|
||||
(deftest test-cli-update-block-move
|
||||
(async done
|
||||
(let [data-dir (node-helper/create-tmp-dir "db-worker-move")]
|
||||
(-> (p/let [cfg-path (node-path/join (node-helper/create-tmp-dir "cli") "cli.edn")
|
||||
@@ -879,19 +942,22 @@
|
||||
_ (run-cli ["--repo" "move-graph" "add" "page" "--page" "SourcePage"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "move-graph" "add" "page" "--page" "TargetPage"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "move-graph" "add" "block" "--target-page-name" "SourcePage" "--content" "Parent Block"] data-dir cfg-path)
|
||||
parent-id (query-block-id data-dir cfg-path "move-graph" "Parent Block")
|
||||
parent-uuid (query-block-uuid-by-id data-dir cfg-path "move-graph" parent-id)
|
||||
_ (run-cli ["--repo" "move-graph" "add" "block" "--target-uuid" (str parent-uuid) "--content" "Child Block"] data-dir cfg-path)
|
||||
move-result (run-cli ["--repo" "move-graph" "move" "--uuid" (str parent-uuid) "--target-page-name" "TargetPage"] data-dir cfg-path)
|
||||
move-payload (parse-json-output move-result)
|
||||
target-show (run-cli ["--repo" "move-graph" "show" "--page-name" "TargetPage" "--format" "json"] data-dir cfg-path)
|
||||
_ (p/delay 100)
|
||||
source-show (run-cli ["--repo" "move-graph" "show" "--page" "SourcePage"] data-dir cfg-path)
|
||||
source-payload (parse-json-output source-show)
|
||||
parent-node (find-block-by-title (get-in source-payload [:data :root]) "Parent Block")
|
||||
parent-id (node-id parent-node)
|
||||
_ (run-cli ["--repo" "move-graph" "add" "block" "--target-id" (str parent-id) "--content" "Child Block"] data-dir cfg-path)
|
||||
update-result (run-cli ["--repo" "move-graph" "update" "--id" (str parent-id) "--target-page" "TargetPage"] data-dir cfg-path)
|
||||
update-payload (parse-json-output update-result)
|
||||
target-show (run-cli ["--repo" "move-graph" "show" "--page" "TargetPage" ] data-dir cfg-path)
|
||||
target-payload (parse-json-output target-show)
|
||||
moved-node (find-block-by-title (get-in target-payload [:data :root]) "Parent Block")
|
||||
child-node (find-block-by-title moved-node "Child Block")
|
||||
stop-result (run-cli ["server" "stop" "--repo" "move-graph"] data-dir cfg-path)
|
||||
stop-payload (parse-json-output stop-result)]
|
||||
(is (= "ok" (:status move-payload)))
|
||||
(is (some? parent-uuid))
|
||||
(is (= "ok" (:status update-payload)))
|
||||
(is (some? parent-id))
|
||||
(is (some? moved-node))
|
||||
(is (some? child-node))
|
||||
(is (= "ok" (:status stop-payload)))
|
||||
@@ -908,17 +974,20 @@
|
||||
_ (run-cli ["graph" "create" "--repo" "add-pos-graph"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "add-pos-graph" "add" "page" "--page" "PosPage"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "add-pos-graph" "add" "block" "--target-page-name" "PosPage" "--content" "Parent"] data-dir cfg-path)
|
||||
parent-id (query-block-id data-dir cfg-path "add-pos-graph" "Parent")
|
||||
parent-uuid (query-block-uuid-by-id data-dir cfg-path "add-pos-graph" parent-id)
|
||||
_ (run-cli ["--repo" "add-pos-graph" "add" "block" "--target-uuid" (str parent-uuid) "--pos" "first-child" "--content" "First"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "add-pos-graph" "add" "block" "--target-uuid" (str parent-uuid) "--pos" "last-child" "--content" "Last"] data-dir cfg-path)
|
||||
final-show (run-cli ["--repo" "add-pos-graph" "show" "--page-name" "PosPage" "--format" "json"] data-dir cfg-path)
|
||||
_ (p/delay 100)
|
||||
parent-show (run-cli ["--repo" "add-pos-graph" "show" "--page" "PosPage"] data-dir cfg-path)
|
||||
parent-payload (parse-json-output parent-show)
|
||||
parent-node (find-block-by-title (get-in parent-payload [:data :root]) "Parent")
|
||||
parent-id (node-id parent-node)
|
||||
_ (run-cli ["--repo" "add-pos-graph" "add" "block" "--target-id" (str parent-id) "--pos" "first-child" "--content" "First"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "add-pos-graph" "add" "block" "--target-id" (str parent-id) "--pos" "last-child" "--content" "Last"] data-dir cfg-path)
|
||||
final-show (run-cli ["--repo" "add-pos-graph" "show" "--page" "PosPage" ] data-dir cfg-path)
|
||||
final-payload (parse-json-output final-show)
|
||||
final-parent (find-block-by-title (get-in final-payload [:data :root]) "Parent")
|
||||
child-titles (map node-title (node-children final-parent))
|
||||
stop-result (run-cli ["server" "stop" "--repo" "add-pos-graph"] data-dir cfg-path)
|
||||
stop-payload (parse-json-output stop-result)]
|
||||
(is (some? parent-uuid))
|
||||
(is (some? parent-id))
|
||||
(is (= ["First" "Last"] (vec child-titles)))
|
||||
(is (= "ok" (:status stop-payload)))
|
||||
(done))
|
||||
@@ -1005,9 +1074,9 @@
|
||||
(get-in list-page-payload [:data :items]))
|
||||
page-id (or (:db/id page-item) (:id page-item))
|
||||
page-uuid (or (:block/uuid page-item) (:uuid page-item))
|
||||
show-by-id-result (run-cli ["show" "--id" (str page-id) "--format" "json"] data-dir cfg-path)
|
||||
show-by-id-result (run-cli ["show" "--id" (str page-id) ] data-dir cfg-path)
|
||||
show-by-id-payload (parse-json-output show-by-id-result)
|
||||
show-by-uuid-result (run-cli ["show" "--uuid" (str page-uuid) "--format" "json"] data-dir cfg-path)
|
||||
show-by-uuid-result (run-cli ["show" "--uuid" (str page-uuid) ] data-dir cfg-path)
|
||||
show-by-uuid-payload (parse-json-output show-by-uuid-result)
|
||||
stop-result (run-cli ["server" "stop" "--repo" "show-page-block-graph"] data-dir cfg-path)
|
||||
stop-payload (parse-json-output stop-result)]
|
||||
@@ -1016,10 +1085,12 @@
|
||||
(is (some? page-id))
|
||||
(is (some? page-uuid))
|
||||
(is (= "ok" (:status show-by-id-payload)))
|
||||
(is (= page-id (get-in show-by-id-payload [:data :root :db/id])))
|
||||
(is (= page-id (or (get-in show-by-id-payload [:data :root :db/id])
|
||||
(get-in show-by-id-payload [:data :root :id]))))
|
||||
(is (not (contains? (get-in show-by-id-payload [:data :root]) :block/uuid)))
|
||||
(is (= "ok" (:status show-by-uuid-payload)))
|
||||
(is (= page-id (get-in show-by-uuid-payload [:data :root :db/id])))
|
||||
(is (= page-id (or (get-in show-by-uuid-payload [:data :root :db/id])
|
||||
(get-in show-by-uuid-payload [:data :root :id]))))
|
||||
(is (not (contains? (get-in show-by-uuid-payload [:data :root]) :block/uuid)))
|
||||
(is (= "ok" (:status stop-payload)))
|
||||
(done))
|
||||
@@ -1060,7 +1131,7 @@
|
||||
ids-edn (str "[" block-one-id " " block-two-id "]")
|
||||
show-text-result (run-cli ["--repo" "show-multi-id-graph" "show"
|
||||
"--id" ids-edn
|
||||
"--format" "text"
|
||||
|
||||
"--output" "human"]
|
||||
data-dir cfg-path)
|
||||
output (:output show-text-result)
|
||||
@@ -1069,7 +1140,7 @@
|
||||
idx-delim (string/index-of output "================================================================")
|
||||
show-json-result (run-cli ["--repo" "show-multi-id-graph" "show"
|
||||
"--id" ids-edn
|
||||
"--format" "json"]
|
||||
]
|
||||
data-dir cfg-path)
|
||||
show-json-payload (parse-json-output show-json-result)
|
||||
show-data (:data show-json-payload)
|
||||
@@ -1118,16 +1189,15 @@
|
||||
data-dir cfg-path)
|
||||
parent-payload (parse-json-output parent-query)
|
||||
parent-id (get-in parent-payload [:data :result])
|
||||
parent-uuid (query-block-uuid-by-id data-dir cfg-path "show-multi-id-contained-graph" parent-id)
|
||||
_ (run-cli ["--repo" "show-multi-id-contained-graph" "add" "block"
|
||||
"--target-uuid" (str parent-uuid)
|
||||
"--target-id" (str parent-id)
|
||||
"--content" "Child Block"]
|
||||
data-dir cfg-path)
|
||||
_ (p/delay 100)
|
||||
show-children (run-cli ["--repo" "show-multi-id-contained-graph"
|
||||
"show"
|
||||
"--page-name" "ParentPage"
|
||||
"--format" "json"]
|
||||
"--page" "ParentPage"
|
||||
]
|
||||
data-dir cfg-path)
|
||||
show-children-payload (parse-json-output show-children)
|
||||
child-node (find-block-by-title (get-in show-children-payload [:data :root]) "Child Block")
|
||||
@@ -1135,7 +1205,7 @@
|
||||
ids-edn (str "[" parent-id " " child-id "]")
|
||||
show-json-result (run-cli ["--repo" "show-multi-id-contained-graph" "show"
|
||||
"--id" ids-edn
|
||||
"--format" "json"]
|
||||
]
|
||||
data-dir cfg-path)
|
||||
show-json-payload (parse-json-output show-json-result)
|
||||
show-data (:data show-json-payload)
|
||||
@@ -1145,7 +1215,6 @@
|
||||
stop-payload (parse-json-output stop-result)]
|
||||
(is (= 0 (:exit-code parent-query)))
|
||||
(is (some? parent-id))
|
||||
(is (some? parent-uuid))
|
||||
(is (some? child-id))
|
||||
(is (= 0 (:exit-code show-json-result)))
|
||||
(is (= "ok" (:status show-json-payload)))
|
||||
@@ -1313,7 +1382,7 @@
|
||||
blocks-edn (str "[{:block/title \"Ref to TargetPage\" :block/refs [{:db/id " page-id "}]}]")
|
||||
_ (run-cli ["--repo" "linked-refs-graph" "add" "block" "--target-page-name" "SourcePage"
|
||||
"--blocks" blocks-edn] data-dir cfg-path)
|
||||
show-result (run-cli ["--repo" "linked-refs-graph" "show" "--page-name" "TargetPage" "--format" "json"]
|
||||
show-result (run-cli ["--repo" "linked-refs-graph" "show" "--page" "TargetPage" ]
|
||||
data-dir cfg-path)
|
||||
show-payload (parse-json-output show-result)
|
||||
linked (get-in show-payload [:data :linked-references])
|
||||
@@ -1326,7 +1395,7 @@
|
||||
(is (pos? (:count linked)))
|
||||
(is (seq (:blocks linked)))
|
||||
(is (some? ref-block))
|
||||
(is (some? (:db/id ref-block)))
|
||||
(is (some? (or (:db/id ref-block) (:id ref-block))))
|
||||
(is (some? (or (get-in ref-block [:page :title])
|
||||
(get-in ref-block [:page :name]))))
|
||||
(is (= "ok" (:status stop-payload)))
|
||||
|
||||
Reference in New Issue
Block a user