Skip to content

Commit 19a331a

Browse files
jfrench9claude
andauthored
Add Ledger API, Document Management, and Search endpoints to client library (#70)
## Summary This PR introduces comprehensive client library support for the Ledger API, Document Management, and Search functionality. It adds 40 new files (6,629 lines) spanning API endpoint definitions, data models, and a high-level `DocumentClient` extension for simplified document operations. ## Key Accomplishments ### Ledger API (`api/ledger/`) - **Account management**: List ledger accounts with filtering, retrieve account tree hierarchy - **Transaction support**: List and retrieve ledger transactions with pagination and filtering - **Financial reporting**: Get ledger summary and trial balance data - New models: `AccountResponse`, `AccountTreeNode`, `LedgerEntryResponse`, `LedgerLineItemResponse`, `LedgerSummaryResponse`, `LedgerTransactionDetailResponse`, `LedgerTransactionSummaryResponse`, `TrialBalanceRow`, and associated list/response wrappers - Added `account_type` field to `AccountResponse`, `AccountTreeNode`, and `TrialBalanceRow` models ### Document Management (`api/documents/`) - **CRUD operations**: Upload (single and bulk), list, and delete documents - **`DocumentClient` extension**: High-level client that wraps raw API endpoints for ergonomic document management, integrated into `RoboSystemsExtensions` - New models: `DocumentListItem`, `DocumentUploadRequest`, `BulkDocumentUploadRequest`, and corresponding response models including error handling types ### Search API (`api/search/`) - **Document search**: Full search capability with `SearchRequest` model supporting additional filtering options - **Section retrieval**: Fetch specific document sections by ID - New models: `SearchHit`, `SearchRequest`, `SearchResponse`, `DocumentSection` ### Shared Infrastructure - `PaginationInfo` model for consistent paginated responses across all endpoints - All models registered in `models/__init__.py` for clean public API surface ## Breaking Changes None. This is a purely additive feature branch — no existing files were removed or had breaking modifications. The only modified files (`extensions.py` and `models/__init__.py`) received additive changes (new imports/registrations). ## Testing Notes - Verify that all new API endpoints correctly serialize/deserialize requests and responses against the live or mocked API - Test `DocumentClient` extension methods (upload, bulk upload, list, delete) end-to-end - Validate ledger filtering and pagination parameters are properly passed through to API calls - Confirm `SearchRequest` additional filtering options work as expected - Ensure the `account_type` field is correctly populated across account and trial balance models ## Infrastructure Considerations - This significantly expands the client library's API surface — documentation should be updated to cover the new Ledger, Document, and Search modules - The bulk upload endpoint may require attention to payload size limits and timeout configurations - New API endpoints imply corresponding backend services must be deployed and available before client consumers adopt this version --- 🤖 Generated with [Claude Code](https://claude.ai/code) **Branch Info:** - Source: `feature/ledger-api` - Target: `main` - Type: feature Co-Authored-By: Claude <noreply@anthropic.com>
2 parents 31950e0 + 543e56d commit 19a331a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+6629
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Contains endpoint functions for accessing the API"""
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
from http import HTTPStatus
2+
from typing import Any, cast
3+
from urllib.parse import quote
4+
5+
import httpx
6+
7+
from ... import errors
8+
from ...client import AuthenticatedClient, Client
9+
from ...models.http_validation_error import HTTPValidationError
10+
from ...types import Response
11+
12+
13+
def _get_kwargs(
14+
graph_id: str,
15+
document_id: str,
16+
) -> dict[str, Any]:
17+
_kwargs: dict[str, Any] = {
18+
"method": "delete",
19+
"url": "/v1/graphs/{graph_id}/documents/{document_id}".format(
20+
graph_id=quote(str(graph_id), safe=""),
21+
document_id=quote(str(document_id), safe=""),
22+
),
23+
}
24+
25+
return _kwargs
26+
27+
28+
def _parse_response(
29+
*, client: AuthenticatedClient | Client, response: httpx.Response
30+
) -> Any | HTTPValidationError | None:
31+
if response.status_code == 204:
32+
response_204 = cast(Any, None)
33+
return response_204
34+
35+
if response.status_code == 422:
36+
response_422 = HTTPValidationError.from_dict(response.json())
37+
38+
return response_422
39+
40+
if client.raise_on_unexpected_status:
41+
raise errors.UnexpectedStatus(response.status_code, response.content)
42+
else:
43+
return None
44+
45+
46+
def _build_response(
47+
*, client: AuthenticatedClient | Client, response: httpx.Response
48+
) -> Response[Any | HTTPValidationError]:
49+
return Response(
50+
status_code=HTTPStatus(response.status_code),
51+
content=response.content,
52+
headers=response.headers,
53+
parsed=_parse_response(client=client, response=response),
54+
)
55+
56+
57+
def sync_detailed(
58+
graph_id: str,
59+
document_id: str,
60+
*,
61+
client: AuthenticatedClient,
62+
) -> Response[Any | HTTPValidationError]:
63+
"""Delete Document
64+
65+
Delete a document and all its sections.
66+
67+
Args:
68+
graph_id (str):
69+
document_id (str):
70+
71+
Raises:
72+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
73+
httpx.TimeoutException: If the request takes longer than Client.timeout.
74+
75+
Returns:
76+
Response[Any | HTTPValidationError]
77+
"""
78+
79+
kwargs = _get_kwargs(
80+
graph_id=graph_id,
81+
document_id=document_id,
82+
)
83+
84+
response = client.get_httpx_client().request(
85+
**kwargs,
86+
)
87+
88+
return _build_response(client=client, response=response)
89+
90+
91+
def sync(
92+
graph_id: str,
93+
document_id: str,
94+
*,
95+
client: AuthenticatedClient,
96+
) -> Any | HTTPValidationError | None:
97+
"""Delete Document
98+
99+
Delete a document and all its sections.
100+
101+
Args:
102+
graph_id (str):
103+
document_id (str):
104+
105+
Raises:
106+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
107+
httpx.TimeoutException: If the request takes longer than Client.timeout.
108+
109+
Returns:
110+
Any | HTTPValidationError
111+
"""
112+
113+
return sync_detailed(
114+
graph_id=graph_id,
115+
document_id=document_id,
116+
client=client,
117+
).parsed
118+
119+
120+
async def asyncio_detailed(
121+
graph_id: str,
122+
document_id: str,
123+
*,
124+
client: AuthenticatedClient,
125+
) -> Response[Any | HTTPValidationError]:
126+
"""Delete Document
127+
128+
Delete a document and all its sections.
129+
130+
Args:
131+
graph_id (str):
132+
document_id (str):
133+
134+
Raises:
135+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
136+
httpx.TimeoutException: If the request takes longer than Client.timeout.
137+
138+
Returns:
139+
Response[Any | HTTPValidationError]
140+
"""
141+
142+
kwargs = _get_kwargs(
143+
graph_id=graph_id,
144+
document_id=document_id,
145+
)
146+
147+
response = await client.get_async_httpx_client().request(**kwargs)
148+
149+
return _build_response(client=client, response=response)
150+
151+
152+
async def asyncio(
153+
graph_id: str,
154+
document_id: str,
155+
*,
156+
client: AuthenticatedClient,
157+
) -> Any | HTTPValidationError | None:
158+
"""Delete Document
159+
160+
Delete a document and all its sections.
161+
162+
Args:
163+
graph_id (str):
164+
document_id (str):
165+
166+
Raises:
167+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
168+
httpx.TimeoutException: If the request takes longer than Client.timeout.
169+
170+
Returns:
171+
Any | HTTPValidationError
172+
"""
173+
174+
return (
175+
await asyncio_detailed(
176+
graph_id=graph_id,
177+
document_id=document_id,
178+
client=client,
179+
)
180+
).parsed
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
from http import HTTPStatus
2+
from typing import Any
3+
from urllib.parse import quote
4+
5+
import httpx
6+
7+
from ... import errors
8+
from ...client import AuthenticatedClient, Client
9+
from ...models.document_list_response import DocumentListResponse
10+
from ...models.http_validation_error import HTTPValidationError
11+
from ...types import UNSET, Response, Unset
12+
13+
14+
def _get_kwargs(
15+
graph_id: str,
16+
*,
17+
source_type: None | str | Unset = UNSET,
18+
) -> dict[str, Any]:
19+
params: dict[str, Any] = {}
20+
21+
json_source_type: None | str | Unset
22+
if isinstance(source_type, Unset):
23+
json_source_type = UNSET
24+
else:
25+
json_source_type = source_type
26+
params["source_type"] = json_source_type
27+
28+
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
29+
30+
_kwargs: dict[str, Any] = {
31+
"method": "get",
32+
"url": "/v1/graphs/{graph_id}/documents".format(
33+
graph_id=quote(str(graph_id), safe=""),
34+
),
35+
"params": params,
36+
}
37+
38+
return _kwargs
39+
40+
41+
def _parse_response(
42+
*, client: AuthenticatedClient | Client, response: httpx.Response
43+
) -> DocumentListResponse | HTTPValidationError | None:
44+
if response.status_code == 200:
45+
response_200 = DocumentListResponse.from_dict(response.json())
46+
47+
return response_200
48+
49+
if response.status_code == 422:
50+
response_422 = HTTPValidationError.from_dict(response.json())
51+
52+
return response_422
53+
54+
if client.raise_on_unexpected_status:
55+
raise errors.UnexpectedStatus(response.status_code, response.content)
56+
else:
57+
return None
58+
59+
60+
def _build_response(
61+
*, client: AuthenticatedClient | Client, response: httpx.Response
62+
) -> Response[DocumentListResponse | HTTPValidationError]:
63+
return Response(
64+
status_code=HTTPStatus(response.status_code),
65+
content=response.content,
66+
headers=response.headers,
67+
parsed=_parse_response(client=client, response=response),
68+
)
69+
70+
71+
def sync_detailed(
72+
graph_id: str,
73+
*,
74+
client: AuthenticatedClient,
75+
source_type: None | str | Unset = UNSET,
76+
) -> Response[DocumentListResponse | HTTPValidationError]:
77+
"""List Documents
78+
79+
List indexed documents for a graph.
80+
81+
Args:
82+
graph_id (str):
83+
source_type (None | str | Unset):
84+
85+
Raises:
86+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
87+
httpx.TimeoutException: If the request takes longer than Client.timeout.
88+
89+
Returns:
90+
Response[DocumentListResponse | HTTPValidationError]
91+
"""
92+
93+
kwargs = _get_kwargs(
94+
graph_id=graph_id,
95+
source_type=source_type,
96+
)
97+
98+
response = client.get_httpx_client().request(
99+
**kwargs,
100+
)
101+
102+
return _build_response(client=client, response=response)
103+
104+
105+
def sync(
106+
graph_id: str,
107+
*,
108+
client: AuthenticatedClient,
109+
source_type: None | str | Unset = UNSET,
110+
) -> DocumentListResponse | HTTPValidationError | None:
111+
"""List Documents
112+
113+
List indexed documents for a graph.
114+
115+
Args:
116+
graph_id (str):
117+
source_type (None | str | Unset):
118+
119+
Raises:
120+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
121+
httpx.TimeoutException: If the request takes longer than Client.timeout.
122+
123+
Returns:
124+
DocumentListResponse | HTTPValidationError
125+
"""
126+
127+
return sync_detailed(
128+
graph_id=graph_id,
129+
client=client,
130+
source_type=source_type,
131+
).parsed
132+
133+
134+
async def asyncio_detailed(
135+
graph_id: str,
136+
*,
137+
client: AuthenticatedClient,
138+
source_type: None | str | Unset = UNSET,
139+
) -> Response[DocumentListResponse | HTTPValidationError]:
140+
"""List Documents
141+
142+
List indexed documents for a graph.
143+
144+
Args:
145+
graph_id (str):
146+
source_type (None | str | Unset):
147+
148+
Raises:
149+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
150+
httpx.TimeoutException: If the request takes longer than Client.timeout.
151+
152+
Returns:
153+
Response[DocumentListResponse | HTTPValidationError]
154+
"""
155+
156+
kwargs = _get_kwargs(
157+
graph_id=graph_id,
158+
source_type=source_type,
159+
)
160+
161+
response = await client.get_async_httpx_client().request(**kwargs)
162+
163+
return _build_response(client=client, response=response)
164+
165+
166+
async def asyncio(
167+
graph_id: str,
168+
*,
169+
client: AuthenticatedClient,
170+
source_type: None | str | Unset = UNSET,
171+
) -> DocumentListResponse | HTTPValidationError | None:
172+
"""List Documents
173+
174+
List indexed documents for a graph.
175+
176+
Args:
177+
graph_id (str):
178+
source_type (None | str | Unset):
179+
180+
Raises:
181+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
182+
httpx.TimeoutException: If the request takes longer than Client.timeout.
183+
184+
Returns:
185+
DocumentListResponse | HTTPValidationError
186+
"""
187+
188+
return (
189+
await asyncio_detailed(
190+
graph_id=graph_id,
191+
client=client,
192+
source_type=source_type,
193+
)
194+
).parsed

0 commit comments

Comments
 (0)