# odoo-fastapi-gateway

<p align="center"><em>A modular FastAPI library for integrating with Odoo via XML-RPC. Install as a package, plug in pre-built routers, and start building.</em></p>

<p align="center">
<a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.11+-blue.svg" alt="Python 3.11+"></a>
<a href="https://fastapi.tiangolo.com"><img src="https://img.shields.io/badge/FastAPI-0.100+-009688.svg" alt="FastAPI"></a>
<a href="https://www.odoo.com/documentation/18.0/developer/reference/external_api.html"><img src="https://img.shields.io/badge/Odoo-18-714B67.svg" alt="Odoo 18"></a>
</p>

---

## Odoo Version

This library is built and tested against **Odoo 18**. It uses the [Odoo 18 External API](https://www.odoo.com/documentation/18.0/developer/reference/external_api.html) via XML-RPC.

- Minimum supported version: **Odoo 18**
- Earlier versions (16, 17) may work but are not officially supported or tested.

## Quick Start

**1. Install**

```bash
python3 -m venv venv && source venv/bin/activate
pip install odoo-fastapi-gateway
```

**2. Configure** — Create a `.env` file with your Odoo host and required secret keys:

```bash
# Generate secret keys
python3 -c "import secrets; print(secrets.token_hex(32))"
```

```ini
ODOO_HOST=http://localhost:8069
ODOO_DB=mydb
JWT_SECRET_KEY=<generated-key>
AES_SECRET_KEY=<generated-key>
```

> Requires a running Odoo 18 instance at the configured `ODOO_HOST`.

**3. Create your app** — `app.py`:

```python
from odoo_api import create_app

app = create_app(
    rate_limits={"/api/login": "10/minute"},  # optional per-route overrides
)
```

See [docs/extending.md](docs/extending.md) for adding custom routers, rate limiting, and more.

**4. Run**

```bash
uvicorn app:app --reload
```

That's it — you now have login, CRUD, and execute endpoints for any Odoo model.

## Features

- **Plug & play routers** — Health, auth, and full CRUD included out of the box
- **JWT + AES-256-CBC** — Passwords encrypted in tokens, never stored in plain text
- **Pydantic validation** — Type-safe request/response models with limit enforcement
- **Async XML-RPC** — All Odoo calls run in a thread pool, non-blocking
- **Extensible** — Add custom routers using the same auth dependency

## API Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/api/health` | — | Health check |
| `POST` | `/api/login` | — | Authenticate with Odoo, returns JWT |
| `GET` | `/api/models/{model}/{record_id}` | Bearer | Read a single record |
| `POST` | `/api/models/{model}/search` | Bearer | Search/list records |
| `POST` | `/api/models/{model}` | Bearer | Create records |
| `PUT` | `/api/models/{model}` | Bearer | Update records |
| `DELETE` | `/api/models/{model}` | Bearer | Delete records |
| `POST` | `/api/models/{model}/execute/{func_name}` | Bearer | Call custom Odoo method |

> **Model name convention:** Use hyphens in URLs — converted to dots automatically.
> `/api/models/sale-order/search` → `sale.order`

## Configuration

Create a `.env` file (or set environment variables):

```ini
ODOO_HOST=http://localhost:8069
JWT_SECRET_KEY=<your-64-char-hex-key>
AES_SECRET_KEY=<your-64-char-hex-key>
```

Generate keys: `python3 -c "import secrets; print(secrets.token_hex(32))"`

| Variable | Default | Description |
|----------|---------|-------------|
| `ODOO_HOST` | `http://odoo:8069` | Odoo server URL |
| `ODOO_DB` | — *(required)* | Odoo database name |
| `JWT_SECRET_KEY` | — *(required)* | JWT signing key (min 32 chars) |
| `AES_SECRET_KEY` | — *(required)* | AES encryption key (min 32 chars) |
| `HASH_ALGORITHM` | `HS256` | JWT hash algorithm |
| `ACCESS_TOKEN_EXPIRE_MINUTES` | `None` | Token TTL (`None` = no expiry) |
| `LIMIT_SEARCH_RECORDS` | `50` | Max records per search |
| `LIMIT_CREATE_RECORDS` | `50` | Max records per create |
| `RATE_LIMIT` | `100/minute` | Rate limit string |
| `LIMITER_STORAGE_URI` | `None` | Redis URI for shared rate-limit counters (e.g. `redis://redis:6379`). Required for multi-worker deployments. Falls back to in-memory if unset. |
| `CORS_ORIGINS` | `*` | Allowed CORS origins (comma-separated, or `*` for all) |

## Installation

### From PyPI

```bash
pip install odoo-fastapi-gateway
```

### Development (editable)

```bash
git clone https://github.com/vdx-vn/odoo-api.git
cd odoo-api && pip install -e .
```

### Dependencies

Installed automatically:

`fastapi` · `uvicorn` · `PyJWT` · `cryptography` · `pydantic-settings` · `python-dotenv`

Optional (not included): `slowapi` (rate limiting) · `gunicorn` (production)

## Documentation

| Topic | Link |
|-------|------|
| Architecture diagrams | [docs/architecture.md](docs/architecture.md) |
| Public API reference | [docs/api-reference.md](docs/api-reference.md) |
| Extending the library | [docs/extending.md](docs/extending.md) |
| Security details | [docs/security.md](docs/security.md) |
| Deployment & Gunicorn | [docs/deployment.md](docs/deployment.md) |
| Publishing to PyPI | [docs/publishing.md](docs/publishing.md) |
| Request body examples | [docs/request-examples.md](docs/request-examples.md) |

## Project Structure

```
src/odoo_api/
├── __init__.py               # Public API exports
├── exceptions.py             # APIException, UnauthorizedException, DecryptionError
├── core/
│   ├── config.py             # AppSettings (pydantic-settings) + get_settings()
│   └── connection.py         # call_xmlrpc, execute, sanitize_model
├── dependencies/
│   ├── auth.py               # JWT creation, AES encrypt/decrypt, authenticate()
│   └── token.py              # get_logged_in_user dependency, auth models
├── models/
│   ├── base.py               # APIBaseModel (Pydantic base with settings access)
│   └── request.py            # Search, Create, Update, Delete, ExecuteFunction
└── routers/
    ├── __init__.py            # get_routers() helper
    ├── health.py              # GET /api/health
    ├── auth.py                # POST /api/login
    └── models.py              # CRUD + execute endpoints
```

## References

- [FastAPI](https://fastapi.tiangolo.com/)
- [Odoo 18 External API](https://www.odoo.com/documentation/18.0/developer/reference/external_api.html)
- [SlowAPI](https://github.com/laurentS/slowapi)
- [Gunicorn](https://docs.gunicorn.org/en/stable/settings.html)
