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
69 changes: 69 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: Deploy Documentation

on:
push:
branches:
- main
- staging
- develop
tags:
- 'v*'
workflow_dispatch:

permissions:
contents: write

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0 # Fetch all history for mike versioning

- name: Configure Git Credentials
run: |
git config user.name github-actions[bot]
git config user.email 41898282+github-actions[bot]@users.noreply.github.com

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.x'
cache: 'pip'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[docs]"

- name: Deploy documentation (main branch)
if: github.ref == 'refs/heads/main'
run: |
mike deploy --push --update-aliases latest stable
mike set-default --push latest

- name: Deploy documentation (staging branch)
if: github.ref == 'refs/heads/staging'
run: |
mike deploy --push staging

- name: Deploy documentation (develop branch)
if: github.ref == 'refs/heads/develop'
run: |
mike deploy --push dev

- name: Set default alias when missing (develop)
if: github.ref == 'refs/heads/develop'
run: |
DEFAULT_ALIAS=$(mike list --remote 2>/dev/null | awk '/^\*/ {print $2}')
if [ -z "$DEFAULT_ALIAS" ]; then
mike set-default --push dev
fi

- name: Deploy documentation (version tag)
if: startsWith(github.ref, 'refs/tags/v')
run: |
VERSION=${GITHUB_REF#refs/tags/v}
mike deploy --push --update-aliases $VERSION stable
mike set-default --push $VERSION
43 changes: 43 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Publish

on:
push:
branches:
- main
- staging
workflow_dispatch:

jobs:
build-and-publish:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v5

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"

- name: Install uv
uses: astral-sh/setup-uv@v3

- name: Build distributions
run: |
rm -rf dist
uv build

- name: Publish to TestPyPI
if: github.ref == 'refs/heads/staging'
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.TEST_PYPI_API_TOKEN }}
repository-url: https://test.pypi.org/legacy/

- name: Publish to PyPI
if: github.ref == 'refs/heads/main'
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
62 changes: 62 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Tests

on:
push:
branches: [ main, develop, staging ]
pull_request:
branches: [ main, develop ]

jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.14", "3.12", "3.13", "3.9", "3.10", "3.11" ]

steps:
- uses: actions/checkout@v5

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]"

- name: Lint with flake8
run: |
# Stop the build if there are Python syntax errors or undefined names
flake8 src/tmo_api --count --select=E9,F63,F7,F82 --show-source --statistics
# Exit-zero treats all errors as warnings
flake8 src/tmo_api --count --exit-zero --max-line-length=88 --extend-ignore=E203,W503 --statistics

- name: Check formatting with black
run: |
black --check src/tmo_api tests/

- name: Check import sorting with isort
run: |
isort --check-only src/tmo_api tests/

- name: Type check with mypy
run: |
mypy src/tmo_api --ignore-missing-imports
continue-on-error: true

- name: Run tests with pytest
run: |
pytest tests/ -v --cov=tmo_api --cov-report=xml --cov-report=term

- name: Upload coverage to Codecov
if: matrix.python-version == '3.11'
uses: codecov/codecov-action@v5
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
file: ./coverage.xml
fail_ci_if_error: false
58 changes: 58 additions & 0 deletions docs/api-reference/client.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Client API Reference

Complete API reference for the `TMOClient` class.

## TMOClient

```python
class TMOClient:
def __init__(
self,
token: str,
database: str,
environment: Union[Environment, str] = Environment.US,
timeout: int = 30,
debug: bool = False
)
```

### Parameters

- **token** (`str`): Your API token from The Mortgage Office
- **database** (`str`): Your database name
- **environment** (`Union[Environment, str]`): API environment or custom URL (default: `Environment.US`)
- **timeout** (`int`): Request timeout in seconds (default: 30)
- **debug** (`bool`): Enable debug logging (default: False)

### Shares Resource Attributes

- **shares_pools**: `PoolsResource` - Shares pool operations
- **shares_partners**: `PartnersResource` - Shares partner operations
- **shares_distributions**: `DistributionsResource` - Shares distribution operations
- **shares_certificates**: `CertificatesResource` - Shares certificate operations
- **shares_history**: `HistoryResource` - Shares history operations

### Capital Resource Attributes

- **capital_pools**: `PoolsResource` - Capital pool operations
- **capital_partners**: `PartnersResource` - Capital partner operations
- **capital_distributions**: `DistributionsResource` - Capital distribution operations
- **capital_history**: `HistoryResource` - Capital history operations

### Methods

#### get(endpoint: str, params: Optional[Dict[str, Any]] = None) → Dict[str, Any]

Make a GET request.

#### post(endpoint: str, json: Optional[Dict[str, Any]] = None) → Dict[str, Any]

Make a POST request.

#### put(endpoint: str, json: Optional[Dict[str, Any]] = None) → Dict[str, Any]

Make a PUT request.

#### delete(endpoint: str) → Dict[str, Any]

Make a DELETE request.
123 changes: 123 additions & 0 deletions docs/api-reference/exceptions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Exceptions API Reference

## TMOException

Base exception for all TMO API errors.

```python
class TMOException(Exception):
def __init__(
self,
message: str,
error_number: Optional[int] = None
)
```

**Attributes:**
- `message` (str): The error message
- `error_number` (Optional[int]): TMO API error number if available

## AuthenticationError

Raised for authentication failures (401/403).

```python
class AuthenticationError(TMOException):
pass
```

**Usage:**
```python
from tmo_api import TMOClient, AuthenticationError

try:
client = TMOClient(token="invalid", database="test")
pools = client.shares_pools.list_all()
except AuthenticationError as e:
print(f"Authentication failed: {e.message}")
if e.error_number:
print(f"Error number: {e.error_number}")
```

## APIError

Raised when the API returns an error response.

```python
class APIError(TMOException):
pass
```

**Usage:**
```python
from tmo_api import TMOClient, APIError

try:
client = TMOClient(token="token", database="db")
pool = client.shares_pools.get_pool("INVALID")
except APIError as e:
print(f"API error: {e.message}")
if e.error_number:
print(f"Error number: {e.error_number}")
```

## ValidationError

Raised for client-side validation errors before making API calls.

```python
class ValidationError(TMOException):
pass
```

**Usage:**
```python
from tmo_api import TMOClient, ValidationError

try:
client = TMOClient(token="token", database="db")
pool = client.shares_pools.get_pool("") # Empty account
except ValidationError as e:
print(f"Validation error: {e.message}")
```

## NetworkError

Raised for network/connection errors (timeouts, connection failures).

```python
class NetworkError(TMOException):
pass
```

**Usage:**
```python
from tmo_api import TMOClient, NetworkError

try:
client = TMOClient(token="token", database="db", timeout=1)
pools = client.shares_pools.list_all()
except NetworkError as e:
print(f"Network error: {e.message}")
```

## Catching All Exceptions

You can catch all TMO API exceptions using the base class:

```python
import os
from tmo_api import TMOClient, TMOException

client = TMOClient(
token=os.environ["TMO_API_TOKEN"],
database=os.environ["TMO_DATABASE"]
)

try:
pools = client.shares_pools.list_all()
except TMOException as e:
print(f"TMO API error: {e.message}")
if hasattr(e, 'error_number') and e.error_number:
print(f"Error number: {e.error_number}")
```
Loading
Loading