apimposter/README.md
nenzen 1b29aeb4f6
All checks were successful
Build, Test & Checkstyle / build (push) Successful in 46s
Add relative time and date (#6)
Adding the ability to set relative times and dates to be returned.

## How do test
You can test using the following yaml-file:
```yml
- method: "GET"
  path: "/"
  response:
    status: 200
    headers:
      Content-Type: "application/json"
    body:
      description: "Relative date/time placeholder examples"
      # ISO formats (default)
      currentTime: "{datetime+0d@08:00}"
      today: "{date+0d}"
      tomorrow: "{date+1d}"
      futureDateTime: "{datetime+2d4h30m}"
      pastDateTime: "{datetime-1d@15:30}"
      # RFC3339 formats
      currentTimeRfc: "{datetime+0d@08:00:rfc3339}"
      tomorrowRfc: "{datetime+1d@13:00:rfc3339}"
      # Custom formats
      todayCustom: "{date+0d:MMM dd, yyyy}"
      tomorrowCustom: "{date+1d:EEEE, MMMM d}"
      futureCustom: "{datetime+1d@15:30:yyyy-MM-dd HH:mm:ss}"
      timeOnly: "{datetime+0d@14:30:HH:mm:ss}"

- method: "GET"
  path: "/examples"
  response:
    status: 200
    headers:
      Content-Type: "application/json"
    body:
      calendar:
        - event: "Morning standup"
          time: "{datetime+1d@09:00}"
          date: "{date+1d:EEEE, MMM dd}"
        - event: "Client meeting"
          time: "{datetime+2d@14:30:rfc3339}"
          date: "{date+2d}"
        - event: "Weekly review"
          time: "{datetime+7d@16:00}"
          date: "{date+7d:MMM dd, yyyy}"
```

Reviewed-on: #6
Reviewed-by: Andreas Svanberg <andreass@dsv.su.se>
Co-authored-by: nenzen <nenzen@dsv.su.se>
Co-committed-by: nenzen <nenzen@dsv.su.se>
2025-11-03 23:38:55 +01:00

388 lines
10 KiB
Markdown

# APImposter 🎭
**A small, flexible API mock service designed with microservice environments in mind.**
---
## What is APImposter?
APImposter is an easy-to-use service built with Spring Boot, enabling quick mocking of API responses from various systems. It allows you to organize your mock data in YAML files.
---
## Features
- **Structured Mock Data:** Organize your mock data in YAML files.
- **Automatic URL Mapping:** Automatically generates URL structure based on your folder hierarchy and YAML file names.
- **Hot Reloading:** Automatically reloads mock data files when they are changed.
- **Dynamic Responses and Global Placeholders:** Supports dynamic values in responses based on URL parameters and you can define global placeholders to reuse values across multiple responses and systems.
- **Conditional Responses:** Return different mock responses based on request data (headers, query, body, or path).
- **Swagger UI:** Automatically generated Swagger UI from YAML files.
---
## Mocks Structure
Mocks are defined in YAML files and stored in a directory specified via `application.properties`. You can organize them in nested folders:
```
/mocks/
├── globals.yaml
├── system1/
│ ├── users.yaml
│ └── index.yaml
└── another-system/
├── endpoint1.yaml
└── subdir/
├── endpoint2.yaml
└── index.yaml
```
### URL Mapping
- `/system1/users.yaml` defines mocks under `/system1/users/...`
- `/system1/index.yaml` defines mocks under`/system1/...`)
- Multiple endpoints can be grouped in one file
- Folders in the YAML file structure become part of the URL path automatically.
### Grouped endpoints in `users.yaml`
```yaml
# mocks/system1/users.yaml
- method: GET
path: "/"
response:
status: 200
body:
users:
- id: 1
name: "Alice"
- id: 2
name: "Bob"
- method: GET
path: "/{id}"
response:
status: 200
body:
id: "{id:int}"
name: "User {id}"
- method: POST
path: "/"
response:
status: 200
body:
message: "User created"
```
### Base-level grouping via `index.yaml`
```yaml
# mocks/system1/index.yaml
- method: GET
path: "/"
response:
status: 200
body:
message: "Welcome to system1"
- method: GET
path: "/info"
response:
status: 200
body:
name: "System One"
version: "1.0"
```
### Placeholder Type Hints
You can include optional type hints in placeholders to ensure JSON values are rendered with correct types.
#### Supported types
| Type | Placeholder | Output |
|---------|------------------|-----------------|
| string | `{name}` | `"Alice"` |
| int | `{id:int}` | `42` *(number)* |
| float | `{price:float}` | `99.95` |
| boolean | `{enabled:bool}` | `true` |
If parsing fails (e.g. `abc` for an `int`), APImposter **falls back to the original string**.
### Relative Date/Time Placeholders
APImposter supports dynamic date and time placeholders that are calculated relative to the current time. This is perfect for testing calendar applications, scheduling systems, or any time-sensitive mock data.
#### Supported formats
| Format | Example | Description |
|--------|---------|-------------|
| `{datetime+/-offset}` | `{datetime+2d4h30m}` | DateTime with flexible time offset |
| `{datetime+/-Nd@HH:MM}` | `{datetime+1d@13:30}` | DateTime N days from now at specific time |
| `{date+/-Nd}` | `{date-7d}` | Date N days from now |
#### Format specifiers (optional)
Add a format specifier after a colon to control the output format:
| Specifier | Description | Example Output |
|-----------|-------------|----------------|
| `iso` (default) | ISO standard format | `2025-09-03T15:30:00` |
| `rfc3339` | RFC 3339 with UTC timezone | `2025-09-03T15:30:00Z` |
| Custom pattern | Any valid DateTimeFormatter pattern | `Sep 03, 2025` |
#### Examples
```yaml
# Basic relative dates (ISO format)
- method: GET
path: "/events"
response:
body:
todayMeeting: "{datetime+0d@08:00}" # Today at 8:00 AM
tomorrowLunch: "{datetime+1d@13:00}" # Tomorrow at 1:00 PM
nextWeek: "{date+7d}" # Date 7 days from now
pastEvent: "{datetime-2d4h30m}" # 2 days, 4h, 30m ago
yesterday: "{date-1d}" # Yesterday's date
# Different output formats
- method: GET
path: "/calendar"
response:
body:
# RFC3339 format
eventTimeRfc: "{datetime+1d@15:30:rfc3339}" # "2025-09-03T15:30:00Z"
# Custom formats
eventDate: "{date+1d:MMM dd, yyyy}" # "Sep 03, 2025"
timeOnly: "{datetime+0d@14:30:HH:mm}" # "14:30"
shortDate: "{date+7d:MM/dd/yy}" # "09/09/25"
```
#### Time components for offset format
- `d` = days
- `h` = hours
- `m` = minutes
All components are optional and can be combined: `{datetime+1d2h}`, `{datetime+30m}`, `{datetime-4h15m}`
**Note:** If a custom format is invalid, the system automatically falls back to ISO format to prevent errors.
---
## Example Requests and Responses
| YAML file | Request | Response Example |
|------------------------------------|----------------------------------|----------------------------------------------|
| `system1/users.yaml` | `GET /system1/users` | `{ "users": [{ "id": 1 }, { "id": 2 }] }` |
| `system1/users.yaml` | `GET /system1/users/42` | `{ "id": 42, "name": "User 42" }` |
| `system1/users.yaml` | `POST /system1/users` | `{ "message": "User created" }` |
| `system1/index.yaml` | `GET /system1` | `{ "message": "Welcome to system1" }` |
| `system1/index.yaml` | `GET /system1/info` | `{ "name": "System One", "version": "1.0" }` |
| `another-system/subdir/index.yaml` | `GET /another-system/subdir/...` | *(Defined response in file)* |
---
## Globals
Global values can be defined in a dedicated file `globals.yaml`:
```yaml
# mocks/globals.yaml
globals:
currentUserId: "user-123"
```
You can reference global values using `{globals.key}` or `{globals.key:type}` in responses.
---
## Conditional Responses
APImposter supports **conditional responses**, allowing different mock responses to be returned **based on request values** - query parameters, headers, path variables, or body fields.
This is useful for simulating realistic API behavior, such as authentication, filtering, or alternate success/error outcomes.
### Supported condition types:
- `path`: matches against path variables
- `query`: matches query parameters (`?filter=active`)
- `headers`: matches HTTP headers (case-insensitive)
- `body`: matches top-level JSON body fields (for POST/PUT)
### Example: conditionals by path
```yaml
- method: "GET"
path: "/users/{id}"
conditionalResponses:
- conditions:
path:
id: "1"
response:
status: 200
headers:
Content-Type: application/json
body:
id: 1
name: "Lennart"
- conditions:
path:
id: "2"
response:
status: 200
headers:
Content-Type: application/json
body:
id: 2
name: "Kurre"
response:
status: 404
headers:
Content-Type: application/json
body:
error: "User not found"
```
### Example: conditionals by header
```yaml
- method: "GET"
path: "/secure"
conditionalResponses:
- conditions:
headers:
authorization: "Bearer abc123"
response:
status: 200
headers:
Content-Type: application/json
body:
message: "Access granted"
response:
status: 401
headers:
Content-Type: application/json
body:
error: "Unauthorized"
```
### Example: conditionals by query parameter
```yaml
- method: "GET"
path: "/products"
conditionalResponses:
- conditions:
query:
category: "books"
response:
status: 200
headers: { Content-Type: "application/json" }
body:
products:
- id: "b1"
name: "Data Structures and Algorithms in Java"
- conditions:
query:
category: "tech"
response:
status: 200
headers: { Content-Type: "application/json" }
body:
products:
- id: "t1"
name: "Keyboard"
response:
status: 200
headers: { Content-Type: "application/json" }
body:
products: []
```
### Example: conditionals by request body
```yaml
- method: "POST"
path: "/login"
conditionalResponses:
- conditions:
body:
username: "admin"
password: "secret"
response:
status: 200
headers:
Content-Type: application/json
body:
token: "abc123"
response:
status: 403
headers:
Content-Type: application/json
body:
error: "Invalid credentials"
```
### Testing with `curl`
```bash
curl http://localhost:8080/mocks/users/1
curl -H "Authorization: Bearer abc123" http://localhost:8080/mocks/secure
curl "http://localhost:8080/mocks/products?category=books"
curl -X POST http://localhost:8080/mocks/login \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "secret"}'
```
Conditions are evaluated **in order**, and the **first matching block wins**. If no condition matches, the fallback `response:` is used.
---
## Configuration
In `application.properties` you can configure the following properties:
- `mock.file-path`: The path to the directory containing your mock data YAML files.
- `mock.reload-interval-ms`: The interval in milliseconds at which the service checks for changes in the mock data files.
- `mock.base-path`: The base path for the mock service. This is prepended to all generated URLs. Default is `/mocks`.
- `mock.default-delay-ms`: The default delay in milliseconds for responses. This can be overridden in the YAML files.
---
## Swagger UI
OpenAPI Specification is generated from the mock data YAML files and can
be accessed via Swagger UI. You will find it at:
```
http://localhost:8080/swagger
```
---
## Quick Start
Clone the project and run it simply with:
```shell
./mvnw spring-boot:run
```
or with Docker:
```shell
docker-compose up
```
---
🎭 **Good luck and happy mocking!** 🎭