Metadata-Version: 2.1
Name: mock-me
Version: 1.0.0
Summary: Instantly generate and run local FastAPI mock servers from JSON schemas
Keywords: mock,api,fastapi,testing,developer-tools,faker
License: MIT
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Testing
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
Project-URL: Homepage, https://github.com/mock-me/mock-me
Project-URL: Repository, https://github.com/mock-me/mock-me
Requires-Python: >=3.10
Requires-Dist: fastapi>=0.110.0
Requires-Dist: uvicorn[standard]>=0.29.0
Requires-Dist: faker>=24.0.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: watchfiles>=0.21.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: httpx>=0.27; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Description-Content-Type: text/markdown

# mock-me ⚡

> Instantly generate and run local FastAPI mock servers from JSON schemas — no backend code required.

```
mock-me start config.json
```

```
  ███╗   ███╗ ██████╗  ██████╗██╗  ██╗      ███╗   ███╗███████╗
  ████╗ ████║██╔═══██╗██╔════╝██║ ██╔╝      ████╗ ████║██╔════╝
  ██╔████╔██║██║   ██║██║     █████╔╝  ─── ██╔████╔██║█████╗
  ██║╚██╔╝██║██║   ██║██║     ██╔═██╗      ██║╚██╔╝██║██╔══╝
  ██║ ╚═╝ ██║╚██████╔╝╚██████╗██║  ██╗     ██║ ╚═╝ ██║███████╗
  ╚═╝     ╚═╝ ╚═════╝  ╚═════╝╚═╝  ╚═╝     ╚═╝     ╚═╝╚══════╝
  Instant FastAPI mock servers from JSON – no backend required
```

---

## Features

- **JSON-first** — one config file defines your entire mock API
- **Schema-aware fake data** — names, emails, UUIDs, dates, nested objects, arrays
- **All HTTP methods** — GET, POST, PUT, PATCH, DELETE
- **Conditional responses** — different responses based on path, query, body, or headers
- **Error simulation** — probability-based or condition-triggered errors
- **Configurable delays** — simulate real-world latency
- **Pagination** — automatic pagination envelope wrapping
- **Template strings** — `"ORD-{{int}}"`, `"{{userId}}"` reflect request params
- **Hot-reload** — watches config file and reloads on change
- **Auto OpenAPI docs** — Swagger UI + ReDoc generated automatically
- **Seeded randomness** — reproducible fake data across runs
- **Export** — generate a standalone FastAPI `server.py` with no mock-me dependency
- **Plugin system** — register custom data generators in Python

---

## Quickstart

### Install

```bash
pip install mock-me
# or from source:
git clone https://github.com/mock-me/mock-me
cd mock-me && pip install -e .
```

### Create a config

```bash
mock-me init --out config.json
```

### Start the server

```bash
mock-me start config.json
```

```
  mock-me v1.0.0
  ─────────────────────────────────────────
  My API  (dev)
  ─────────────────────────────────────────

  GET     /api/users                   [Users]
  POST    /api/users                   [Users]
  GET     /api/users/{id}              [Users]
  DELETE  /api/users/{id}              [Users]

  Swagger UI → http://localhost:8000/docs
  ReDoc      → http://localhost:8000/redoc
  Health     → http://localhost:8000/__health

  Listening on http://0.0.0.0:8000
```

### Options

```bash
mock-me start config.json --port 9000          # override port
mock-me start config.json --host 127.0.0.1     # override host
mock-me start config.json --reload             # watch config for changes
mock-me start config.json --seed 42            # reproducible data
```

---

## CLI Commands

| Command | Description |
|---------|-------------|
| `mock-me start config.json` | Start the mock server |
| `mock-me validate config.json` | Validate config without starting |
| `mock-me export config.json --out server.py` | Export a standalone FastAPI server |
| `mock-me init --out config.json` | Create a starter config file |

---

## Config Reference

### Top-Level Options

