OCaml CLI and library to the Karakeep bookmarking app
1(*---------------------------------------------------------------------------
2 Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved.
3 SPDX-License-Identifier: ISC
4 ---------------------------------------------------------------------------*)
5
6open Karakeep
7
8let print_bookmark bookmark =
9 let title = bookmark_title bookmark in
10 let url =
11 match bookmark.content with
12 | Link lc -> lc.url
13 | Text tc -> Option.value tc.source_url ~default:"(text content)"
14 | Asset ac -> Option.value ac.source_url ~default:"(asset content)"
15 | Unknown -> "(unknown content)"
16 in
17 let tags_str =
18 String.concat ", "
19 (List.map (fun (tag : bookmark_tag) -> tag.name) bookmark.tags)
20 in
21 Printf.printf "- %s\n URL: %s\n Created: %s\n Tags: %s\n---\n\n" title url
22 (Ptime.to_rfc3339 bookmark.created_at)
23 tags_str
24
25let () =
26 (* Suppress verbose TLS/HTTP logging *)
27 Requests.Cmd.setup_log_sources (Some Logs.Warning);
28
29 (* Load API key from file *)
30 let api_key =
31 try
32 let ic = open_in ".karakeep-api" in
33 let key = input_line ic in
34 close_in ic;
35 String.trim key
36 with _ ->
37 Printf.eprintf "Error: Could not load API key from .karakeep-api file\n";
38 exit 1
39 in
40
41 (* Test configuration *)
42 let base_url = "https://hoard.recoil.org" in
43
44 Eio_main.run @@ fun env ->
45 Eio.Switch.run @@ fun sw ->
46
47 let client = Karakeep.create ~sw ~env ~base_url ~api_key in
48
49 (* Test 1: fetch_bookmarks - get a single page with pagination info *)
50 Printf.printf "=== Test 1: fetch_bookmarks (paginated) ===\n";
51 (try
52 let response = fetch_bookmarks client ~limit:3 () in
53 Printf.printf "Found bookmarks, showing %d (page 1)\n"
54 (List.length response.bookmarks);
55 Printf.printf "Next cursor: %s\n\n"
56 (match response.next_cursor with Some c -> c | None -> "none");
57 List.iter print_bookmark response.bookmarks;
58
59 (* Test 2: fetch_all_bookmarks - get multiple pages automatically *)
60 Printf.printf "=== Test 2: fetch_all_bookmarks (with limit) ===\n";
61 let all_bookmarks = fetch_all_bookmarks client ~page_size:2 ~max_pages:2 () in
62 Printf.printf "Fetched %d bookmarks from up to 2 pages\n\n"
63 (List.length all_bookmarks);
64
65 List.iter print_bookmark
66 (List.fold_left
67 (fun acc x -> if List.length acc < 4 then acc @ [ x ] else acc)
68 [] all_bookmarks);
69 Printf.printf "... and %d more bookmarks\n\n"
70 (max 0 (List.length all_bookmarks - 4));
71
72 (* Test 3: fetch_bookmark_details - get a specific bookmark *)
73 (match response.bookmarks with
74 | first_bookmark :: _ ->
75 Printf.printf "=== Test 3: fetch_bookmark_details ===\n";
76 Printf.printf "Fetching details for bookmark ID: %s\n\n"
77 first_bookmark.id;
78
79 (try
80 let bookmark = fetch_bookmark_details client first_bookmark.id in
81 print_bookmark bookmark
82 with e ->
83 Printf.printf "Error fetching bookmark details: %s\n" (Printexc.to_string e))
84 | [] ->
85 Printf.printf "No bookmarks found to test fetch_bookmark_details\n")
86 with e ->
87 Printf.printf "Error in basic tests: %s\n" (Printexc.to_string e);
88 Printf.printf "Skipping remaining tests due to API error.\n")