040-hide-db-prefix-in-user-visible-graph-names.md

This commit is contained in:
rcmerci
2026-02-26 19:21:41 +08:00
parent 7d4a241e6b
commit a4b8f2aeff
18 changed files with 477 additions and 53 deletions

View File

@@ -38,6 +38,6 @@
(defn list-graphs
[]
(let [db-graphs (->> (cli-common-graph/get-db-based-graphs)
(map #(string/replace-first % common-config/db-version-prefix ""))
(map common-config/strip-leading-db-version-prefix)
sort)]
(println (string/join "\n" db-graphs))))
(println (string/join "\n" db-graphs))))

View File

@@ -27,5 +27,7 @@
(remove (fn [s] (= s common-config/unlinked-graphs-dir)))
(map graph-name->path)
(keep (fn [s]
(when-not (string/starts-with? s common-config/file-version-prefix)
(str common-config/db-version-prefix s)))))))
(when (and (string? s)
(not (string/starts-with? s common-config/file-version-prefix)))
(common-config/canonicalize-db-version-repo s))))
distinct)))

View File

@@ -33,6 +33,25 @@
(defonce db-version-prefix "logseq_db_")
(defonce file-version-prefix "logseq_local_")
(defn strip-leading-db-version-prefix
"Strip exactly one leading db prefix for user-facing display values."
[s]
(if (and (string? s)
(string/starts-with? s db-version-prefix))
(subs s (count db-version-prefix))
s))
(defn canonicalize-db-version-repo
"Normalize any repo/graph name to exactly one leading db prefix."
[s]
(when (seq s)
(let [s (str s)
stripped (loop [name s]
(if (string/starts-with? name db-version-prefix)
(recur (subs name (count db-version-prefix)))
name))]
(str db-version-prefix stripped))))
(defonce local-assets-dir "assets")
(defonce unlinked-graphs-dir "Unlinked graphs")

View File