```json
{
  "name": "My API",
  "description": "Optional description",
  "version": "1.0.0",
  "host": "0.0.0.0",
  "port": 8000,
  "environment": "dev",
  "seed": 42,
  "globalDelay": 0.1,
  "globalHeaders": {
    "X-Server": "mock-me"
  },
  "pagination": { ... },
  "plugins": ["./my_plugin.py"],
  "endpoints": [ ... ]
}
```

| Field | Type | Description |
|-------|------|-------------|
| `name` | string | Server/API name (shown in Swagger) |
| `port` | int | Port to listen on (default: 8000) |
| `host` | string | Bind host (default: 0.0.0.0) |
| `seed` | int | Random seed for reproducible data |
| `globalDelay` | float | Seconds of delay added to every request |
| `globalHeaders` | object | Headers added to every response |
| `plugins` | string[] | Paths to custom generator plugins |

### Pagination

```json
"pagination": {
  "defaultPageSize": 10,
  "maxPageSize": 100,
  "pageParam": "page",
  "sizeParam": "size",
  "totalParam": "total",
  "dataParam": "items"
}
```

When an endpoint has `"paginated": true`, responses are wrapped:

```json
{
  "items": [...],
  "total": 143,
  "page": 1,
  "size": 10,
  "pages": 15,
  "hasNext": true,
  "hasPrev": false
}
```

---

## Endpoint Config

```json
{
  "path": "/api/users/{id}",
  "method": "GET",
  "description": "Get a user by ID",
  "tags": ["Users"],
  "delay": 0.2,
  "pathParams": { ... },
  "queryParams": { ... },
  "headerParams": { ... },
  "bodySchema": { ... },
  "response": { ... },
  "conditionalResponses": [ ... ],
  "errors": [ ... ]
}
```

---

## Field Schemas (Response Body)

Every field in `response.body` uses a schema:

```json
"fieldName": {
  "type": "string",
  "faker": "email"
}
```

### Supported Types

| Type | Description | Example |
|------|-------------|---------|
| `string` | String with optional faker rule | `{"type":"string","faker":"email"}` |
| `int` / `integer` | Integer with optional min/max | `{"type":"int","min":1,"max":100}` |
| `float` / `number` | Float with optional min/max | `{"type":"float","min":0.5,"max":9.99}` |
| `bool` / `boolean` | Random true/false | `{"type":"bool"}` |
| `uuid` | UUID v4 | `{"type":"uuid"}` |
| `date` / `datetime` | ISO datetime string | `{"type":"date"}` |
| `enum` | Random value from list | `{"type":"enum","values":["a","b","c"]}` |
| `object` | Nested object | `{"type":"object","properties":{...}}` |
| `array` | Array of items | `{"type":"array","items":{...},"minItems":2,"maxItems":5}` |

### Faker Rules

Use a shorthand string or a full rule object:

```json
"email": { "type": "string", "faker": "email" }
"name":  { "type": "string", "faker": "name" }
"bio":   { "type": "string", "faker": { "type": "paragraph" } }
```

**Available faker types:**

```
name, first_name, last_name, email, username, password, phone
address, street, city, state, country, zip
url, domain, ipv4, ipv6, mac
uuid, word, sentence, paragraph, text, slug
company, job, currency, color, hex_color
image_url, iban, ssn, license_plate, user_agent
mime_type, file_name, file_extension, lorem
```

### Static & Template Values

```json
"status":  { "type": "string", "value": "active" }
"orderId": { "type": "string", "template": "ORD-{{int}}" }
"userId":  { "type": "string", "template": "{{userId}}" }
```

Template variables resolve in this order:
1. Request path params (e.g. `{{userId}}` → `abc123`)
2. Query/body/header params
3. Faker methods (e.g. `{{name}}`, `{{email}}`)
4. Built-ins: `{{int}}`, `{{float}}`, `{{uuid}}`

### Nullable Fields

```json
"deletedAt": { "type": "date", "nullable": true }
```

~10% chance of returning `null`.

---

## Conditional Responses

Return different responses based on request input. Conditions use dotted paths:

| Key prefix | Matches |
|------------|---------|
| `path.userId` | Path parameter `userId` |
| `query.status` | Query param `?status=...` |
| `body.email` | Request body field `email` |
| `header.x-api-key` | Request header |

