mirror of
https://github.com/logseq/logseq.git
synced 2026-05-15 16:32:21 +00:00
044-logseq-cli-upsert-block-page.md
This commit is contained in:
191
docs/agent-guide/044-logseq-cli-upsert-block-page.md
Normal file
191
docs/agent-guide/044-logseq-cli-upsert-block-page.md
Normal file
@@ -0,0 +1,191 @@
|
||||
# Logseq CLI Upsert Block and Upsert Page Implementation Plan
|
||||
|
||||
Goal: Consolidate block and page write commands by replacing `add block`, `add page`, and `update` with `upsert block` and `upsert page` while preserving current db-worker-node write behavior.
|
||||
|
||||
Architecture: Keep db-worker-node RPC and outliner operation contracts unchanged, and implement command consolidation in CLI parsing, action building, execution, and formatting layers.
|
||||
Architecture: Reuse existing helper logic from `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/add.cljs` and `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/update.cljs` first, then fold shared behavior into `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/upsert.cljs`.
|
||||
Architecture: Route all mutations through existing `:thread-api/apply-outliner-ops` and `:thread-api/pull` calls so `/Users/rcmerci/gh-repos/logseq/src/main/frontend/worker/db_worker_node.cljs` and `/Users/rcmerci/gh-repos/logseq/src/main/frontend/worker/db_core.cljs` require no new thread APIs.
|
||||
|
||||
Tech Stack: ClojureScript, babashka.cli, Promesa, Logseq CLI transport, db-worker-node, and outliner ops.
|
||||
|
||||
Related: Relates to `docs/agent-guide/027-logseq-cli-update-command.md` and builds on `docs/agent-guide/041-logseq-cli-add-block-json-identifiers.md`.
|
||||
Document naming follows @planning-documents with sequence `044`.
|
||||
|
||||
## Problem statement
|
||||
|
||||
The current CLI splits block mutations across `add block` and `update`, while page writes are exposed as `add page`.
|
||||
This creates an inconsistent user model and duplicates validation and formatting paths in `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/commands.cljs` and `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/format.cljs`.
|
||||
The current block update property options only support built-in properties, which prevents consistent upsert behavior for custom properties.
|
||||
The current `add page` flow applies tags after create, but page property behavior for existing pages is not fully upsert-like because `create-page` may no-op when the page exists.
|
||||
The db-worker-node layer already exposes stable generic APIs, so this feature should be implemented as a CLI surface refactor without protocol changes.
|
||||
|
||||
## Testing Plan
|
||||
|
||||
I will use @test-driven-development for all implementation batches.
|
||||
I will write all RED tests for parser, action builder, formatter, and integration flows before changing implementation behavior.
|
||||
I will add parser and builder tests in `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/commands_test.cljs` for `upsert block` and `upsert page` command forms and for hard-removal behavior of `add` and `update`.
|
||||
I will add formatter tests in `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/format_test.cljs` for human output of `:upsert-block` and `:upsert-page`.
|
||||
I will add integration tests in `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/integration_test.cljs` that verify block creation, block update, block move, page creation, and page update through only `upsert` commands.
|
||||
I will add one integration test that verifies `upsert page` updates properties on an existing page, which closes the current `add page` gap.
|
||||
I will verify RED failures come from missing behavior and not from broken test setup.
|
||||
I will run focused GREEN tests after minimal implementation, then refactor, then rerun focused tests and full lint-test.
|
||||
|
||||
NOTE: I will write *all* tests before I add any implementation behavior.
|
||||
|
||||
## Current implementation baseline
|
||||
|
||||
| Area | Current implementation | Change target |
|
||||
| --- | --- | --- |
|
||||
| Command entries | `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/add.cljs` defines `["add" "block"]` and `["add" "page"]`, and `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/update.cljs` defines `["update"]`. | `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/upsert.cljs` defines `["upsert" "block"]` and `["upsert" "page"]` together with existing upsert subcommands. |
|
||||
| Validation and dispatch | `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/commands.cljs` validates and dispatches `:add-block`, `:add-page`, and `:update-block` separately. | Replace with `:upsert-block` and `:upsert-page` parse and dispatch paths. |
|
||||
| Help and group summaries | `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/core.cljs` top-level summary and group handling still expose `add` and `update`. | Expose only `upsert` for these write cases. |
|
||||
| Formatter | `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/format.cljs` has `format-add-block`, `format-add-page`, and `format-update-block`. | Replace with upsert-focused formatter routes while preserving existing output contract style. |
|
||||
| Worker APIs | `/Users/rcmerci/gh-repos/logseq/src/main/frontend/worker/db_worker_node.cljs` and `/Users/rcmerci/gh-repos/logseq/src/main/frontend/worker/db_core.cljs` already provide `:thread-api/apply-outliner-ops`. | No new worker endpoint or transport shape. |
|
||||
| Page create behavior | `/Users/rcmerci/gh-repos/logseq/deps/outliner/src/logseq/outliner/page.cljs` `create!` can return existing page with no transaction. | `upsert page` applies properties and tags explicitly on existing page to ensure real upsert behavior. |
|
||||
|
||||
## Interface contract proposal
|
||||
|
||||
`upsert block` supports two modes with deterministic priority.
|
||||
If `--id` or `--uuid` is provided, `upsert block` always runs update mode.
|
||||
If neither `--id` nor `--uuid` is provided, `upsert block` runs create mode.
|
||||
For `upsert block` update mode, add, update, and remove property options must support all existing properties, not only built-in properties.
|
||||
`upsert page` requires `--page` and always resolves an existing or newly created page, then applies add, update, and remove semantics for tags and properties.
|
||||
For `upsert page`, all add, update, and remove tag or property operations require the target tag and property to already exist, otherwise the command returns an error.
|
||||
|
||||
| Command | Input signal | Behavior | Existing code path to reuse |
|
||||
| --- | --- | --- | --- |
|
||||
| `upsert block` create mode | `--id` and `--uuid` are absent and a content source is present. | Insert blocks under target with existing add semantics and support add, update, and remove tag or property options in post-insert ops. | `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/add.cljs` add helpers plus update option helpers. |
|
||||
| `upsert block` update mode | `--id` or `--uuid` is present. | Move and or add, update, and remove tags or properties with existing update semantics, and support both built-in and custom properties in property options. | `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/update.cljs` update helpers. |
|
||||
| `upsert page` | `--page` is present. | Create page if missing, then apply add, update, and remove tags and properties for both new and existing pages, with strict existing-tag and existing-property validation. | `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/add.cljs` plus explicit remove op wiring. |
|
||||
|
||||
## Architecture sketch
|
||||
|
||||
```text
|
||||
CLI args
|
||||
-> parse-args in commands.cljs
|
||||
-> build-action returns :upsert-block or :upsert-page
|
||||
-> execute routes to upsert command executor
|
||||
-> transport/invoke :thread-api/apply-outliner-ops and :thread-api/pull
|
||||
-> db-worker-node /v1/invoke passthrough
|
||||
-> db_core thread-api handlers and outliner-op/apply-ops!
|
||||
```
|
||||
|
||||
## Plan
|
||||
|
||||
1. Add RED parser tests in `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/commands_test.cljs` for `upsert block` create mode with `--content` and for update mode with `--id` plus `--update-tags`.
|
||||
2. Add RED parser tests in `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/commands_test.cljs` that confirm `--id` or `--uuid` forces update mode even when create inputs are also present.
|
||||
3. Add RED parser tests in `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/commands_test.cljs` for `upsert page --page <name>` with tags and properties.
|
||||
4. Add RED parser tests in `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/commands_test.cljs` to define expected unknown-command behavior for legacy `add block`, `add page`, and `update`.
|
||||
5. Add RED builder tests in `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/commands_test.cljs` for `:upsert-block` action shape in create mode.
|
||||
6. Add RED builder tests in `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/commands_test.cljs` for `:upsert-block` action shape in update mode, including custom property option inputs.
|
||||
7. Add RED builder tests in `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/commands_test.cljs` for `:upsert-page` action shape including resolved options.
|
||||
8. Add RED execute tests in `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/commands_test.cljs` to verify `:upsert-block` delegates to insert-style ops for create mode.
|
||||
9. Add RED execute tests in `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/commands_test.cljs` to verify `:upsert-block` delegates to move and property ops for update mode across built-in and custom properties.
|
||||
10. Add RED execute tests in `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/commands_test.cljs` to verify `:upsert-page` applies add, update, and remove property or tag ops on an already existing page.
|
||||
11. Add RED execute tests in `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/commands_test.cljs` to verify `:upsert-page` returns errors when any referenced tag or property does not exist.
|
||||
12. Add RED formatter tests in `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/format_test.cljs` for human output text of `:upsert-block` and `:upsert-page`.
|
||||
13. Add RED integration tests in `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/integration_test.cljs` for `upsert block` create mode id outputs.
|
||||
14. Add RED integration tests in `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/integration_test.cljs` for `upsert block` update mode move behavior and custom property updates.
|
||||
15. Add RED integration tests in `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/integration_test.cljs` for `upsert page` create and update-existing behaviors.
|
||||
16. Add RED integration tests in `/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/integration_test.cljs` for `upsert page` erroring when referenced tags or properties do not exist.
|
||||
17. Run focused RED commands and confirm failures are expectation failures rather than transport or fixture errors.
|
||||
18. Extend `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/upsert.cljs` spec to include block and page options while keeping existing tag and property specs.
|
||||
19. Implement `build-block-action` in `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/upsert.cljs` to classify create mode versus update mode with `--id` or `--uuid` priority, and normalize property options for all property identifiers.
|
||||
20. Implement `build-page-action` in `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/upsert.cljs` for `upsert page`.
|
||||
21. Extract or reuse add helper functions from `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/add.cljs` for reading blocks, parsing tags, parsing properties, and resolving ids.
|
||||
22. Extract or reuse update helper functions from `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/update.cljs` for source and target resolution and move option mapping.
|
||||
23. Implement `execute-upsert-block` in `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/upsert.cljs` that branches by mode and calls reused logic without behavior drift, including custom property update support.
|
||||
24. Implement `execute-upsert-page` in `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/upsert.cljs` so add, update, and remove property or tag ops are applied after resolving the page entity in both create and existing-page paths.
|
||||
25. Enforce strict `upsert page` validation and execution behavior where missing tags or properties fail fast instead of creating missing entities.
|
||||
26. Update `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/commands.cljs` table entries and finalize-command validation for `:upsert-block` and `:upsert-page`.
|
||||
27. Update `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/commands.cljs` build and execute case dispatch to remove `:add-block`, `:add-page`, and `:update-block` routing.
|
||||
28. Update `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/core.cljs` top-level summary and group-help triggers to reflect the new command family.
|
||||
29. Update `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/format.cljs` with `format-upsert-block` and `format-upsert-page` and command dispatch keys.
|
||||
30. Keep `/Users/rcmerci/gh-repos/logseq/src/main/frontend/worker/db_worker_node.cljs` and `/Users/rcmerci/gh-repos/logseq/src/main/frontend/worker/db_core.cljs` unchanged unless test evidence proves a missing worker behavior.
|
||||
31. Update CLI documentation in `/Users/rcmerci/gh-repos/logseq/docs/cli/logseq-cli.md` to replace add and update examples with upsert equivalents.
|
||||
32. Run focused GREEN tests and confirm parser, builder, formatter, and integration cases pass.
|
||||
33. Refactor duplicated helper wiring between add, update, and upsert modules while preserving test behavior.
|
||||
34. Rerun focused test set after refactor to confirm no regressions.
|
||||
35. Run `bb dev:lint-and-test` as final regression verification.
|
||||
|
||||
## Edge cases
|
||||
|
||||
`upsert block` with `--id` or `--uuid` plus create inputs must deterministically run update mode because source selectors have priority.
|
||||
`upsert block` update mode must keep current `--pos` validation where `sibling` is invalid for page targets and `--pos` requires a target.
|
||||
`upsert block` update mode property options must support all existing properties, including non built-in properties.
|
||||
`upsert block` create mode must preserve current default target fallback to today journal when no target selector is provided.
|
||||
`upsert block` create mode with `--blocks` or `--blocks-file` must keep the existing restriction that tags and properties cannot be combined if that restriction is still required by current insert behavior.
|
||||
`upsert block` and `upsert page` must support remove options for tags and properties in addition to add and update options.
|
||||
`upsert page` must return stable `data.result` id vectors for JSON, EDN, and human output just like current add command id outputs.
|
||||
`upsert page` on an existing page must apply property updates and removals explicitly so upsert semantics are true for both create and existing states.
|
||||
`upsert page` must error when any tag or property referenced by add, update, or remove options does not already exist.
|
||||
Legacy command behavior must be hard removal with standard `unknown-command` errors for `add block`, `add page`, and `update`.
|
||||
Help output must not regress command grouping or ANSI formatting alignment in `commands_test`.
|
||||
|
||||
## Verification commands and expected output
|
||||
|
||||
Run parser and builder tests during RED.
|
||||
|
||||
```bash
|
||||
bb dev:test -v logseq.cli.commands-test
|
||||
```
|
||||
|
||||
Expected RED behavior is failing assertions for missing `upsert block` and `upsert page` paths before implementation.
|
||||
|
||||
Run formatter tests during RED.
|
||||
|
||||
```bash
|
||||
bb dev:test -v logseq.cli.format-test
|
||||
```
|
||||
|
||||
Expected RED behavior is failing assertions for unknown command formatter branches for upsert block and upsert page.
|
||||
|
||||
Run focused integration tests after implementation.
|
||||
|
||||
```bash
|
||||
bb dev:test -v logseq.cli.integration-test/test-cli-upsert-block-create-json-output-returns-ids
|
||||
bb dev:test -v logseq.cli.integration-test/test-cli-upsert-block-update-move
|
||||
bb dev:test -v logseq.cli.integration-test/test-cli-upsert-page-create-and-update-existing
|
||||
```
|
||||
|
||||
Expected GREEN behavior is zero failures and zero errors for these tests.
|
||||
|
||||
Run full verification.
|
||||
|
||||
```bash
|
||||
bb dev:lint-and-test
|
||||
```
|
||||
|
||||
Expected GREEN behavior is full suite pass with exit code `0`.
|
||||
|
||||
## Testing Details
|
||||
|
||||
Behavior tests will assert command-level outcomes through real CLI execution and Datascript queries instead of only checking mock invocation counts.
|
||||
Unit-level command tests will assert parse, validation, and action-shape behavior at module boundaries.
|
||||
Integration tests will verify persisted graph state changes for both create and update paths of upsert block and upsert page.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- Keep db-worker-node API contracts unchanged and implement all command-surface changes in CLI modules only.
|
||||
- Add `upsert block` and `upsert page` entries to `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/upsert.cljs`.
|
||||
- Reuse add and update helper functions to minimize behavior drift and reduce migration risk.
|
||||
- Ensure `upsert block` and `upsert page` support add, update, and remove options for tags and properties.
|
||||
- Ensure `upsert block` property update options accept all existing properties, including custom properties, not only built-in properties.
|
||||
- Ensure `upsert page` applies tags and properties after resolving page entity so existing pages are updated too.
|
||||
- Ensure `upsert page` fails fast when referenced tags or properties do not exist, and never auto-creates them through upsert-page mutation options.
|
||||
- Remove old command routes in `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/commands.cljs` for `add block`, `add page`, and `update`, returning standard `unknown-command`.
|
||||
- Update formatter dispatch in `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/format.cljs` for new command ids.
|
||||
- Update command summaries in `/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/core.cljs` to keep help output accurate.
|
||||
- Update `/Users/rcmerci/gh-repos/logseq/docs/cli/logseq-cli.md` examples and command reference text.
|
||||
- Keep implementation batches aligned to @test-driven-development RED, GREEN, and refactor phases.
|
||||
|
||||
## Question
|
||||
|
||||
No open questions.
|
||||
Decided: remove `add block`, `add page`, and `update` immediately.
|
||||
Decided: in `upsert block`, `--id` or `--uuid` means update mode and absence of both means create mode.
|
||||
Decided: support add, update, and remove semantics for tags and properties.
|
||||
Decided: in `upsert block` update mode, property mutation options support all existing properties, including custom properties.
|
||||
Decided: for `upsert page`, add, update, and remove tag or property options require existing tags and properties, otherwise return error.
|
||||
|
||||
---
|
||||
@@ -94,10 +94,11 @@ Inspect and edit commands:
|
||||
- `list page [--expand] [--limit <n>] [--offset <n>] [--sort <field>] [--order asc|desc]` - list pages
|
||||
- `list tag [--expand] [--limit <n>] [--offset <n>] [--sort <field>] [--order asc|desc]` - list tags
|
||||
- `list property [--expand] [--limit <n>] [--offset <n>] [--sort <field>] [--order asc|desc]` - list properties
|
||||
- `add block --content <text> [--target-page-name <name>|--target-id <id>|--target-uuid <uuid>] [--pos first-child|last-child|sibling]` - add blocks; defaults to today’s journal page if no target is given
|
||||
- `add block --blocks <edn> [--target-page-name <name>|--target-id <id>|--target-uuid <uuid>] [--pos first-child|last-child|sibling]` - insert blocks via EDN vector
|
||||
- `add block --blocks-file <path> [--target-page-name <name>|--target-id <id>|--target-uuid <uuid>] [--pos first-child|last-child|sibling]` - insert blocks from an EDN file
|
||||
- `add page --page <name>` - create a page
|
||||
- `upsert block --content <text> [--target-page <name>|--target-id <id>|--target-uuid <uuid>] [--pos first-child|last-child|sibling]` - create blocks; defaults to today’s journal page if no target is given
|
||||
- `upsert block --blocks <edn> [--target-page <name>|--target-id <id>|--target-uuid <uuid>] [--pos first-child|last-child|sibling]` - insert blocks via EDN vector
|
||||
- `upsert block --blocks-file <path> [--target-page <name>|--target-id <id>|--target-uuid <uuid>] [--pos first-child|last-child|sibling]` - insert blocks from an EDN file
|
||||
- `upsert block --id <id>|--uuid <uuid> [--target-id <id>|--target-uuid <uuid>|--target-page <name>] [--pos first-child|last-child|sibling] [--update-tags <edn-vector>] [--update-properties <edn-map>] [--remove-tags <edn-vector>] [--remove-properties <edn-vector>]` - update and/or move a block
|
||||
- `upsert page --page <name> [--tags <edn-vector>] [--properties <edn-map>] [--update-tags <edn-vector>] [--update-properties <edn-map>] [--remove-tags <edn-vector>] [--remove-properties <edn-vector>]` - create or update a page
|
||||
- `move --id <id>|--uuid <uuid> --target-id <id>|--target-uuid <uuid>|--target-page <name> [--pos first-child|last-child|sibling]` - move a block and its children (defaults to first-child)
|
||||
- `remove --id <id>|--uuid <uuid>|--page <name>` - remove blocks (by db/id or UUID) or pages
|
||||
- `search <query> [--type page|block|tag|property|all] [--tag <name>] [--case-sensitive] [--sort updated-at|created-at] [--order asc|desc]` - search across pages, blocks, tags, and properties (query is positional)
|
||||
@@ -115,8 +116,10 @@ Subcommands:
|
||||
list page [options] List pages
|
||||
list tag [options] List tags
|
||||
list property [options] List properties
|
||||
add block [options] Add blocks
|
||||
add page [options] Create page
|
||||
upsert block [options] Upsert block
|
||||
upsert page [options] Upsert page
|
||||
upsert tag [options] Upsert tag
|
||||
upsert property [options] Upsert property
|
||||
move [options] Move block
|
||||
remove [options] Remove block or page
|
||||
search <query> [options] Search graph
|
||||
@@ -138,15 +141,15 @@ Output formats:
|
||||
- Global `--output <human|json|edn>` applies to all commands
|
||||
- For `graph export`, `--output` refers to the destination file path. Output formatting is controlled via `:output-format` in config or `LOGSEQ_CLI_OUTPUT`.
|
||||
- Human output is plain text. List/search commands render tables with a final `Count: N` line. For list and search subcommands, the ID column uses `:db/id` (not UUID). If `:db/ident` exists, an `IDENT` column is included. Search table columns are `ID` and `TITLE`. Block titles can include multiple lines; multi-line rows align additional lines under the `TITLE` column. Times such as list `UPDATED-AT`/`CREATED-AT` and `graph info` `Created at` are shown in human-friendly relative form. Errors include error codes and may include a `Hint:` line. Use `--output json|edn` for structured output.
|
||||
- `add page` and `add block` return created entity ids in `data.result` for JSON/EDN output, and include ids in human output.
|
||||
- `upsert page` and `upsert block` return entity ids in `data.result` for JSON/EDN output, and include ids in human output.
|
||||
- Human example:
|
||||
```text
|
||||
Added page:
|
||||
Upserted page:
|
||||
[123]
|
||||
```
|
||||
- Human example:
|
||||
```text
|
||||
Added blocks:
|
||||
Upserted blocks:
|
||||
[201 202]
|
||||
```
|
||||
- JSON example: `{"status":"ok","data":{"result":[123]}}`
|
||||
@@ -180,7 +183,7 @@ Examples:
|
||||
node ./dist/logseq.js graph create --repo demo
|
||||
node ./dist/logseq.js graph export --type edn --output /tmp/demo.edn --repo demo
|
||||
node ./dist/logseq.js graph import --type edn --input /tmp/demo.edn --repo demo-import
|
||||
node ./dist/logseq.js add block --target-page-name TestPage --content "hello world"
|
||||
node ./dist/logseq.js upsert block --target-page TestPage --content "hello world"
|
||||
node ./dist/logseq.js move --uuid <uuid> --target-page TargetPage
|
||||
node ./dist/logseq.js search "hello"
|
||||
node ./dist/logseq.js show --page TestPage --output json
|
||||
|
||||
@@ -16,28 +16,6 @@
|
||||
[logseq.db.frontend.property.type :as db-property-type]
|
||||
[promesa.core :as p]))
|
||||
|
||||
(def ^:private content-add-spec
|
||||
{:content {:desc "Block content for add"}
|
||||
:blocks {:desc "EDN vector of blocks for add"}
|
||||
:blocks-file {:desc "EDN file of blocks for add"}
|
||||
:tags {:desc "EDN vector of tags. Identifiers can be id, :db/ident, or :block/title."}
|
||||
:properties {:desc "EDN map of built-in properties. Identifiers can be id, :db/ident, or :block/title."}
|
||||
:target-id {:desc "Target block db/id"
|
||||
:coerce :long}
|
||||
:target-uuid {:desc "Target block UUID"}
|
||||
:target-page-name {:desc "Target page name"}
|
||||
:pos {:desc "Position (first-child, last-child, sibling). Default: last-child"}
|
||||
:status {:desc "Task status (todo, doing, done, etc.)"}})
|
||||
|
||||
(def ^:private add-page-spec
|
||||
{:page {:desc "Page name"}
|
||||
:tags {:desc "EDN vector of tags. Identifiers can be id, :db/ident, or :block/title."}
|
||||
:properties {:desc "EDN map of built-in properties. Identifiers can be id, :db/ident, or :block/title."}})
|
||||
|
||||
(def entries
|
||||
[(core/command-entry ["add" "block"] :add-block "Add blocks" content-add-spec)
|
||||
(core/command-entry ["add" "page"] :add-page "Create page" add-page-spec)])
|
||||
|
||||
(defn- today-page-title
|
||||
[config repo]
|
||||
(p/let [journal (transport/invoke config :thread-api/pull false
|
||||
@@ -194,24 +172,6 @@
|
||||
ordered-uuids))]
|
||||
(created-ids-in-order ordered-uuids entities :block)))))
|
||||
|
||||
(defn- resolve-created-page-ids
|
||||
[config repo page create-result]
|
||||
(let [page-uuid (some-> create-result second normalized-uuid)]
|
||||
(if page-uuid
|
||||
(p/let [page-entity (transport/invoke config :thread-api/pull false
|
||||
[repo [:db/id :block/uuid] [:block/uuid page-uuid]])]
|
||||
(created-ids-in-order [page-uuid] [page-entity] :page))
|
||||
(p/let [page-entity (transport/invoke config :thread-api/pull false
|
||||
[repo [:db/id :block/uuid]
|
||||
[:block/name (common-util/page-name-sanity-lc page)]])
|
||||
page-id (:db/id page-entity)]
|
||||
(if (some? page-id)
|
||||
[page-id]
|
||||
(throw (ex-info "unable to resolve created page id"
|
||||
{:code :add-id-resolution-failed
|
||||
:entity-kind :page
|
||||
:page page})))))))
|
||||
|
||||
(defn- extract-page-refs
|
||||
[title]
|
||||
(when (string? title)
|
||||
@@ -531,85 +491,95 @@
|
||||
(true? (get-in property [:schema :public?])))
|
||||
|
||||
(defn parse-properties-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 map")
|
||||
([value]
|
||||
(parse-properties-option value {:allow-non-built-in? false}))
|
||||
([value {:keys [allow-non-built-in?]
|
||||
:or {allow-non-built-in? false}}]
|
||||
(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 map")
|
||||
|
||||
(not (map? parsed))
|
||||
(invalid-options-result "properties must be a map")
|
||||
(not (map? parsed))
|
||||
(invalid-options-result "properties must be a map")
|
||||
|
||||
(empty? parsed)
|
||||
(invalid-options-result "properties must be a non-empty map")
|
||||
(empty? parsed)
|
||||
(invalid-options-result "properties must be a non-empty map")
|
||||
|
||||
:else
|
||||
(loop [prop-entries (seq parsed)
|
||||
acc {}]
|
||||
(if (empty? prop-entries)
|
||||
{:ok? true :value acc}
|
||||
(let [[k v] (first prop-entries)
|
||||
key-result (normalize-property-key-input k)]
|
||||
(if-not key-result
|
||||
(invalid-options-result (str "invalid property key: " k))
|
||||
(let [{:keys [type value]} key-result
|
||||
key-ident value]
|
||||
(if (= type :id)
|
||||
(recur (rest prop-entries) (assoc acc key-ident v))
|
||||
(let [property (get db-property/built-in-properties key-ident)]
|
||||
(cond
|
||||
(nil? property)
|
||||
(invalid-options-result (str "unknown built-in property: " key-ident))
|
||||
:else
|
||||
(loop [prop-entries (seq parsed)
|
||||
acc {}]
|
||||
(if (empty? prop-entries)
|
||||
{:ok? true :value acc}
|
||||
(let [[k v] (first prop-entries)
|
||||
key-result (normalize-property-key-input k)]
|
||||
(if-not key-result
|
||||
(invalid-options-result (str "invalid property key: " k))
|
||||
(let [{:keys [type value]} key-result
|
||||
key-ident value]
|
||||
(if (= type :id)
|
||||
(recur (rest prop-entries) (assoc acc key-ident v))
|
||||
(let [property (get db-property/built-in-properties key-ident)]
|
||||
(cond
|
||||
(nil? property)
|
||||
(if allow-non-built-in?
|
||||
(recur (rest prop-entries) (assoc acc key-ident v))
|
||||
(invalid-options-result (str "unknown built-in property: " key-ident)))
|
||||
|
||||
(not (property-public? property))
|
||||
(invalid-options-result (str "property is not public: " key-ident))
|
||||
(not (property-public? property))
|
||||
(invalid-options-result (str "property is not public: " key-ident))
|
||||
|
||||
:else
|
||||
(let [{:keys [ok? value message]} (normalize-property-values property v)
|
||||
normalized-value value]
|
||||
(if-not ok?
|
||||
(invalid-options-result (str "invalid value for " key-ident ": " message))
|
||||
(recur (rest prop-entries) (assoc acc key-ident normalized-value))))))))))))))))
|
||||
:else
|
||||
(let [{:keys [ok? value message]} (normalize-property-values property v)
|
||||
normalized-value value]
|
||||
(if-not ok?
|
||||
(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")
|
||||
([value]
|
||||
(parse-properties-vector-option value {:allow-non-built-in? false}))
|
||||
([value {:keys [allow-non-built-in?]
|
||||
:or {allow-non-built-in? false}}]
|
||||
(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")
|
||||
(not (vector? parsed))
|
||||
(invalid-options-result "properties must be a vector")
|
||||
|
||||
(empty? parsed)
|
||||
(invalid-options-result "properties must be a non-empty 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))
|
||||
: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)
|
||||
(if allow-non-built-in?
|
||||
(recur (rest prop-entries) (conj acc value))
|
||||
(invalid-options-result (str "unknown built-in property: " value)))
|
||||
|
||||
(not (property-public? property))
|
||||
(invalid-options-result (str "property is not public: " value))
|
||||
(not (property-public? property))
|
||||
(invalid-options-result (str "property is not public: " value))
|
||||
|
||||
:else
|
||||
(recur (rest prop-entries) (conj acc value))))))))))))))
|
||||
:else
|
||||
(recur (rest prop-entries) (conj acc value)))))))))))))))
|
||||
|
||||
(defn invalid-options?
|
||||
[opts]
|
||||
@@ -755,129 +725,219 @@
|
||||
:date (resolve-date-page-id config repo value)
|
||||
(p/resolved value))))
|
||||
|
||||
(def ^:private property-entity-selector
|
||||
[:db/id :db/ident :block/name :block/title
|
||||
:logseq.property/type :db/cardinality :logseq.property/public?])
|
||||
|
||||
(defn- property-entity?
|
||||
[entity]
|
||||
(some? (:logseq.property/type entity)))
|
||||
|
||||
(defn- property-entity-public?
|
||||
[entity]
|
||||
(not (false? (:logseq.property/public? entity))))
|
||||
|
||||
(defn- property-entity->property
|
||||
[entity]
|
||||
{:schema {:type (or (:logseq.property/type entity) :default)
|
||||
:cardinality (if (= :db.cardinality/many (:db/cardinality entity))
|
||||
:many
|
||||
:one)
|
||||
:public? (property-entity-public? entity)}})
|
||||
|
||||
(defn- lookup-property-entity
|
||||
[config repo property-key]
|
||||
(let [lookup-by-title (fn [title]
|
||||
(pull-entity config repo property-entity-selector
|
||||
[:block/name (common-util/page-name-sanity-lc title)]))]
|
||||
(cond
|
||||
(number? property-key)
|
||||
(pull-entity config repo property-entity-selector property-key)
|
||||
|
||||
(keyword? property-key)
|
||||
(p/let [entity (pull-entity config repo property-entity-selector [:db/ident property-key])]
|
||||
(if (or (:db/id entity) (qualified-keyword? property-key))
|
||||
entity
|
||||
(lookup-by-title (name property-key))))
|
||||
|
||||
(string? property-key)
|
||||
(let [text (string/trim property-key)
|
||||
ident (normalize-property-key text)]
|
||||
(if-not (seq text)
|
||||
(p/resolved nil)
|
||||
(p/let [entity (lookup-by-title text)]
|
||||
(if (:db/id entity)
|
||||
entity
|
||||
(if ident
|
||||
(pull-entity config repo property-entity-selector [:db/ident ident])
|
||||
(p/resolved nil))))))
|
||||
|
||||
:else
|
||||
(p/resolved nil))))
|
||||
|
||||
(defn- resolve-property-entry-allow-non-built-in
|
||||
[config repo property-key]
|
||||
(p/let [entity (lookup-property-entity config repo property-key)
|
||||
ident (:db/ident entity)]
|
||||
(cond
|
||||
(nil? (:db/id entity))
|
||||
(throw (ex-info "property not found"
|
||||
{:code :property-not-found
|
||||
:property property-key}))
|
||||
|
||||
(not (property-entity? entity))
|
||||
(throw (ex-info "target is not a property"
|
||||
{:code :invalid-property-target
|
||||
:property property-key}))
|
||||
|
||||
(nil? ident)
|
||||
(throw (ex-info "property not found"
|
||||
{:code :property-not-found
|
||||
:property property-key}))
|
||||
|
||||
(not (property-entity-public? entity))
|
||||
(throw (ex-info "property is not public"
|
||||
{:code :property-not-public
|
||||
:property ident}))
|
||||
|
||||
:else
|
||||
{:ident ident
|
||||
:property (property-entity->property entity)})))
|
||||
|
||||
(defn resolve-properties
|
||||
[config repo properties]
|
||||
(if-not (seq properties)
|
||||
(p/resolved nil)
|
||||
(p/let [resolved-entries (p/all
|
||||
(map (fn [[k v]]
|
||||
(p/let [{:keys [ident property]}
|
||||
(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 {:ident k :property property}))
|
||||
([config repo properties]
|
||||
(resolve-properties config repo properties {:allow-non-built-in? false}))
|
||||
([config repo properties {:keys [allow-non-built-in?]
|
||||
:or {allow-non-built-in? false}}]
|
||||
(if-not (seq properties)
|
||||
(p/resolved nil)
|
||||
(p/let [resolved-entries (p/all
|
||||
(map (fn [[k v]]
|
||||
(p/let [{:keys [ident property]}
|
||||
(if allow-non-built-in?
|
||||
(resolve-property-entry-allow-non-built-in config repo 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 {:ident k :property property}))
|
||||
|
||||
(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}))
|
||||
(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}))
|
||||
(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}))
|
||||
(not (property-public? property))
|
||||
(throw (ex-info "property is not public"
|
||||
{:code :property-not-public :property ident}))
|
||||
|
||||
:else
|
||||
{:ident ident :property property}))
|
||||
:else
|
||||
{:ident ident :property property}))
|
||||
|
||||
(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 ident :property property}))
|
||||
(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 ident :property property}))
|
||||
|
||||
:else
|
||||
(p/rejected (ex-info "invalid property key"
|
||||
{:code :invalid-property :property k})))
|
||||
{:keys [ok? value message]} (normalize-property-values property v)]
|
||||
(when-not ok?
|
||||
(throw (ex-info "invalid property value"
|
||||
{:code :invalid-property-value
|
||||
:property ident
|
||||
:message message})))
|
||||
(let [many? (= :many (get-in property [:schema :cardinality]))
|
||||
values (if many?
|
||||
(if (and (coll? value) (not (string? value))) value [value])
|
||||
[value])]
|
||||
(p/let [resolved (p/all (map #(resolve-property-value config repo property %) values))
|
||||
final-value (if many? (vec resolved) (first resolved))]
|
||||
[ident final-value]))))
|
||||
properties))]
|
||||
(into {} resolved-entries))))
|
||||
:else
|
||||
(p/rejected (ex-info "invalid property key"
|
||||
{:code :invalid-property :property k}))))
|
||||
{:keys [ok? value message]} (normalize-property-values property v)]
|
||||
(when-not ok?
|
||||
(throw (ex-info "invalid property value"
|
||||
{:code :invalid-property-value
|
||||
:property ident
|
||||
:message message})))
|
||||
(let [many? (= :many (get-in property [:schema :cardinality]))
|
||||
values (if many?
|
||||
(if (and (coll? value) (not (string? value))) value [value])
|
||||
[value])]
|
||||
(p/let [resolved (p/all (map #(resolve-property-value config repo property %) values))
|
||||
final-value (if many? (vec resolved) (first resolved))]
|
||||
[ident final-value]))))
|
||||
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))
|
||||
([config repo properties]
|
||||
(resolve-property-identifiers config repo properties {:allow-non-built-in? false}))
|
||||
([config repo properties {:keys [allow-non-built-in?]
|
||||
:or {allow-non-built-in? false}}]
|
||||
(if-not (seq properties)
|
||||
(p/resolved nil)
|
||||
(p/let [resolved-entries (p/all
|
||||
(map (fn [k]
|
||||
(if allow-non-built-in?
|
||||
(p/let [{:keys [ident]} (resolve-property-entry-allow-non-built-in config repo k)]
|
||||
ident)
|
||||
(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}))
|
||||
(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}))
|
||||
(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}))
|
||||
(not (property-public? property))
|
||||
(throw (ex-info "property is not public"
|
||||
{:code :property-not-public :property ident}))
|
||||
|
||||
:else
|
||||
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))
|
||||
(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))))
|
||||
: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]}]
|
||||
@@ -978,35 +1038,6 @@
|
||||
:properties properties
|
||||
:blocks blocks}}))))))))
|
||||
|
||||
(defn build-add-page-action
|
||||
[options repo]
|
||||
(if-not (seq repo)
|
||||
{:ok? false
|
||||
:error {:code :missing-repo
|
||||
:message "repo is required for add"}}
|
||||
(let [page (some-> (:page options) string/trim)]
|
||||
(if (seq page)
|
||||
(let [tags-result (parse-tags-option (:tags options))
|
||||
properties-result (parse-properties-option (:properties options))]
|
||||
(cond
|
||||
(not (:ok? tags-result))
|
||||
tags-result
|
||||
|
||||
(not (:ok? properties-result))
|
||||
properties-result
|
||||
|
||||
:else
|
||||
{:ok? true
|
||||
:action {:type :add-page
|
||||
:repo repo
|
||||
:graph (core/repo->graph repo)
|
||||
:page page
|
||||
:tags (:value tags-result)
|
||||
:properties (:value properties-result)}}))
|
||||
{:ok? false
|
||||
:error {:code :missing-page-name
|
||||
:message "page name is required"}}))))
|
||||
|
||||
(defn execute-add-block
|
||||
[action config]
|
||||
(-> (p/let [cfg (cli-server/ensure-server! config (:repo action))
|
||||
@@ -1065,31 +1096,3 @@
|
||||
created-ids (resolve-created-block-ids cfg (:repo action) blocks-for-insert insert-result)]
|
||||
{:status :ok
|
||||
:data {:result created-ids}})))
|
||||
|
||||
(defn execute-add-page
|
||||
[action config]
|
||||
(-> (p/let [cfg (cli-server/ensure-server! config (:repo action))
|
||||
tags (resolve-tags cfg (:repo action) (:tags action))
|
||||
tag-ids (when (seq tags)
|
||||
(->> tags (map :db/id) (remove nil?) vec))
|
||||
properties (resolve-properties cfg (:repo action) (:properties action))
|
||||
options (cond-> {}
|
||||
(seq properties) (assoc :properties properties))
|
||||
ops [[:create-page [(:page action) options]]]
|
||||
create-result (transport/invoke cfg :thread-api/apply-outliner-ops false [(:repo action) ops {}])
|
||||
_ (when (seq tag-ids)
|
||||
(p/let [page-name (common-util/page-name-sanity-lc (:page action))
|
||||
page (pull-entity cfg (:repo action) [:db/id :block/uuid] [:block/name page-name])
|
||||
page-uuid (:block/uuid page)]
|
||||
(when-not page-uuid
|
||||
(throw (ex-info "page not found" {:code :page-not-found :page (:page action)})))
|
||||
(p/all
|
||||
(map (fn [tag-id]
|
||||
(transport/invoke cfg :thread-api/apply-outliner-ops false
|
||||
[(:repo action)
|
||||
[[:batch-set-property [[page-uuid] :block/tags tag-id {}]]]
|
||||
{}]))
|
||||
tag-ids))))
|
||||
created-ids (resolve-created-page-ids cfg (:repo action) (:page action) create-result)]
|
||||
{:status :ok
|
||||
:data {:result created-ids}})))
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
(defn top-level-summary
|
||||
[table]
|
||||
(let [groups [{:title "Graph Inspect and Edit"
|
||||
:commands #{"list" "add" "upsert" "remove" "update" "query" "show"}}
|
||||
:commands #{"list" "upsert" "remove" "query" "show"}}
|
||||
{:title "Graph Management"
|
||||
:commands #{"graph" "server" "doctor"}}]
|
||||
render-group (fn [{:keys [title commands]}]
|
||||
|
||||
@@ -8,23 +8,6 @@
|
||||
[logseq.common.util :as common-util]
|
||||
[promesa.core :as p]))
|
||||
|
||||
(def ^:private update-spec
|
||||
{:id {:desc "Source block db/id"
|
||||
:coerce :long}
|
||||
:uuid {:desc "Source block UUID"}
|
||||
:target-id {:desc "Target block db/id"
|
||||
: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"}
|
||||
: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 ["update"] :update-block "Update block" update-spec)])
|
||||
|
||||
(def ^:private update-positions
|
||||
#{"first-child" "last-child" "sibling"})
|
||||
|
||||
@@ -152,9 +135,13 @@
|
||||
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))
|
||||
update-properties-result (add-command/parse-properties-option
|
||||
(:update-properties options)
|
||||
{:allow-non-built-in? true})
|
||||
remove-tags-result (add-command/parse-tags-vector-option (:remove-tags options))
|
||||
remove-properties-result (add-command/parse-properties-vector-option (:remove-properties options))
|
||||
remove-properties-result (add-command/parse-properties-vector-option
|
||||
(:remove-properties options)
|
||||
{:allow-non-built-in? true})
|
||||
update-tags (:value update-tags-result)
|
||||
update-properties (:value update-properties-result)
|
||||
remove-tags (:value remove-tags-result)
|
||||
@@ -222,9 +209,12 @@
|
||||
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))
|
||||
update-properties (add-command/resolve-properties
|
||||
cfg (:repo action) (:update-properties action)
|
||||
{:allow-non-built-in? true})
|
||||
remove-properties (add-command/resolve-property-identifiers
|
||||
cfg (:repo action) (:remove-properties action)
|
||||
{:allow-non-built-in? true})
|
||||
block-id (:db/id source)
|
||||
block-ids [block-id]
|
||||
update-tag-ids (when (seq update-tags)
|
||||
|
||||
@@ -1,12 +1,43 @@
|
||||
(ns logseq.cli.command.upsert
|
||||
"Upsert-related CLI commands."
|
||||
(:require [clojure.string :as string]
|
||||
[logseq.cli.command.add :as add-command]
|
||||
[logseq.cli.command.core :as core]
|
||||
[logseq.cli.command.update :as update-command]
|
||||
[logseq.cli.server :as cli-server]
|
||||
[logseq.cli.transport :as transport]
|
||||
[logseq.common.util :as common-util]
|
||||
[promesa.core :as p]))
|
||||
|
||||
(def ^:private upsert-block-spec
|
||||
{:id {:desc "Source block db/id (forces update mode)"
|
||||
:coerce :long}
|
||||
:uuid {:desc "Source block UUID (forces update mode)"}
|
||||
:target-id {:desc "Target block db/id"
|
||||
:coerce :long}
|
||||
:target-uuid {:desc "Target block UUID"}
|
||||
:target-page {:desc "Target page name"}
|
||||
:pos {:desc "Position (first-child, last-child, sibling). Default: create=last-child, update=first-child"}
|
||||
:content {:desc "Block content for create mode"}
|
||||
:blocks {:desc "EDN vector of blocks for create mode"}
|
||||
:blocks-file {:desc "EDN file of blocks for create mode"}
|
||||
:status {:desc "Task status (todo, doing, done, etc.)"}
|
||||
:tags {:desc "Tags to add in create mode (EDN vector). Identifiers can be id, :db/ident, or :block/title."}
|
||||
:properties {:desc "Properties to add in create mode (EDN map). Identifiers can be id, :db/ident, or :block/title."}
|
||||
: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 ^:private upsert-page-spec
|
||||
{:page {:desc "Page name"}
|
||||
:tags {:desc "Tags to add (EDN vector). Identifiers can be id, :db/ident, or :block/title."}
|
||||
:properties {:desc "Properties to add (EDN map). Identifiers can be id, :db/ident, or :block/title."}
|
||||
: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 ^:private upsert-tag-spec
|
||||
{:name {:desc "Tag name"}})
|
||||
|
||||
@@ -20,7 +51,9 @@
|
||||
:coerce :boolean}})
|
||||
|
||||
(def entries
|
||||
[(core/command-entry ["upsert" "tag"] :upsert-tag "Upsert tag" upsert-tag-spec)
|
||||
[(core/command-entry ["upsert" "block"] :upsert-block "Upsert block" upsert-block-spec)
|
||||
(core/command-entry ["upsert" "page"] :upsert-page "Upsert page" upsert-page-spec)
|
||||
(core/command-entry ["upsert" "tag"] :upsert-tag "Upsert tag" upsert-tag-spec)
|
||||
(core/command-entry ["upsert" "property"] :upsert-property "Upsert property" upsert-property-spec)])
|
||||
|
||||
(def ^:private property-types
|
||||
@@ -56,6 +89,16 @@
|
||||
(defn invalid-options?
|
||||
[command opts]
|
||||
(case command
|
||||
:upsert-block
|
||||
(let [opts (cond-> opts
|
||||
(seq (:target-page opts))
|
||||
(assoc :target-page-name (:target-page opts)))
|
||||
update-mode? (or (some? (:id opts))
|
||||
(seq (some-> (:uuid opts) string/trim)))]
|
||||
(if update-mode?
|
||||
(update-command/invalid-options? opts)
|
||||
(add-command/invalid-options? opts)))
|
||||
|
||||
:upsert-property
|
||||
(let [type' (normalize-property-type (:type opts))
|
||||
cardinality' (normalize-property-cardinality (:cardinality opts))]
|
||||
@@ -71,6 +114,115 @@
|
||||
|
||||
nil))
|
||||
|
||||
(defn update-mode?
|
||||
[opts]
|
||||
(or (some? (:id opts))
|
||||
(seq (some-> (:uuid opts) string/trim))))
|
||||
|
||||
(defn build-block-action
|
||||
[options args repo]
|
||||
(let [update-mode* (update-mode? options)]
|
||||
(if update-mode*
|
||||
(let [options (cond-> options
|
||||
(seq (:target-page options))
|
||||
(assoc :target-page (:target-page options)))]
|
||||
(-> (update-command/build-action options repo)
|
||||
(update :action
|
||||
(fn [action]
|
||||
(when action
|
||||
(assoc action :type :upsert-block :mode :update))))))
|
||||
(let [options (cond-> options
|
||||
(seq (:target-page options))
|
||||
(assoc :target-page-name (:target-page options))
|
||||
true
|
||||
(dissoc :target-page))
|
||||
create-result (add-command/build-add-block-action options args repo)
|
||||
update-tags-result (add-command/parse-tags-option (:update-tags options))
|
||||
update-properties-result (add-command/parse-properties-option
|
||||
(:update-properties options)
|
||||
{:allow-non-built-in? true})
|
||||
remove-tags-result (add-command/parse-tags-vector-option (:remove-tags options))
|
||||
remove-properties-result (add-command/parse-properties-vector-option
|
||||
(:remove-properties options)
|
||||
{:allow-non-built-in? true})]
|
||||
(cond
|
||||
(not (:ok? create-result))
|
||||
create-result
|
||||
|
||||
(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
|
||||
(-> create-result
|
||||
(update :action
|
||||
(fn [action]
|
||||
(-> action
|
||||
(assoc :type :upsert-block
|
||||
:mode :create
|
||||
:update-tags (:value update-tags-result)
|
||||
:update-properties (:value update-properties-result)
|
||||
:remove-tags (:value remove-tags-result)
|
||||
:remove-properties (:value remove-properties-result)))))))))))
|
||||
|
||||
(defn build-page-action
|
||||
[options repo]
|
||||
(if-not (seq repo)
|
||||
{:ok? false
|
||||
:error {:code :missing-repo
|
||||
:message "repo is required for upsert"}}
|
||||
(let [page (some-> (:page options) string/trim)
|
||||
tags-result (add-command/parse-tags-option (:tags options))
|
||||
properties-result (add-command/parse-properties-option (:properties options))
|
||||
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))]
|
||||
(cond
|
||||
(not (seq page))
|
||||
{:ok? false
|
||||
:error {:code :missing-page-name
|
||||
:message "page name is required"}}
|
||||
|
||||
(not (:ok? tags-result))
|
||||
tags-result
|
||||
|
||||
(not (:ok? properties-result))
|
||||
properties-result
|
||||
|
||||
(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 :upsert-page
|
||||
:repo repo
|
||||
:graph (core/repo->graph repo)
|
||||
:page page
|
||||
:tags (:value tags-result)
|
||||
:properties (:value properties-result)
|
||||
:update-tags (:value update-tags-result)
|
||||
:update-properties (:value update-properties-result)
|
||||
:remove-tags (:value remove-tags-result)
|
||||
:remove-properties (:value remove-properties-result)}}))))
|
||||
|
||||
(defn build-tag-action
|
||||
[options repo]
|
||||
(if-not (seq repo)
|
||||
@@ -143,6 +295,135 @@
|
||||
(transport/invoke config :thread-api/pull false
|
||||
[repo selector [:block/name (common-util/page-name-sanity-lc page-name)]]))
|
||||
|
||||
(defn- ensure-property-identifiers-exist!
|
||||
[config repo property-idents]
|
||||
(if (seq property-idents)
|
||||
(p/all
|
||||
(map (fn [property-ident]
|
||||
(p/let [entity (transport/invoke config :thread-api/pull false
|
||||
[repo [:db/id] [:db/ident property-ident]])]
|
||||
(when-not (:db/id entity)
|
||||
(throw (ex-info "property not found"
|
||||
{:code :property-not-found
|
||||
:property property-ident})))))
|
||||
(distinct property-idents)))
|
||||
(p/resolved nil)))
|
||||
|
||||
(defn- ensure-page-entity!
|
||||
[config repo page-name]
|
||||
(p/let [existing (pull-page-by-name config repo page-name [:db/id :block/uuid])]
|
||||
(if (:db/id existing)
|
||||
existing
|
||||
(p/let [_ (transport/invoke config :thread-api/apply-outliner-ops false
|
||||
[repo [[:create-page [page-name {}]]] {}])
|
||||
created (pull-page-by-name config repo page-name [:db/id :block/uuid])]
|
||||
(if (:db/id created)
|
||||
created
|
||||
(throw (ex-info "page not found after upsert"
|
||||
{:code :page-not-found
|
||||
:page page-name})))))))
|
||||
|
||||
(defn- append-tag-and-property-ops
|
||||
[ops block-ids {:keys [update-tag-ids remove-tag-ids update-properties remove-properties]}]
|
||||
(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))))
|
||||
|
||||
(defn- execute-extra-upsert-block-ops!
|
||||
[action config block-ids]
|
||||
(if (seq block-ids)
|
||||
(p/let [cfg (cli-server/ensure-server! config (:repo 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)
|
||||
{:allow-non-built-in? true})
|
||||
remove-properties (add-command/resolve-property-identifiers
|
||||
cfg (:repo action) (:remove-properties action)
|
||||
{:allow-non-built-in? true})
|
||||
update-property-idents (keys (or update-properties {}))
|
||||
_ (ensure-property-identifiers-exist! cfg (:repo action) update-property-idents)
|
||||
_ (ensure-property-identifiers-exist! cfg (:repo action) remove-properties)
|
||||
ops (append-tag-and-property-ops []
|
||||
block-ids
|
||||
{:update-tag-ids (->> update-tags (map :db/id) (remove nil?) distinct vec)
|
||||
:remove-tag-ids (->> remove-tags (map :db/id) (remove nil?) distinct vec)
|
||||
:update-properties update-properties
|
||||
:remove-properties remove-properties})]
|
||||
(when (seq ops)
|
||||
(transport/invoke cfg :thread-api/apply-outliner-ops false
|
||||
[(:repo action) ops {}])))
|
||||
(p/resolved nil)))
|
||||
|
||||
(defn execute-upsert-block
|
||||
[action config]
|
||||
(-> (if (= :update (:mode action))
|
||||
(update-command/execute-update (assoc action :type :update-block) config)
|
||||
(p/let [result (add-command/execute-add-block (assoc action :type :add-block) config)
|
||||
created-ids (vec (or (get-in result [:data :result]) []))
|
||||
_ (execute-extra-upsert-block-ops! action config created-ids)]
|
||||
{:status :ok
|
||||
:data {:result created-ids}}))
|
||||
(p/catch (fn [e]
|
||||
{:status :error
|
||||
:error {:code (or (get-in (ex-data e) [:code]) :exception)
|
||||
:message (or (ex-message e) (str e))}}))))
|
||||
|
||||
(defn execute-upsert-page
|
||||
[action config]
|
||||
(-> (p/let [cfg (cli-server/ensure-server! config (:repo action))
|
||||
page (ensure-page-entity! cfg (:repo action) (:page action))
|
||||
page-id (:db/id page)
|
||||
block-ids [page-id]
|
||||
add-tags (add-command/resolve-tags cfg (:repo action) (:tags 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))
|
||||
add-properties (add-command/resolve-properties cfg (:repo action) (:properties 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))
|
||||
merged-properties (merge (or add-properties {}) (or update-properties {}))
|
||||
_ (ensure-property-identifiers-exist! cfg (:repo action) (keys merged-properties))
|
||||
_ (ensure-property-identifiers-exist! cfg (:repo action) remove-properties)
|
||||
update-tag-ids (->> (concat (or add-tags []) (or update-tags []))
|
||||
(map :db/id)
|
||||
(remove nil?)
|
||||
distinct
|
||||
vec)
|
||||
remove-tag-ids (->> remove-tags (map :db/id) (remove nil?) distinct vec)
|
||||
ops (append-tag-and-property-ops []
|
||||
block-ids
|
||||
{:update-tag-ids update-tag-ids
|
||||
:remove-tag-ids remove-tag-ids
|
||||
:update-properties merged-properties
|
||||
:remove-properties remove-properties})
|
||||
_ (when (seq ops)
|
||||
(transport/invoke cfg :thread-api/apply-outliner-ops false
|
||||
[(:repo action) ops {}]))]
|
||||
{:status :ok
|
||||
:data {:result [page-id]}})
|
||||
(p/catch (fn [e]
|
||||
{:status :error
|
||||
:error {:code (or (get-in (ex-data e) [:code]) :exception)
|
||||
:message (or (ex-message e) (str e))}}))))
|
||||
|
||||
(defn- tag-entity?
|
||||
[entity]
|
||||
(some #(= :logseq.class/Tag (:db/ident %))
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
"Command parsing and action building for the Logseq CLI."
|
||||
(:require [babashka.cli :as cli]
|
||||
[clojure.string :as string]
|
||||
[logseq.cli.command.add :as add-command]
|
||||
[logseq.cli.command.core :as command-core]
|
||||
[logseq.cli.command.doctor :as doctor-command]
|
||||
[logseq.cli.command.graph :as graph-command]
|
||||
@@ -12,7 +11,6 @@
|
||||
[logseq.cli.command.server :as server-command]
|
||||
[logseq.cli.command.show :as show-command]
|
||||
[logseq.cli.command.upsert :as upsert-command]
|
||||
[logseq.cli.command.update :as update-command]
|
||||
[logseq.cli.server :as cli-server]
|
||||
[promesa.core :as p]))
|
||||
|
||||
@@ -49,13 +47,6 @@
|
||||
:message "block or page is required"}
|
||||
:summary summary})
|
||||
|
||||
(defn- missing-source-result
|
||||
[summary]
|
||||
{:ok? false
|
||||
:error {:code :missing-source
|
||||
:message "source block is required"}
|
||||
:summary summary})
|
||||
|
||||
(defn- missing-page-name-result
|
||||
[summary]
|
||||
{:ok? false
|
||||
@@ -113,10 +104,8 @@
|
||||
(vec (concat graph-command/entries
|
||||
server-command/entries
|
||||
list-command/entries
|
||||
add-command/entries
|
||||
upsert-command/entries
|
||||
remove-command/entries
|
||||
update-command/entries
|
||||
query-command/entries
|
||||
show-command/entries
|
||||
doctor-command/entries)))
|
||||
@@ -162,7 +151,7 @@
|
||||
(seq (:blocks-file opts))
|
||||
has-args?)
|
||||
show-targets (filter some? [(:id opts) (:uuid opts) (:page opts)])
|
||||
update-sources (filter some? [(:id opts) (some-> (:uuid opts) string/trim)])]
|
||||
upsert-update-mode? (upsert-command/update-mode? opts)]
|
||||
(cond
|
||||
(:help opts)
|
||||
(command-core/help-result cmd-summary)
|
||||
@@ -171,13 +160,13 @@
|
||||
(not (seq graph)))
|
||||
(missing-graph-result summary)
|
||||
|
||||
(and (= command :add-block) (not has-content?))
|
||||
(and (= command :upsert-block) (not upsert-update-mode?) (not has-content?))
|
||||
(missing-content-result summary)
|
||||
|
||||
(and (= command :add-block) (add-command/invalid-options? opts))
|
||||
(command-core/invalid-options-result summary (add-command/invalid-options? opts))
|
||||
(and (= command :upsert-block) (upsert-command/invalid-options? command opts))
|
||||
(command-core/invalid-options-result summary (upsert-command/invalid-options? command opts))
|
||||
|
||||
(and (= command :add-page) (not (seq (:page opts))))
|
||||
(and (= command :upsert-page) (not (seq (:page opts))))
|
||||
(missing-page-name-result summary)
|
||||
|
||||
(and (= command :upsert-tag) (not (seq (some-> (:name opts) string/trim))))
|
||||
@@ -199,12 +188,6 @@
|
||||
(empty? (filter some? [(:id opts) (some-> (:name opts) string/trim)])))
|
||||
(missing-target-result summary)
|
||||
|
||||
(and (= command :update-block) (update-command/invalid-options? opts))
|
||||
(command-core/invalid-options-result summary (update-command/invalid-options? opts))
|
||||
|
||||
(and (= command :update-block) (empty? update-sources))
|
||||
(missing-source-result summary)
|
||||
|
||||
(and (= command :show) (empty? show-targets))
|
||||
(missing-target-result summary)
|
||||
|
||||
@@ -282,7 +265,7 @@
|
||||
:message "missing command"}
|
||||
:summary summary})
|
||||
|
||||
(and (= 1 (count args)) (#{"graph" "server" "list" "add" "upsert" "remove" "query"} (first args)))
|
||||
(and (= 1 (count args)) (#{"graph" "server" "list" "upsert" "remove" "query"} (first args)))
|
||||
(command-core/help-result (command-core/group-summary (first args) table))
|
||||
|
||||
:else
|
||||
@@ -380,11 +363,11 @@
|
||||
(:list-page :list-tag :list-property)
|
||||
(list-command/build-action command options repo)
|
||||
|
||||
:add-block
|
||||
(add-command/build-add-block-action options args repo)
|
||||
:upsert-block
|
||||
(upsert-command/build-block-action options args repo)
|
||||
|
||||
:add-page
|
||||
(add-command/build-add-page-action options repo)
|
||||
:upsert-page
|
||||
(upsert-command/build-page-action options repo)
|
||||
|
||||
:upsert-tag
|
||||
(upsert-command/build-tag-action options repo)
|
||||
@@ -392,9 +375,6 @@
|
||||
:upsert-property
|
||||
(upsert-command/build-property-action options repo)
|
||||
|
||||
:update-block
|
||||
(update-command/build-action options repo)
|
||||
|
||||
(:remove-block :remove-page :remove-tag :remove-property)
|
||||
(remove-command/build-action command options repo)
|
||||
|
||||
@@ -438,11 +418,10 @@
|
||||
:list-page (list-command/execute-list-page action config)
|
||||
:list-tag (list-command/execute-list-tag action config)
|
||||
: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)
|
||||
:upsert-block (upsert-command/execute-upsert-block action config)
|
||||
:upsert-page (upsert-command/execute-upsert-page action config)
|
||||
:upsert-tag (upsert-command/execute-upsert-tag action config)
|
||||
:upsert-property (upsert-command/execute-upsert-property action config)
|
||||
:update-block (update-command/execute-update action config)
|
||||
:remove-block (remove-command/execute-remove-block action config)
|
||||
:remove-page (remove-command/execute-remove-page action config)
|
||||
:remove-tag (remove-command/execute-remove-tag action config)
|
||||
|
||||
@@ -249,17 +249,24 @@
|
||||
(cond-> [(str "Server " (name status) ": " repo)]
|
||||
(and host port) (conj (str "Host: " host " Port: " port))))))
|
||||
|
||||
(defn- format-add-block
|
||||
[_context ids]
|
||||
(str "Added blocks:\n" (pr-str (vec (or ids [])))))
|
||||
(defn- format-upsert-block
|
||||
[{:keys [repo source target update-tags update-properties remove-tags remove-properties]} result]
|
||||
(if (vector? result)
|
||||
(str "Upserted blocks:\n" (pr-str (vec (or result []))))
|
||||
(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 "Upserted block: " source (or move-fragment "") " (repo: " repo (or changes "") ")"))))
|
||||
|
||||
(defn- format-add-page
|
||||
(defn- format-upsert-page
|
||||
[_context ids]
|
||||
(str "Added page:\n" (pr-str (vec (or ids [])))))
|
||||
|
||||
(defn- format-add-tag
|
||||
[_context ids]
|
||||
(str "Added tag:\n" (pr-str (vec (or ids [])))))
|
||||
(str "Upserted page:\n" (pr-str (vec (or ids [])))))
|
||||
|
||||
(defn- format-upsert-tag
|
||||
[_context ids]
|
||||
@@ -293,19 +300,6 @@
|
||||
(str "Removed property: " name " (repo: " repo ")")
|
||||
(str "Removed property: " id " (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))
|
||||
@@ -352,16 +346,14 @@
|
||||
(format-server-action command data)
|
||||
:list-page (format-list-page (:items data) now-ms)
|
||||
(:list-tag :list-property) (format-list-tag-or-property (:items data) now-ms)
|
||||
:add-block (format-add-block context (:result data))
|
||||
:add-page (format-add-page context (:result data))
|
||||
:add-tag (format-add-tag context (:result data))
|
||||
:upsert-block (format-upsert-block context (:result data))
|
||||
:upsert-page (format-upsert-page context (:result data))
|
||||
:upsert-tag (format-upsert-tag context (:result data))
|
||||
:upsert-property (format-upsert-property context (:result data))
|
||||
:remove-block (format-remove-block context)
|
||||
:remove-page (format-remove-page context)
|
||||
:remove-tag (format-remove-tag context)
|
||||
:remove-property (format-remove-property 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))
|
||||
|
||||
@@ -59,10 +59,8 @@
|
||||
(is (string/includes? plain-summary "Graph Inspect and Edit"))
|
||||
(is (string/includes? plain-summary "Graph Management"))
|
||||
(is (string/includes? plain-summary "list"))
|
||||
(is (string/includes? plain-summary "add"))
|
||||
(is (string/includes? plain-summary "upsert"))
|
||||
(is (string/includes? plain-summary "remove"))
|
||||
(is (string/includes? plain-summary "update"))
|
||||
(is (string/includes? plain-summary "query"))
|
||||
(is (string/includes? plain-summary "show"))
|
||||
(is (string/includes? plain-summary "doctor"))
|
||||
@@ -72,15 +70,14 @@
|
||||
(is (contains-bold? summary "list page"))
|
||||
(is (contains-bold? summary "list tag"))
|
||||
(is (contains-bold? summary "list property"))
|
||||
(is (contains-bold? summary "add block"))
|
||||
(is (contains-bold? summary "add page"))
|
||||
(is (contains-bold? summary "upsert block"))
|
||||
(is (contains-bold? summary "upsert page"))
|
||||
(is (contains-bold? summary "upsert tag"))
|
||||
(is (contains-bold? summary "upsert property"))
|
||||
(is (contains-bold? summary "remove block"))
|
||||
(is (contains-bold? summary "remove page"))
|
||||
(is (contains-bold? summary "remove tag"))
|
||||
(is (contains-bold? summary "remove property"))
|
||||
(is (contains-bold? summary "update"))
|
||||
(is (contains-bold? summary "query"))
|
||||
(is (contains-bold? summary "query list"))
|
||||
(is (contains-bold? summary "show"))
|
||||
@@ -161,16 +158,17 @@
|
||||
(is (contains-bold? summary "--id"))
|
||||
(is (contains-bold? summary "--uuid"))))
|
||||
|
||||
(testing "update command shows help"
|
||||
(testing "upsert block command shows help"
|
||||
(let [result (binding [style/*color-enabled?* true]
|
||||
(commands/parse-args ["update" "--help"]))
|
||||
(commands/parse-args ["upsert" "block" "--help"]))
|
||||
summary (:summary result)
|
||||
plain-summary (strip-ansi summary)]
|
||||
(is (true? (:help? result)))
|
||||
(is (string/includes? plain-summary "Usage: logseq update"))
|
||||
(is (string/includes? plain-summary "Usage: logseq upsert block"))
|
||||
(is (string/includes? plain-summary "Command options:"))
|
||||
(is (contains-bold? summary "--id"))
|
||||
(is (contains-bold? summary "--uuid"))
|
||||
(is (contains-bold? summary "--content"))
|
||||
(is (contains-bold? summary "--target-id"))
|
||||
(is (contains-bold? summary "--target-uuid"))
|
||||
(is (contains-bold? summary "--update-tags"))
|
||||
@@ -207,17 +205,11 @@
|
||||
(is (seq lines))
|
||||
(is (every? #(not (string/includes? % "[options]")) lines)))))
|
||||
|
||||
(deftest test-parse-args-help-add-upsert-group
|
||||
(testing "add group shows subcommands"
|
||||
(let [result (binding [style/*color-enabled?* true]
|
||||
(commands/parse-args ["add"]))
|
||||
summary (:summary result)
|
||||
plain-summary (strip-ansi summary)]
|
||||
(is (true? (:help? result)))
|
||||
(is (string/includes? plain-summary "add block"))
|
||||
(is (string/includes? plain-summary "add page"))
|
||||
(is (contains-bold? summary "add block"))
|
||||
(is (contains-bold? summary "add page"))))
|
||||
(deftest test-parse-args-help-upsert-group
|
||||
(testing "add group is removed"
|
||||
(let [result (commands/parse-args ["add"])]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :unknown-command (get-in result [:error :code])))))
|
||||
|
||||
(testing "upsert group shows subcommands"
|
||||
(let [result (binding [style/*color-enabled?* true]
|
||||
@@ -225,8 +217,12 @@
|
||||
summary (:summary result)
|
||||
plain-summary (strip-ansi summary)]
|
||||
(is (true? (:help? result)))
|
||||
(is (string/includes? plain-summary "upsert block"))
|
||||
(is (string/includes? plain-summary "upsert page"))
|
||||
(is (string/includes? plain-summary "upsert tag"))
|
||||
(is (string/includes? plain-summary "upsert property"))
|
||||
(is (contains-bold? summary "upsert block"))
|
||||
(is (contains-bold? summary "upsert page"))
|
||||
(is (contains-bold? summary "upsert tag"))
|
||||
(is (contains-bold? summary "upsert property")))))
|
||||
|
||||
@@ -282,6 +278,14 @@
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :unknown-command (get-in result [:error :code]))))))
|
||||
|
||||
(testing "rejects removed write commands"
|
||||
(doseq [args [["add" "block" "--content" "x"]
|
||||
["add" "page" "--page" "Home"]
|
||||
["update" "--id" "1" "--update-tags" "[\"TagA\"]"]]]
|
||||
(let [result (commands/parse-args args)]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :unknown-command (get-in result [:error :code]))))))
|
||||
|
||||
(testing "errors on missing command"
|
||||
(let [result (commands/parse-args [])]
|
||||
(is (false? (:ok? result)))
|
||||
@@ -634,11 +638,11 @@
|
||||
(deftest test-help-tags-properties-identifiers
|
||||
(testing "add help mentions tag and property identifiers"
|
||||
(let [summary (:summary (binding [style/*color-enabled?* true]
|
||||
(commands/parse-args ["add" "block" "--help"])))]
|
||||
(commands/parse-args ["upsert" "block" "--help"])))]
|
||||
(is (string/includes? (strip-ansi summary)
|
||||
"Identifiers can be id, :db/ident, or :block/title.")))
|
||||
(let [summary (:summary (binding [style/*color-enabled?* true]
|
||||
(commands/parse-args ["add" "page" "--help"])))]
|
||||
(commands/parse-args ["upsert" "page" "--help"])))]
|
||||
(is (string/includes? (strip-ansi summary)
|
||||
"Identifiers can be id, :db/ident, or :block/title.")))))
|
||||
|
||||
@@ -927,126 +931,149 @@
|
||||
"--name" "owner"
|
||||
"--cardinality" "triple"])]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :invalid-options (get-in result [:error :code])))))
|
||||
(is (= :invalid-options (get-in result [:error :code]))))))
|
||||
|
||||
(testing "update requires source selector"
|
||||
(let [result (commands/parse-args ["update" "--target-id" "10"])]
|
||||
(deftest test-verb-subcommand-parse-upsert-block-mode
|
||||
|
||||
(testing "upsert block create mode requires content when source selectors are absent"
|
||||
(let [result (commands/parse-args ["upsert" "block" "--target-id" "10"])]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :missing-source (get-in result [:error :code])))))
|
||||
(is (= :missing-content (get-in result [:error :code])))))
|
||||
|
||||
(testing "update requires target or update/remove options"
|
||||
(let [result (commands/parse-args ["update" "--id" "1"])]
|
||||
(testing "upsert block update mode requires target or update/remove options"
|
||||
(let [result (commands/parse-args ["upsert" "block" "--id" "1"])]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :invalid-options (get-in result [:error :code])))))
|
||||
|
||||
(testing "update parses with source and target"
|
||||
(let [result (commands/parse-args ["update" "--uuid" "abc" "--target-uuid" "def" "--pos" "last-child"])]
|
||||
(testing "upsert block parses with source and target"
|
||||
(let [result (commands/parse-args ["upsert" "block" "--uuid" "abc" "--target-uuid" "def" "--pos" "last-child"])]
|
||||
(is (true? (:ok? result)))
|
||||
(is (= :update-block (:command result)))
|
||||
(is (= :upsert-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])))))
|
||||
|
||||
(testing "update parses with tags and properties"
|
||||
(let [result (commands/parse-args ["update" "--id" "1"
|
||||
(testing "upsert block parses with update tags and properties"
|
||||
(let [result (commands/parse-args ["upsert" "block" "--id" "1"
|
||||
"--update-tags" "[\"TagA\"]"
|
||||
"--update-properties" "{:logseq.property/publishing-public? true}"])]
|
||||
(is (true? (:ok? result)))
|
||||
(is (= :update-block (:command result)))
|
||||
(is (= :upsert-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"
|
||||
(testing "upsert block allows updates without move target"
|
||||
(let [result (commands/parse-args ["upsert" "block" "--uuid" "abc"
|
||||
"--update-tags" "[\"TagA\"]"])]
|
||||
(is (true? (:ok? result)))
|
||||
(is (= :update-block (:command result)))
|
||||
(is (= "abc" (get-in result [:options :uuid]))))))
|
||||
(is (= :upsert-block (:command result)))
|
||||
(is (= "abc" (get-in result [:options :uuid])))))
|
||||
|
||||
(testing "upsert block forces update mode when id and content are both provided"
|
||||
(let [result (commands/parse-args ["upsert" "block"
|
||||
"--id" "1"
|
||||
"--content" "hello"
|
||||
"--update-tags" "[\"TagA\"]"])]
|
||||
(is (true? (:ok? result)))
|
||||
(is (= :upsert-block (:command result)))
|
||||
(is (= 1 (get-in result [:options :id])))
|
||||
(is (= "hello" (get-in result [:options :content])))
|
||||
(is (= "[\"TagA\"]" (get-in result [:options :update-tags]))))))
|
||||
|
||||
(deftest test-verb-subcommand-parse-add
|
||||
(testing "add block requires content source"
|
||||
(let [result (commands/parse-args ["add" "block"])]
|
||||
(testing "upsert block create mode requires content source"
|
||||
(let [result (commands/parse-args ["upsert" "block"])]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :missing-content (get-in result [:error :code])))))
|
||||
|
||||
(testing "add block parses with content"
|
||||
(let [result (commands/parse-args ["add" "block" "--content" "hello"])]
|
||||
(testing "upsert block create mode parses with content"
|
||||
(let [result (commands/parse-args ["upsert" "block" "--content" "hello"])]
|
||||
(is (true? (:ok? result)))
|
||||
(is (= :add-block (:command result)))
|
||||
(is (= :upsert-block (:command result)))
|
||||
(is (= "hello" (get-in result [:options :content])))))
|
||||
|
||||
(testing "add block parses with target selectors and pos"
|
||||
(let [result (commands/parse-args ["add" "block"
|
||||
(testing "upsert block create mode parses with target selectors and pos"
|
||||
(let [result (commands/parse-args ["upsert" "block"
|
||||
"--content" "hello"
|
||||
"--target-uuid" "abc"
|
||||
"--pos" "first-child"])]
|
||||
(is (true? (:ok? result)))
|
||||
(is (= :add-block (:command result)))
|
||||
(is (= :upsert-block (:command result)))
|
||||
(is (= "abc" (get-in result [:options :target-uuid])))
|
||||
(is (= "first-child" (get-in result [:options :pos])))))
|
||||
|
||||
(testing "add block parses with tags and properties"
|
||||
(let [result (commands/parse-args ["add" "block"
|
||||
(testing "upsert block create mode parses with tags and properties"
|
||||
(let [result (commands/parse-args ["upsert" "block"
|
||||
"--content" "hello"
|
||||
"--tags" "[\"TagA\" \"TagB\"]"
|
||||
"--properties" "{:logseq.property/publishing-public? true}"])]
|
||||
(is (true? (:ok? result)))
|
||||
(is (= :add-block (:command result)))
|
||||
(is (= :upsert-block (:command result)))
|
||||
(is (= "[\"TagA\" \"TagB\"]" (get-in result [:options :tags])))
|
||||
(is (= "{:logseq.property/publishing-public? true}" (get-in result [:options :properties])))))
|
||||
|
||||
(testing "add block rejects invalid pos"
|
||||
(let [result (commands/parse-args ["add" "block"
|
||||
(testing "upsert block rejects invalid pos"
|
||||
(let [result (commands/parse-args ["upsert" "block"
|
||||
"--content" "hello"
|
||||
"--pos" "middle"])]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :invalid-options (get-in result [:error :code])))))
|
||||
|
||||
(testing "add block rejects tags with blocks payload"
|
||||
(let [result (commands/parse-args ["add" "block"
|
||||
(testing "upsert block rejects tags with blocks payload"
|
||||
(let [result (commands/parse-args ["upsert" "block"
|
||||
"--blocks" "[]"
|
||||
"--tags" "[\"TagA\"]"])]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :invalid-options (get-in result [:error :code])))))
|
||||
|
||||
(testing "add block rejects properties with blocks-file payload"
|
||||
(let [result (commands/parse-args ["add" "block"
|
||||
(testing "upsert block rejects properties with blocks-file payload"
|
||||
(let [result (commands/parse-args ["upsert" "block"
|
||||
"--blocks-file" "/tmp/blocks.edn"
|
||||
"--properties" "{:logseq.property/publishing-public? true}"])]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :invalid-options (get-in result [:error :code])))))
|
||||
|
||||
(testing "add page requires page name"
|
||||
(let [result (commands/parse-args ["add" "page"])]
|
||||
(testing "upsert page requires page name"
|
||||
(let [result (commands/parse-args ["upsert" "page"])]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :missing-page-name (get-in result [:error :code])))))
|
||||
|
||||
(testing "add page parses with name"
|
||||
(let [result (commands/parse-args ["add" "page" "--page" "Home"])]
|
||||
(testing "upsert page parses with name"
|
||||
(let [result (commands/parse-args ["upsert" "page" "--page" "Home"])]
|
||||
(is (true? (:ok? result)))
|
||||
(is (= :add-page (:command result)))
|
||||
(is (= :upsert-page (:command result)))
|
||||
(is (= "Home" (get-in result [:options :page])))))
|
||||
|
||||
(testing "add page parses with tags and properties"
|
||||
(let [result (commands/parse-args ["add" "page"
|
||||
(testing "upsert page parses with tags and properties"
|
||||
(let [result (commands/parse-args ["upsert" "page"
|
||||
"--page" "Home"
|
||||
"--tags" "[\"TagA\"]"
|
||||
"--properties" "{:logseq.property/publishing-public? true}"])]
|
||||
(is (true? (:ok? result)))
|
||||
(is (= :add-page (:command result)))
|
||||
(is (= :upsert-page (:command result)))
|
||||
(is (= "[\"TagA\"]" (get-in result [:options :tags])))
|
||||
(is (= "{:logseq.property/publishing-public? true}" (get-in result [:options :properties])))))
|
||||
|
||||
(testing "add tag is no longer supported"
|
||||
(testing "upsert page parses update and remove options"
|
||||
(let [result (commands/parse-args ["upsert" "page"
|
||||
"--page" "Home"
|
||||
"--update-tags" "[\"TagB\"]"
|
||||
"--remove-properties" "[:logseq.property/deadline]"])]
|
||||
(is (true? (:ok? result)))
|
||||
(is (= :upsert-page (:command result)))
|
||||
(is (= "[\"TagB\"]" (get-in result [:options :update-tags])))
|
||||
(is (= "[:logseq.property/deadline]" (get-in result [:options :remove-properties])))))
|
||||
|
||||
(testing "legacy add tag is no longer supported"
|
||||
(let [result (commands/parse-args ["add" "tag" "--name" "Quote"])]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :unknown-command (get-in result [:error :code]))))))
|
||||
|
||||
(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"])]
|
||||
(testing "upsert block update mode parses with target page"
|
||||
(let [result (commands/parse-args ["upsert" "block" "--id" "1" "--target-page" "Home"])]
|
||||
(is (true? (:ok? result)))
|
||||
(is (= :update-block (:command result)))
|
||||
(is (= :upsert-block (:command result)))
|
||||
(is (= 1 (get-in result [:options :id])))
|
||||
(is (= "Home" (get-in result [:options :target-page]))))))
|
||||
|
||||
@@ -1165,10 +1192,10 @@
|
||||
(deftest test-verb-subcommand-parse-flags
|
||||
(testing "verb subcommands reject unknown flags"
|
||||
(doseq [args [["list" "page" "--wat"]
|
||||
["add" "block" "--wat"]
|
||||
["upsert" "block" "--wat"]
|
||||
["upsert" "page" "--wat"]
|
||||
["remove" "block" "--wat"]
|
||||
["upsert" "tag" "--wat"]
|
||||
["update" "--wat"]
|
||||
["show" "--wat"]]]
|
||||
(let [result (commands/parse-args args)]
|
||||
(is (false? (:ok? result)))
|
||||
@@ -1269,19 +1296,19 @@
|
||||
(is (= :missing-repo (get-in result [:error :code])))))
|
||||
|
||||
(testing "add block requires content"
|
||||
(let [parsed {:ok? true :command :add-block :options {}}
|
||||
(let [parsed {:ok? true :command :upsert-block :options {}}
|
||||
result (commands/build-action parsed {:repo "demo"})]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :missing-content (get-in result [:error :code])))))
|
||||
|
||||
(testing "add block builds insert-blocks op"
|
||||
(let [parsed {:ok? true :command :add-block :options {:content "hello"}}
|
||||
(let [parsed {:ok? true :command :upsert-block :options {:content "hello"}}
|
||||
result (commands/build-action parsed {:repo "demo"})]
|
||||
(is (true? (:ok? result)))
|
||||
(is (= :add-block (get-in result [:action :type])))))
|
||||
(is (= :upsert-block (get-in result [:action :type])))))
|
||||
|
||||
(testing "add page requires name"
|
||||
(let [parsed {:ok? true :command :add-page :options {}}
|
||||
(let [parsed {:ok? true :command :upsert-page :options {}}
|
||||
result (commands/build-action parsed {:repo "demo"})]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :missing-page-name (get-in result [:error :code])))))
|
||||
@@ -1374,7 +1401,7 @@
|
||||
|
||||
(deftest test-build-action-add-validates-properties
|
||||
(testing "add block rejects unknown property"
|
||||
(let [parsed (commands/parse-args ["add" "block"
|
||||
(let [parsed (commands/parse-args ["upsert" "block"
|
||||
"--content" "hello"
|
||||
"--properties" "{:not/a 1}"])
|
||||
result (commands/build-action parsed {:repo "demo"})]
|
||||
@@ -1382,7 +1409,7 @@
|
||||
(is (= :invalid-options (get-in result [:error :code])))))
|
||||
|
||||
(testing "add block accepts property title key"
|
||||
(let [parsed (commands/parse-args ["add" "block"
|
||||
(let [parsed (commands/parse-args ["upsert" "block"
|
||||
"--content" "hello"
|
||||
"--properties" "{\"Publishing Public?\" true}"])
|
||||
result (commands/build-action parsed {:repo "demo"})]
|
||||
@@ -1391,7 +1418,7 @@
|
||||
(-> result :action :properties keys first)))))
|
||||
|
||||
(testing "add block rejects non-public built-in property"
|
||||
(let [parsed (commands/parse-args ["add" "block"
|
||||
(let [parsed (commands/parse-args ["upsert" "block"
|
||||
"--content" "hello"
|
||||
"--properties" "{:logseq.property/heading 1}"])
|
||||
result (commands/build-action parsed {:repo "demo"})]
|
||||
@@ -1399,7 +1426,7 @@
|
||||
(is (= :invalid-options (get-in result [:error :code])))))
|
||||
|
||||
(testing "add block rejects invalid checkbox value"
|
||||
(let [parsed (commands/parse-args ["add" "block"
|
||||
(let [parsed (commands/parse-args ["upsert" "block"
|
||||
"--content" "hello"
|
||||
"--properties" "{:logseq.property/publishing-public? \"nope\"}"])
|
||||
result (commands/build-action parsed {:repo "demo"})]
|
||||
@@ -1408,7 +1435,7 @@
|
||||
|
||||
(deftest test-build-action-add-accepts-tag-ids
|
||||
(testing "add block accepts numeric tag ids"
|
||||
(let [parsed (commands/parse-args ["add" "block"
|
||||
(let [parsed (commands/parse-args ["upsert" "block"
|
||||
"--content" "hello"
|
||||
"--tags" "[42]"])
|
||||
result (commands/build-action parsed {:repo "demo"})]
|
||||
@@ -1424,63 +1451,87 @@
|
||||
(is (= {:type :id :value 42} (normalize-property-key-input 42)))))
|
||||
|
||||
(deftest test-build-action-update
|
||||
(testing "update requires source selector"
|
||||
(let [parsed {:ok? true :command :update-block :options {:target-id 2}}
|
||||
(testing "upsert block create mode requires content when source selector is absent"
|
||||
(let [parsed {:ok? true :command :upsert-block :options {:target-id 2}}
|
||||
result (commands/build-action parsed {:repo "demo"})]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :missing-source (get-in result [:error :code])))))
|
||||
(is (= :missing-content (get-in result [:error :code])))))
|
||||
|
||||
(testing "update requires target or update/remove options"
|
||||
(let [parsed {:ok? true :command :update-block :options {:id 1}}
|
||||
(testing "upsert block update mode requires target or update/remove options"
|
||||
(let [parsed {:ok? true :command :upsert-block :options {:id 1}}
|
||||
result (commands/build-action parsed {:repo "demo"})]
|
||||
(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
|
||||
:command :upsert-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 (= :upsert-block (get-in result [:action :type])))
|
||||
(is (= ["TagA"] (get-in result [:action :update-tags])))))
|
||||
|
||||
(testing "update rejects invalid update tags"
|
||||
(let [parsed {:ok? true
|
||||
:command :update-block
|
||||
:command :upsert-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]))))))
|
||||
(is (= :invalid-options (get-in result [:error :code])))))
|
||||
|
||||
(testing "upsert block forces update mode when id and content are both provided"
|
||||
(let [parsed {:ok? true
|
||||
:command :upsert-block
|
||||
:options {:id 1 :content "hello" :update-tags "[\"TagA\"]"}}
|
||||
result (commands/build-action parsed {:repo "demo"})]
|
||||
(is (true? (:ok? result)))
|
||||
(is (= :upsert-block (get-in result [:action :type])))
|
||||
(is (= 1 (get-in result [:action :id])))
|
||||
(is (= ["TagA"] (get-in result [:action :update-tags])))))
|
||||
|
||||
(testing "update accepts custom property identifiers"
|
||||
(let [parsed {:ok? true
|
||||
:command :upsert-block
|
||||
:options {:id 1
|
||||
:update-properties "{:user.property/owner \"alice\"}"
|
||||
:remove-properties "[:user.property/owner]"}}
|
||||
result (commands/build-action parsed {:repo "demo"})]
|
||||
(is (true? (:ok? result)))
|
||||
(is (= :upsert-block (get-in result [:action :type])))
|
||||
(is (= {:user.property/owner "alice"}
|
||||
(get-in result [:action :update-properties])))
|
||||
(is (= [:user.property/owner]
|
||||
(get-in result [:action :remove-properties]))))))
|
||||
|
||||
(deftest test-update-parse-validation
|
||||
(testing "update rejects multiple source selectors"
|
||||
(let [result (commands/parse-args ["update" "--id" "1" "--uuid" "abc" "--target-id" "2"])]
|
||||
(let [result (commands/parse-args ["upsert" "block" "--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"])]
|
||||
(let [result (commands/parse-args ["upsert" "block" "--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"])]
|
||||
(let [result (commands/parse-args ["upsert" "block" "--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"])]
|
||||
(let [result (commands/parse-args ["upsert" "block" "--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"])]
|
||||
(let [result (commands/parse-args ["upsert" "block" "--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\"]"])]
|
||||
(let [result (commands/parse-args ["upsert" "block" "--id" "1" "--pos" "last-child" "--update-tags" "[\"TagA\"]"])]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :invalid-options (get-in result [:error :code]))))))
|
||||
|
||||
@@ -1637,6 +1688,178 @@
|
||||
(set! transport/invoke orig-invoke)
|
||||
(done)))))))
|
||||
|
||||
(deftest test-execute-upsert-block-create-applies-extra-tag-property-ops
|
||||
(async done
|
||||
(let [ops* (atom nil)
|
||||
orig-list-graphs cli-server/list-graphs
|
||||
orig-ensure-server! cli-server/ensure-server!
|
||||
orig-execute-add-block add-command/execute-add-block
|
||||
orig-resolve-tags add-command/resolve-tags
|
||||
orig-resolve-properties add-command/resolve-properties
|
||||
orig-resolve-property-identifiers add-command/resolve-property-identifiers
|
||||
orig-invoke transport/invoke
|
||||
action {:type :upsert-block
|
||||
:mode :create
|
||||
:repo "demo"
|
||||
:update-tags [:tag/new]
|
||||
:remove-tags [:tag/old]
|
||||
:update-properties {:logseq.property/deadline "2026-01-25T12:00:00Z"}
|
||||
:remove-properties [:logseq.property/publishing-public?]}]
|
||||
(set! cli-server/list-graphs (fn [_] ["demo"]))
|
||||
(set! cli-server/ensure-server! (fn [_ _] {:base-url "http://example"}))
|
||||
(set! add-command/execute-add-block (fn [_ _]
|
||||
(p/resolved {:status :ok
|
||||
:data {:result [11 12]}})))
|
||||
(set! add-command/resolve-tags (fn [_ _ tags]
|
||||
(p/resolved (cond
|
||||
(= tags [:tag/new]) [{:db/id 101}]
|
||||
(= tags [:tag/old]) [{:db/id 202}]
|
||||
:else nil))))
|
||||
(set! add-command/resolve-properties (fn [_ _ properties & _] (p/resolved properties)))
|
||||
(set! add-command/resolve-property-identifiers (fn [_ _ properties & _] (p/resolved properties)))
|
||||
(set! transport/invoke (fn [_ method _ args]
|
||||
(case method
|
||||
:thread-api/pull (let [[_ _ lookup] args]
|
||||
(if (and (vector? lookup)
|
||||
(= :db/ident (first lookup)))
|
||||
{:db/id 99}
|
||||
{}))
|
||||
:thread-api/apply-outliner-ops (let [[_ ops _] args]
|
||||
(reset! ops* ops)
|
||||
{:result :ok})
|
||||
(throw (ex-info "unexpected invoke" {:method method :args args})))))
|
||||
(-> (p/let [result (commands/execute action {})]
|
||||
(is (= :ok (:status result)))
|
||||
(is (= [11 12] (get-in result [:data :result])))
|
||||
(is (= [[:batch-delete-property-value [[11 12] :block/tags 202]]
|
||||
[:batch-remove-property [[11 12] :logseq.property/publishing-public?]]
|
||||
[:batch-set-property [[11 12] :block/tags 101 {}]]
|
||||
[:batch-set-property [[11 12] :logseq.property/deadline "2026-01-25T12:00:00Z" {}]]]
|
||||
@ops*)))
|
||||
(p/catch (fn [e]
|
||||
(is false (str "unexpected error: " e))))
|
||||
(p/finally (fn []
|
||||
(set! cli-server/list-graphs orig-list-graphs)
|
||||
(set! cli-server/ensure-server! orig-ensure-server!)
|
||||
(set! add-command/execute-add-block orig-execute-add-block)
|
||||
(set! add-command/resolve-tags orig-resolve-tags)
|
||||
(set! add-command/resolve-properties orig-resolve-properties)
|
||||
(set! add-command/resolve-property-identifiers orig-resolve-property-identifiers)
|
||||
(set! transport/invoke orig-invoke)
|
||||
(done)))))))
|
||||
|
||||
(deftest test-execute-upsert-page-applies-ops-on-existing-page
|
||||
(async done
|
||||
(let [ops* (atom nil)
|
||||
orig-list-graphs cli-server/list-graphs
|
||||
orig-ensure-server! cli-server/ensure-server!
|
||||
orig-resolve-tags add-command/resolve-tags
|
||||
orig-resolve-properties add-command/resolve-properties
|
||||
orig-resolve-property-identifiers add-command/resolve-property-identifiers
|
||||
orig-invoke transport/invoke
|
||||
action {:type :upsert-page
|
||||
:repo "demo"
|
||||
:page "Home"
|
||||
:tags [:tag/new]
|
||||
:update-tags [:tag/next]
|
||||
:remove-tags [:tag/old]
|
||||
:properties {:logseq.property/deadline "2026-01-25T12:00:00Z"}
|
||||
:update-properties {:logseq.property/publishing-public? true}
|
||||
:remove-properties [:logseq.property/deadline]}]
|
||||
(set! cli-server/list-graphs (fn [_] ["demo"]))
|
||||
(set! cli-server/ensure-server! (fn [_ _] {:base-url "http://example"}))
|
||||
(set! add-command/resolve-tags (fn [_ _ tags]
|
||||
(p/resolved (cond
|
||||
(= tags [:tag/new]) [{:db/id 101}]
|
||||
(= tags [:tag/next]) [{:db/id 303}]
|
||||
(= tags [:tag/old]) [{:db/id 202}]
|
||||
:else nil))))
|
||||
(set! add-command/resolve-properties (fn [_ _ properties & _] (p/resolved properties)))
|
||||
(set! add-command/resolve-property-identifiers (fn [_ _ properties & _] (p/resolved properties)))
|
||||
(set! transport/invoke (fn [_ method _ args]
|
||||
(case method
|
||||
:thread-api/pull (let [[_ _ lookup] args]
|
||||
(cond
|
||||
(= lookup [:block/name "home"])
|
||||
{:db/id 50
|
||||
:block/uuid (uuid "00000000-0000-0000-0000-000000000050")}
|
||||
|
||||
(and (vector? lookup) (= :db/ident (first lookup)))
|
||||
{:db/id 888}
|
||||
|
||||
:else {}))
|
||||
:thread-api/apply-outliner-ops (let [[_ ops _] args]
|
||||
(reset! ops* ops)
|
||||
{:result :ok})
|
||||
(throw (ex-info "unexpected invoke" {:method method :args args})))))
|
||||
(-> (p/let [result (commands/execute action {})
|
||||
ops @ops*]
|
||||
(is (= :ok (:status result)))
|
||||
(is (= [50] (get-in result [:data :result])))
|
||||
(is (= 6 (count ops)))
|
||||
(is (some #(= [:batch-delete-property-value [[50] :block/tags 202]] %) ops))
|
||||
(is (some #(= [:batch-remove-property [[50] :logseq.property/deadline]] %) ops))
|
||||
(is (some #(= [:batch-set-property [[50] :block/tags 101 {}]] %) ops))
|
||||
(is (some #(= [:batch-set-property [[50] :block/tags 303 {}]] %) ops))
|
||||
(is (some #(= [:batch-set-property [[50] :logseq.property/deadline "2026-01-25T12:00:00Z" {}]] %) ops))
|
||||
(is (some #(= [:batch-set-property [[50] :logseq.property/publishing-public? true {}]] %) ops)))
|
||||
(p/catch (fn [e]
|
||||
(is false (str "unexpected error: " e))))
|
||||
(p/finally (fn []
|
||||
(set! cli-server/list-graphs orig-list-graphs)
|
||||
(set! cli-server/ensure-server! orig-ensure-server!)
|
||||
(set! add-command/resolve-tags orig-resolve-tags)
|
||||
(set! add-command/resolve-properties orig-resolve-properties)
|
||||
(set! add-command/resolve-property-identifiers orig-resolve-property-identifiers)
|
||||
(set! transport/invoke orig-invoke)
|
||||
(done)))))))
|
||||
|
||||
(deftest test-execute-upsert-page-errors-when-property-does-not-exist
|
||||
(async done
|
||||
(let [orig-list-graphs cli-server/list-graphs
|
||||
orig-ensure-server! cli-server/ensure-server!
|
||||
orig-resolve-tags add-command/resolve-tags
|
||||
orig-resolve-properties add-command/resolve-properties
|
||||
orig-resolve-property-identifiers add-command/resolve-property-identifiers
|
||||
orig-invoke transport/invoke
|
||||
action {:type :upsert-page
|
||||
:repo "demo"
|
||||
:page "Home"
|
||||
:update-properties {:logseq.property/deadline "2026-01-25T12:00:00Z"}}]
|
||||
(set! cli-server/list-graphs (fn [_] ["demo"]))
|
||||
(set! cli-server/ensure-server! (fn [_ _] {:base-url "http://example"}))
|
||||
(set! add-command/resolve-tags (fn [_ _ _] (p/resolved nil)))
|
||||
(set! add-command/resolve-properties (fn [_ _ properties & _] (p/resolved properties)))
|
||||
(set! add-command/resolve-property-identifiers (fn [_ _ properties & _] (p/resolved properties)))
|
||||
(set! transport/invoke (fn [_ method _ args]
|
||||
(case method
|
||||
:thread-api/pull (let [[_ _ lookup] args]
|
||||
(cond
|
||||
(= lookup [:block/name "home"])
|
||||
{:db/id 50}
|
||||
|
||||
(and (vector? lookup) (= :db/ident (first lookup)))
|
||||
{}
|
||||
|
||||
:else {}))
|
||||
:thread-api/apply-outliner-ops
|
||||
(throw (ex-info "should not apply ops when property lookup fails"
|
||||
{:args args}))
|
||||
(throw (ex-info "unexpected invoke" {:method method :args args})))))
|
||||
(-> (p/let [result (commands/execute action {})]
|
||||
(is (= :error (:status result)))
|
||||
(is (= :property-not-found (get-in result [:error :code]))))
|
||||
(p/catch (fn [e]
|
||||
(is false (str "unexpected error: " e))))
|
||||
(p/finally (fn []
|
||||
(set! cli-server/list-graphs orig-list-graphs)
|
||||
(set! cli-server/ensure-server! orig-ensure-server!)
|
||||
(set! add-command/resolve-tags orig-resolve-tags)
|
||||
(set! add-command/resolve-properties orig-resolve-properties)
|
||||
(set! add-command/resolve-property-identifiers orig-resolve-property-identifiers)
|
||||
(set! transport/invoke orig-invoke)
|
||||
(done)))))))
|
||||
|
||||
(deftest test-execute-remove-tag-property
|
||||
(async done
|
||||
(let [ops* (atom [])
|
||||
@@ -1749,7 +1972,8 @@
|
||||
orig-resolve-properties add-command/resolve-properties
|
||||
orig-resolve-property-identifiers add-command/resolve-property-identifiers
|
||||
orig-invoke transport/invoke
|
||||
action {:type :update-block
|
||||
action {:type :upsert-block
|
||||
:mode :update
|
||||
:repo "demo"
|
||||
:id 1
|
||||
:target-id 2
|
||||
@@ -1765,8 +1989,8 @@
|
||||
(= tags [:tag/new]) [{:db/id 101}]
|
||||
(= tags [:tag/old]) [{:db/id 202}]
|
||||
:else nil))))
|
||||
(set! add-command/resolve-properties (fn [_ _ properties] (p/resolved properties)))
|
||||
(set! add-command/resolve-property-identifiers (fn [_ _ properties] (p/resolved properties)))
|
||||
(set! add-command/resolve-properties (fn [_ _ properties & _] (p/resolved properties)))
|
||||
(set! add-command/resolve-property-identifiers (fn [_ _ properties & _] (p/resolved properties)))
|
||||
(set! transport/invoke (fn [_ method _ args]
|
||||
(swap! calls* conj {:method method :args args})
|
||||
(case method
|
||||
|
||||
@@ -91,23 +91,23 @@
|
||||
result)))))
|
||||
|
||||
(deftest test-human-output-add-upsert-remove
|
||||
(testing "add block renders ids in two lines"
|
||||
(testing "upsert block renders ids in two lines"
|
||||
(let [result (format/format-result {:status :ok
|
||||
:command :add-block
|
||||
:command :upsert-block
|
||||
:context {:repo "demo-repo"
|
||||
:blocks ["a" "b"]}
|
||||
:data {:result [201 202]}}
|
||||
{:output-format nil})]
|
||||
(is (= "Added blocks:\n[201 202]" result))))
|
||||
(is (= "Upserted blocks:\n[201 202]" result))))
|
||||
|
||||
(testing "add page renders ids in two lines"
|
||||
(testing "upsert page renders ids in two lines"
|
||||
(let [result (format/format-result {:status :ok
|
||||
:command :add-page
|
||||
:command :upsert-page
|
||||
:context {:repo "demo-repo"
|
||||
:page "Home"}
|
||||
:data {:result [123]}}
|
||||
{:output-format nil})]
|
||||
(is (= "Added page:\n[123]" result))))
|
||||
(is (= "Upserted page:\n[123]" result))))
|
||||
|
||||
(testing "upsert tag renders ids in two lines"
|
||||
(let [result (format/format-result {:status :ok
|
||||
@@ -163,9 +163,9 @@
|
||||
{:output-format nil})]
|
||||
(is (= "Removed property: owner (repo: demo-repo)" result))))
|
||||
|
||||
(testing "update block renders a succinct success line"
|
||||
(testing "upsert block update mode renders a succinct success line"
|
||||
(let [result (format/format-result {:status :ok
|
||||
:command :update-block
|
||||
:command :upsert-block
|
||||
:context {:repo "demo-repo"
|
||||
:source "source-uuid"
|
||||
:target "target-uuid"
|
||||
@@ -175,17 +175,17 @@
|
||||
:remove-properties [:logseq.property/deadline]}
|
||||
:data {:result {:ok true}}}
|
||||
{:output-format nil})]
|
||||
(is (= "Updated block: source-uuid -> target-uuid (repo: demo-repo, tags:+1, properties:+1, remove-tags:+1, remove-properties:+1)" result))))
|
||||
(is (= "Upserted 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"
|
||||
(testing "upsert block update without move target renders a succinct success line"
|
||||
(let [result (format/format-result {:status :ok
|
||||
:command :update-block
|
||||
:command :upsert-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)))))
|
||||
(is (= "Upserted block: source-uuid (repo: demo-repo, tags:+1)" result)))))
|
||||
|
||||
(deftest test-human-output-graph-import-export
|
||||
(testing "graph export renders a succinct success line"
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
(p/let [cfg-path (node-path/join (node-helper/create-tmp-dir "cli") "cli.edn")
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :json}")
|
||||
_ (run-cli ["graph" "create" "--repo" "tags-graph"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "tags-graph" "add" "page" "--page" "Home"] data-dir cfg-path)]
|
||||
_ (run-cli ["--repo" "tags-graph" "upsert" "page" "--page" "Home"] data-dir cfg-path)]
|
||||
{:cfg-path cfg-path :repo "tags-graph"}))
|
||||
|
||||
(defn- stop-repo!
|
||||
@@ -269,7 +269,7 @@
|
||||
(-> (p/let [cfg-path (node-path/join (node-helper/create-tmp-dir "cli") "cli.edn")
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :json}")
|
||||
_ (run-cli ["graph" "create" "--repo" "content-graph"] data-dir cfg-path)
|
||||
add-page-result (run-cli ["--repo" "content-graph" "add" "page" "--page" "TestPage"] data-dir cfg-path)
|
||||
add-page-result (run-cli ["--repo" "content-graph" "upsert" "page" "--page" "TestPage"] data-dir cfg-path)
|
||||
add-page-payload (parse-json-output add-page-result)
|
||||
list-page-result (run-cli ["--repo" "content-graph" "list" "page"] data-dir cfg-path)
|
||||
list-page-payload (parse-json-output list-page-result)
|
||||
@@ -277,7 +277,7 @@
|
||||
list-tag-payload (parse-json-output list-tag-result)
|
||||
list-property-result (run-cli ["--repo" "content-graph" "list" "property"] data-dir cfg-path)
|
||||
list-property-payload (parse-json-output list-property-result)
|
||||
add-block-result (run-cli ["--repo" "content-graph" "add" "block" "--target-page-name" "TestPage" "--content" "Test block"] data-dir cfg-path)
|
||||
add-block-result (run-cli ["--repo" "content-graph" "upsert" "block" "--target-page" "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" "TestPage"] data-dir cfg-path)
|
||||
@@ -288,7 +288,8 @@
|
||||
stop-payload (parse-json-output stop-result)]
|
||||
(is (= 0 (:exit-code add-page-result)))
|
||||
(is (= "ok" (:status add-page-payload)))
|
||||
(is (= "ok" (:status add-block-payload)))
|
||||
(is (= "ok" (:status add-block-payload))
|
||||
(pr-str (:error add-block-payload)))
|
||||
(is (= "ok" (:status list-page-payload)))
|
||||
(is (vector? (get-in list-page-payload [:data :items])))
|
||||
(is (= "ok" (:status list-tag-payload)))
|
||||
@@ -306,14 +307,14 @@
|
||||
(is false (str "unexpected error: " e))
|
||||
(done)))))))
|
||||
|
||||
(deftest ^:long test-cli-add-page-json-output-returns-id
|
||||
(deftest ^:long test-cli-upsert-page-json-output-returns-id
|
||||
(async done
|
||||
(let [data-dir (node-helper/create-tmp-dir "db-worker-add-page-json-id")
|
||||
repo "add-page-json-id-graph"]
|
||||
(-> (p/let [cfg-path (node-path/join (node-helper/create-tmp-dir "cli") "cli.edn")
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :json}")
|
||||
_ (run-cli ["graph" "create" "--repo" repo] data-dir cfg-path)
|
||||
add-page-result (run-cli ["--repo" repo "add" "page" "--page" "Home"] data-dir cfg-path)
|
||||
add-page-result (run-cli ["--repo" repo "upsert" "page" "--page" "Home"] data-dir cfg-path)
|
||||
add-page-payload (parse-json-output add-page-result)
|
||||
page-ids (get-in add-page-payload [:data :result])
|
||||
page-id (first page-ids)
|
||||
@@ -335,7 +336,7 @@
|
||||
(is false (str "unexpected error: " e))
|
||||
(done)))))))
|
||||
|
||||
(deftest ^:long test-cli-add-block-json-output-returns-ids
|
||||
(deftest ^:long test-cli-upsert-block-create-json-output-returns-ids
|
||||
(async done
|
||||
(let [data-dir (node-helper/create-tmp-dir "db-worker-add-block-json-ids")
|
||||
repo "add-block-json-ids-graph"]
|
||||
@@ -345,10 +346,10 @@
|
||||
{:block/title "Sibling"}])
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :json}")
|
||||
_ (run-cli ["graph" "create" "--repo" repo] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" repo "add" "page" "--page" "Home"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" repo "upsert" "page" "--page" "Home"] data-dir cfg-path)
|
||||
add-block-result (run-cli ["--repo" repo
|
||||
"add" "block"
|
||||
"--target-page-name" "Home"
|
||||
"upsert" "block"
|
||||
"--target-page" "Home"
|
||||
"--blocks" blocks-edn]
|
||||
data-dir cfg-path)
|
||||
add-block-payload (parse-json-output add-block-result)
|
||||
@@ -361,8 +362,10 @@
|
||||
set)
|
||||
stop-result (run-cli ["server" "stop" "--repo" repo] data-dir cfg-path)
|
||||
stop-payload (parse-json-output stop-result)]
|
||||
(is (= 0 (:exit-code add-block-result)))
|
||||
(is (= "ok" (:status add-block-payload)))
|
||||
(is (= 0 (:exit-code add-block-result))
|
||||
(pr-str (:error add-block-payload)))
|
||||
(is (= "ok" (:status add-block-payload))
|
||||
(pr-str (:error add-block-payload)))
|
||||
(is (vector? block-ids))
|
||||
(is (= 3 (count block-ids)))
|
||||
(is (= 3 (count (distinct block-ids))))
|
||||
@@ -383,15 +386,15 @@
|
||||
_ (run-cli ["graph" "create" "--repo" repo] data-dir cfg-path)
|
||||
add-page-result (run-cli ["--repo" repo
|
||||
"--output" "edn"
|
||||
"add" "page"
|
||||
"upsert" "page"
|
||||
"--page" "Home"]
|
||||
data-dir cfg-path)
|
||||
add-page-payload (parse-edn-output add-page-result)
|
||||
page-ids (get-in add-page-payload [:data :result])
|
||||
add-block-result (run-cli ["--repo" repo
|
||||
"--output" "edn"
|
||||
"add" "block"
|
||||
"--target-page-name" "Home"
|
||||
"upsert" "block"
|
||||
"--target-page" "Home"
|
||||
"--content" "EDN block"]
|
||||
data-dir cfg-path)
|
||||
add-block-payload (parse-edn-output add-block-result)
|
||||
@@ -421,18 +424,18 @@
|
||||
(-> (p/let [cfg-path (node-path/join (node-helper/create-tmp-dir "cli") "cli.edn")
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :json}")
|
||||
_ (run-cli ["graph" "create" "--repo" repo] data-dir cfg-path)
|
||||
add-page-result (run-cli ["--repo" repo "add" "page" "--page" "ChainPage"] data-dir cfg-path)
|
||||
add-page-result (run-cli ["--repo" repo "upsert" "page" "--page" "ChainPage"] data-dir cfg-path)
|
||||
add-page-payload (parse-json-output add-page-result)
|
||||
page-id (first-result-id add-page-payload)
|
||||
add-block-result (run-cli ["--repo" repo
|
||||
"add" "block"
|
||||
"upsert" "block"
|
||||
"--target-id" (str page-id)
|
||||
"--content" "Chain block"]
|
||||
data-dir cfg-path)
|
||||
add-block-payload (parse-json-output add-block-result)
|
||||
block-id (first-result-id add-block-payload)
|
||||
update-result (run-cli ["--repo" repo
|
||||
"update"
|
||||
"upsert" "block"
|
||||
"--id" (str block-id)
|
||||
"--update-properties" "{:logseq.property/publishing-public? true}"]
|
||||
data-dir cfg-path)
|
||||
@@ -462,16 +465,96 @@
|
||||
(is false (str "unexpected error: " e))
|
||||
(done)))))))
|
||||
|
||||
(deftest ^:long test-cli-upsert-page-create-and-update-existing
|
||||
(async done
|
||||
(let [data-dir (node-helper/create-tmp-dir "db-worker-upsert-page-existing")
|
||||
repo "upsert-page-existing-graph"]
|
||||
(-> (p/let [cfg-path (node-path/join (node-helper/create-tmp-dir "cli") "cli.edn")
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :json}")
|
||||
_ (run-cli ["graph" "create" "--repo" repo] data-dir cfg-path)
|
||||
create-result (run-cli ["--repo" repo "upsert" "page" "--page" "Home"] data-dir cfg-path)
|
||||
create-payload (parse-json-output create-result)
|
||||
page-id (first-result-id create-payload)
|
||||
update-result (run-cli ["--repo" repo
|
||||
"upsert" "page"
|
||||
"--page" "Home"
|
||||
"--update-properties" "{:logseq.property/publishing-public? true}"]
|
||||
data-dir cfg-path)
|
||||
update-payload (parse-json-output update-result)
|
||||
update-id (first-result-id update-payload)
|
||||
property-after-update (query-property data-dir cfg-path repo "Home"
|
||||
":logseq.property/publishing-public?")
|
||||
remove-result (run-cli ["--repo" repo
|
||||
"upsert" "page"
|
||||
"--page" "Home"
|
||||
"--remove-properties" "[:logseq.property/publishing-public?]"]
|
||||
data-dir cfg-path)
|
||||
remove-payload (parse-json-output remove-result)
|
||||
remove-id (first-result-id remove-payload)
|
||||
property-after-remove (query-property data-dir cfg-path repo "Home"
|
||||
":logseq.property/publishing-public?")
|
||||
stop-result (run-cli ["server" "stop" "--repo" repo] data-dir cfg-path)
|
||||
stop-payload (parse-json-output stop-result)]
|
||||
(is (= 0 (:exit-code create-result)))
|
||||
(is (= "ok" (:status create-payload)))
|
||||
(is (number? page-id))
|
||||
(is (= 0 (:exit-code update-result)))
|
||||
(is (= "ok" (:status update-payload)))
|
||||
(is (= page-id update-id))
|
||||
(is (= true property-after-update))
|
||||
(is (= 0 (:exit-code remove-result)))
|
||||
(is (= "ok" (:status remove-payload)))
|
||||
(is (= page-id remove-id))
|
||||
(is (nil? property-after-remove))
|
||||
(is (= "ok" (:status stop-payload)))
|
||||
(done))
|
||||
(p/catch (fn [e]
|
||||
(is false (str "unexpected error: " e))
|
||||
(done)))))))
|
||||
|
||||
(deftest ^:long test-cli-upsert-page-errors-on-missing-tags-properties
|
||||
(async done
|
||||
(let [data-dir (node-helper/create-tmp-dir "db-worker-upsert-page-missing")
|
||||
repo "upsert-page-missing-graph"]
|
||||
(-> (p/let [cfg-path (node-path/join (node-helper/create-tmp-dir "cli") "cli.edn")
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :json}")
|
||||
_ (run-cli ["graph" "create" "--repo" repo] data-dir cfg-path)
|
||||
missing-tag-result (run-cli ["--repo" repo
|
||||
"upsert" "page"
|
||||
"--page" "Home"
|
||||
"--update-tags" "[\"MissingTag\"]"]
|
||||
data-dir cfg-path)
|
||||
missing-tag-payload (parse-json-output missing-tag-result)
|
||||
missing-property-result (run-cli ["--repo" repo
|
||||
"upsert" "page"
|
||||
"--page" "Home"
|
||||
"--update-properties" "{:not/a 1}"]
|
||||
data-dir cfg-path)
|
||||
missing-property-payload (parse-json-output missing-property-result)
|
||||
stop-result (run-cli ["server" "stop" "--repo" repo] data-dir cfg-path)
|
||||
stop-payload (parse-json-output stop-result)]
|
||||
(is (= 1 (:exit-code missing-tag-result)))
|
||||
(is (= "error" (:status missing-tag-payload)))
|
||||
(is (= :tag-not-found (keyword (get-in missing-tag-payload [:error :code]))))
|
||||
(is (= 1 (:exit-code missing-property-result)))
|
||||
(is (= "error" (:status missing-property-payload)))
|
||||
(is (= :invalid-options (keyword (get-in missing-property-payload [:error :code]))))
|
||||
(is (= "ok" (:status stop-payload)))
|
||||
(done))
|
||||
(p/catch (fn [e]
|
||||
(is false (str "unexpected error: " e))
|
||||
(done)))))))
|
||||
|
||||
(deftest ^:long test-cli-add-block-rewrites-page-ref
|
||||
(async done
|
||||
(let [data-dir (node-helper/create-tmp-dir "db-worker-ref-rewrite")]
|
||||
(-> (p/let [cfg-path (node-path/join (node-helper/create-tmp-dir "cli") "cli.edn")
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :json}")
|
||||
_ (run-cli ["graph" "create" "--repo" "ref-rewrite-graph"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "ref-rewrite-graph" "add" "page" "--page" "Home"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "ref-rewrite-graph" "upsert" "page" "--page" "Home"] data-dir cfg-path)
|
||||
add-block-result (run-cli ["--repo" "ref-rewrite-graph"
|
||||
"add" "block"
|
||||
"--target-page-name" "Home"
|
||||
"upsert" "block"
|
||||
"--target-page" "Home"
|
||||
"--content" "See [[New Page]]"]
|
||||
data-dir cfg-path)
|
||||
add-block-payload (parse-json-output-safe add-block-result "add-block")
|
||||
@@ -513,10 +596,10 @@
|
||||
(-> (p/let [cfg-path (node-path/join (node-helper/create-tmp-dir "cli") "cli.edn")
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :json}")
|
||||
_ (run-cli ["graph" "create" "--repo" "uuid-ref-graph"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "uuid-ref-graph" "add" "page" "--page" "Home"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "uuid-ref-graph" "upsert" "page" "--page" "Home"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "uuid-ref-graph"
|
||||
"add" "block"
|
||||
"--target-page-name" "Home"
|
||||
"upsert" "block"
|
||||
"--target-page" "Home"
|
||||
"--content" "Target block"]
|
||||
data-dir cfg-path)
|
||||
_ (p/delay 100)
|
||||
@@ -525,8 +608,8 @@
|
||||
(pr-str ["Target block"]))
|
||||
target-uuid (first (first (get-in target-query-payload [:data :result])))
|
||||
add-block-result (run-cli ["--repo" "uuid-ref-graph"
|
||||
"add" "block"
|
||||
"--target-page-name" "Home"
|
||||
"upsert" "block"
|
||||
"--target-page" "Home"
|
||||
"--content" (str "See [[" target-uuid "]]")]
|
||||
data-dir cfg-path)
|
||||
add-block-payload (parse-json-output add-block-result)
|
||||
@@ -564,11 +647,11 @@
|
||||
(-> (p/let [cfg-path (node-path/join (node-helper/create-tmp-dir "cli") "cli.edn")
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :json}")
|
||||
_ (run-cli ["graph" "create" "--repo" "missing-uuid-ref-graph"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "missing-uuid-ref-graph" "add" "page" "--page" "Home"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "missing-uuid-ref-graph" "upsert" "page" "--page" "Home"] data-dir cfg-path)
|
||||
missing-uuid (str (random-uuid))
|
||||
add-block-result (run-cli ["--repo" "missing-uuid-ref-graph"
|
||||
"add" "block"
|
||||
"--target-page-name" "Home"
|
||||
"upsert" "block"
|
||||
"--target-page" "Home"
|
||||
"--content" (str "See [[" missing-uuid "]]")]
|
||||
data-dir cfg-path)
|
||||
add-block-payload (parse-json-output add-block-result)
|
||||
@@ -588,23 +671,23 @@
|
||||
(let [data-dir (node-helper/create-tmp-dir "db-worker-tags")]
|
||||
(-> (p/let [{:keys [cfg-path repo]} (setup-tags-graph data-dir)
|
||||
add-page-result (run-cli ["--repo" "tags-graph"
|
||||
"add" "page"
|
||||
"upsert" "page"
|
||||
"--page" "TaggedPage"
|
||||
"--tags" "[\"Quote\"]"
|
||||
"--properties" "{:logseq.property/publishing-public? true}"]
|
||||
data-dir cfg-path)
|
||||
add-page-payload (parse-json-output add-page-result)
|
||||
add-block-result (run-cli ["--repo" "tags-graph"
|
||||
"add" "block"
|
||||
"--target-page-name" "Home"
|
||||
"upsert" "block"
|
||||
"--target-page" "Home"
|
||||
"--content" "Tagged block"
|
||||
"--tags" "[\"Quote\"]"
|
||||
"--properties" "{:logseq.property/deadline \"2026-01-25T12:00:00Z\"}"]
|
||||
data-dir cfg-path)
|
||||
add-block-payload (parse-json-output add-block-result)
|
||||
add-block-ident-result (run-cli ["--repo" "tags-graph"
|
||||
"add" "block"
|
||||
"--target-page-name" "Home"
|
||||
"upsert" "block"
|
||||
"--target-page" "Home"
|
||||
"--content" "Tagged block ident"
|
||||
"--tags" "[:logseq.class/Quote-block]"]
|
||||
data-dir cfg-path)
|
||||
@@ -612,14 +695,14 @@
|
||||
deadline-prop-title (get-in db-property/built-in-properties [:logseq.property/deadline :title])
|
||||
publishing-prop-title (get-in db-property/built-in-properties [:logseq.property/publishing-public? :title])
|
||||
add-page-title-result (run-cli ["--repo" "tags-graph"
|
||||
"add" "page"
|
||||
"upsert" "page"
|
||||
"--page" "TaggedPageTitle"
|
||||
"--properties" (str "{\"" publishing-prop-title "\" true}")]
|
||||
data-dir cfg-path)
|
||||
add-page-title-payload (parse-json-output add-page-title-result)
|
||||
add-block-title-result (run-cli ["--repo" "tags-graph"
|
||||
"add" "block"
|
||||
"--target-page-name" "Home"
|
||||
"upsert" "block"
|
||||
"--target-page" "Home"
|
||||
"--content" "Tagged block title"
|
||||
"--properties" (str "{\"" deadline-prop-title "\" \"2026-01-25T12:00:00Z\"}")]
|
||||
data-dir cfg-path)
|
||||
@@ -670,15 +753,15 @@
|
||||
deadline-id (find-item-id (get-in list-property-payload [:data :items]) deadline-title)
|
||||
publishing-id (find-item-id (get-in list-property-payload [:data :items]) publishing-title)
|
||||
add-page-id-result (run-cli ["--repo" repo
|
||||
"add" "page"
|
||||
"upsert" "page"
|
||||
"--page" "TaggedPageId"
|
||||
"--tags" (pr-str [quote-tag-id])
|
||||
"--properties" (pr-str {publishing-id true})]
|
||||
data-dir cfg-path)
|
||||
add-page-id-payload (parse-json-output add-page-id-result)
|
||||
add-block-id-result (run-cli ["--repo" repo
|
||||
"add" "block"
|
||||
"--target-page-name" "Home"
|
||||
"upsert" "block"
|
||||
"--target-page" "Home"
|
||||
"--content" "Tagged block id"
|
||||
"--tags" (pr-str [quote-tag-id])
|
||||
"--properties" (pr-str {deadline-id "2026-01-25T12:00:00Z"})]
|
||||
@@ -793,8 +876,8 @@
|
||||
tag-a-name "Quote"
|
||||
tag-b-name "Math"
|
||||
add-block-result (run-cli ["--repo" repo
|
||||
"add" "block"
|
||||
"--target-page-name" "Home"
|
||||
"upsert" "block"
|
||||
"--target-page" "Home"
|
||||
"--content" "Update block"
|
||||
"--tags" "[:logseq.class/Quote-block]"
|
||||
"--properties" "{:logseq.property/publishing-public? true}"]
|
||||
@@ -806,7 +889,7 @@
|
||||
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"
|
||||
"upsert" "block"
|
||||
"--id" (str block-id)
|
||||
"--update-tags" "[:logseq.class/Math-block]"
|
||||
"--remove-tags" "[:logseq.class/Quote-block]"
|
||||
@@ -828,6 +911,63 @@
|
||||
(is false (str "unexpected error: " e))
|
||||
(done)))))))
|
||||
|
||||
(deftest ^:long test-cli-upsert-block-update-custom-property
|
||||
(async done
|
||||
(let [data-dir (node-helper/create-tmp-dir "db-worker-upsert-block-custom-property")]
|
||||
(-> (p/let [{:keys [cfg-path repo]} (setup-tags-graph data-dir)
|
||||
upsert-property-result (run-cli ["--repo" repo
|
||||
"upsert" "property"
|
||||
"--name" "owner"
|
||||
"--type" "default"]
|
||||
data-dir cfg-path)
|
||||
upsert-property-payload (parse-json-output upsert-property-result)
|
||||
add-block-result (run-cli ["--repo" repo
|
||||
"upsert" "block"
|
||||
"--target-page" "Home"
|
||||
"--content" "Block with custom property"]
|
||||
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]) "Block with custom property")
|
||||
block-id (node-id block-node)
|
||||
update-result (run-cli ["--repo" repo
|
||||
"upsert" "block"
|
||||
"--id" (str block-id)
|
||||
"--update-properties" "{:user.property/owner \"alice\"}"]
|
||||
data-dir cfg-path)
|
||||
update-payload (parse-json-output update-result)
|
||||
_ (p/delay 100)
|
||||
property-after-update (query-property data-dir cfg-path repo "Block with custom property"
|
||||
":user.property/owner")
|
||||
remove-result (run-cli ["--repo" repo
|
||||
"upsert" "block"
|
||||
"--id" (str block-id)
|
||||
"--remove-properties" "[:user.property/owner]"]
|
||||
data-dir cfg-path)
|
||||
remove-payload (parse-json-output remove-result)
|
||||
_ (p/delay 100)
|
||||
property-after-remove (query-property data-dir cfg-path repo "Block with custom property"
|
||||
":user.property/owner")
|
||||
stop-payload (stop-repo! data-dir cfg-path repo)]
|
||||
(is (= 0 (:exit-code upsert-property-result)))
|
||||
(is (= "ok" (:status upsert-property-payload)))
|
||||
(is (= 0 (:exit-code add-block-result)))
|
||||
(is (= "ok" (:status add-block-payload)))
|
||||
(is (some? block-id))
|
||||
(is (= 0 (:exit-code update-result)))
|
||||
(is (= "ok" (:status update-payload)))
|
||||
(is (some? property-after-update))
|
||||
(is (= 0 (:exit-code remove-result)))
|
||||
(is (= "ok" (:status remove-payload)))
|
||||
(is (nil? property-after-remove))
|
||||
(is (= "ok" (:status stop-payload)))
|
||||
(done))
|
||||
(p/catch (fn [e]
|
||||
(is false (str "unexpected error: " e))
|
||||
(done)))))))
|
||||
|
||||
(deftest ^:long test-cli-add-tags-rejects-missing-tag
|
||||
(async done
|
||||
(let [data-dir (node-helper/create-tmp-dir "db-worker-tags-missing")]
|
||||
@@ -835,8 +975,8 @@
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :json}")
|
||||
_ (run-cli ["graph" "create" "--repo" "tags-missing-graph"] data-dir cfg-path)
|
||||
add-block-result (run-cli ["--repo" "tags-missing-graph"
|
||||
"add" "block"
|
||||
"--target-page-name" "Home"
|
||||
"upsert" "block"
|
||||
"--target-page" "Home"
|
||||
"--content" "Block with missing tag"
|
||||
"--tags" "[\"MissingTag\"]"]
|
||||
data-dir cfg-path)
|
||||
@@ -864,7 +1004,7 @@
|
||||
repo "upsert-tag-create-graph"
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :json}")
|
||||
_ (run-cli ["graph" "create" "--repo" repo] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" repo "add" "page" "--page" "Home"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" repo "upsert" "page" "--page" "Home"] data-dir cfg-path)
|
||||
upsert-tag-result (run-cli ["--repo" repo
|
||||
"upsert" "tag"
|
||||
"--name" "CliQuote"]
|
||||
@@ -876,8 +1016,8 @@
|
||||
(map #(or (:block/title %) (:title %) (:name %)))
|
||||
set)
|
||||
add-block-result (run-cli ["--repo" repo
|
||||
"add" "block"
|
||||
"--target-page-name" "Home"
|
||||
"upsert" "block"
|
||||
"--target-page" "Home"
|
||||
"--content" "Tagged by upsert tag"
|
||||
"--tags" "[\"CliQuote\"]"]
|
||||
data-dir cfg-path)
|
||||
@@ -908,7 +1048,7 @@
|
||||
repo "upsert-tag-conflict-graph"
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :json}")
|
||||
_ (run-cli ["graph" "create" "--repo" repo] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" repo "add" "page" "--page" "ConflictPage"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" repo "upsert" "page" "--page" "ConflictPage"] data-dir cfg-path)
|
||||
upsert-tag-result (run-cli ["--repo" repo
|
||||
"upsert" "tag"
|
||||
"--name" "ConflictPage"]
|
||||
@@ -1037,13 +1177,13 @@
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :json}")
|
||||
create-result (run-cli ["graph" "create" "--repo" "query-graph"] data-dir cfg-path)
|
||||
create-payload (parse-json-output create-result)
|
||||
_ (run-cli ["--repo" "query-graph" "add" "page" "--page" "QueryPage"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "query-graph" "add" "block"
|
||||
"--target-page-name" "QueryPage"
|
||||
_ (run-cli ["--repo" "query-graph" "upsert" "page" "--page" "QueryPage"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "query-graph" "upsert" "block"
|
||||
"--target-page" "QueryPage"
|
||||
"--content" "Query block"]
|
||||
data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "query-graph" "add" "block"
|
||||
"--target-page-name" "QueryPage"
|
||||
_ (run-cli ["--repo" "query-graph" "upsert" "block"
|
||||
"--target-page" "QueryPage"
|
||||
"--content" "Query block"]
|
||||
data-dir cfg-path)
|
||||
_ (p/delay 100)
|
||||
@@ -1074,22 +1214,22 @@
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :json}")
|
||||
create-result (run-cli ["graph" "create" "--repo" "task-query-graph"] data-dir cfg-path)
|
||||
create-payload (parse-json-output create-result)
|
||||
_ (run-cli ["--repo" "task-query-graph" "add" "page" "--page" "Tasks"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "task-query-graph" "upsert" "page" "--page" "Tasks"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "task-query-graph"
|
||||
"add" "block"
|
||||
"--target-page-name" "Tasks"
|
||||
"upsert" "block"
|
||||
"--target-page" "Tasks"
|
||||
"--content" "Task one"
|
||||
"--status" "doing"]
|
||||
data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "task-query-graph"
|
||||
"add" "block"
|
||||
"--target-page-name" "Tasks"
|
||||
"upsert" "block"
|
||||
"--target-page" "Tasks"
|
||||
"--content" "Task two"
|
||||
"--status" "doing"]
|
||||
data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "task-query-graph"
|
||||
"add" "block"
|
||||
"--target-page-name" "Tasks"
|
||||
"upsert" "block"
|
||||
"--target-page" "Tasks"
|
||||
"--content" "Task three"
|
||||
"--status" "todo"]
|
||||
data-dir cfg-path)
|
||||
@@ -1194,9 +1334,9 @@
|
||||
(-> (p/let [cfg-path (node-path/join (node-helper/create-tmp-dir "cli") "cli.edn")
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :json}")
|
||||
_ (run-cli ["graph" "create" "--repo" "recent-updated-graph"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "recent-updated-graph" "add" "page" "--page" "RecentPage"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "recent-updated-graph" "add" "block"
|
||||
"--target-page-name" "RecentPage"
|
||||
_ (run-cli ["--repo" "recent-updated-graph" "upsert" "page" "--page" "RecentPage"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "recent-updated-graph" "upsert" "block"
|
||||
"--target-page" "RecentPage"
|
||||
"--content" "Recent block"]
|
||||
data-dir cfg-path)
|
||||
_ (p/delay 100)
|
||||
@@ -1291,20 +1431,20 @@
|
||||
(-> (p/let [cfg-path (node-path/join (node-helper/create-tmp-dir "cli") "cli.edn")
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :json}")
|
||||
_ (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)
|
||||
_ (run-cli ["--repo" "nested-refs" "upsert" "page" "--page" "NestedPage"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "nested-refs" "upsert" "block" "--target-page" "NestedPage" "--content" "Inner"] data-dir cfg-path)
|
||||
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"
|
||||
_ (run-cli ["--repo" "nested-refs" "upsert" "block" "--target-page" "NestedPage"
|
||||
"--content" middle-content] data-dir cfg-path)
|
||||
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"
|
||||
_ (run-cli ["--repo" "nested-refs" "upsert" "block" "--target-page" "NestedPage"
|
||||
"--content" (str "Outer [[" middle-uuid "]]")] 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)
|
||||
@@ -1327,15 +1467,15 @@
|
||||
(-> (p/let [cfg-path (node-path/join (node-helper/create-tmp-dir "cli") "cli.edn")
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :json}")
|
||||
_ (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)
|
||||
_ (run-cli ["--repo" "linked-refs-graph" "upsert" "page" "--page" "TargetPage"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "linked-refs-graph" "upsert" "page" "--page" "SourcePage"] data-dir cfg-path)
|
||||
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)
|
||||
_ (run-cli ["--repo" "linked-refs-graph" "upsert" "block" "--target-page" "SourcePage" "--content" ref-content] 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)
|
||||
@@ -1365,22 +1505,22 @@
|
||||
(is false (str "unexpected error: " e))
|
||||
(done)))))))
|
||||
|
||||
(deftest ^:long test-cli-update-block-move
|
||||
(deftest ^:long test-cli-upsert-block-update-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")
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :json}")
|
||||
_ (run-cli ["graph" "create" "--repo" "move-graph"] data-dir cfg-path)
|
||||
_ (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)
|
||||
_ (run-cli ["--repo" "move-graph" "upsert" "page" "--page" "SourcePage"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "move-graph" "upsert" "page" "--page" "TargetPage"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "move-graph" "upsert" "block" "--target-page" "SourcePage" "--content" "Parent Block"] 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)
|
||||
_ (run-cli ["--repo" "move-graph" "upsert" "block" "--target-id" (str parent-id) "--content" "Child Block"] data-dir cfg-path)
|
||||
update-result (run-cli ["--repo" "move-graph" "upsert" "block" "--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)
|
||||
@@ -1404,15 +1544,15 @@
|
||||
(-> (p/let [cfg-path (node-path/join (node-helper/create-tmp-dir "cli") "cli.edn")
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :json}")
|
||||
_ (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)
|
||||
_ (run-cli ["--repo" "add-pos-graph" "upsert" "page" "--page" "PosPage"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "add-pos-graph" "upsert" "block" "--target-page" "PosPage" "--content" "Parent"] 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)
|
||||
_ (run-cli ["--repo" "add-pos-graph" "upsert" "block" "--target-id" (str parent-id) "--pos" "first-child" "--content" "First"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "add-pos-graph" "upsert" "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")
|
||||
@@ -1452,7 +1592,7 @@
|
||||
(-> (p/let [cfg-path (node-path/join (node-helper/create-tmp-dir "cli") "cli.edn")
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :json}")
|
||||
_ (run-cli ["graph" "create" "--repo" "list-id-graph"] data-dir cfg-path)
|
||||
_ (run-cli ["add" "page" "--page" "TestPage"] data-dir cfg-path)
|
||||
_ (run-cli ["upsert" "page" "--page" "TestPage"] data-dir cfg-path)
|
||||
list-page-result (run-cli ["list" "page"] data-dir cfg-path)
|
||||
list-page-payload (parse-json-output list-page-result)
|
||||
list-tag-result (run-cli ["list" "tag"] data-dir cfg-path)
|
||||
@@ -1479,7 +1619,7 @@
|
||||
(-> (p/let [cfg-path (node-path/join (node-helper/create-tmp-dir "cli") "cli.edn")
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :json}")
|
||||
_ (run-cli ["graph" "create" "--repo" "human-list-graph"] data-dir cfg-path)
|
||||
_ (run-cli ["add" "page" "--page" "TestPage"] data-dir cfg-path)
|
||||
_ (run-cli ["upsert" "page" "--page" "TestPage"] data-dir cfg-path)
|
||||
list-page-result (run-cli ["list" "page" "--output" "human"] data-dir cfg-path)
|
||||
output (:output list-page-result)]
|
||||
(is (= 0 (:exit-code list-page-result)))
|
||||
@@ -1497,7 +1637,7 @@
|
||||
(-> (p/let [cfg-path (node-path/join (node-helper/create-tmp-dir "cli") "cli.edn")
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :json}")
|
||||
_ (run-cli ["graph" "create" "--repo" "show-page-block-graph"] data-dir cfg-path)
|
||||
_ (run-cli ["add" "page" "--page" "TestPage"] data-dir cfg-path)
|
||||
_ (run-cli ["upsert" "page" "--page" "TestPage"] data-dir cfg-path)
|
||||
list-page-result (run-cli ["list" "page" "--expand"] data-dir cfg-path)
|
||||
list-page-payload (parse-json-output list-page-result)
|
||||
page-item (some (fn [item]
|
||||
@@ -1536,14 +1676,14 @@
|
||||
(-> (p/let [cfg-path (node-path/join (node-helper/create-tmp-dir "cli") "cli.edn")
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :json}")
|
||||
_ (run-cli ["graph" "create" "--repo" "show-multi-id-graph"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "show-multi-id-graph" "add" "page" "--page" "MultiPage"]
|
||||
_ (run-cli ["--repo" "show-multi-id-graph" "upsert" "page" "--page" "MultiPage"]
|
||||
data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "show-multi-id-graph" "add" "block"
|
||||
"--target-page-name" "MultiPage"
|
||||
_ (run-cli ["--repo" "show-multi-id-graph" "upsert" "block"
|
||||
"--target-page" "MultiPage"
|
||||
"--content" "Multi show one"]
|
||||
data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "show-multi-id-graph" "add" "block"
|
||||
"--target-page-name" "MultiPage"
|
||||
_ (run-cli ["--repo" "show-multi-id-graph" "upsert" "block"
|
||||
"--target-page" "MultiPage"
|
||||
"--content" "Multi show two"]
|
||||
data-dir cfg-path)
|
||||
_ (p/delay 100)
|
||||
@@ -1608,10 +1748,10 @@
|
||||
(-> (p/let [cfg-path (node-path/join (node-helper/create-tmp-dir "cli") "cli.edn")
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :json}")
|
||||
_ (run-cli ["graph" "create" "--repo" "show-multi-id-contained-graph"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "show-multi-id-contained-graph" "add" "page" "--page" "ParentPage"]
|
||||
_ (run-cli ["--repo" "show-multi-id-contained-graph" "upsert" "page" "--page" "ParentPage"]
|
||||
data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "show-multi-id-contained-graph" "add" "block"
|
||||
"--target-page-name" "ParentPage"
|
||||
_ (run-cli ["--repo" "show-multi-id-contained-graph" "upsert" "block"
|
||||
"--target-page" "ParentPage"
|
||||
"--content" "Parent Block"]
|
||||
data-dir cfg-path)
|
||||
parent-query (run-cli ["--repo" "show-multi-id-contained-graph" "query"
|
||||
@@ -1620,7 +1760,7 @@
|
||||
data-dir cfg-path)
|
||||
parent-payload (parse-json-output parent-query)
|
||||
parent-id (get-in parent-payload [:data :result])
|
||||
_ (run-cli ["--repo" "show-multi-id-contained-graph" "add" "block"
|
||||
_ (run-cli ["--repo" "show-multi-id-contained-graph" "upsert" "block"
|
||||
"--target-id" (str parent-id)
|
||||
"--content" "Child Block"]
|
||||
data-dir cfg-path)
|
||||
@@ -1662,14 +1802,14 @@
|
||||
(-> (p/let [cfg-path (node-path/join (node-helper/create-tmp-dir "cli") "cli.edn")
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :json}")
|
||||
_ (run-cli ["graph" "create" "--repo" "query-pipe-graph"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "query-pipe-graph" "add" "page" "--page" "PipePage"]
|
||||
_ (run-cli ["--repo" "query-pipe-graph" "upsert" "page" "--page" "PipePage"]
|
||||
data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "query-pipe-graph" "add" "block"
|
||||
"--target-page-name" "PipePage"
|
||||
_ (run-cli ["--repo" "query-pipe-graph" "upsert" "block"
|
||||
"--target-page" "PipePage"
|
||||
"--content" "Pipe One"]
|
||||
data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "query-pipe-graph" "add" "block"
|
||||
"--target-page-name" "PipePage"
|
||||
_ (run-cli ["--repo" "query-pipe-graph" "upsert" "block"
|
||||
"--target-page" "PipePage"
|
||||
"--content" "Pipe Two"]
|
||||
data-dir cfg-path)
|
||||
_ (p/delay 100)
|
||||
@@ -1736,14 +1876,14 @@
|
||||
(-> (p/let [cfg-path (node-path/join (node-helper/create-tmp-dir "cli") "cli.edn")
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :json}")
|
||||
_ (run-cli ["graph" "create" "--repo" "query-stdin-graph"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "query-stdin-graph" "add" "page" "--page" "PipePage"]
|
||||
_ (run-cli ["--repo" "query-stdin-graph" "upsert" "page" "--page" "PipePage"]
|
||||
data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "query-stdin-graph" "add" "block"
|
||||
"--target-page-name" "PipePage"
|
||||
_ (run-cli ["--repo" "query-stdin-graph" "upsert" "block"
|
||||
"--target-page" "PipePage"
|
||||
"--content" "Pipe One"]
|
||||
data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "query-stdin-graph" "add" "block"
|
||||
"--target-page-name" "PipePage"
|
||||
_ (run-cli ["--repo" "query-stdin-graph" "upsert" "block"
|
||||
"--target-page" "PipePage"
|
||||
"--content" "Pipe Two"]
|
||||
data-dir cfg-path)
|
||||
_ (p/delay 100)
|
||||
@@ -1798,8 +1938,8 @@
|
||||
(-> (p/let [cfg-path (node-path/join (node-helper/create-tmp-dir "cli") "cli.edn")
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :json}")
|
||||
_ (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)
|
||||
_ (run-cli ["--repo" "linked-refs-graph" "upsert" "page" "--page" "TargetPage"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" "linked-refs-graph" "upsert" "page" "--page" "SourcePage"] data-dir cfg-path)
|
||||
list-page-result (run-cli ["--repo" "linked-refs-graph" "list" "page" "--expand"]
|
||||
data-dir cfg-path)
|
||||
list-page-payload (parse-json-output list-page-result)
|
||||
@@ -1809,7 +1949,7 @@
|
||||
(get-in list-page-payload [:data :items]))
|
||||
page-id (or (:db/id page-item) (:id page-item))
|
||||
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"
|
||||
_ (run-cli ["--repo" "linked-refs-graph" "upsert" "block" "--target-page" "SourcePage"
|
||||
"--blocks" blocks-edn] data-dir cfg-path)
|
||||
show-result (run-cli ["--repo" "linked-refs-graph" "show" "--page" "TargetPage"]
|
||||
data-dir cfg-path)
|
||||
@@ -1842,8 +1982,8 @@
|
||||
import-graph "import-edn-graph"
|
||||
export-path (node-path/join (node-helper/create-tmp-dir "exports") "graph.edn")
|
||||
_ (run-cli ["graph" "create" "--repo" export-graph] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" export-graph "add" "page" "--page" "ExportPage"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" export-graph "add" "block" "--target-page-name" "ExportPage" "--content" "Export content"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" export-graph "upsert" "page" "--page" "ExportPage"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" export-graph "upsert" "block" "--target-page" "ExportPage" "--content" "Export content"] data-dir cfg-path)
|
||||
export-result (run-cli ["--repo" export-graph
|
||||
"graph" "export"
|
||||
"--type" "edn"
|
||||
@@ -1881,8 +2021,8 @@
|
||||
import-graph "import-sqlite-graph"
|
||||
export-path (node-path/join (node-helper/create-tmp-dir "exports") "graph.sqlite")
|
||||
_ (run-cli ["graph" "create" "--repo" export-graph] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" export-graph "add" "page" "--page" "SQLiteExportPage"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" export-graph "add" "block" "--target-page-name" "SQLiteExportPage" "--content" "SQLite export content"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" export-graph "upsert" "page" "--page" "SQLiteExportPage"] data-dir cfg-path)
|
||||
_ (run-cli ["--repo" export-graph "upsert" "block" "--target-page" "SQLiteExportPage" "--content" "SQLite export content"] data-dir cfg-path)
|
||||
export-result (run-cli ["--repo" export-graph
|
||||
"graph" "export"
|
||||
"--type" "sqlite"
|
||||
|
||||
Reference in New Issue
Block a user