@@ -0,0 +1,204 @@
# Hide Db Prefix In User Visible Graph Names Implementation Plan
Goal: Ensure user-visible graph names strip exactly one leading `logseq_db_` prefix while preventing new multi-prefix repos from being created.
Architecture: Keep display normalization as single-pass prefix stripping for web, Electron, and CLI user-facing fields.
Architecture: Add shared canonicalization at ingestion and graph-discovery boundaries so internal repo identifiers are normalized to exactly one leading prefix before persistence and routing.
Architecture: Follow `@test-driven-development` with failing tests first across frontend, Electron boundary code, RTC ingestion, and CLI paths.
Tech Stack: ClojureScript, Rum, Electron IPC, db-worker-node runtime, Logseq CLI formatting pipeline, Babashka tests.
Related: Relates to `docs/agent-guide/033-desktop-db-worker-node-backend.md`.
Related: Builds on `docs/agent-guide/038-electron-db-worker-switch-graph.md`.
## Problem statement
Graph names shown in the web graph list can expose `logseq_db_` when the stored repo value has multiple leading prefixes.
Current rendering logic intentionally strips only one leading prefix, so malformed values like `logseq_db_logseq_db_demo` still render as `logseq_db_demo`.
The product decision is to keep one-layer stripping behavior and fix the upstream causes that create multi-prefix repo identifiers.
Multi-prefix data can enter through legacy disk graph discovery, Electron graph mapping, RTC remote metadata ingestion, and CLI graph-name conversion paths.
The required outcome is stable single-prefix internal repo identifiers plus single-pass display normalization, so normal graphs render as `demo` and malformed legacy doubles render as `logseq_db_demo`.
## Current and target normalization path
```text
Current path with multi-prefix source data.
input repo: logseq_db_logseq_db_demo
-> display helper strips once
-> output: logseq_db_demo
-> user-visible prefix leak occurs.
Target path with source canonicalization + single-pass display stripping.
ingress repo: logseq_db_logseq_db_demo
-> canonicalize to one internal prefix: logseq_db_demo
-> display helper strips once
-> output: demo
Legacy persisted double-prefix fallback path (no migration).
input repo from old state: logseq_db_logseq_db_demo
-> display helper strips once
-> output: logseq_db_demo
```
## Testing Plan
I will follow `@test-driven-development` and write all failing tests before implementation.
I will add frontend unit tests in `/Users/rcmerci/gh-repos/logseq/src/test/frontend/util/text_test.cljs` to assert single-pass prefix stripping behavior.
I will add frontend persist tests in `/Users/rcmerci/gh-repos/logseq/src/test/frontend/db/persist_test.cljs` to assert merged graph sources are canonicalized to one internal prefix before UI state usage.
I will add RTC tests in `/Users/rcmerci/gh-repos/logseq/src/test/frontend/handler/db_based/rtc_test.cljs` to assert remote graph payload ingestion cannot create local double-prefix repos.
I will extend CLI formatter tests in `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/format_test.cljs` to assert user-facing fields strip exactly one prefix and never introduce additional prefixes.
I will extend CLI command tests in `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/commands_test.cljs` for `graph list` and server output paths with unprefixed and prefix-like graph names, and assert prefix-like `--repo` values are treated as graph-name content instead of invalid input.
I will add legacy graph discovery tests in `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/common/graph_test.cljs` to verify old directory names do not produce multi-prefix repo ids.
I will run focused tests after RED and GREEN phases, then run `bb dev:lint-and-test` before completion.
I will review changes against `@prompts/review.md` before merge.
NOTE: I will write all tests before I add any implementation behavior.
## Implementation plan
### Phase 1: Add failing tests for one-layer display stripping and multi-prefix prevention.
1. Add failing unit tests in `/Users/rcmerci/gh-repos/logseq/src/test/frontend/util/text_test.cljs` for `logseq_db_demo -> demo`, `logseq_db_logseq_db_demo -> logseq_db_demo`, and middle-substring preservation.
2. Add failing tests in `/Users/rcmerci/gh-repos/logseq/src/test/frontend/db/persist_test.cljs` to verify worker and Electron graph sources are canonicalized to one internal prefix.
3. Add failing tests in `/Users/rcmerci/gh-repos/logseq/src/test/frontend/handler/db_based/rtc_test.cljs` for remote graph mapping and download paths with prefixed and double-prefixed payload names.
4. Extend `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/format_test.cljs` with failing cases where `:repo` or `:graph` includes one or two prefixes and output uses one-layer stripping only.
5. Extend `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/commands_test.cljs` with failing cases for graph list and server output using unprefixed and prefix-like `--repo` values, and assert prefix-like values are not rejected by argument validation.
6. Add failing tests in `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/common/graph_test.cljs` for legacy directory names that already contain `logseq_db_`.
7. Run focused tests and confirm failures reflect behavior gaps rather than setup errors.
### Phase 2: Implement shared helpers for display normalization and repo canonicalization.
8. Add shared helpers in `/Users/rcmerci/gh-repos/logseq/deps/common/src/logseq/common/config.cljs` for single-pass display stripping and exact-one-prefix canonicalization.
9. Keep display helper semantics strict to remove only one leading prefix and preserve all middle substrings.
10. Keep canonicalization helper semantics strict to collapse any number of leading prefixes to exactly one.
11. Use function names that clearly separate display behavior from internal repo id normalization.
12. Keep helpers pure and dependency-light so they can be reused in frontend, Electron, and CLI namespaces.
### Phase 3: Apply web app fixes with one-layer display semantics.
13. Update `/Users/rcmerci/gh-repos/logseq/src/main/frontend/util/text.cljs` so `get-graph-name-from-path` calls the shared single-pass display helper.
14. Update `/Users/rcmerci/gh-repos/logseq/src/main/frontend/config.cljs` so `db-graph-name` remains one-layer stripping and does not over-strip.
15. Update `/Users/rcmerci/gh-repos/logseq/src/main/frontend/db/conn.cljs` so `get-short-repo-name` uses shared one-layer display normalization.
16. Update `/Users/rcmerci/gh-repos/logseq/src/main/frontend/components/repo.cljs` rendering paths only if needed to ensure all user-visible labels share one-layer stripping behavior.
17. Update `/Users/rcmerci/gh-repos/logseq/src/main/frontend/db/persist.cljs` so merged graph sources are canonicalized to one internal prefix before entering repo state.
18. Keep `data-testid` and internal routing keys unchanged unless canonicalization is required to prevent multi-prefix key creation.
### Phase 4: Fix Electron and graph discovery paths that can create multi-prefix repos.
19. Update `/Users/rcmerci/gh-repos/logseq/deps/cli/src/logseq/cli/common/graph.cljs` so `get-db-based-graphs` canonicalizes discovered graph repo names to one prefix.
20. Update `/Users/rcmerci/gh-repos/logseq/src/electron/electron/handler.cljs` and `/Users/rcmerci/gh-repos/logseq/src/electron/electron/utils.cljs` to canonicalize repo identifiers at IPC mapping boundaries.
21. Verify Electron IPC `getGraphs` keeps stable internal repo identifiers and no path emits new double-prefixed repos.
### Phase 5: Fix RTC ingestion paths that can reintroduce multi-prefix repos.
22. Update `/Users/rcmerci/gh-repos/logseq/src/main/frontend/handler/db_based/rtc.cljs` so remote graph payload mapping canonicalizes incoming graph names before repo/url construction.
23. Update `/Users/rcmerci/gh-repos/logseq/src/main/frontend/worker/rtc/full_upload_download_graph.cljs` so download naming canonicalizes to one internal prefix before local repo creation.
24. Ensure existing graphs with prefixed remote names remain accessible after normalization.
25. Ensure new uploads and downloads cannot create `logseq_db_logseq_db_*` local repo ids.
### Phase 6: Apply CLI fixes for one-layer display and one-prefix internal ids.
26. Update `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/core.cljs` so `repo->graph` strips exactly one leading prefix for user-visible output.
27. Verify `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/format.cljs` uses one-layer display normalization consistently for human, JSON, and EDN user-facing graph fields.
28. Update `/Users/rcmerci/gh-repos/logseq/deps/cli/src/logseq/cli/commands/graph.cljs` to canonicalize internal repo identifiers before list rendering and server-target resolution.
29. Ensure CLI parsing and command execution treat `--repo` as a graph-name string, so leading `logseq_db_` is interpreted as part of graph name when present and is not rejected by input validation.
### Phase 7: Verification and release gate.
30. Run `bb dev:test -v 'frontend.util.text-test'` and confirm one-layer strip behavior and canonicalization tests pass.
31. Run `bb dev:test -v 'frontend.db.persist-test'` and confirm merged-source canonicalization behavior is stable.
32. Run `bb dev:test -v 'frontend.handler.db-based.rtc-test'` and confirm remote ingestion cannot produce multi-prefix repos.
33. Run `bb dev:test -v 'logseq.cli.format-test'` and confirm CLI display fields apply one-layer strip behavior.
34. Run `bb dev:test -v 'logseq.cli.commands-test'` and confirm graph command behavior remains stable with unprefixed and prefix-like `--repo` input.
35. Run `bb dev:test -v 'logseq.cli.common.graph-test'` and confirm legacy graph discovery does not emit double-prefixed repo names.
36. Run `bb dev:lint-and-test` and confirm `0 failures, 0 errors`.
37. Perform a manual graph-list smoke check on web and Electron to confirm normal graphs display without prefix and legacy doubles display with one remaining prefix.
38. Perform a manual CLI smoke check with `logseq graph list`, `logseq server status --repo demo`, and `logseq server status --repo logseq_db_demo` to confirm both inputs are accepted as graph names.
## Edge cases
| Scenario | Expected behavior |
|---|---|
| Internal repo is `logseq_db_demo`. | User-visible name is `demo`. |
| Internal repo is `logseq_db_logseq_db_demo`. | User-visible name is `logseq_db_demo`. |
| Graph name contains middle substring like `my_logseq_db_notes`. | Middle substring is preserved and only one leading prefix is removed when present. |
| Legacy disk directory is named `logseq_db_demo`. | Discovery canonicalization keeps exactly one prefix and does not create `logseq_db_logseq_db_demo`. |
| Legacy disk directory is named `logseq_db_logseq_db_demo`. | Discovery canonicalization collapses to internal repo `logseq_db_demo`. |
| Remote graph payload returns `graph-name` as `logseq_db_demo`. | RTC mapping keeps internal repo `logseq_db_demo` and user-visible name `demo`. |
| Remote graph payload returns `graph-name` as `logseq_db_logseq_db_demo`. | RTC mapping canonicalizes to internal repo `logseq_db_demo`, and user-visible name is `demo` after one-layer display strip. |
| CLI receives `--repo demo`. | Command works and output graph name is `demo`. |
| CLI receives `--repo logseq_db_demo`. | Command treats `logseq_db_` as part of graph name and does not fail argument validation. |
| CLI receives `--repo logseq_db_logseq_db_demo`. | Command treats the full value as graph name content and does not fail argument validation. |
| Non-user-visible fields like `data-testid` include repo id. | Existing selectors remain unchanged unless canonicalization is required to prevent duplicate graph entries. |
## Verification commands and expected outputs
```bash
bb dev:test -v 'frontend.util.text-test'
bb dev:test -v 'frontend.db.persist-test'
bb dev:test -v 'frontend.handler.db-based.rtc-test'
bb dev:test -v 'logseq.cli.format-test'
bb dev:test -v 'logseq.cli.commands-test'
bb dev:test -v 'logseq.cli.common.graph-test'
bb dev:lint-and-test
```
Each command should finish with `0 failures, 0 errors`.
Web and Electron manual checks should show no new multi-prefix repo entries.
Web and Electron display should strip one prefix only at render time.
CLI human output should match one-layer strip semantics, and CLI `--repo` should treat prefix-like values as normal graph names.
## Testing Details
The tests verify user-visible behavior and repo-id canonicalization at ingress and discovery boundaries.
Frontend tests assert display output and merged-state behavior rather than helper internals alone.
RTC and Electron tests assert that incoming prefixed names cannot generate additional prefixes in local repo ids.
CLI tests assert command behavior and output formatting remain stable for unprefixed and prefix-like graph-name input.
## Implementation Details
- Keep display normalization as single-pass prefix stripping.
- Add one shared helper for exact-one-prefix repo canonicalization.
- Canonicalize ingress and discovery data before it reaches persistent repo state.
- Reuse shared helpers across frontend, Electron, and CLI.
- Preserve `data-testid` compatibility unless canonicalization makes key updates unavoidable.
- Avoid one-time metadata migration for existing persisted graphs.
- Treat CLI `--repo` input as raw graph name where `logseq_db_` may be part of the name.
- Keep internal thread-api contracts based on prefixed repo ids.
- Follow `@test-driven-development` for RED, GREEN, and REFACTOR order.
- Validate final patch with `@prompts/review.md` checklist.
## Question
Decision: Display normalization removes only one leading prefix, so `logseq_db_logseq_db_xxxx` displays as `logseq_db_xxxx` in app surfaces that read legacy uncanonicalized values.
Decision: The implementation focus is to identify and fix all paths that can create multi-prefix repo identifiers, so newly produced data stays canonical.
Decision: This change does not include a one-time metadata migration for existing persisted legacy values.
Decision: CLI `--repo` option treats leading `logseq_db_` as graph-name content, not as forbidden prefix.
Decision: Treat `data-testid` stability as a strict compatibility requirement for `clj-e2e`.
---

