Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ async def main():

# Get all transactions
response = await client.transactions.get_transactions()
print("Transactions:", response["transactions"])
print("Transactions:", response["report"]["transactions"])


# Run the async function
Expand Down
16 changes: 9 additions & 7 deletions extend/resources/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,28 @@ async def _request(
params: Optional[Dict] = None,
data: Optional[Dict[str, Any]] = None,
files: Optional[Dict[str, Any]] = None,
base_url_override: Optional[str] = None,
) -> Any:
if params is not None:
params = {k: v for k, v in params.items() if v is not None}
match method:
case "get":
return await self._api_client.get(self.build_full_path(path), params)
return await self._api_client.get(self.build_full_path(path, base_url_override), params)
case "post":
return await self._api_client.post(self.build_full_path(path), params)
return await self._api_client.post(self.build_full_path(path, base_url_override), params)
case "put":
return await self._api_client.put(self.build_full_path(path), params)
return await self._api_client.put(self.build_full_path(path, base_url_override), params)
case "patch":
return await self._api_client.patch(self.build_full_path(path), params)
return await self._api_client.patch(self.build_full_path(path, base_url_override), params)
case "post_multipart":
return await self._api_client.post_multipart(
self.build_full_path(path),
self.build_full_path(path, base_url_override),
data=data,
files=files
)
case _:
raise ValueError(f"Unsupported HTTP method: {method}")

def build_full_path(self, path):
return f"{self._base_url}{path or ''}"
def build_full_path(self, path, base_url_override):
base = base_url_override if base_url_override is not None else self._base_url
return f"{base}{path or ''}"
2 changes: 1 addition & 1 deletion extend/resources/transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ async def get_transactions(
"sort": sort_field,
}

return await self._request(method="get", params=params)
return await self._request(method="get", params=params, base_url_override='/reports/transactions/v2')

async def get_transaction(self, transaction_id: str) -> Dict:
"""Get detailed information about a specific transaction.
Expand Down
4 changes: 2 additions & 2 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,13 +250,13 @@ async def test_cancel_and_close_virtual_card(extend, mocker, mock_virtual_card):
@pytest.mark.asyncio
async def test_get_transactions(extend, mocker, mock_transaction):
mock_response: Any = {
"transactions": [mock_transaction, mock_transaction]
"report": {"transactions": [mock_transaction, mock_transaction]}
}

mocker.patch.object(extend._api_client, 'get', return_value=mock_response)

response = await extend.transactions.get_transactions()
assert len(response["transactions"]) == 2
assert len(response["report"]["transactions"]) == 2


# Additional recurrence validation tests
Expand Down
23 changes: 13 additions & 10 deletions tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,12 +184,13 @@ async def test_list_transactions(self, extend):

# Verify response structure
assert isinstance(response, dict), "Response should be a dictionary"
assert "transactions" in response, "Response should contain 'transactions' key"
assert isinstance(response["transactions"], list), "Transactions should be a list"
assert "transactions" in response["report"], "Response should contain 'report' key"
assert isinstance(response["report"]["transactions"], list), "Transactions should be a list"

# If there are transactions, verify their structure
if response["transactions"]:
transaction = response["transactions"][0]
if response["report"] and response["report"]['transactions']:
transactions = response["report"]['transactions']
transaction = transactions[0]
required_fields = ["id", "status", "virtualCardId", "merchantName", "type", "authBillingAmountCents"]
for field in required_fields:
assert field in transaction, f"Transaction should contain '{field}' field"
Expand All @@ -216,10 +217,11 @@ async def test_list_transactions_with_sorting(self, extend):

# Verify response contains transactions and basic structure
assert isinstance(response, dict), f"Response for sort {sort_field} should be a dictionary"
assert "transactions" in response, f"Response for sort {sort_field} should contain 'transactions' key"
assert "report" in response, f"Response for sort {sort_field} should contain 'report' key"
assert "transactions" in response["report"], f"Report should contain 'transactions' key"

# If we have enough data, test opposite sort direction for comparison
if len(response["transactions"]) > 1:
if len(response["report"]["transactions"]) > 1:
# Determine the field name and opposite sort field
is_desc = sort_field.startswith("-")
field_name = sort_field[1:] if is_desc else sort_field
Expand All @@ -232,8 +234,8 @@ async def test_list_transactions_with_sorting(self, extend):
)

# Get IDs in both sort orders for comparison
sorted_ids = [tx["id"] for tx in response["transactions"]]
opposite_sorted_ids = [tx["id"] for tx in opposite_response["transactions"]]
sorted_ids = [tx["id"] for tx in response["report"]["transactions"]]
opposite_sorted_ids = [tx["id"] for tx in opposite_response["report"]["transactions"]]

# If we have the same set of transactions in both responses,
# verify that different sort directions produce different orders
Expand Down Expand Up @@ -469,8 +471,9 @@ async def test_create_receipt_attachment(self, extend):

# Retrieve a valid transaction id from existing transactions
transactions_response = await extend.transactions.get_transactions(page=0, per_page=1)
assert transactions_response.get("transactions"), "No transactions available for testing receipt attachment"
transaction_id = transactions_response["transactions"][0]["id"]
assert transactions_response["report"][
"transactions"], "No transactions available for testing receipt attachment"
transaction_id = transactions_response["report"]["transactions"][0]["id"]

# Call the receipt attachment upload method
response = await extend.receipt_attachments.create_receipt_attachment(
Expand Down