```json
"conditionalResponses": [
  {
    "when": { "path.id": "000" },
    "response": {
      "status": 404,
      "body": {
        "error": { "type": "string", "value": "Not found" }
      }
    }
  },
  {
    "when": { "query.role": "admin", "header.x-api-key": "*" },
    "response": {
      "status": 200,
      "body": { "admin": { "type": "bool", "value": true } }
    }
  }
]
```

Use `"*"` to check presence (any non-null value).

---

## Error Simulation

```json
"errors": [
  {
    "probability": 0.05,
    "response": {
      "status": 503,
      "body": { "error": { "type": "string", "value": "Service unavailable" } }
    }
  },
  {
    "trigger": { "body.email": "locked@example.com" },
    "response": {
      "status": 403,
      "body": { "error": { "type": "string", "value": "Account locked" } }
    }
  }
]
```

- `probability` — triggers randomly (0.05 = 5% of requests)
- `trigger` — triggers when condition matches (same syntax as `conditionalResponses.when`)

---

## Delays

```json
{
  "globalDelay": 0.05,
  "endpoints": [
    {
      "path": "/api/slow",
      "delay": 2.0,
      "response": {
        "delay": 0.5
      }
    }
  ]
}
```

Precedence: `response.delay` > `endpoint.delay` > `globalDelay`

---

## Plugin System

Create a Python file with a `GENERATORS` dict:

```python
# my_plugin.py
import random

def _product_sku():
    return f"SKU-{random.randint(10000, 99999)}"

def _geo_point():
    return {
        "lat": round(random.uniform(-90, 90), 6),
        "lng": round(random.uniform(-180, 180), 6)
    }

GENERATORS = {
    "product_sku": _product_sku,
    "geo_point": _geo_point,
}
```

Reference in config:

```json
{
  "plugins": ["./my_plugin.py"],
  "endpoints": [{
    "path": "/products",
    "response": {
      "body": {
        "sku":      { "type": "product_sku" },
        "location": { "type": "geo_point" }
      }
    }
  }]
}
```

---

## Export Standalone Server

Generate a self-contained FastAPI server with zero mock-me dependency:

```bash
mock-me export config.json --out server.py

# Run it anywhere:
pip install fastapi uvicorn faker
python server.py
```

---

## Built-in Endpoints

| Path | Description |
|------|-------------|
| `/__health` | Health check + server metadata |
| `/__config` | View the parsed config |
| `/docs` | Swagger UI |
| `/redoc` | ReDoc documentation |
| `/openapi.json` | OpenAPI schema |

---

## Example: Minimal Config

```json
{
  "name": "Blog API",
  "port": 8000,
  "endpoints": [
    {
      "path": "/posts",
      "method": "GET",
      "tags": ["Posts"],
      "response": {
        "status": 200,
        "paginated": true,
        "body": {
          "id":        { "type": "uuid" },
          "title":     { "type": "string", "faker": "sentence" },
          "content":   { "type": "string", "faker": "paragraph" },
          "author":    { "type": "string", "faker": "name" },
          "published": { "type": "bool" },
          "createdAt": { "type": "date" }
        }
      }
    }
  ]
}
```

See `sample_config.json` for a full e-commerce API with 14 endpoints, nested objects, conditionals, errors, and pagination.

---

## Project Structure

```
mock-me/
├── mock_me/
│   ├── __init__.py     # Package metadata
│   ├── models.py       # Pydantic config schema (ServerConfig, EndpointConfig, etc.)
│   ├── engine.py       # Data engine – fake data generation
│   ├── server.py       # FastAPI app builder – dynamic route registration
│   ├── loader.py       # JSON config loader + hot-reload watcher
│   ├── exporter.py     # Standalone server.py code generator
│   └── cli.py          # CLI entry point (start, validate, export, init)
├── sample_config.json  # Full e-commerce demo (14 endpoints)
├── minimal_config.json # Beginner example
├── example_plugin.py   # Custom generator plugin example
└── pyproject.toml
```

---

## License

MIT
