Skip to content

Commit b1d4115

Browse files
committed
Split client into separate files
1 parent a62431e commit b1d4115

17 files changed

+555
-499
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ pip install rushdb
1111
## Quick Start
1212

1313
```python
14-
from rushdb import RushDBClient
14+
from src.rushdb import RushDBClient
1515

1616
# Initialize the client
1717
client = RushDBClient("http://localhost:8000", "your-api-key")

rushdb/__init__.py

Lines changed: 0 additions & 9 deletions
This file was deleted.

rushdb/client/__init__.py

Lines changed: 0 additions & 8 deletions
This file was deleted.

rushdb/client/client.py

Lines changed: 0 additions & 479 deletions
This file was deleted.

src/__init__.py

Whitespace-only changes.

src/rushdb/__init__.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
"""RushDB Client Package
2+
3+
Exposes the RushDBClient class.
4+
"""
5+
6+
from .client import RushDBClient
7+
from .common import RushDBError, RelationOptions, RelationDetachOptions
8+
from .record import Record
9+
from .transaction import Transaction
10+
from .property import Property
11+
12+
__all__ = ['RushDBClient', 'RushDBError', 'Record', 'RelationOptions', 'RelationDetachOptions', 'Transaction', 'Property']

src/rushdb/client.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
"""RushDB Client"""
2+
3+
import json
4+
import urllib.request
5+
import urllib.parse
6+
import urllib.error
7+
from typing import Any, Dict, Optional
8+
9+
from src.rushdb.common import RushDBError
10+
from src.rushdb.labels_api import LabelsAPI
11+
from src.rushdb.properties_api import PropertiesAPI
12+
from src.rushdb.records_api import RecordsAPI
13+
from src.rushdb.transactions_api import TransactionsAPI
14+
15+
class RushDBClient:
16+
"""Main client for interacting with RushDB."""
17+
DEFAULT_BASE_URL = "https://api.rushdb.com"
18+
19+
def __init__(self, api_key: str, base_url: Optional[str] = None):
20+
"""Initialize the RushDB client.
21+
22+
Args:
23+
api_key: The API key for authentication
24+
base_url: Optional base URL for the RushDB server (default: https://api.rushdb.com)
25+
"""
26+
self.base_url = (base_url or self.DEFAULT_BASE_URL).rstrip('/')
27+
self.api_key = api_key
28+
self.records = RecordsAPI(self)
29+
self.properties = PropertiesAPI(self)
30+
self.labels = LabelsAPI(self)
31+
self.transactions = TransactionsAPI(self)
32+
33+
def _make_request(self, method: str, path: str, data: Optional[Dict] = None, headers: Optional[Dict[str, str]] = None, params: Optional[Dict[str, Any]] = None) -> Any:
34+
"""Make an HTTP request to the RushDB server.
35+
36+
Args:
37+
method: HTTP method (GET, POST, PUT, DELETE)
38+
path: API endpoint path
39+
data: Request body data
40+
headers: Optional request headers
41+
params: Optional URL query parameters
42+
43+
Returns:
44+
The parsed JSON response
45+
"""
46+
# Ensure path starts with /
47+
if not path.startswith('/'):
48+
path = '/' + path
49+
50+
# Clean and encode path components
51+
path = path.strip()
52+
path_parts = [urllib.parse.quote(part, safe='') for part in path.split('/') if part]
53+
clean_path = '/' + '/'.join(path_parts)
54+
55+
# Build URL with query parameters
56+
url = f"{self.base_url}{clean_path}"
57+
if params:
58+
query_string = urllib.parse.urlencode(params)
59+
url = f"{url}?{query_string}"
60+
61+
# Prepare headers
62+
request_headers = {
63+
'token': self.api_key,
64+
'Content-Type': 'application/json',
65+
**(headers or {})
66+
}
67+
68+
try:
69+
# Prepare request body
70+
body = None
71+
if data is not None:
72+
body = json.dumps(data).encode('utf-8')
73+
74+
# Create and send request
75+
request = urllib.request.Request(
76+
url,
77+
data=body,
78+
headers=request_headers,
79+
method=method
80+
)
81+
82+
with urllib.request.urlopen(request) as response:
83+
return json.loads(response.read().decode('utf-8'))
84+
except urllib.error.HTTPError as e:
85+
error_body = json.loads(e.read().decode('utf-8'))
86+
raise RushDBError(error_body.get('message', str(e)), error_body)
87+
except urllib.error.URLError as e:
88+
raise RushDBError(f"Connection error: {str(e)}")
89+
except json.JSONDecodeError as e:
90+
raise RushDBError(f"Invalid JSON response: {str(e)}")
91+
92+
def ping(self) -> bool:
93+
"""Check if the server is reachable."""
94+
try:
95+
self._make_request('GET', '/')
96+
return True
97+
except RushDBError:
98+
return False

src/rushdb/common.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from typing import Dict, List, Optional, Union, TypedDict, Literal
2+
3+
# Relation types
4+
RelationDirection = Literal['in', 'out']
5+
6+
class RelationOptions(TypedDict, total=False):
7+
"""Options for creating relations."""
8+
direction: Optional[RelationDirection]
9+
type: Optional[str]
10+
11+
class RelationDetachOptions(TypedDict, total=False):
12+
"""Options for detaching relations."""
13+
direction: Optional[RelationDirection]
14+
typeOrTypes: Optional[Union[str, List[str]]]
15+
16+
class RushDBError(Exception):
17+
"""Custom exception for RushDB client errors."""
18+
def __init__(self, message: str, details: Optional[Dict] = None):
19+
super().__init__(message)
20+
self.details = details or {}

src/rushdb/labels_api.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from typing import List
2+
3+
from src.rushdb import RushDBClient
4+
5+
6+
class LabelsAPI:
7+
"""API for managing labels in RushDB."""
8+
def __init__(self, client: 'RushDBClient'):
9+
self.client = client
10+
11+
def list(self) -> List[str]:
12+
"""List all labels."""
13+
return self.client._make_request('GET', '/api/v1/labels')
14+
15+
def create(self, label: str) -> None:
16+
"""Create a new label."""
17+
return self.client._make_request('POST', '/api/v1/labels', {'name': label})
18+
19+
def delete(self, label: str) -> None:
20+
"""Delete a label."""
21+
return self.client._make_request('DELETE', f'/api/v1/labels/{label}')

src/rushdb/properties_api.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from typing import List, Dict, Any, Optional
2+
3+
from src.rushdb import RushDBClient
4+
from src.rushdb.property import Property, PropertyValuesData
5+
from src.rushdb.transaction import Transaction
6+
7+
8+
class PropertiesAPI:
9+
"""API for managing properties in RushDB."""
10+
def __init__(self, client: 'RushDBClient'):
11+
self.client = client
12+
13+
def list(self, transaction: Optional[Transaction] = None) -> List[Property]:
14+
"""List all properties."""
15+
headers = Transaction._build_transaction_header(transaction.id if transaction else None)
16+
17+
return self.client._make_request('GET', '/api/v1/properties', headers=headers)
18+
19+
def create(self, data: Dict[str, Any], transaction: Optional[Transaction] = None) -> Property:
20+
"""Create a new property."""
21+
headers = Transaction._build_transaction_header(transaction.id if transaction else None)
22+
23+
return self.client._make_request('POST', '/api/v1/properties', data, headers)
24+
25+
def get(self, property_id: str, transaction: Optional[Transaction] = None) -> Property:
26+
"""Get a property by ID."""
27+
headers = Transaction._build_transaction_header(transaction.id if transaction else None)
28+
29+
return self.client._make_request('GET', f'/api/v1/properties/{property_id}', headers=headers)
30+
31+
def update(self, property_id: str, data: Dict[str, Any], transaction: Optional[Transaction] = None) -> Property:
32+
"""Update a property."""
33+
headers = Transaction._build_transaction_header(transaction.id if transaction else None)
34+
35+
return self.client._make_request('PUT', f'/api/v1/properties/{property_id}', data, headers)
36+
37+
def delete(self, property_id: str, transaction: Optional[Transaction] = None) -> None:
38+
"""Delete a property."""
39+
headers = Transaction._build_transaction_header(transaction.id if transaction else None)
40+
41+
return self.client._make_request('DELETE', f'/api/v1/properties/{property_id}', headers=headers)
42+
43+
def get_values(self, property_id: str, transaction: Optional[Transaction] = None) -> PropertyValuesData:
44+
"""Get values data for a property."""
45+
headers = Transaction._build_transaction_header(transaction.id if transaction else None)
46+
47+
return self.client._make_request('GET', f'/api/v1/properties/{property_id}/values', headers=headers)

0 commit comments

Comments
 (0)