karakeep-python-api 0.2.1__tar.gz → 0.2.2__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-0.2.1/karakeep_python_api.egg-info → karakeep_python_api-0.2.2}/PKG-INFO +1 -1
  2. {karakeep_python_api-0.2.1 → karakeep_python_api-0.2.2}/karakeep_python_api/karakeep_api.py +81 -55
  3. {karakeep_python_api-0.2.1 → karakeep_python_api-0.2.2/karakeep_python_api.egg-info}/PKG-INFO +1 -1
  4. {karakeep_python_api-0.2.1 → karakeep_python_api-0.2.2}/setup.py +1 -1
  5. {karakeep_python_api-0.2.1 → karakeep_python_api-0.2.2}/tests/test_karakeep_api.py +2 -4
  6. {karakeep_python_api-0.2.1 → karakeep_python_api-0.2.2}/LICENSE +0 -0
  7. {karakeep_python_api-0.2.1 → karakeep_python_api-0.2.2}/MANIFEST.in +0 -0
  8. {karakeep_python_api-0.2.1 → karakeep_python_api-0.2.2}/README.md +0 -0
  9. {karakeep_python_api-0.2.1 → karakeep_python_api-0.2.2}/karakeep_python_api/__init__.py +0 -0
  10. {karakeep_python_api-0.2.1 → karakeep_python_api-0.2.2}/karakeep_python_api/__main__.py +0 -0
  11. {karakeep_python_api-0.2.1 → karakeep_python_api-0.2.2}/karakeep_python_api/datatypes.py +0 -0
  12. {karakeep_python_api-0.2.1 → karakeep_python_api-0.2.2}/karakeep_python_api/openapi_reference.json +0 -0
  13. {karakeep_python_api-0.2.1 → karakeep_python_api-0.2.2}/karakeep_python_api.egg-info/SOURCES.txt +0 -0
  14. {karakeep_python_api-0.2.1 → karakeep_python_api-0.2.2}/karakeep_python_api.egg-info/dependency_links.txt +0 -0
  15. {karakeep_python_api-0.2.1 → karakeep_python_api-0.2.2}/karakeep_python_api.egg-info/entry_points.txt +0 -0
  16. {karakeep_python_api-0.2.1 → karakeep_python_api-0.2.2}/karakeep_python_api.egg-info/requires.txt +0 -0
  17. {karakeep_python_api-0.2.1 → karakeep_python_api-0.2.2}/karakeep_python_api.egg-info/top_level.txt +0 -0
  18. {karakeep_python_api-0.2.1 → karakeep_python_api-0.2.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: karakeep_python_api
3
- Version: 0.2.1
3
+ Version: 0.2.2
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
@@ -85,7 +85,7 @@ class KarakeepAPI:
85
85
  """
86
86
 
87
87
  # Version reflects the client library version, updated by bumpver
88
- VERSION: str = "0.2.1"
88
+ VERSION: str = "0.2.2"
89
89
 
90
90
  def __init__(
91
91
  self,
@@ -955,16 +955,18 @@ class KarakeepAPI:
955
955
 
956
956
  @optional_typecheck
957
957
  def attach_tags_to_a_bookmark(
958
- self, bookmark_id: str, tags_data: dict
958
+ self,
959
+ bookmark_id: str,
960
+ tag_ids: Optional[List[str]] = None,
961
+ tag_names: Optional[List[str]] = None,
959
962
  ) -> Dict[str, Any]:
960
963
  """
961
964
  Attach one or more tags to a bookmark. Corresponds to POST /bookmarks/{bookmarkId}/tags.
962
965
 
963
966
  Args:
964
967
  bookmark_id: The ID (string) of the bookmark.
965
- tags_data: Dictionary specifying the tags to attach. Must contain a "tags" key
966
- which is a list of objects, each having *either* "tagId" (string) *or* "tagName" (string).
967
- Example: `{"tags": [{"tagId": "existing_tag_id"}, {"tagName": "new_or_existing_tag_name"}]}`
968
+ tag_ids: List of existing tag IDs to attach (optional).
969
+ tag_names: List of tag names to attach (will create tags if they don't exist) (optional).
968
970
 
969
971
  Returns:
970
972
  dict: A dictionary containing the list of attached tag IDs under the key "attached".
@@ -972,38 +974,49 @@ class KarakeepAPI:
972
974
  Validation is not performed on this response type by default.
973
975
 
974
976
  Raises:
975
- ValueError: If tags_data structure is invalid.
977
+ ValueError: If no tags are provided or if arguments are invalid.
976
978
  APIError: If the API request fails (e.g., 404 bookmark not found).
977
979
  """
978
- # Validate the tags_data structure
979
- if not isinstance(tags_data, dict):
980
- raise ValueError("tags_data must be a dictionary")
980
+ # Validate that at least one tag source is provided
981
+ if not tag_ids and not tag_names:
982
+ raise ValueError("At least one of 'tag_ids' or 'tag_names' must be provided")
981
983
 
982
- if "tags" not in tags_data:
983
- raise ValueError("tags_data must contain a 'tags' key")
984
+ # Validate input types
985
+ if tag_ids is not None and not isinstance(tag_ids, list):
986
+ raise ValueError("'tag_ids' must be a list of strings")
984
987
 
985
- tags_list = tags_data["tags"]
986
- if not isinstance(tags_list, list):
987
- raise ValueError("The 'tags' value must be a list")
988
+ if tag_names is not None and not isinstance(tag_names, list):
989
+ raise ValueError("'tag_names' must be a list of strings")
988
990
 
989
- # Validate each tag object in the list
990
- for i, tag in enumerate(tags_list):
991
- if not isinstance(tag, dict):
992
- raise ValueError(f"Tag at index {i} must be a dictionary")
991
+ # Validate individual elements
992
+ if tag_ids:
993
+ for i, tag_id in enumerate(tag_ids):
994
+ if not isinstance(tag_id, str) or not tag_id.strip():
995
+ raise ValueError(f"Tag ID at index {i} must be a non-empty string")
993
996
 
994
- has_tag_id = "tagId" in tag
995
- has_tag_name = "tagName" in tag
997
+ if tag_names:
998
+ for i, tag_name in enumerate(tag_names):
999
+ if not isinstance(tag_name, str) or not tag_name.strip():
1000
+ raise ValueError(f"Tag name at index {i} must be a non-empty string")
996
1001
 
997
- if not has_tag_id and not has_tag_name:
998
- raise ValueError(
999
- f"Tag at index {i} must contain either 'tagId' or 'tagName'"
1000
- )
1002
+ # Construct the tags_data dict in the format expected by the API
1003
+ tags_list = []
1004
+
1005
+ if tag_ids:
1006
+ for tag_id in tag_ids:
1007
+ tags_list.append({"tagId": tag_id.strip()})
1008
+
1009
+ if tag_names:
1010
+ for tag_name in tag_names:
1011
+ tags_list.append({"tagName": tag_name.strip()})
1001
1012
 
1002
- if has_tag_id and not isinstance(tag["tagId"], str):
1003
- raise ValueError(f"Tag at index {i}: 'tagId' must be a string")
1013
+ tags_data = {"tags": tags_list}
1004
1014
 
1005
- if has_tag_name and not isinstance(tag["tagName"], str):
1006
- raise ValueError(f"Tag at index {i}: 'tagName' must be a string")
1015
+ # Optional validation using Tag datatype if validation is enabled
1016
+ if not self.disable_response_validation:
1017
+ # Validate the constructed structure matches expected format
1018
+ # This is primarily for development/debugging purposes
1019
+ logger.debug("Validating constructed tags_data structure")
1007
1020
 
1008
1021
  endpoint = f"bookmarks/{bookmark_id}/tags"
1009
1022
  response_data = self._call("POST", endpoint, data=tags_data)
@@ -1013,16 +1026,18 @@ class KarakeepAPI:
1013
1026
 
1014
1027
  @optional_typecheck
1015
1028
  def detach_tags_from_a_bookmark(
1016
- self, bookmark_id: str, tags_data: dict
1029
+ self,
1030
+ bookmark_id: str,
1031
+ tag_ids: Optional[List[str]] = None,
1032
+ tag_names: Optional[List[str]] = None,
1017
1033
  ) -> Dict[str, Any]:
1018
1034
  """
1019
1035
  Detach one or more tags from a bookmark. Corresponds to DELETE /bookmarks/{bookmarkId}/tags.
1020
1036
 
1021
1037
  Args:
1022
1038
  bookmark_id: The ID (string) of the bookmark.
1023
- tags_data: Dictionary specifying the tags to detach. Must contain a "tags" key
1024
- which is a list of objects, each having *either* "tagId" (string) *or* "tagName" (string).
1025
- Example: `{"tags": [{"tagId": "tag_id_to_remove"}, {"tagName": "tag_name_to_remove"}]}`
1039
+ tag_ids: List of existing tag IDs to detach (optional).
1040
+ tag_names: List of tag names to detach (optional).
1026
1041
 
1027
1042
  Returns:
1028
1043
  dict: A dictionary containing the list of detached tag IDs under the key "detached".
@@ -1030,38 +1045,49 @@ class KarakeepAPI:
1030
1045
  Validation is not performed on this response type by default.
1031
1046
 
1032
1047
  Raises:
1033
- ValueError: If tags_data structure is invalid.
1048
+ ValueError: If no tags are provided or if arguments are invalid.
1034
1049
  APIError: If the API request fails (e.g., 404 bookmark not found).
1035
1050
  """
1036
- # Validate the tags_data structure
1037
- if not isinstance(tags_data, dict):
1038
- raise ValueError("tags_data must be a dictionary")
1051
+ # Validate that at least one tag source is provided
1052
+ if not tag_ids and not tag_names:
1053
+ raise ValueError("At least one of 'tag_ids' or 'tag_names' must be provided")
1039
1054
 
1040
- if "tags" not in tags_data:
1041
- raise ValueError("tags_data must contain a 'tags' key")
1055
+ # Validate input types
1056
+ if tag_ids is not None and not isinstance(tag_ids, list):
1057
+ raise ValueError("'tag_ids' must be a list of strings")
1042
1058
 
1043
- tags_list = tags_data["tags"]
1044
- if not isinstance(tags_list, list):
1045
- raise ValueError("The 'tags' value must be a list")
1059
+ if tag_names is not None and not isinstance(tag_names, list):
1060
+ raise ValueError("'tag_names' must be a list of strings")
1046
1061
 
1047
- # Validate each tag object in the list
1048
- for i, tag in enumerate(tags_list):
1049
- if not isinstance(tag, dict):
1050
- raise ValueError(f"Tag at index {i} must be a dictionary")
1062
+ # Validate individual elements
1063
+ if tag_ids:
1064
+ for i, tag_id in enumerate(tag_ids):
1065
+ if not isinstance(tag_id, str) or not tag_id.strip():
1066
+ raise ValueError(f"Tag ID at index {i} must be a non-empty string")
1051
1067
 
1052
- has_tag_id = "tagId" in tag
1053
- has_tag_name = "tagName" in tag
1068
+ if tag_names:
1069
+ for i, tag_name in enumerate(tag_names):
1070
+ if not isinstance(tag_name, str) or not tag_name.strip():
1071
+ raise ValueError(f"Tag name at index {i} must be a non-empty string")
1054
1072
 
1055
- if not has_tag_id and not has_tag_name:
1056
- raise ValueError(
1057
- f"Tag at index {i} must contain either 'tagId' or 'tagName'"
1058
- )
1073
+ # Construct the tags_data dict in the format expected by the API
1074
+ tags_list = []
1075
+
1076
+ if tag_ids:
1077
+ for tag_id in tag_ids:
1078
+ tags_list.append({"tagId": tag_id.strip()})
1079
+
1080
+ if tag_names:
1081
+ for tag_name in tag_names:
1082
+ tags_list.append({"tagName": tag_name.strip()})
1059
1083
 
1060
- if has_tag_id and not isinstance(tag["tagId"], str):
1061
- raise ValueError(f"Tag at index {i}: 'tagId' must be a string")
1084
+ tags_data = {"tags": tags_list}
1062
1085
 
1063
- if has_tag_name and not isinstance(tag["tagName"], str):
1064
- raise ValueError(f"Tag at index {i}: 'tagName' must be a string")
1086
+ # Optional validation using Tag datatype if validation is enabled
1087
+ if not self.disable_response_validation:
1088
+ # Validate the constructed structure matches expected format
1089
+ # This is primarily for development/debugging purposes
1090
+ logger.debug("Validating constructed tags_data structure")
1065
1091
 
1066
1092
  endpoint = f"bookmarks/{bookmark_id}/tags"
1067
1093
  response_data = self._call("DELETE", endpoint, data=tags_data)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: karakeep_python_api
3
- Version: 0.2.1
3
+ Version: 0.2.2
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="0.2.1",
10
+ version="0.2.2",
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",
@@ -530,9 +530,8 @@ def test_tag_lifecycle_on_bookmark(karakeep_client: KarakeepAPI, managed_bookmar
530
530
  try:
531
531
  # 1. Attach a new tag by name to the bookmark
532
532
  logger.info(f"\nAttempting to attach tag '{initial_tag_name}' to bookmark {bookmark_id}")
533
- attach_payload = {"tags": [{"tagName": initial_tag_name}]}
534
533
  attach_response = karakeep_client.attach_tags_to_a_bookmark(
535
- bookmark_id=bookmark_id, tags_data=attach_payload
534
+ bookmark_id=bookmark_id, tag_names=[initial_tag_name]
536
535
  )
537
536
  assert "attached" in attach_response and len(attach_response["attached"]) == 1, \
538
537
  "Failed to attach tag or response format incorrect"
@@ -563,9 +562,8 @@ def test_tag_lifecycle_on_bookmark(karakeep_client: KarakeepAPI, managed_bookmar
563
562
 
564
563
  # 4. Detach the tag from the bookmark
565
564
  logger.info(f"\nAttempting to detach tag {tag_id_to_manage} from bookmark {bookmark_id}")
566
- detach_payload = {"tags": [{"tagId": tag_id_to_manage}]}
567
565
  detach_response = karakeep_client.detach_tags_from_a_bookmark(
568
- bookmark_id=bookmark_id, tags_data=detach_payload
566
+ bookmark_id=bookmark_id, tag_ids=[tag_id_to_manage]
569
567
  )
570
568
  assert "detached" in detach_response and tag_id_to_manage in detach_response["detached"], \
571
569
  "Failed to detach tag or response format incorrect"