karakeep-python-api 1.2.4__tar.gz → 1.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (18) hide show
  1. {karakeep_python_api-1.2.4/karakeep_python_api.egg-info → karakeep_python_api-1.3.0}/PKG-INFO +1 -1
  2. {karakeep_python_api-1.2.4 → karakeep_python_api-1.3.0}/karakeep_python_api/datatypes.py +26 -16
  3. {karakeep_python_api-1.2.4 → karakeep_python_api-1.3.0}/karakeep_python_api/karakeep_api.py +97 -27
  4. {karakeep_python_api-1.2.4 → karakeep_python_api-1.3.0}/karakeep_python_api/openapi_reference.json +237 -46
  5. {karakeep_python_api-1.2.4 → karakeep_python_api-1.3.0/karakeep_python_api.egg-info}/PKG-INFO +1 -1
  6. {karakeep_python_api-1.2.4 → karakeep_python_api-1.3.0}/setup.py +1 -1
  7. {karakeep_python_api-1.2.4 → karakeep_python_api-1.3.0}/tests/test_karakeep_api.py +5 -1
  8. {karakeep_python_api-1.2.4 → karakeep_python_api-1.3.0}/LICENSE +0 -0
  9. {karakeep_python_api-1.2.4 → karakeep_python_api-1.3.0}/MANIFEST.in +0 -0
  10. {karakeep_python_api-1.2.4 → karakeep_python_api-1.3.0}/README.md +0 -0
  11. {karakeep_python_api-1.2.4 → karakeep_python_api-1.3.0}/karakeep_python_api/__init__.py +0 -0
  12. {karakeep_python_api-1.2.4 → karakeep_python_api-1.3.0}/karakeep_python_api/__main__.py +0 -0
  13. {karakeep_python_api-1.2.4 → karakeep_python_api-1.3.0}/karakeep_python_api.egg-info/SOURCES.txt +0 -0
  14. {karakeep_python_api-1.2.4 → karakeep_python_api-1.3.0}/karakeep_python_api.egg-info/dependency_links.txt +0 -0
  15. {karakeep_python_api-1.2.4 → karakeep_python_api-1.3.0}/karakeep_python_api.egg-info/entry_points.txt +0 -0
  16. {karakeep_python_api-1.2.4 → karakeep_python_api-1.3.0}/karakeep_python_api.egg-info/requires.txt +0 -0
  17. {karakeep_python_api-1.2.4 → karakeep_python_api-1.3.0}/karakeep_python_api.egg-info/top_level.txt +0 -0
  18. {karakeep_python_api-1.2.4 → karakeep_python_api-1.3.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: karakeep_python_api
3
- Version: 1.2.4
3
+ Version: 1.3.0
4
4
  Summary: Community python client for the Karakeep API.
5
5
  Home-page: https://github.com/thiswillbeyourgithub/karakeep_python_api/
6
6
  License: GPLv3
@@ -17,8 +17,8 @@ class StatusTypes(str, Enum):
17
17
 
18
18
 
19
19
  class NumBookmarksByAttachedType(BaseModel):
20
- ai: Optional[float] = None
21
- human: Optional[float] = None
20
+ ai: Optional[int] = None
21
+ human: Optional[int] = None
22
22
 
23
23
 
24
24
  class TagShort(BaseModel):
@@ -30,7 +30,7 @@ class TagShort(BaseModel):
30
30
  class Tag(BaseModel):
31
31
  id: str
32
32
  name: str
33
- numBookmarks: float
33
+ numBookmarks: int
34
34
  numBookmarksByAttachedType: NumBookmarksByAttachedType
35
35
 
36
36
 
@@ -112,6 +112,11 @@ class Bookmark(BaseModel):
112
112
  summarizationStatus: Optional[Literal["success", "failure", "pending"]] = None
113
113
  note: Optional[str] = None
114
114
  summary: Optional[str] = None
115
+ source: Optional[
116
+ Literal[
117
+ "api", "web", "cli", "mobile", "extension", "singlefile", "rss", "import"
118
+ ]
119
+ ] = None
115
120
  tags: List[TagShort]
116
121
  content: Union[
117
122
  ContentTypeLink, ContentTypeText, ContentTypeAsset, ContentTypeUnknown
@@ -121,7 +126,18 @@ class Bookmark(BaseModel):
121
126
 
122
127
  class PaginatedBookmarks(BaseModel):
123
128
  bookmarks: List[Bookmark]
124
- nextCursor: Optional[str]
129
+ nextCursor: Optional[str] = ""
130
+
131
+
132
+ class ListModel(BaseModel):
133
+ id: str
134
+ name: str
135
+ description: Optional[str] = None
136
+ icon: str
137
+ parentId: Optional[str]
138
+ type: Optional[Literal["manual", "smart"]] = "manual"
139
+ query: Optional[str] = None
140
+ public: bool
125
141
 
126
142
 
127
143
  class Highlight(BaseModel):
@@ -136,17 +152,11 @@ class Highlight(BaseModel):
136
152
  createdAt: str
137
153
 
138
154
 
139
- class ListModel(BaseModel):
140
- id: str
141
- name: str
142
- description: Optional[str] = None
143
- icon: str
144
- parentId: Optional[str]
145
- type: Optional[Literal["manual", "smart"]] = "manual"
146
- query: Optional[str] = None
147
- public: bool
148
-
149
-
150
155
  class PaginatedHighlights(BaseModel):
151
156
  highlights: List[Highlight]
152
- nextCursor: Optional[str]
157
+ nextCursor: Optional[str] = ""
158
+
159
+
160
+ class PaginatedTags(BaseModel):
161
+ tags: List[Tag]
162
+ nextCursor: Optional[str] = ""
@@ -85,7 +85,7 @@ class KarakeepAPI:
85
85
  """
86
86
 
87
87
  # Version reflects the client library version, updated by bumpver
88
- VERSION: str = "1.2.4"
88
+ VERSION: str = "1.3.0"
89
89
 
90
90
  def __init__(
91
91
  self,
@@ -758,6 +758,19 @@ class KarakeepAPI:
758
758
  summary: Optional[str] = None,
759
759
  createdAt: Optional[str] = None, # ISO 8601 format string
760
760
  crawlPriority: Optional[Literal["low", "normal"]] = None,
761
+ importSessionId: Optional[str] = None,
762
+ source: Optional[
763
+ Literal[
764
+ "api",
765
+ "web",
766
+ "cli",
767
+ "mobile",
768
+ "extension",
769
+ "singlefile",
770
+ "rss",
771
+ "import",
772
+ ]
773
+ ] = None,
761
774
  # Link specific
762
775
  url: Optional[str] = None,
763
776
  precrawledArchiveId: Optional[str] = None,
@@ -783,6 +796,8 @@ class KarakeepAPI:
783
796
  summary: Optional summary content for the bookmark.
784
797
  createdAt: Optional creation timestamp override (ISO 8601 format string).
785
798
  crawlPriority: Optional either 'low' or 'normal'.
799
+ importSessionId: Optional import session ID for tracking imported bookmarks.
800
+ source: Optional source of the bookmark ("api", "web", "cli", "mobile", "extension", "singlefile", "rss", "import").
786
801
 
787
802
  --- Link Type Specific ---
788
803
  url: The URL for the link bookmark. Required if type='link'.
@@ -799,7 +814,9 @@ class KarakeepAPI:
799
814
  sourceUrl: Optional source URL where the asset originated.
800
815
 
801
816
  Returns:
802
- datatypes.Bookmark: The created bookmark.
817
+ datatypes.Bookmark: The bookmark object.
818
+ Returns with status 200 if the bookmark already exists,
819
+ or status 201 if the bookmark was newly created.
803
820
  If response validation is disabled, returns the raw API response (dict/list).
804
821
 
805
822
  Raises:
@@ -825,6 +842,10 @@ class KarakeepAPI:
825
842
  request_body["createdAt"] = createdAt
826
843
  if crawlPriority is not None:
827
844
  request_body["crawlPriority"] = crawlPriority
845
+ if importSessionId is not None:
846
+ request_body["importSessionId"] = importSessionId
847
+ if source is not None:
848
+ request_body["source"] = source
828
849
 
829
850
  # Add type-specific fields and perform validation
830
851
  if type == "link":
@@ -1169,6 +1190,54 @@ class KarakeepAPI:
1169
1190
  # No Pydantic validation applied here as the spec defines a simple dict response
1170
1191
  return response_data
1171
1192
 
1193
+ @optional_typecheck
1194
+ def get_lists_of_a_bookmark(
1195
+ self, bookmark_id: str
1196
+ ) -> Union[List[datatypes.ListModel], Dict[str, Any], List[Any]]:
1197
+ """
1198
+ Get all lists associated with a specific bookmark. Corresponds to GET /bookmarks/{bookmarkId}/lists.
1199
+
1200
+ Args:
1201
+ bookmark_id: The ID (string) of the bookmark.
1202
+
1203
+ Returns:
1204
+ List[datatypes.ListModel]: A list of list objects associated with the bookmark.
1205
+ If response validation is disabled, returns the raw API response (dict/list).
1206
+
1207
+ Raises:
1208
+ APIError: If the API request fails (e.g., 404 bookmark not found).
1209
+ pydantic.ValidationError: If response validation fails (and is not disabled).
1210
+ """
1211
+ endpoint = f"bookmarks/{bookmark_id}/lists"
1212
+ response_data = self._call("GET", endpoint)
1213
+
1214
+ if self.disable_response_validation:
1215
+ logger.debug("Skipping response validation as requested.")
1216
+ # Return raw data, which might be {"lists": [...]} or something else
1217
+ return response_data
1218
+ else:
1219
+ # Response schema is {"lists": [ListModel]}, extract the list and validate
1220
+ if (
1221
+ isinstance(response_data, dict)
1222
+ and "lists" in response_data
1223
+ and isinstance(response_data["lists"], list)
1224
+ ):
1225
+ try:
1226
+ return [
1227
+ datatypes.ListModel.model_validate(lst)
1228
+ for lst in response_data["lists"]
1229
+ ]
1230
+ except (
1231
+ Exception
1232
+ ) as e: # Catch validation errors during list comprehension
1233
+ logger.error(f"Validation failed for one or more lists: {e}")
1234
+ raise # Re-raise the validation error
1235
+ else:
1236
+ # Raise error if format is unexpected and validation is enabled
1237
+ raise APIError(
1238
+ f"Unexpected response format for get_lists_of_a_bookmark when validation is enabled: {response_data}"
1239
+ )
1240
+
1172
1241
  @optional_typecheck
1173
1242
  def get_highlights_of_a_bookmark(
1174
1243
  self, bookmark_id: str
@@ -1601,46 +1670,47 @@ class KarakeepAPI:
1601
1670
  return None # Explicitly return None for 204
1602
1671
 
1603
1672
  @optional_typecheck
1604
- def get_all_tags(self) -> Union[List[datatypes.Tag], Dict[str, Any], List[Any]]:
1673
+ def get_all_tags(
1674
+ self,
1675
+ name_contains: Optional[str] = None,
1676
+ sort: Optional[Literal["name", "usage", "relevance"]] = None,
1677
+ attached_by: Optional[Literal["ai", "human", "none"]] = None,
1678
+ cursor: Optional[str] = None,
1679
+ limit: Optional[int] = None,
1680
+ ) -> Union[datatypes.PaginatedTags, Dict[str, Any], List[Any]]:
1605
1681
  """
1606
1682
  Get all tags for the current user. Corresponds to GET /tags.
1607
1683
 
1684
+ Args:
1685
+ name_contains: Filter tags by name substring (optional).
1686
+ sort: Sort order for tags ("name", "usage", "relevance"). Default from API is "usage" (optional).
1687
+ attached_by: Filter tags by how they were attached ("ai", "human", "none") (optional).
1688
+ cursor: Pagination cursor for the next page (optional).
1689
+ limit: Maximum number of tags to return (optional).
1690
+
1608
1691
  Returns:
1609
- List[datatypes.Tag]: A list of tag objects, including bookmark counts.
1692
+ datatypes.PaginatedTags: Paginated list of tags with nextCursor for pagination.
1610
1693
  If response validation is disabled, returns the raw API response (dict/list).
1611
1694
 
1612
1695
  Raises:
1613
1696
  APIError: If the API request fails.
1614
1697
  pydantic.ValidationError: If response validation fails (and is not disabled).
1615
1698
  """
1616
- response_data = self._call("GET", "tags")
1699
+ params = {
1700
+ "nameContains": name_contains,
1701
+ "sort": sort,
1702
+ "attachedBy": attached_by,
1703
+ "cursor": cursor,
1704
+ "limit": limit,
1705
+ }
1706
+ response_data = self._call("GET", "tags", params=params)
1617
1707
 
1618
1708
  if self.disable_response_validation:
1619
1709
  logger.debug("Skipping response validation as requested.")
1620
- # Return raw data, which might be {"tags": [...]} or something else
1621
1710
  return response_data
1622
1711
  else:
1623
- # Response schema is {"tags": [Tag]}, extract the list and validate
1624
- if (
1625
- isinstance(response_data, dict)
1626
- and "tags" in response_data
1627
- and isinstance(response_data["tags"], list)
1628
- ):
1629
- try:
1630
- return [
1631
- datatypes.Tag.model_validate(tag)
1632
- for tag in response_data["tags"]
1633
- ]
1634
- except (
1635
- Exception
1636
- ) as e: # Catch validation errors during list comprehension
1637
- logger.error(f"Validation failed for one or more tags: {e}")
1638
- raise # Re-raise the validation error
1639
- else:
1640
- # Raise error if format is unexpected and validation is enabled
1641
- raise APIError(
1642
- f"Unexpected response format for get_all_tags when validation is enabled: {response_data}"
1643
- )
1712
+ # Response should match PaginatedTags schema
1713
+ return datatypes.PaginatedTags.model_validate(response_data)
1644
1714
 
1645
1715
  @optional_typecheck
1646
1716
  def create_a_new_tag(self, name: str) -> Dict[str, Any]:
@@ -94,6 +94,20 @@
94
94
  "type": "string",
95
95
  "nullable": true
96
96
  },
97
+ "source": {
98
+ "type": "string",
99
+ "nullable": true,
100
+ "enum": [
101
+ "api",
102
+ "web",
103
+ "cli",
104
+ "mobile",
105
+ "extension",
106
+ "singlefile",
107
+ "rss",
108
+ "import"
109
+ ]
110
+ },
97
111
  "tags": {
98
112
  "type": "array",
99
113
  "items": {
@@ -349,6 +363,50 @@
349
363
  "Cursor": {
350
364
  "type": "string"
351
365
  },
366
+ "List": {
367
+ "type": "object",
368
+ "properties": {
369
+ "id": {
370
+ "type": "string"
371
+ },
372
+ "name": {
373
+ "type": "string"
374
+ },
375
+ "description": {
376
+ "type": "string",
377
+ "nullable": true
378
+ },
379
+ "icon": {
380
+ "type": "string"
381
+ },
382
+ "parentId": {
383
+ "type": "string",
384
+ "nullable": true
385
+ },
386
+ "type": {
387
+ "type": "string",
388
+ "enum": [
389
+ "manual",
390
+ "smart"
391
+ ],
392
+ "default": "manual"
393
+ },
394
+ "query": {
395
+ "type": "string",
396
+ "nullable": true
397
+ },
398
+ "public": {
399
+ "type": "boolean"
400
+ }
401
+ },
402
+ "required": [
403
+ "id",
404
+ "name",
405
+ "icon",
406
+ "parentId",
407
+ "public"
408
+ ]
409
+ },
352
410
  "Highlight": {
353
411
  "type": "object",
354
412
  "properties": {
@@ -400,50 +458,6 @@
400
458
  "createdAt"
401
459
  ]
402
460
  },
403
- "List": {
404
- "type": "object",
405
- "properties": {
406
- "id": {
407
- "type": "string"
408
- },
409
- "name": {
410
- "type": "string"
411
- },
412
- "description": {
413
- "type": "string",
414
- "nullable": true
415
- },
416
- "icon": {
417
- "type": "string"
418
- },
419
- "parentId": {
420
- "type": "string",
421
- "nullable": true
422
- },
423
- "type": {
424
- "type": "string",
425
- "enum": [
426
- "manual",
427
- "smart"
428
- ],
429
- "default": "manual"
430
- },
431
- "query": {
432
- "type": "string",
433
- "nullable": true
434
- },
435
- "public": {
436
- "type": "boolean"
437
- }
438
- },
439
- "required": [
440
- "id",
441
- "name",
442
- "icon",
443
- "parentId",
444
- "public"
445
- ]
446
- },
447
461
  "Tag": {
448
462
  "type": "object",
449
463
  "properties": {
@@ -693,6 +707,22 @@
693
707
  "low",
694
708
  "normal"
695
709
  ]
710
+ },
711
+ "importSessionId": {
712
+ "type": "string"
713
+ },
714
+ "source": {
715
+ "type": "string",
716
+ "enum": [
717
+ "api",
718
+ "web",
719
+ "cli",
720
+ "mobile",
721
+ "extension",
722
+ "singlefile",
723
+ "rss",
724
+ "import"
725
+ ]
696
726
  }
697
727
  }
698
728
  },
@@ -781,8 +811,18 @@
781
811
  }
782
812
  },
783
813
  "responses": {
814
+ "200": {
815
+ "description": "The bookmark already exists",
816
+ "content": {
817
+ "application/json": {
818
+ "schema": {
819
+ "$ref": "#/components/schemas/Bookmark"
820
+ }
821
+ }
822
+ }
823
+ },
784
824
  "201": {
785
- "description": "The created bookmark",
825
+ "description": "The bookmark got created",
786
826
  "content": {
787
827
  "application/json": {
788
828
  "schema": {
@@ -1135,6 +1175,20 @@
1135
1175
  "summary": {
1136
1176
  "type": "string",
1137
1177
  "nullable": true
1178
+ },
1179
+ "source": {
1180
+ "type": "string",
1181
+ "nullable": true,
1182
+ "enum": [
1183
+ "api",
1184
+ "web",
1185
+ "cli",
1186
+ "mobile",
1187
+ "extension",
1188
+ "singlefile",
1189
+ "rss",
1190
+ "import"
1191
+ ]
1138
1192
  }
1139
1193
  },
1140
1194
  "required": [
@@ -1245,6 +1299,20 @@
1245
1299
  "summary": {
1246
1300
  "type": "string",
1247
1301
  "nullable": true
1302
+ },
1303
+ "source": {
1304
+ "type": "string",
1305
+ "nullable": true,
1306
+ "enum": [
1307
+ "api",
1308
+ "web",
1309
+ "cli",
1310
+ "mobile",
1311
+ "extension",
1312
+ "singlefile",
1313
+ "rss",
1314
+ "import"
1315
+ ]
1248
1316
  }
1249
1317
  },
1250
1318
  "required": [
@@ -1469,6 +1537,70 @@
1469
1537
  }
1470
1538
  }
1471
1539
  },
1540
+ "/bookmarks/{bookmarkId}/lists": {
1541
+ "get": {
1542
+ "description": "Get lists of a bookmark",
1543
+ "summary": "Get lists of a bookmark",
1544
+ "tags": [
1545
+ "Bookmarks"
1546
+ ],
1547
+ "security": [
1548
+ {
1549
+ "bearerAuth": []
1550
+ }
1551
+ ],
1552
+ "parameters": [
1553
+ {
1554
+ "$ref": "#/components/parameters/BookmarkId"
1555
+ }
1556
+ ],
1557
+ "responses": {
1558
+ "200": {
1559
+ "description": "The list of highlights",
1560
+ "content": {
1561
+ "application/json": {
1562
+ "schema": {
1563
+ "type": "object",
1564
+ "properties": {
1565
+ "lists": {
1566
+ "type": "array",
1567
+ "items": {
1568
+ "$ref": "#/components/schemas/List"
1569
+ }
1570
+ }
1571
+ },
1572
+ "required": [
1573
+ "lists"
1574
+ ]
1575
+ }
1576
+ }
1577
+ }
1578
+ },
1579
+ "404": {
1580
+ "description": "Bookmark not found",
1581
+ "content": {
1582
+ "application/json": {
1583
+ "schema": {
1584
+ "type": "object",
1585
+ "properties": {
1586
+ "code": {
1587
+ "type": "string"
1588
+ },
1589
+ "message": {
1590
+ "type": "string"
1591
+ }
1592
+ },
1593
+ "required": [
1594
+ "code",
1595
+ "message"
1596
+ ]
1597
+ }
1598
+ }
1599
+ }
1600
+ }
1601
+ }
1602
+ }
1603
+ },
1472
1604
  "/bookmarks/{bookmarkId}/highlights": {
1473
1605
  "get": {
1474
1606
  "description": "Get highlights of a bookmark",
@@ -2292,6 +2424,60 @@
2292
2424
  "bearerAuth": []
2293
2425
  }
2294
2426
  ],
2427
+ "parameters": [
2428
+ {
2429
+ "schema": {
2430
+ "type": "string"
2431
+ },
2432
+ "required": false,
2433
+ "name": "nameContains",
2434
+ "in": "query"
2435
+ },
2436
+ {
2437
+ "schema": {
2438
+ "type": "string",
2439
+ "enum": [
2440
+ "name",
2441
+ "usage",
2442
+ "relevance"
2443
+ ],
2444
+ "default": "usage"
2445
+ },
2446
+ "required": false,
2447
+ "name": "sort",
2448
+ "in": "query"
2449
+ },
2450
+ {
2451
+ "schema": {
2452
+ "type": "string",
2453
+ "enum": [
2454
+ "ai",
2455
+ "human",
2456
+ "none"
2457
+ ]
2458
+ },
2459
+ "required": false,
2460
+ "name": "attachedBy",
2461
+ "in": "query"
2462
+ },
2463
+ {
2464
+ "schema": {
2465
+ "type": "string"
2466
+ },
2467
+ "required": false,
2468
+ "name": "cursor",
2469
+ "in": "query"
2470
+ },
2471
+ {
2472
+ "schema": {
2473
+ "type": "number",
2474
+ "nullable": true
2475
+ },
2476
+ "required": false,
2477
+ "name": "limit",
2478
+ "in": "query"
2479
+ }
2480
+ ],
2295
2481
  "responses": {
2296
2482
  "200": {
2297
2483
  "description": "Object with all tags data.",
@@ -2305,10 +2491,15 @@
2305
2491
  "items": {
2306
2492
  "$ref": "#/components/schemas/Tag"
2307
2493
  }
2494
+ },
2495
+ "nextCursor": {
2496
+ "type": "string",
2497
+ "nullable": true
2308
2498
  }
2309
2499
  },
2310
2500
  "required": [
2311
- "tags"
2501
+ "tags",
2502
+ "nextCursor"
2312
2503
  ]
2313
2504
  }
2314
2505
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: karakeep_python_api
3
- Version: 1.2.4
3
+ Version: 1.3.0
4
4
  Summary: Community python client for the Karakeep API.
5
5
  Home-page: https://github.com/thiswillbeyourgithub/karakeep_python_api/
6
6
  License: GPLv3
@@ -7,7 +7,7 @@ with open("README.md", "r") as readme:
7
7
 
8
8
  setup(
9
9
  name="karakeep_python_api",
10
- version="1.2.4",
10
+ version="1.3.0",
11
11
  description="Community python client for the Karakeep API.", # Simplified description
12
12
  long_description=long_description,
13
13
  long_description_content_type="text/markdown",
@@ -118,7 +118,11 @@ def test_get_all_tags(karakeep_client: KarakeepAPI):
118
118
  """Test retrieving all tags."""
119
119
  try:
120
120
  tags = karakeep_client.get_all_tags()
121
- assert isinstance(tags, list), "Response should be a list"
121
+ assert isinstance(
122
+ tags, datatypes.PaginatedTags
123
+ ), "Response should be of type PaginatedTags"
124
+ tags = tags.tags
125
+ assert isinstance(tags, list), "tags var should be of type list at this point"
122
126
  if tags: # Only check elements if the list is not empty
123
127
  assert all(
124
128
  isinstance(item, datatypes.Tag) for item in tags