pyvikunja 0.6__tar.gz → 0.8__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pyvikunja
3
- Version: 0.6
3
+ Version: 0.8
4
4
  Summary: A Python wrapper for Vikunja API
5
5
  Author: Joseph Shufflebotham
6
6
  License: AGPL
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pyvikunja"
7
- version = "0.6"
7
+ version = "0.8"
8
8
  description = "A Python wrapper for Vikunja API"
9
9
  authors = [
10
10
  {name = "Joseph Shufflebotham"}
@@ -1,5 +1,6 @@
1
1
  import logging
2
2
  from typing import List, Dict, Any, Optional
3
+ from urllib.parse import urlparse, urlunparse
3
4
 
4
5
  import httpx
5
6
 
@@ -15,6 +16,7 @@ logger = logging.getLogger(__name__)
15
16
 
16
17
  class APIError(Exception):
17
18
  """Custom exception for API-related errors."""
19
+
18
20
  def __init__(self, status_code: int, message: str):
19
21
  super().__init__(f"HTTP {status_code}: {message}")
20
22
  self.status_code = status_code
@@ -23,12 +25,43 @@ class APIError(Exception):
23
25
 
24
26
  class VikunjaAPI:
25
27
  def __init__(self, base_url: str, token: str):
26
- self.base_url = base_url
28
+ self.host = self._normalize_host(base_url)
29
+ self.api_base_url = self._normalize_api_base_url(self.host)
27
30
  self.headers = {"Authorization": f"Bearer {token}"}
28
31
  self.client = httpx.AsyncClient()
29
32
 
30
- async def _request(self, method: str, endpoint: str, params: Optional[Dict] = None, data: Optional[Dict] = None) -> Optional[Any]:
31
- url = f"{self.base_url}{endpoint}"
33
+ @property
34
+ def web_ui_link(self):
35
+ return self.host
36
+
37
+ def _normalize_host(self, url: str) -> str:
38
+ """Ensures the host has a valid protocol and retains ports if provided."""
39
+ if "://" not in url:
40
+ url = f"https://{url}" # Default to HTTPS if no scheme provided
41
+
42
+ parsed = urlparse(url)
43
+
44
+ # Default to HTTPS if no scheme is provided
45
+ scheme = parsed.scheme if parsed.scheme else "https"
46
+
47
+ # Ensure netloc is correctly used (handles ports)
48
+ netloc = parsed.netloc if parsed.netloc else parsed.path # Handles cases where netloc is empty
49
+
50
+ # Rebuild the host URL
51
+ host = urlunparse((scheme, netloc, "", "", "", ""))
52
+
53
+ return host.rstrip("/")
54
+
55
+ def _normalize_api_base_url(self, host: str) -> str:
56
+ """Ensures the API base URL includes /api/v1."""
57
+ if not host.endswith("/api/v1"):
58
+ return f"{host}/api/v1"
59
+ return host
60
+
61
+
62
+ async def _request(self, method: str, endpoint: str, params: Optional[Dict] = None, data: Optional[Dict] = None) -> \
63
+ Optional[Any]:
64
+ url = f"{self.api_base_url}{endpoint}"
32
65
  try:
33
66
  response = await self.client.request(method, url, headers=self.headers, params=params, json=data)
34
67
  response.raise_for_status()
@@ -41,6 +74,22 @@ class VikunjaAPI:
41
74
  logger.error(f"Unexpected error occurred: {e} | URL: {url}")
42
75
  return None
43
76
 
77
+ async def ping(self) -> bool:
78
+ """Tests if the API key is valid by calling the /projects endpoint."""
79
+ """Not chosen the /user endpoint here because it was always returning 401 with an API Token"""
80
+ url = f"{self.api_base_url}/projects"
81
+
82
+ try:
83
+ response = await self.client.get(url, headers=self.headers, timeout=10)
84
+ response.raise_for_status()
85
+
86
+ if response.status_code == 200:
87
+ return True
88
+ else:
89
+ raise httpx.HTTPError(f"Non-200 Response from server {response.status_code}")
90
+ except httpx.HTTPError as e:
91
+ raise e
92
+
44
93
  # Projects
45
94
  async def get_projects(self, page: int = 1, per_page: int = 20) -> List[Project]:
46
95
  response = await self._request("GET", "/projects", params={"page": page, "per_page": per_page})
@@ -114,5 +163,3 @@ class VikunjaAPI:
114
163
 
115
164
  async def delete_team(self, team_id: int) -> Optional[Team]:
116
165
  return await self._request("DELETE", f"/teams/{team_id}")
117
-
118
-
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pyvikunja
3
- Version: 0.6
3
+ Version: 0.8
4
4
  Summary: A Python wrapper for Vikunja API
5
5
  Author: Joseph Shufflebotham
6
6
  License: AGPL
File without changes
File without changes
File without changes
File without changes