pysequence-client 0.1.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.
@@ -0,0 +1,16 @@
1
+ Metadata-Version: 2.4
2
+ Name: pysequence-client
3
+ Version: 0.1.0
4
+ Summary: Unofficial Python client for the GetSequence API server
5
+ License: MIT
6
+ Author: Christian De Leon
7
+ Author-email: christian@deleon.me
8
+ Requires-Python: >=3.11
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Requires-Dist: httpx (>=0.28.1,<0.29.0)
16
+ Requires-Dist: pydantic (>=2.12.5,<3.0.0)
@@ -0,0 +1,20 @@
1
+ [project]
2
+ name = "pysequence-client"
3
+ version = "0.1.0"
4
+ description = "Unofficial Python client for the GetSequence API server"
5
+ authors = [
6
+ {name = "Christian De Leon", email = "christian@deleon.me"}
7
+ ]
8
+ license = {text = "MIT"}
9
+ requires-python = ">=3.11"
10
+ dependencies = [
11
+ "httpx (>=0.28.1,<0.29.0)",
12
+ "pydantic (>=2.12.5,<3.0.0)",
13
+ ]
14
+
15
+ [tool.poetry]
16
+ packages = [{include = "pysequence_client", from = "src"}]
17
+
18
+ [build-system]
19
+ requires = ["poetry-core>=2.0.0,<3.0.0"]
20
+ build-backend = "poetry.core.masonry.api"
@@ -0,0 +1,2 @@
1
+ from pysequence_client.client import SequenceApiClient
2
+ from pysequence_client.exceptions import ApiError
@@ -0,0 +1,113 @@
1
+ """HTTP client for the GetSequence API server."""
2
+
3
+ import logging
4
+ from typing import Any
5
+
6
+ import httpx
7
+ from pysequence_client.exceptions import ApiError
8
+
9
+
10
+ log = logging.getLogger(__name__)
11
+
12
+
13
+ class SequenceApiClient:
14
+ """Client for the GetSequence REST API server."""
15
+
16
+ def __init__(self, base_url: str, api_key: str, timeout: float = 30.0) -> None:
17
+ self._client = httpx.Client(
18
+ base_url=base_url,
19
+ headers={"X-API-Key": api_key},
20
+ timeout=timeout,
21
+ )
22
+
23
+ def _request(self, method: str, path: str, **kwargs: Any) -> Any:
24
+ resp = self._client.request(method, path, **kwargs)
25
+
26
+ if resp.status_code >= 400:
27
+ content_type = resp.headers.get("content-type", "")
28
+
29
+ if content_type.startswith("application/json"):
30
+ detail = resp.json().get("detail", resp.text)
31
+ else:
32
+ detail = resp.text
33
+
34
+ raise ApiError(resp.status_code, detail)
35
+
36
+ return resp.json()
37
+
38
+ # -- Pod operations --
39
+
40
+ def get_pods(self) -> list[dict[str, Any]]:
41
+ return self._request("GET", "/api/pods")
42
+
43
+ def get_total_balance(self) -> dict[str, Any]:
44
+ return self._request("GET", "/api/pods/balance")
45
+
46
+ def get_pod_balance(self, pod_name: str) -> dict[str, Any] | None:
47
+ try:
48
+ return self._request("GET", f"/api/pods/{pod_name}/balance")
49
+
50
+ except ApiError as e:
51
+ if e.status_code == 404:
52
+ return None
53
+
54
+ raise
55
+
56
+ def get_pod_detail(self, pod_id: str) -> dict[str, Any]:
57
+ return self._request("GET", f"/api/pods/detail/{pod_id}")
58
+
59
+ # -- Account operations --
60
+
61
+ def get_all_accounts(self) -> dict[str, Any]:
62
+ return self._request("GET", "/api/accounts")
63
+
64
+ # -- Activity operations --
65
+
66
+ def get_activity_summary(self) -> dict[str, Any]:
67
+ return self._request("GET", "/api/activity/summary")
68
+
69
+ def get_activity(
70
+ self,
71
+ first: int = 10,
72
+ statuses: list[str] | None = None,
73
+ directions: list[str] | None = None,
74
+ activity_types: list[str] | None = None,
75
+ ) -> dict[str, Any]:
76
+ params: dict[str, Any] = {"first": first}
77
+
78
+ if statuses:
79
+ params["statuses"] = ",".join(statuses)
80
+
81
+ if directions:
82
+ params["directions"] = ",".join(directions)
83
+
84
+ if activity_types:
85
+ params["activity_types"] = ",".join(activity_types)
86
+
87
+ return self._request("GET", "/api/activity", params=params)
88
+
89
+ def get_transfer_detail(self, transfer_id: str) -> dict[str, Any]:
90
+ return self._request("GET", f"/api/transfers/{transfer_id}")
91
+
92
+ # -- Transfer operations --
93
+
94
+ def transfer(
95
+ self,
96
+ source_id: str,
97
+ destination_id: str,
98
+ amount_cents: int,
99
+ description: str = "",
100
+ ) -> dict[str, Any]:
101
+ return self._request(
102
+ "POST",
103
+ "/api/transfers",
104
+ json={
105
+ "source_id": source_id,
106
+ "destination_id": destination_id,
107
+ "amount_cents": amount_cents,
108
+ "description": description,
109
+ },
110
+ )
111
+
112
+ def close(self) -> None:
113
+ self._client.close()
@@ -0,0 +1,8 @@
1
+
2
+ class ApiError(Exception):
3
+ """HTTP error from the GetSequence API server."""
4
+
5
+ def __init__(self, status_code: int, detail: str):
6
+ self.status_code = status_code
7
+ self.detail = detail
8
+ super().__init__(f"API error {status_code}: {detail}")
@@ -0,0 +1,70 @@
1
+ from pydantic import BaseModel
2
+
3
+
4
+ class Pod(BaseModel):
5
+ id: str
6
+ name: str
7
+ organization_id: str
8
+ balance_cents: int
9
+ balance: str
10
+
11
+
12
+ class Port(BaseModel):
13
+ id: str
14
+ name: str
15
+ organization_id: str
16
+ balance_cents: int
17
+ balance: str
18
+
19
+
20
+ class ExternalAccount(BaseModel):
21
+ id: str
22
+ name: str
23
+ organization_id: str
24
+ balance_cents: int
25
+ balance: str
26
+
27
+
28
+ class TotalBalance(BaseModel):
29
+ total_balance_cents: int
30
+ total_balance: str
31
+ pod_count: int
32
+
33
+
34
+ class TransferParty(BaseModel):
35
+ name: str
36
+
37
+
38
+ class Transfer(BaseModel):
39
+ id: str
40
+ status: str
41
+ amount: str
42
+ amount_cents: int
43
+ source: TransferParty
44
+ destination: TransferParty
45
+ direction: str
46
+ activity_type: str
47
+ created_at: str | None = None
48
+
49
+
50
+ class PageInfo(BaseModel):
51
+ end_cursor: str
52
+ has_next_page: bool
53
+
54
+
55
+ class ActivityPage(BaseModel):
56
+ transfers: list[Transfer]
57
+ page_info: PageInfo
58
+
59
+
60
+ class ActivitySummary(BaseModel):
61
+ transfer_count: int
62
+ rule_executions: int
63
+ total_incoming_cents: int
64
+ total_incoming: str
65
+
66
+
67
+ class AllAccounts(BaseModel):
68
+ pods: list[Pod]
69
+ ports: list[Port]
70
+ accounts: list[ExternalAccount]