View File

@@ -29,6 +29,7 @@
[electron.window :as win]
[electron.graph-switch-flow :as graph-switch-flow]
[logseq.cli.common.graph :as cli-common-graph]
[logseq.common.config :as common-config]
[logseq.common.graph :as common-graph]
[logseq.db.sqlite.util :as sqlite-util]
[promesa.core :as p]))
@@ -200,24 +201,30 @@
[]
(distinct (cli-common-graph/get-db-based-graphs)))
(defn- canonical-repo
[graph]
(common-config/canonicalize-db-version-repo graph))
;; TODO support alias mechanism
(defn get-graph-name
"Given a graph's name of string, returns the graph's fullname. For example, given
`cat`, returns `logseq_db_cat`. Returns `nil` if no such graph exists."
[graph-identifier]
(->> (get-graphs)
(some #(when (or
(= (utils/normalize-lc %) (utils/normalize-lc (str sqlite-util/db-version-prefix graph-identifier)))
(string/ends-with? (utils/normalize-lc %)
(str "/" (utils/normalize-lc graph-identifier))))
%))))
(when-let [repo (canonical-repo graph-identifier)]
(let [graph-name (common-config/strip-leading-db-version-prefix repo)]
(->> (get-graphs)
(some #(when (or
(= (utils/normalize-lc %) (utils/normalize-lc repo))
(string/ends-with? (utils/normalize-lc %)
(str "/" (utils/normalize-lc graph-name))))
%))))))
(defmethod handle :getGraphs [_window [_]]
(get-graphs))
(defmethod handle :deleteGraph [_window [_ graph]]
(when graph
(db/unlink-graph! graph)))
(when-let [repo (canonical-repo graph)]
(db/unlink-graph! repo)))
;; DB related IPCs start
@@ -228,20 +235,22 @@
(defmethod handle :db-worker-runtime [^js window [_ repo]]
(if (string/blank? repo)
(p/rejected (ex-info "repo is required" {:code :missing-repo}))
(db-worker/ensure-runtime! repo (.-id window))))
(db-worker/ensure-runtime! (canonical-repo repo) (.-id window))))
(defmethod handle :db-export [_window [_ repo data]]
(logger/warn ::db-export-compat
{:repo repo
:message "legacy db-export IPC path invoked; desktop should use db-worker runtime"})
(db/ensure-graph-dir! repo)
(db/save-db! repo data))
(when-let [repo (canonical-repo repo)]
(logger/warn ::db-export-compat
{:repo repo
:message "legacy db-export IPC path invoked; desktop should use db-worker runtime"})
(db/ensure-graph-dir! repo)
(db/save-db! repo data)))
(defmethod handle :db-get [_window [_ repo]]
(logger/warn ::db-get-compat
{:repo repo
:message "legacy db-get IPC path invoked; desktop should use db-worker runtime"})
(db/get-db repo))
(when-let [repo (canonical-repo repo)]
(logger/warn ::db-get-compat
{:repo repo
:message "legacy db-get IPC path invoked; desktop should use db-worker runtime"})
(db/get-db repo)))
;; DB related IPCs End

