mirror of
https://github.com/logseq/logseq.git
synced 2026-05-18 18:02:35 +00:00
fix lint, remove deprecated cmds
This commit is contained in:
@@ -62,6 +62,8 @@ frontend.worker.rtc.op-mem-layer/_sync-loop-canceler
|
||||
frontend.worker.db-worker/init
|
||||
;; Used by shadow.cljs (node entrypoint)
|
||||
frontend.worker.db-worker-node/main
|
||||
;; CLI entrypoint (shadow-cljs :node-script)
|
||||
logseq.cli.main/main
|
||||
;; Future use?
|
||||
frontend.worker.rtc.hash/hash-blocks
|
||||
;; Repl fn
|
||||
|
||||
@@ -17,7 +17,7 @@ The CLI should provide a stable interface for scripting and troubleshooting, and
|
||||
|
||||
## Testing Plan
|
||||
|
||||
I will add an integration test that starts db-worker-node on a test port and verifies the CLI can connect and run a simple request like ping or status.
|
||||
I will add an integration test that starts db-worker-node on a test port and verifies the CLI can connect and run a simple graph/content request.
|
||||
I will add unit tests for command parsing, configuration precedence, and error formatting.
|
||||
I will add unit tests for the client transport layer to ensure timeouts and retries behave correctly.
|
||||
I will add unit tests for new graph/content commands (parsing, validation, and request mapping).
|
||||
@@ -45,7 +45,7 @@ The CLI will use JSON for request and response bodies for ease of scripting.
|
||||
|
||||
## Implementation plan
|
||||
|
||||
1. Use TodoWrite to track the full task list and include the @test-driven-development red-green-refactor steps.
|
||||
1. Use tool(update_plan) to track the full task list and include the @test-driven-development red-green-refactor steps.
|
||||
2. Read @test-driven-development guidelines and confirm the red phase will include all CLI tests first.
|
||||
3. Identify existing db-worker-node request handlers and document their request and response shapes.
|
||||
4. Define the initial CLI command surface as a table that includes command, input, output, and errors.
|
||||
@@ -63,14 +63,28 @@ The CLI will use JSON for request and response bodies for ease of scripting.
|
||||
16. Refactor for naming and reuse while keeping tests green.
|
||||
17. Document how to build and run the CLI in a short section in README.md.
|
||||
|
||||
## Current status (2026-01-14)
|
||||
|
||||
Implemented:
|
||||
- CLI build target, entrypoint, config resolution, transport, formatting, and command wiring.
|
||||
- Graph commands: list/create/switch/remove/validate/info.
|
||||
- Content commands: add/remove/search/tree.
|
||||
- Unit tests for config/commands/format/transport and integration tests for graph/content commands.
|
||||
- CLI docs moved to `docs/cli/logseq-cli.md` and linked from README.
|
||||
|
||||
Not fully aligned with plan:
|
||||
- Red-first TDD sequence was not strictly followed (some tests added after initial implementation).
|
||||
- README section was replaced by a link to the dedicated doc.
|
||||
- `search` currently queries `:block/title` only (no page name/content search).
|
||||
|
||||
Open follow-ups (optional):
|
||||
- Expand `search` to include page name/content and update tests.
|
||||
- Add any additional graph metadata to `graph-info` beyond `:logseq.kv/graph-created-at` and `:logseq.kv/schema-version`.
|
||||
|
||||
## Command surface definition
|
||||
|
||||
| Command | Input | Output | Errors |
|
||||
| --- | --- | --- | --- |
|
||||
| ping | none | ok message | server unavailable, timeout |
|
||||
| status | none | server version, db state | server unavailable, timeout |
|
||||
| query | query string or file | query result JSON | invalid query, parse error |
|
||||
| export | target path and format | export result | unsupported format, write error |
|
||||
| graph-list | none | list of graphs | server unavailable, timeout |
|
||||
| graph-create | graph name | created graph + set current graph | invalid name, server unavailable |
|
||||
| graph-switch | graph name | switched graph + set current graph | missing graph, server unavailable |
|
||||
@@ -79,7 +93,7 @@ The CLI will use JSON for request and response bodies for ease of scripting.
|
||||
| graph-info | graph name or current graph | graph metadata/info | missing graph, server unavailable |
|
||||
| add | block/page payload | created block IDs | invalid input, server unavailable |
|
||||
| remove | block/page id or name | removal confirmation | invalid input, server unavailable |
|
||||
| search | query string | matched blocks/pages | invalid input, server unavailable |
|
||||
| search | text query | matched blocks/pages | invalid input, server unavailable |
|
||||
| tree | block/page id or name | hierarchical tree output | invalid input, server unavailable |
|
||||
|
||||
## Edge cases
|
||||
@@ -141,7 +155,7 @@ I will keep unit tests focused on pure functions like parsing, formatting, and c
|
||||
|
||||
## Question
|
||||
|
||||
Which exact db-worker-node endpoints and request schemas should the CLI use for ping, status, query, and export.
|
||||
Which exact db-worker-node endpoints and request schemas should the CLI use for graph/content commands.
|
||||
- Answer: all thread-apis are available in http endpoint, check @src/main/frontend/worker/db_worker_node.cljs
|
||||
|
||||
Do we want WebSocket or HTTP as the default transport for the CLI.
|
||||
|
||||
@@ -18,7 +18,7 @@ node ./static/db-worker-node.js
|
||||
## Run the CLI
|
||||
|
||||
```bash
|
||||
node ./static/logseq-cli.js ping --base-url http://127.0.0.1:9101
|
||||
node ./static/logseq-cli.js graph-list --base-url http://127.0.0.1:9101
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
(ns logseq.cli.commands
|
||||
"Command parsing and action building for the Logseq CLI."
|
||||
(:require ["fs" :as fs]
|
||||
[cljs-time.coerce :as tc]
|
||||
[cljs.reader :as reader]
|
||||
@@ -12,11 +13,7 @@
|
||||
[promesa.core :as p]))
|
||||
|
||||
(def ^:private command->keyword
|
||||
{"ping" :ping
|
||||
"status" :status
|
||||
"query" :query
|
||||
"export" :export
|
||||
"graph-list" :graph-list
|
||||
{"graph-list" :graph-list
|
||||
"graph-create" :graph-create
|
||||
"graph-switch" :graph-switch
|
||||
"graph-remove" :graph-remove
|
||||
@@ -45,7 +42,7 @@
|
||||
[nil "--json" "Output JSON"
|
||||
:id :json?
|
||||
:default false]
|
||||
[nil "--format FORMAT" "Output format (tree/export)"]
|
||||
[nil "--format FORMAT" "Output format (tree)"]
|
||||
[nil "--limit N" "Limit results"
|
||||
:parse-fn #(js/parseInt % 10)]
|
||||
[nil "--page PAGE" "Page name"]
|
||||
@@ -54,10 +51,7 @@
|
||||
[nil "--content TEXT" "Block content for add"]
|
||||
[nil "--blocks EDN" "EDN vector of blocks for add"]
|
||||
[nil "--blocks-file PATH" "EDN file of blocks for add"]
|
||||
[nil "--text TEXT" "Search text"]
|
||||
[nil "--query QUERY" "EDN query input"]
|
||||
[nil "--file PATH" "Path to EDN query file"]
|
||||
[nil "--out PATH" "Output path"]])
|
||||
[nil "--text TEXT" "Search text"]])
|
||||
|
||||
(defn parse-args
|
||||
[args]
|
||||
@@ -115,21 +109,6 @@
|
||||
(first command-args)
|
||||
(:repo config)))
|
||||
|
||||
(defn- read-query
|
||||
[{:keys [query file]}]
|
||||
(cond
|
||||
(seq query)
|
||||
{:ok? true :value (reader/read-string query)}
|
||||
|
||||
(seq file)
|
||||
(let [contents (.toString (fs/readFileSync file) "utf8")]
|
||||
{:ok? true :value (reader/read-string contents)})
|
||||
|
||||
:else
|
||||
{:ok? false
|
||||
:error {:code :missing-query
|
||||
:message "query is required"}}))
|
||||
|
||||
(defn- read-blocks
|
||||
[options command-args]
|
||||
(cond
|
||||
@@ -151,14 +130,6 @@
|
||||
:error {:code :missing-content
|
||||
:message "content is required"}}))
|
||||
|
||||
(defn- ensure-vector
|
||||
[value]
|
||||
(if (vector? value)
|
||||
{:ok? true :value value}
|
||||
{:ok? false
|
||||
:error {:code :invalid-query
|
||||
:message "query must be a vector"}}))
|
||||
|
||||
(defn- ensure-blocks
|
||||
[value]
|
||||
(if (vector? value)
|
||||
@@ -289,6 +260,139 @@
|
||||
(when (seq graph)
|
||||
(graph->repo graph))))
|
||||
|
||||
(defn- missing-graph-error
|
||||
[]
|
||||
{:ok? false
|
||||
:error {:code :missing-graph
|
||||
:message "graph name is required"}})
|
||||
|
||||
(defn- missing-repo-error
|
||||
[message]
|
||||
{:ok? false
|
||||
:error {:code :missing-repo
|
||||
:message message}})
|
||||
|
||||
(defn- build-graph-action
|
||||
[command graph repo]
|
||||
(case command
|
||||
:graph-list
|
||||
{:ok? true
|
||||
:action {:type :invoke
|
||||
:method "thread-api/list-db"
|
||||
:direct-pass? false
|
||||
:args []}}
|
||||
|
||||
:graph-create
|
||||
(if-not (seq graph)
|
||||
(missing-graph-error)
|
||||
{:ok? true
|
||||
:action {:type :invoke
|
||||
:method "thread-api/create-or-open-db"
|
||||
:direct-pass? false
|
||||
:args [repo {}]
|
||||
:persist-repo (repo->graph repo)}})
|
||||
|
||||
:graph-switch
|
||||
(if-not (seq graph)
|
||||
(missing-graph-error)
|
||||
{:ok? true
|
||||
:action {:type :graph-switch
|
||||
:repo repo
|
||||
:graph (repo->graph repo)}})
|
||||
|
||||
:graph-remove
|
||||
(if-not (seq graph)
|
||||
(missing-graph-error)
|
||||
{:ok? true
|
||||
:action {:type :invoke
|
||||
:method "thread-api/unsafe-unlink-db"
|
||||
:direct-pass? false
|
||||
:args [repo]}})
|
||||
|
||||
:graph-validate
|
||||
(if-not (seq repo)
|
||||
(missing-graph-error)
|
||||
{:ok? true
|
||||
:action {:type :invoke
|
||||
:method "thread-api/validate-db"
|
||||
:direct-pass? false
|
||||
:args [repo]}})
|
||||
|
||||
:graph-info
|
||||
(if-not (seq repo)
|
||||
(missing-graph-error)
|
||||
{:ok? true
|
||||
:action {:type :graph-info
|
||||
:repo repo
|
||||
:graph (repo->graph repo)}})))
|
||||
|
||||
(defn- build-add-action
|
||||
[options args repo]
|
||||
(if-not (seq repo)
|
||||
(missing-repo-error "repo is required for add")
|
||||
(let [blocks-result (read-blocks options args)]
|
||||
(if-not (:ok? blocks-result)
|
||||
blocks-result
|
||||
(let [vector-result (ensure-blocks (:value blocks-result))]
|
||||
(if-not (:ok? vector-result)
|
||||
vector-result
|
||||
{:ok? true
|
||||
:action {:type :add
|
||||
:repo repo
|
||||
:graph (repo->graph repo)
|
||||
:page (:page options)
|
||||
:parent (:parent options)
|
||||
:blocks (:value vector-result)}}))))))
|
||||
|
||||
(defn- build-remove-action
|
||||
[options repo]
|
||||
(if-not (seq repo)
|
||||
(missing-repo-error "repo is required for remove")
|
||||
(let [block (:block options)
|
||||
page (:page options)]
|
||||
(if (or (seq block) (seq page))
|
||||
{:ok? true
|
||||
:action {:type :remove
|
||||
:repo repo
|
||||
:block block
|
||||
:page page}}
|
||||
{:ok? false
|
||||
:error {:code :missing-target
|
||||
:message "block or page is required"}}))))
|
||||
|
||||
(defn- build-search-action
|
||||
[options args repo]
|
||||
(if-not (seq repo)
|
||||
(missing-repo-error "repo is required for search")
|
||||
(let [text (or (:text options) (string/join " " args))]
|
||||
(if (seq text)
|
||||
{:ok? true
|
||||
:action {:type :search
|
||||
:repo repo
|
||||
:text text
|
||||
:limit (:limit options)}}
|
||||
{:ok? false
|
||||
:error {:code :missing-search-text
|
||||
:message "search text is required"}}))))
|
||||
|
||||
(defn- build-tree-action
|
||||
[options repo]
|
||||
(if-not (seq repo)
|
||||
(missing-repo-error "repo is required for tree")
|
||||
(let [block (:block options)
|
||||
page (:page options)
|
||||
target (or block page)]
|
||||
(if (seq target)
|
||||
{:ok? true
|
||||
:action {:type :tree
|
||||
:repo repo
|
||||
:block block
|
||||
:page page
|
||||
:format (some-> (:format options) string/lower-case)}}
|
||||
{:ok? false
|
||||
:error {:code :missing-target
|
||||
:message "block or page is required"}}))))
|
||||
|
||||
(defn build-action
|
||||
[parsed config]
|
||||
(if-not (:ok? parsed)
|
||||
@@ -297,198 +401,20 @@
|
||||
graph (pick-graph options args config)
|
||||
repo (resolve-repo graph)]
|
||||
(case command
|
||||
:ping
|
||||
{:ok? true :action {:type :ping}}
|
||||
|
||||
:status
|
||||
{:ok? true :action {:type :status}}
|
||||
|
||||
:query
|
||||
(if-not (seq repo)
|
||||
{:ok? false
|
||||
:error {:code :missing-repo
|
||||
:message "repo is required for query"}}
|
||||
(let [query-result (read-query options)]
|
||||
(if-not (:ok? query-result)
|
||||
query-result
|
||||
(let [vector-result (ensure-vector (:value query-result))]
|
||||
(if-not (:ok? vector-result)
|
||||
vector-result
|
||||
{:ok? true
|
||||
:action {:type :invoke
|
||||
:method "thread-api/q"
|
||||
:direct-pass? false
|
||||
:args [repo (:value vector-result)]}})))))
|
||||
|
||||
:export
|
||||
(let [format (some-> (:format options) string/lower-case)
|
||||
out (:out options)
|
||||
repo repo]
|
||||
(cond
|
||||
(not (seq repo))
|
||||
{:ok? false
|
||||
:error {:code :missing-repo
|
||||
:message "repo is required for export"}}
|
||||
|
||||
(not (seq out))
|
||||
{:ok? false
|
||||
:error {:code :missing-output
|
||||
:message "output path is required"}}
|
||||
|
||||
(= format "edn")
|
||||
{:ok? true
|
||||
:action {:type :invoke
|
||||
:method "thread-api/export-edn"
|
||||
:direct-pass? false
|
||||
:args [repo {}]
|
||||
:write {:format :edn
|
||||
:path out}}}
|
||||
|
||||
(= format "db")
|
||||
{:ok? true
|
||||
:action {:type :invoke
|
||||
:method "thread-api/export-db"
|
||||
:direct-pass? true
|
||||
:args [repo]
|
||||
:write {:format :db
|
||||
:path out}}}
|
||||
|
||||
:else
|
||||
{:ok? false
|
||||
:error {:code :unsupported-format
|
||||
:message (str "unsupported format: " format)}}))
|
||||
|
||||
:graph-list
|
||||
{:ok? true
|
||||
:action {:type :invoke
|
||||
:method "thread-api/list-db"
|
||||
:direct-pass? false
|
||||
:args []}}
|
||||
|
||||
:graph-create
|
||||
(if-not (seq graph)
|
||||
{:ok? false
|
||||
:error {:code :missing-graph
|
||||
:message "graph name is required"}}
|
||||
{:ok? true
|
||||
:action {:type :invoke
|
||||
:method "thread-api/create-or-open-db"
|
||||
:direct-pass? false
|
||||
:args [repo {}]
|
||||
:persist-repo (repo->graph repo)}})
|
||||
|
||||
:graph-switch
|
||||
(if-not (seq graph)
|
||||
{:ok? false
|
||||
:error {:code :missing-graph
|
||||
:message "graph name is required"}}
|
||||
{:ok? true
|
||||
:action {:type :graph-switch
|
||||
:repo repo
|
||||
:graph (repo->graph repo)}})
|
||||
|
||||
:graph-remove
|
||||
(if-not (seq graph)
|
||||
{:ok? false
|
||||
:error {:code :missing-graph
|
||||
:message "graph name is required"}}
|
||||
{:ok? true
|
||||
:action {:type :invoke
|
||||
:method "thread-api/unsafe-unlink-db"
|
||||
:direct-pass? false
|
||||
:args [repo]}})
|
||||
|
||||
:graph-validate
|
||||
(if-not (seq repo)
|
||||
{:ok? false
|
||||
:error {:code :missing-graph
|
||||
:message "graph name is required"}}
|
||||
{:ok? true
|
||||
:action {:type :invoke
|
||||
:method "thread-api/validate-db"
|
||||
:direct-pass? false
|
||||
:args [repo]}})
|
||||
|
||||
:graph-info
|
||||
(if-not (seq repo)
|
||||
{:ok? false
|
||||
:error {:code :missing-graph
|
||||
:message "graph name is required"}}
|
||||
{:ok? true
|
||||
:action {:type :graph-info
|
||||
:repo repo
|
||||
:graph (repo->graph repo)}})
|
||||
(:graph-list :graph-create :graph-switch :graph-remove :graph-validate :graph-info)
|
||||
(build-graph-action command graph repo)
|
||||
|
||||
:add
|
||||
(if-not (seq repo)
|
||||
{:ok? false
|
||||
:error {:code :missing-repo
|
||||
:message "repo is required for add"}}
|
||||
(let [blocks-result (read-blocks options args)]
|
||||
(if-not (:ok? blocks-result)
|
||||
blocks-result
|
||||
(let [vector-result (ensure-blocks (:value blocks-result))]
|
||||
(if-not (:ok? vector-result)
|
||||
vector-result
|
||||
{:ok? true
|
||||
:action {:type :add
|
||||
:repo repo
|
||||
:graph (repo->graph repo)
|
||||
:page (:page options)
|
||||
:parent (:parent options)
|
||||
:blocks (:value vector-result)}})))))
|
||||
(build-add-action options args repo)
|
||||
|
||||
:remove
|
||||
(if-not (seq repo)
|
||||
{:ok? false
|
||||
:error {:code :missing-repo
|
||||
:message "repo is required for remove"}}
|
||||
(let [block (:block options)
|
||||
page (:page options)]
|
||||
(if (or (seq block) (seq page))
|
||||
{:ok? true
|
||||
:action {:type :remove
|
||||
:repo repo
|
||||
:block block
|
||||
:page page}}
|
||||
{:ok? false
|
||||
:error {:code :missing-target
|
||||
:message "block or page is required"}})))
|
||||
(build-remove-action options repo)
|
||||
|
||||
:search
|
||||
(if-not (seq repo)
|
||||
{:ok? false
|
||||
:error {:code :missing-repo
|
||||
:message "repo is required for search"}}
|
||||
(let [text (or (:text options) (string/join " " args))]
|
||||
(if (seq text)
|
||||
{:ok? true
|
||||
:action {:type :search
|
||||
:repo repo
|
||||
:text text
|
||||
:limit (:limit options)}}
|
||||
{:ok? false
|
||||
:error {:code :missing-search-text
|
||||
:message "search text is required"}})))
|
||||
(build-search-action options args repo)
|
||||
|
||||
:tree
|
||||
(if-not (seq repo)
|
||||
{:ok? false
|
||||
:error {:code :missing-repo
|
||||
:message "repo is required for tree"}}
|
||||
(let [block (:block options)
|
||||
page (:page options)
|
||||
target (or block page)]
|
||||
(if (seq target)
|
||||
{:ok? true
|
||||
:action {:type :tree
|
||||
:repo repo
|
||||
:block block
|
||||
:page page
|
||||
:format (some-> (:format options) string/lower-case)}}
|
||||
{:ok? false
|
||||
:error {:code :missing-target
|
||||
:message "block or page is required"}})))
|
||||
(build-tree-action options repo)
|
||||
|
||||
{:ok? false
|
||||
:error {:code :unknown-command
|
||||
@@ -497,18 +423,6 @@
|
||||
(defn execute
|
||||
[action config]
|
||||
(case (:type action)
|
||||
:ping
|
||||
(-> (transport/ping config)
|
||||
(p/then (fn [_]
|
||||
{:status :ok :data {:message "ok"}})))
|
||||
|
||||
:status
|
||||
(-> (p/let [ready? (transport/ready config)
|
||||
dbs (transport/list-db config)]
|
||||
{:status :ok
|
||||
:data {:ready ready?
|
||||
:dbs dbs}}))
|
||||
|
||||
:invoke
|
||||
(-> (p/let [result (transport/invoke config
|
||||
(:method action)
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
(ns logseq.cli.config
|
||||
"CLI configuration resolution and persistence."
|
||||
(:require [cljs.reader :as reader]
|
||||
[clojure.string :as string]
|
||||
[goog.object :as gobj]
|
||||
["fs" :as fs]
|
||||
["os" :as os]
|
||||
["path" :as path]))
|
||||
["path" :as node-path]))
|
||||
|
||||
(defn- parse-int
|
||||
[value]
|
||||
@@ -13,7 +14,7 @@
|
||||
|
||||
(defn- default-config-path
|
||||
[]
|
||||
(path/join (.homedir os) ".logseq" "cli.edn"))
|
||||
(node-path/join (.homedir os) ".logseq" "cli.edn"))
|
||||
|
||||
(defn- read-config-file
|
||||
[config-path]
|
||||
@@ -24,7 +25,7 @@
|
||||
(defn- ensure-config-dir!
|
||||
[config-path]
|
||||
(when (seq config-path)
|
||||
(let [dir (path/dirname config-path)]
|
||||
(let [dir (node-path/dirname config-path)]
|
||||
(when (and (seq dir) (not (fs/existsSync dir)))
|
||||
(.mkdirSync fs dir #js {:recursive true})))))
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
(ns logseq.cli.format
|
||||
(:require [clojure.string :as string]
|
||||
[clojure.walk :as walk]))
|
||||
"Formatting helpers for CLI output."
|
||||
(:require [clojure.walk :as walk]))
|
||||
|
||||
(defn- normalize-json
|
||||
[value]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
(ns logseq.cli.main
|
||||
"CLI entrypoint for invoking db-worker-node."
|
||||
(:refer-clojure :exclude [run!])
|
||||
(:require [clojure.string :as string]
|
||||
[logseq.cli.commands :as commands]
|
||||
@@ -11,14 +12,14 @@
|
||||
(string/join "\n"
|
||||
["logseq-cli <command> [options]"
|
||||
""
|
||||
"Commands: ping, status, query, export, graph-list, graph-create, graph-switch, graph-remove, graph-validate, graph-info, add, remove, search, tree"
|
||||
"Commands: graph-list, graph-create, graph-switch, graph-remove, graph-validate, graph-info, add, remove, search, tree"
|
||||
""
|
||||
"Options:"
|
||||
summary]))
|
||||
|
||||
(defn run!
|
||||
([args] (run! args {:exit? true}))
|
||||
([args {:keys [exit?] :or {exit? true}}]
|
||||
([args] (run! args {}))
|
||||
([args _opts]
|
||||
(let [parsed (commands/parse-args args)]
|
||||
(cond
|
||||
(:help? parsed)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
(ns logseq.cli.transport
|
||||
"HTTP transport for communicating with db-worker-node."
|
||||
(:require [clojure.string :as string]
|
||||
[logseq.db :as ldb]
|
||||
[promesa.core :as p]
|
||||
@@ -66,39 +67,23 @@
|
||||
(defn request
|
||||
[{:keys [method url headers body timeout-ms retries]
|
||||
:or {retries 0}}]
|
||||
(p/loop [attempt 0]
|
||||
(-> (p/let [response (<raw-request {:method method
|
||||
:url url
|
||||
:headers headers
|
||||
:body body
|
||||
:timeout-ms timeout-ms})]
|
||||
(if (<= 200 (:status response) 299)
|
||||
response
|
||||
(throw (ex-info "http request failed"
|
||||
{:code :http-error
|
||||
:status (:status response)
|
||||
:body (:body response)}))))
|
||||
(p/catch (fn [error]
|
||||
(if (and (< attempt retries) (retryable-error? error))
|
||||
(p/recur (inc attempt))
|
||||
(throw error)))))))
|
||||
|
||||
(defn ping
|
||||
[{:keys [base-url timeout-ms retries]}]
|
||||
(request {:method "GET"
|
||||
:url (str (string/replace base-url #"/$" "") "/healthz")
|
||||
:timeout-ms timeout-ms
|
||||
:retries retries
|
||||
:headers {}}))
|
||||
|
||||
(defn ready
|
||||
[{:keys [base-url timeout-ms retries]}]
|
||||
(-> (request {:method "GET"
|
||||
:url (str (string/replace base-url #"/$" "") "/readyz")
|
||||
:timeout-ms timeout-ms
|
||||
:retries retries
|
||||
:headers {}})
|
||||
(p/then (fn [_] true))))
|
||||
(letfn [(attempt-request [attempt]
|
||||
(-> (p/let [response (<raw-request {:method method
|
||||
:url url
|
||||
:headers headers
|
||||
:body body
|
||||
:timeout-ms timeout-ms})]
|
||||
(if (<= 200 (:status response) 299)
|
||||
response
|
||||
(throw (ex-info "http request failed"
|
||||
{:code :http-error
|
||||
:status (:status response)
|
||||
:body (:body response)}))))
|
||||
(p/catch (fn [error]
|
||||
(if (and (< attempt retries) (retryable-error? error))
|
||||
(attempt-request (inc attempt))
|
||||
(throw error))))))]
|
||||
(attempt-request 0)))
|
||||
|
||||
(defn invoke
|
||||
[{:keys [base-url auth-token timeout-ms retries]}
|
||||
@@ -123,10 +108,6 @@
|
||||
result
|
||||
(ldb/read-transit-str resultTransit)))))
|
||||
|
||||
(defn list-db
|
||||
[config]
|
||||
(invoke config "thread-api/list-db" false []))
|
||||
|
||||
(defn write-output
|
||||
[{:keys [format path data]}]
|
||||
(case format
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
[logseq.cli.commands :as commands]))
|
||||
|
||||
(deftest test-parse-args
|
||||
(testing "parses ping"
|
||||
(let [result (commands/parse-args ["ping"])]
|
||||
(is (true? (:ok? result)))
|
||||
(is (= :ping (:command result)))))
|
||||
(testing "rejects removed commands"
|
||||
(doseq [command ["ping" "status" "query" "export"]]
|
||||
(let [result (commands/parse-args [command])]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :unknown-command (get-in result [:error :code]))))))
|
||||
|
||||
(testing "errors on missing command"
|
||||
(let [result (commands/parse-args [])]
|
||||
@@ -18,39 +19,6 @@
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :unknown-command (get-in result [:error :code]))))))
|
||||
|
||||
(deftest test-build-action
|
||||
(testing "query requires repo"
|
||||
(let [parsed {:ok? true
|
||||
:command :query
|
||||
:options {:query "[:find ?e :where [?e :block/name]]"}}
|
||||
result (commands/build-action parsed {})]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :missing-repo (get-in result [:error :code])))))
|
||||
|
||||
(testing "query uses repo from config"
|
||||
(let [parsed {:ok? true
|
||||
:command :query
|
||||
:options {:query "[:find ?e :where [?e :block/name]]"}}
|
||||
result (commands/build-action parsed {:repo "test-repo"})]
|
||||
(is (true? (:ok? result)))
|
||||
(is (= "thread-api/q" (get-in result [:action :method])))))
|
||||
|
||||
(testing "export rejects unsupported format"
|
||||
(let [parsed {:ok? true
|
||||
:command :export
|
||||
:options {:repo "repo" :format "nope" :out "output.edn"}}
|
||||
result (commands/build-action parsed {})]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :unsupported-format (get-in result [:error :code])))))
|
||||
|
||||
(testing "export builds edn action"
|
||||
(let [parsed {:ok? true
|
||||
:command :export
|
||||
:options {:repo "repo" :format "edn" :out "output.edn"}}
|
||||
result (commands/build-action parsed {})]
|
||||
(is (true? (:ok? result)))
|
||||
(is (= "thread-api/export-edn" (get-in result [:action :method]))))))
|
||||
|
||||
(deftest test-graph-commands
|
||||
(testing "graph-list uses list-db"
|
||||
(let [parsed {:ok? true :command :graph-list :options {}}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
(ns logseq.cli.config-test
|
||||
(:require [cljs.reader :as reader]
|
||||
[cljs.test :refer [deftest is testing]]
|
||||
[cljs.test :refer [deftest is]]
|
||||
[frontend.test.node-helper :as node-helper]
|
||||
[goog.object :as gobj]
|
||||
[logseq.cli.config :as config]
|
||||
["fs" :as fs]
|
||||
["path" :as path]))
|
||||
["path" :as node-path]))
|
||||
|
||||
(defn- with-env
|
||||
[env f]
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
(deftest test-config-precedence
|
||||
(let [dir (node-helper/create-tmp-dir)
|
||||
cfg-path (path/join dir "cli.edn")
|
||||
cfg-path (node-path/join dir "cli.edn")
|
||||
_ (fs/writeFileSync cfg-path
|
||||
(str "{:base-url \"http://file:7777\" "
|
||||
":auth-token \"file-token\" "
|
||||
@@ -53,7 +53,7 @@
|
||||
|
||||
(deftest test-env-overrides-file
|
||||
(let [dir (node-helper/create-tmp-dir)
|
||||
cfg-path (path/join dir "cli.edn")
|
||||
cfg-path (node-path/join dir "cli.edn")
|
||||
_ (fs/writeFileSync cfg-path "{:base-url \"http://file:7777\" :repo \"file-repo\"}")
|
||||
env {"LOGSEQ_DB_WORKER_URL" "http://env:9999"
|
||||
"LOGSEQ_CLI_REPO" "env-repo"}
|
||||
@@ -63,7 +63,7 @@
|
||||
|
||||
(deftest test-update-config
|
||||
(let [dir (node-helper/create-tmp-dir "cli")
|
||||
cfg-path (path/join dir "cli.edn")
|
||||
cfg-path (node-path/join dir "cli.edn")
|
||||
_ (fs/writeFileSync cfg-path "{:repo \"old\"}")
|
||||
_ (config/update-config! {:config-path cfg-path} {:repo "new"})
|
||||
contents (.toString (fs/readFileSync cfg-path) "utf8")
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
[logseq.cli.main :as cli-main]
|
||||
[promesa.core :as p]
|
||||
["fs" :as fs]
|
||||
["path" :as path]))
|
||||
["path" :as node-path]))
|
||||
|
||||
(defn- run-cli
|
||||
[args url cfg-path]
|
||||
@@ -16,22 +16,6 @@
|
||||
[result]
|
||||
(js->clj (js/JSON.parse (:output result)) :keywordize-keys true))
|
||||
|
||||
(deftest test-cli-ping
|
||||
(async done
|
||||
(let [data-dir (node-helper/create-tmp-dir "db-worker")]
|
||||
(-> (p/let [daemon (db-worker-node/start-daemon! {:host "127.0.0.1"
|
||||
:port 0
|
||||
:data-dir data-dir})
|
||||
url (str "http://127.0.0.1:" (:port daemon))
|
||||
result (cli-main/run! ["ping" "--base-url" url "--json"] {:exit? false})]
|
||||
(is (= 0 (:exit-code result)))
|
||||
(is (= "{\"status\":\"ok\",\"data\":{\"message\":\"ok\"}}" (:output result)))
|
||||
(p/let [_ ((:stop! daemon))]
|
||||
(done)))
|
||||
(p/catch (fn [e]
|
||||
(is false (str "unexpected error: " e))
|
||||
(done)))))))
|
||||
|
||||
(deftest test-cli-graph-list
|
||||
(async done
|
||||
(let [data-dir (node-helper/create-tmp-dir "db-worker")]
|
||||
@@ -39,7 +23,7 @@
|
||||
:port 0
|
||||
:data-dir data-dir})
|
||||
url (str "http://127.0.0.1:" (:port daemon))
|
||||
cfg-path (path/join (node-helper/create-tmp-dir "cli") "cli.edn")
|
||||
cfg-path (node-path/join (node-helper/create-tmp-dir "cli") "cli.edn")
|
||||
result (run-cli ["graph-list"] url cfg-path)
|
||||
payload (parse-json-output result)]
|
||||
(is (= 0 (:exit-code result)))
|
||||
@@ -58,7 +42,7 @@
|
||||
:port 0
|
||||
:data-dir data-dir})
|
||||
url (str "http://127.0.0.1:" (:port daemon))
|
||||
cfg-path (path/join (node-helper/create-tmp-dir "cli") "cli.edn")
|
||||
cfg-path (node-path/join (node-helper/create-tmp-dir "cli") "cli.edn")
|
||||
_ (fs/writeFileSync cfg-path "{}")
|
||||
create-result (run-cli ["graph-create" "--graph" "demo-graph"] url cfg-path)
|
||||
create-payload (parse-json-output create-result)
|
||||
@@ -82,7 +66,7 @@
|
||||
:port 0
|
||||
:data-dir data-dir})
|
||||
url (str "http://127.0.0.1:" (:port daemon))
|
||||
cfg-path (path/join (node-helper/create-tmp-dir "cli") "cli.edn")
|
||||
cfg-path (node-path/join (node-helper/create-tmp-dir "cli") "cli.edn")
|
||||
_ (fs/writeFileSync cfg-path "{}")
|
||||
_ (run-cli ["graph-create" "--graph" "content-graph"] url cfg-path)
|
||||
add-result (run-cli ["add" "--page" "TestPage" "--content" "hello world"] url cfg-path)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(ns logseq.cli.transport-test
|
||||
(:require [cljs.test :refer [deftest is async testing]]
|
||||
(:require [cljs.test :refer [deftest is async]]
|
||||
[promesa.core :as p]
|
||||
[logseq.cli.transport :as transport]))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user