adss 1.0__py3-none-any.whl → 1.2__py3-none-any.whl
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.
- adss/__init__.py +24 -0
- adss/auth.py +121 -0
- adss/client.py +671 -0
- adss/endpoints/__init__.py +14 -0
- adss/endpoints/admin.py +433 -0
- adss/endpoints/images.py +898 -0
- adss/endpoints/metadata.py +216 -0
- adss/endpoints/queries.py +498 -0
- adss/endpoints/users.py +311 -0
- adss/exceptions.py +57 -0
- adss/models/__init__.py +13 -0
- adss/models/metadata.py +138 -0
- adss/models/query.py +134 -0
- adss/models/user.py +123 -0
- adss/utils.py +107 -0
- {adss-1.0.dist-info → adss-1.2.dist-info}/METADATA +1 -1
- adss-1.2.dist-info/RECORD +30 -0
- {adss-1.0.dist-info → adss-1.2.dist-info}/WHEEL +1 -1
- adss-1.0.dist-info/RECORD +0 -16
- {adss-1.0.dist-info → adss-1.2.dist-info}/LICENSE +0 -0
- {adss-1.0.dist-info → adss-1.2.dist-info}/top_level.txt +0 -0
adss/__init__.py
CHANGED
@@ -0,0 +1,24 @@
|
|
1
|
+
"""
|
2
|
+
Astronomy TAP Client - A Python client for interacting with the Astronomy TAP Service API.
|
3
|
+
|
4
|
+
This package provides a comprehensive client for working with the Astronomy TAP Service,
|
5
|
+
including authentication, query execution, user management, and administrative functions.
|
6
|
+
"""
|
7
|
+
|
8
|
+
__version__ = "0.1.0"
|
9
|
+
|
10
|
+
from .client import ADSSClient
|
11
|
+
from .exceptions import (
|
12
|
+
ADSSClientError, AuthenticationError, PermissionDeniedError,
|
13
|
+
ResourceNotFoundError, QueryExecutionError
|
14
|
+
)
|
15
|
+
from .models.user import User, Role
|
16
|
+
from .models.query import Query, QueryResult
|
17
|
+
from .models.metadata import Schema, Table, Column
|
18
|
+
|
19
|
+
__all__ = [
|
20
|
+
'ADSSClient',
|
21
|
+
'ADSSClientError', 'AuthenticationError', 'PermissionDeniedError',
|
22
|
+
'ResourceNotFoundError', 'QueryExecutionError',
|
23
|
+
'User', 'Role', 'Query', 'QueryResult', 'Schema', 'Table', 'Column'
|
24
|
+
]
|
adss/auth.py
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
import requests
|
2
|
+
from typing import Dict, Optional, Tuple
|
3
|
+
|
4
|
+
from adss.exceptions import AuthenticationError
|
5
|
+
from adss.utils import handle_response_errors
|
6
|
+
from adss.models.user import User
|
7
|
+
|
8
|
+
|
9
|
+
class Auth:
|
10
|
+
"""
|
11
|
+
Handles authentication, token management, and HTTP requests for the TAP client.
|
12
|
+
"""
|
13
|
+
|
14
|
+
def __init__(self, base_url: str, verify_ssl: bool = True):
|
15
|
+
self.base_url = base_url.rstrip('/')
|
16
|
+
self.token: Optional[str] = None
|
17
|
+
self.current_user: Optional[User] = None
|
18
|
+
self.verify_ssl = verify_ssl
|
19
|
+
|
20
|
+
def login(self, username: str, password: str, **kwargs) -> Tuple[str, User]:
|
21
|
+
"""
|
22
|
+
Log in with username and password, obtaining an authentication token.
|
23
|
+
"""
|
24
|
+
login_url = f"{self.base_url}/adss/v1/auth/login"
|
25
|
+
data = {"username": username, "password": password}
|
26
|
+
|
27
|
+
try:
|
28
|
+
# Use our own request() method here
|
29
|
+
response = self.request(
|
30
|
+
method="POST",
|
31
|
+
url=login_url,
|
32
|
+
auth_required=False,
|
33
|
+
data=data,
|
34
|
+
**kwargs
|
35
|
+
)
|
36
|
+
handle_response_errors(response)
|
37
|
+
|
38
|
+
token_data = response.json()
|
39
|
+
self.token = token_data.get("access_token")
|
40
|
+
if not self.token:
|
41
|
+
raise AuthenticationError("Login succeeded but no token returned")
|
42
|
+
|
43
|
+
# Now fetch user info (this will use auth_required=True internally)
|
44
|
+
self.current_user = self._get_current_user(**kwargs)
|
45
|
+
return self.token, self.current_user
|
46
|
+
|
47
|
+
except requests.RequestException as e:
|
48
|
+
raise AuthenticationError(f"Login failed: {e}")
|
49
|
+
|
50
|
+
def logout(self) -> None:
|
51
|
+
self.token = None
|
52
|
+
self.current_user = None
|
53
|
+
|
54
|
+
def is_authenticated(self) -> bool:
|
55
|
+
return self.token is not None
|
56
|
+
|
57
|
+
def _get_current_user(self, **kwargs) -> User:
|
58
|
+
"""
|
59
|
+
Fetch the current user's information using the stored token.
|
60
|
+
"""
|
61
|
+
if not self.token:
|
62
|
+
raise AuthenticationError("Not authenticated")
|
63
|
+
|
64
|
+
me_url = f"{self.base_url}/adss/v1/users/me"
|
65
|
+
auth_headers = self._get_auth_headers()
|
66
|
+
|
67
|
+
try:
|
68
|
+
# Again, use request() so SSL and auth headers are applied consistently
|
69
|
+
response = self.request(
|
70
|
+
method="GET",
|
71
|
+
url=me_url,
|
72
|
+
headers=auth_headers,
|
73
|
+
auth_required=True,
|
74
|
+
**kwargs
|
75
|
+
)
|
76
|
+
handle_response_errors(response)
|
77
|
+
|
78
|
+
user_data = response.json()
|
79
|
+
return User.from_dict(user_data)
|
80
|
+
|
81
|
+
except requests.RequestException as e:
|
82
|
+
raise AuthenticationError(f"Failed to get user info: {e}")
|
83
|
+
|
84
|
+
def _get_auth_headers(self) -> Dict[str, str]:
|
85
|
+
headers = {"Accept": "application/json"}
|
86
|
+
if self.token:
|
87
|
+
headers["Authorization"] = f"Bearer {self.token}"
|
88
|
+
return headers
|
89
|
+
|
90
|
+
def request(
|
91
|
+
self,
|
92
|
+
method: str,
|
93
|
+
url: str,
|
94
|
+
headers: Optional[Dict[str, str]] = None,
|
95
|
+
auth_required: bool = False,
|
96
|
+
**kwargs
|
97
|
+
) -> requests.Response:
|
98
|
+
"""
|
99
|
+
Make an HTTP request with automatic base_url prefix, SSL config, and auth headers.
|
100
|
+
"""
|
101
|
+
if auth_required and not self.is_authenticated():
|
102
|
+
raise AuthenticationError("Authentication required for this request")
|
103
|
+
|
104
|
+
# Prepend base_url if needed
|
105
|
+
if not url.startswith(('http://', 'https://')):
|
106
|
+
url = f"{self.base_url}/{url.lstrip('/')}"
|
107
|
+
|
108
|
+
# Merge headers
|
109
|
+
final_headers = self._get_auth_headers()
|
110
|
+
if headers:
|
111
|
+
final_headers.update(headers)
|
112
|
+
|
113
|
+
# Apply verify_ssl unless overridden
|
114
|
+
if 'verify' not in kwargs:
|
115
|
+
kwargs['verify'] = self.verify_ssl
|
116
|
+
|
117
|
+
return requests.request(method, url, headers=final_headers, **kwargs)
|
118
|
+
|
119
|
+
def refresh_user_info(self, **kwargs) -> User:
|
120
|
+
self.current_user = self._get_current_user(**kwargs)
|
121
|
+
return self.current_user
|