View File

@@ -7,7 +7,7 @@
[electron.configs :as cfgs]
[electron.logger :as logger]
[logseq.cli.common.graph :as cli-common-graph]
[logseq.db.sqlite.util :as sqlite-util]
[logseq.common.config :as common-config]
[promesa.core :as p]))
(defonce *win (atom nil)) ;; The main window
@@ -240,14 +240,17 @@
(defn get-graph-dir
"required by all internal state in the electron section"
[graph-name]
(when (string/starts-with? graph-name sqlite-util/db-version-prefix)
(node-path/join (cli-common-graph/get-db-graphs-dir) (string/replace-first graph-name sqlite-util/db-version-prefix ""))))
(when (and (string? graph-name)
(string/starts-with? graph-name common-config/db-version-prefix))
(let [repo (common-config/canonicalize-db-version-repo graph-name)]
(node-path/join (cli-common-graph/get-db-graphs-dir)
(common-config/strip-leading-db-version-prefix repo)))))
(comment
(defn get-graph-name
"Reverse `get-graph-dir`"
[graph-dir]
(str sqlite-util/db-version-prefix (node-path/basename graph-dir))))
(str common-config/db-version-prefix (node-path/basename graph-dir))))
(defn decode-protected-assets-schema-path
[schema-path]

View File

@@ -238,7 +238,7 @@
(defn db-graph-name
[repo-with-prefix]
(string/replace-first repo-with-prefix db-version-prefix ""))
(common-config/strip-leading-db-version-prefix repo-with-prefix))
(defn db-based-graph?
([]
@@ -257,7 +257,7 @@
(path/path-join (get-in @state/state [:system/info :home-dir])
"logseq"
"graphs"
(string/replace repo db-version-prefix "")))
(db-graph-name repo)))
(defn get-electron-backup-dir
[repo]
@@ -269,7 +269,7 @@
(if (util/electron?)
(get-local-dir repo-url)
(str "memory:///"
(string/replace-first repo-url db-version-prefix "")))))
(db-graph-name repo-url)))))
(defn get-repo-config-path
[]

View File

@@ -1,7 +1,6 @@
(ns frontend.db.conn
"Contains db connections."
(:require [clojure.string :as string]
[datascript.core :as d]
(:require [datascript.core :as d]
[frontend.config :as config]
[frontend.db.conn-state :as db-conn-state]
[frontend.mobile.util :as mobile-util]
@@ -52,7 +51,7 @@
:else
repo-name)]
(if (config/db-based-graph? repo-name')
(string/replace-first repo-name' config/db-version-prefix "")
(config/db-graph-name repo-name')
repo-name')))
(defn remove-conn!

View File

@@ -3,7 +3,6 @@
(:require [cljs-bean.core :as bean]
[clojure.string :as string]
[electron.ipc :as ipc]
[frontend.config :as config]
[frontend.persist-db :as persist-db]
[frontend.util :as util]
[logseq.common.config :as common-config]
@@ -23,12 +22,13 @@
(map
(fn [{:keys [name] :as repo}]
(assoc repo :name
(str config/db-version-prefix name)))))
(common-config/canonicalize-db-version-repo name)))))
electron-disk-graphs (when (util/electron?) (ipc/ipc "getGraphs"))]
(distinct
(concat
repos'
(map (fn [repo-name] {:name repo-name})
(map (fn [repo-name]
{:name (common-config/canonicalize-db-version-repo repo-name)})
(some-> electron-disk-graphs bean/->clj))))))
(defn delete-graph!

View File

@@ -9,6 +9,7 @@
[frontend.state :as state]
[frontend.util :as util]
[lambdaisland.glogi :as log]
[logseq.common.config :as common-config]
[logseq.db :as ldb]
[logseq.db-sync.malli-schema :as db-sync-schema]
[logseq.db.sqlite.util :as sqlite-util]
@@ -286,7 +287,7 @@
(if base
(p/let [_ (js/Promise. user-handler/task--ensure-id&access-token)
body (coerce-http-request :graphs/create
{:graph-name (string/replace repo config/db-version-prefix "")
{:graph-name (common-config/strip-leading-db-version-prefix repo)
:schema-version schema-version
:graph-e2ee? graph-e2ee?})
result (if (nil? body)
@@ -334,15 +335,15 @@
([graph-name graph-uuid graph-e2ee?]
(state/set-state! :rtc/downloading-graph-uuid graph-uuid)
(state/pub-event!
[:rtc/log {:type :rtc.log/download
[:rtc/log {:type :rtc.log/download
:sub-type :download-progress
:graph-uuid graph-uuid
:message "Preparing graph snapshot download"}])
(let [graph-e2ee? (normalize-graph-e2ee? graph-e2ee?)
graph (common-config/canonicalize-db-version-repo graph-name)
base (http-base)]
(-> (if (and graph-uuid base)
(-> (p/let [_ (js/Promise. user-handler/task--ensure-id&access-token)
graph (str config/db-version-prefix graph-name)
pull-resp (fetch-json (str base "/sync/" graph-uuid "/pull")
{:method "GET"}
{:response-schema :sync/pull})
@@ -398,12 +399,14 @@
{:response-schema :graphs/list})
graphs (:graphs resp)
result (mapv (fn [graph]
(let [graph-e2ee? (if (contains? graph :graph-e2ee?)
(let [repo (common-config/canonicalize-db-version-repo (:graph-name graph))
graph-name (common-config/strip-leading-db-version-prefix repo)
graph-e2ee? (if (contains? graph :graph-e2ee?)
(normalize-graph-e2ee? (:graph-e2ee? graph))
true)]
(merge
{:url (str config/db-version-prefix (:graph-name graph))
:GraphName (:graph-name graph)
{:url repo
:GraphName graph-name
:GraphSchemaVersion (:schema-version graph)
:GraphUUID (:graph-id graph)
:rtc-graph? true

View File

@@ -101,4 +101,4 @@
On iOS, repo-url might be nil"
[repo-url]
(when (not-empty repo-url)
(string/replace-first repo-url config/db-version-prefix "")))
(config/db-graph-name repo-url)))

View File

@@ -210,15 +210,14 @@
(defn graph->repo
[graph]
(when (seq graph)
(if (string/starts-with? graph common-config/db-version-prefix)
graph
(str common-config/db-version-prefix graph))))
(some-> graph
string/trim
common-config/canonicalize-db-version-repo))
(defn repo->graph
[repo]
(when (seq repo)
(string/replace-first repo common-config/db-version-prefix "")))
(common-config/strip-leading-db-version-prefix repo)))
(defn resolve-repo
[graph]

View File

@@ -0,0 +1,31 @@
(ns frontend.db.persist-test
(:require [cljs.test :refer [async deftest is]]
[electron.ipc :as ipc]
[frontend.db.persist :as db-persist]
[frontend.persist-db :as persist-db]
[frontend.util :as util]
[promesa.core :as p]))
(deftest get-all-graphs-canonicalizes-db-prefixes-from-all-sources
(async done
(-> (p/with-redefs [persist-db/<list-db (fn []
(p/resolved [{:name "demo"}
{:name "logseq_db_prefixed"}
{:name "logseq_db_logseq_db_legacy"}
{:name "logseq_local_local-only"}]))
util/electron? (constantly true)
ipc/ipc (fn [_channel]
(p/resolved #js ["logseq_db_remote"
"logseq_db_logseq_db_remote-legacy"]))]
(p/let [graphs (db-persist/get-all-graphs)
names (mapv :name graphs)]
(is (= #{"logseq_db_demo"
"logseq_db_prefixed"
"logseq_db_legacy"
"logseq_db_remote"
"logseq_db_remote-legacy"}
(set names)))
(is (not-any? #(re-find #"^logseq_db_logseq_db_" %) names))))
(p/catch (fn [error]
(is false (str error))))
(p/finally done))))

View File

@@ -2,6 +2,7 @@
(:require [cljs.test :refer [deftest is async]]
[clojure.string :as string]
[frontend.db :as db]
[frontend.handler.repo :as repo-handler]
[frontend.handler.db-based.sync :as db-sync]
[frontend.handler.user :as user-handler]
[frontend.state :as state]
@@ -152,6 +153,37 @@
(is false (str e))
(done)))))))
(deftest get-remote-graphs-canonicalizes-prefixed-graph-names
(async done
(let [set-state-calls (atom [])]
(-> (p/with-redefs [db-sync/http-base (constantly "http://base")
user-handler/task--ensure-id&access-token (fn [resolve _reject]
(resolve true))
db-sync/fetch-json (fn [url _opts _schema]
(if (string/ends-with? url "/graphs")
(p/resolved {:graphs [{:graph-name "logseq_db_demo"
:graph-id "graph-1"
:schema-version 1}
{:graph-name "logseq_db_logseq_db_legacy"
:graph-id "graph-2"
:schema-version 1}]})
(p/rejected (ex-info "unexpected fetch-json URL"
{:url url}))))
state/set-state! (fn [k v]
(swap! set-state-calls conj [k v]))
repo-handler/refresh-repos! (fn [] nil)]
(p/let [result (db-sync/<get-remote-graphs)
urls (mapv :url result)]
(is (= ["logseq_db_demo" "logseq_db_legacy"] urls))
(is (not-any? #(re-find #"^logseq_db_logseq_db_" %) urls))
(is (some (fn [[k v]]
(and (= :rtc/graphs k)
(= urls (mapv :url v))))
@set-state-calls))))
(p/catch (fn [error]
(is false (str error))))
(p/finally done)))))
(deftest rtc-download-graph-emits-feedback-before-snapshot-fetch-test
(let [trace (atom [])
log-events (atom [])]
@@ -242,3 +274,56 @@
(set! js/fetch original-fetch)
(is false (str error))
(done)))))))
(deftest rtc-download-graph-canonicalizes-prefixed-graph-name-before-import-test
(async done
(let [import-calls (atom [])
rows [[1 "content-1" "addresses-1"]]
framed-bytes (encode-framed-rows rows)
original-fetch js/fetch
stream-url "http://base/sync/graph-2/snapshot/stream"]
(-> (p/let [gzip-bytes (<gzip-bytes framed-bytes)]
(set! js/fetch
(fn [url opts]
(let [method (or (aget opts "method") "GET")]
(cond
(and (= url stream-url) (= method "GET"))
(js/Promise.resolve
#js {:ok true
:status 200
:headers #js {:get (fn [header]
(when (= header "content-length")
(str (.-byteLength gzip-bytes))))}
:arrayBuffer (fn [] (js/Promise.resolve (.-buffer gzip-bytes)))})
:else
(js/Promise.resolve #js {:ok true :status 200})))))
(-> (p/with-redefs [db-sync/http-base (constantly "http://base")
db-sync/fetch-json (fn [url _opts _schema]
(cond
(string/ends-with? url "/pull")
(p/resolved {:t 8})
:else
(p/rejected (ex-info "unexpected fetch-json URL"
{:url url}))))
user-handler/task--ensure-id&access-token (fn [resolve _reject]
(resolve true))
state/<invoke-db-worker (fn [& args]
(swap! import-calls conj args)
(p/resolved :ok))
state/set-state! (fn [& _] nil)
state/pub-event! (fn [& _] nil)]
(db-sync/<rtc-download-graph! "logseq_db_demo" "graph-2" true))
(p/finally (fn [] (set! js/fetch original-fetch)))))
(p/then (fn [_]
(is (= 1 (count @import-calls)))
(let [[op graph] (first @import-calls)]
(is (= :thread-api/db-sync-import-kvs-rows op))
(is (= "logseq_db_demo" graph))
(is (not (string/starts-with? graph "logseq_db_logseq_db_"))))
(done)))
(p/catch (fn [error]
(set! js/fetch original-fetch)
(is false (str error))
(done)))))))

View File

@@ -34,4 +34,10 @@
(map #(text-util/wrapped-by? "prop::value" % "" "::") (take 12 (range)))
'(false false false false false false true true true true true true)
(map #(text-util/wrapped-by? "prop::value" % "::" "") (take 12 (range)))))
(map #(text-util/wrapped-by? "prop::value" % "::" "") (take 12 (range)))))
(deftest get-graph-name-from-path-strips-only-one-leading-db-prefix
(are [input expected] (= expected (text-util/get-graph-name-from-path input))
"logseq_db_demo" "demo"
"logseq_db_logseq_db_demo" "logseq_db_demo"
"my_logseq_db_notes" "my_logseq_db_notes"))

View File

@@ -1080,7 +1080,14 @@
"--input" "import.zip"
"--repo" "demo"])]
(is (false? (:ok? result)))
(is (= :invalid-options (get-in result [:error :code]))))))
(is (= :invalid-options (get-in result [:error :code])))))
(testing "server status accepts prefix-like repo option values"
(let [result (commands/parse-args ["server" "status"
"--repo" "logseq_db_logseq_db_demo"])]
(is (true? (:ok? result)))
(is (= :server-status (:command result)))
(is (= "logseq_db_logseq_db_demo" (get-in result [:options :repo]))))))
(deftest test-verb-subcommand-parse-flags
(testing "verb subcommands reject unknown flags"
@@ -1156,7 +1163,14 @@
(let [parsed {:ok? true :command :server-stop :options {:repo "demo"}}
result (commands/build-action parsed {})]
(is (true? (:ok? result)))
(is (= :server-stop (get-in result [:action :type]))))))
(is (= :server-stop (get-in result [:action :type])))))
(testing "server status canonicalizes multi-prefixed repo option"
(let [parsed {:ok? true :command :server-status :options {:repo "logseq_db_logseq_db_demo"}}
result (commands/build-action parsed {})]
(is (true? (:ok? result)))
(is (= :server-status (get-in result [:action :type])))
(is (= "logseq_db_demo" (get-in result [:action :repo]))))))
(deftest test-build-action-doctor
(testing "doctor builds action"
@@ -1565,10 +1579,13 @@
(deftest test-execute-graph-list-strips-db-prefix
(async done
(let [orig-list-graphs cli-server/list-graphs]
(set! cli-server/list-graphs (fn [_] ["logseq_db_demo" "logseq_db_other"]))
(set! cli-server/list-graphs (fn [_] ["logseq_db_demo"
"logseq_db_logseq_db_other"
"my_logseq_db_notes"]))
(-> (p/let [result (commands/execute {:type :graph-list} {})]
(is (= :ok (:status result)))
(is (= ["demo" "other"] (get-in result [:data :graphs]))))
(is (= ["demo" "logseq_db_other" "my_logseq_db_notes"]
(get-in result [:data :graphs]))))
(p/catch (fn [e]
(is false (str "unexpected error: " e))))
(p/finally (fn []

View File

@@ -0,0 +1,20 @@
(ns logseq.cli.common.graph-test
(:require [cljs.test :refer [deftest is]]
[clojure.string :as string]
[frontend.test.node-helper :as node-helper]
[logseq.cli.common.graph :as cli-common-graph]
["fs" :as fs]
["path" :as node-path]))
(deftest get-db-based-graphs-canonicalizes-legacy-prefixed-directory-names
(let [graphs-dir (node-helper/create-tmp-dir "cli-common-graph")
_ (doseq [dir ["demo"
"logseq_db_demo"
"logseq_db_logseq_db_demo"
"logseq_local_file-graph"
"Unlinked graphs"]]
(fs/mkdirSync (node-path/join graphs-dir dir) #js {:recursive true}))]
(with-redefs [cli-common-graph/get-db-graphs-dir (fn [] graphs-dir)]
(let [graphs (cli-common-graph/get-db-based-graphs)]
(is (= #{"logseq_db_demo"} (set graphs)))
(is (not-any? #(string/starts-with? % "logseq_db_logseq_db_") graphs))))))

View File

@@ -175,7 +175,34 @@
{:output-format nil})]
(is (= (str "Server ready: demo-repo\n"
"Host: 127.0.0.1 Port: 1234")
result)))))
result))))
(testing "server status strips only one leading db prefix and keeps middle substrings"
(let [double-prefixed (format/format-result {:status :ok
:command :server-status
:data {:repo "logseq_db_logseq_db_demo"
:status :ready}}
{:output-format nil})
middle-substring (format/format-result {:status :ok
:command :server-status
:data {:repo "my_logseq_db_notes"
:status :ready}}
{:output-format nil})]
(is (= "Server ready: logseq_db_demo" double-prefixed))
(is (= "Server ready: my_logseq_db_notes" middle-substring)))))
(deftest test-json-output-normalizes-graph-fields-with-single-leading-strip-only
(let [result (format/format-result {:status :ok
:data {:repo "logseq_db_logseq_db_demo"
:graph "my_logseq_db_notes"
:graphs ["logseq_db_logseq_db_demo"
"my_logseq_db_notes"]}}
{:output-format :edn})
parsed (reader/read-string result)]
(is (= "logseq_db_demo" (get-in parsed [:data :repo])))
(is (= "my_logseq_db_notes" (get-in parsed [:data :graph])))
(is (= ["logseq_db_demo" "my_logseq_db_notes"]
(get-in parsed [:data :graphs])))))
(deftest test-human-output-server-list-includes-owner
(testing "server list shows owner column and value"