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
25 changes: 11 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Python library for the Extend API
[![Python Version](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![Documentation](https://img.shields.io/badge/docs-stoplight-blue)](https://paywithextend.stoplight.io/)

A Python client for the Extend API, providing a simple and intuitive interface for managing virtual cards, transactions,
and more.
Expand All @@ -14,9 +15,11 @@ and more.
- Create and manage virtual cards
- Handle recurring card operations
- Track transactions
- Full async/await support
- Type hints and validation
- Comprehensive test suite
- Expense management

## Documentation

For detailed API documentation, please visit our [Stoplight documentation](https://paywithextend.stoplight.io/).

## Installation

Expand All @@ -29,7 +32,7 @@ pip install extend-python
### From Source

```bash
git clone https://github.com/yourusername/extend-python.git
git clone https://github.com/paywithextend/extend-python.git
cd extend-python
pip install -e .
```
Expand Down Expand Up @@ -74,7 +77,7 @@ The following environment variables are required for integration tests and examp

1. Clone the repository:
```bash
git clone https://github.com/yourusername/extend-python.git
git clone https://github.com/paywithextend/extend-python.git
cd extend-python
```

Expand All @@ -101,12 +104,6 @@ The following environment variables are required for integration tests and examp
pytest tests/test_integration.py
```

5. Format code:
```bash
black .
isort .
```

## Testing

The project includes both unit tests and integration tests:
Expand All @@ -119,20 +116,20 @@ To run integration tests, make sure you have set up the required environment var

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to
discuss what you would like to change.
We welcome contributions from the community!

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request


## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## Acknowledgments

- [Extend API Documentation](https://docs.extend.com)
- [Extend API Documentation](https://paywithextend.stoplight.io/)
- [httpx](https://www.python-httpx.org/) for async HTTP requests
2 changes: 1 addition & 1 deletion extend/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
elif env == "prod":
from .config_prod import API_HOST, API_VERSION
else:
from .config_stage import API_HOST, API_VERSION
from .config_prod import API_HOST, API_VERSION

__all__ = [
"API_HOST",
Expand Down
62 changes: 34 additions & 28 deletions tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,34 +402,41 @@ class TestTransactionExpenseData:
@pytest.mark.asyncio
async def test_update_transaction_expense_data_with_specific_category_and_label(self, extend):
"""Test updating the expense data for a transaction using a specific expense category and label."""
# Retrieve available expense categories (active ones)
categories_response = await extend.expense_data.get_expense_categories(active=True)
assert "expenseCategories" in categories_response, "Response should include 'expenseCategories'"
expense_categories = categories_response["expenseCategories"]
assert expense_categories, "No expense categories available for testing"

# For this test, pick the first expense category
category = expense_categories[0]
category_id = category["id"]

# Retrieve the labels for the chosen expense category
labels_response = await extend.expense_data.get_expense_category_labels(
category_id=category_id,
page=0,
per_page=10
# Create a new expense category for testing
u = uuid.uuid4()
category_name = f"Integration Test Category {u}"
category_code = f"INTEG-CAT-{u}"
category_resp = await extend.expense_data.create_expense_category(
name=category_name,
code=category_code,
required=True,
active=True,
free_text_allowed=False,
)
assert "expenseLabels" in labels_response, "Response should include 'expenseLabels'"
expense_labels = labels_response["expenseLabels"]
assert expense_labels, "No expense labels available for the selected category"
category_id = category_resp["id"]
assert category_resp["name"] == category_name
assert category_resp["code"] == category_code

# Pick the first label from the list
label = expense_labels[0]
label_id = label["id"]
# Create a label under the category
u1 = uuid.uuid4()
label_name = f"Integration Label {u1}"
label_code = f"INTEG-LABEL-{u1}"
label_resp = await extend.expense_data.create_expense_category_label(
category_id=category_id,
name=label_name,
code=label_code,
active=True
)
label_id = label_resp["id"]
assert label_resp["name"] == label_name
assert label_resp["code"] == label_code

# Retrieve at least one transaction to update expense data
transactions_response = await extend.transactions.get_transactions(per_page=1)
assert transactions_response.get("transactions"), "No transactions available for testing expense data update"
transaction = transactions_response["transactions"][0]
assert "report" in transactions_response, "Response should include 'report'"
assert "transactions" in transactions_response["report"], "Response should include 'transactions'"
assert transactions_response["report"]["transactions"], "No transactions available for testing expense data update"
transaction = transactions_response["report"]["transactions"][0]
transaction_id = transaction["id"]

# Prepare the expense data payload with the specific category and label
Expand All @@ -447,11 +454,10 @@ async def test_update_transaction_expense_data_with_specific_category_and_label(

# Verify the response contains the transaction id and expected expense details
assert "id" in response, "Response should include the transaction id"
if "expenseDetails" in response:
# Depending on the API response, the structure might vary; adjust assertions accordingly
assert response["expenseDetails"] == update_payload["expenseDetails"], (
"Expense details in the response should match the update payload"
)
assert "expenseDetails" in response, "Response should include expenseDetails"
assert len(response["expenseDetails"]) == 1, "Response should contain exactly one expense detail"
assert response["expenseDetails"][0]["categoryId"] == category_id, "Category ID should match"
assert response["expenseDetails"][0]["labelId"] == label_id, "Label ID should match"


@pytest.mark.integration
Expand Down