Organize documentation by moving requirements (HighLevelReqs, Component-*, lmxproxy_protocol) to docs/requirements/ and test infrastructure docs to docs/test_infra/. Updates all cross-references in README, CLAUDE.md, infra/README, component docs, and 23 plan files.
6.8 KiB
Test Infrastructure: REST API Server (Flask)
Overview
The test REST API server is a lightweight Python/Flask application that provides HTTP endpoints matching the patterns used by ScadaLink's External System Gateway and Inbound API components. It supports simple parameter/response methods, complex nested object/list methods, authentication, and error simulation.
Image & Ports
- Image: Custom build from
infra/restapi/Dockerfile(Python 3.13 + Flask) - API port: 5200
Configuration
| Setting | Value | Description |
|---|---|---|
API_NO_AUTH |
0 |
Set to 1 to disable API key authentication |
PORT |
5200 |
Server listen port |
Authentication
The server validates requests using one of two methods:
- API Key:
X-API-Key: scadalink-test-key-1header - Basic Auth: Any username/password (accepts all credentials)
The GET /api/Ping endpoint is always unauthenticated (health check).
Auth can be disabled entirely by setting API_NO_AUTH=1 in the Docker Compose environment or passing --no-auth when running directly.
For appsettings.Development.json (External System Gateway):
{
"ExternalSystems": {
"TestApi": {
"BaseUrl": "http://localhost:5200",
"AuthMode": "ApiKey",
"ApiKey": "scadalink-test-key-1"
}
}
}
Endpoints
Simple Methods
| Method | Path | HTTP | Params | Response | Description |
|---|---|---|---|---|---|
| Ping | /api/Ping |
GET | — | {"pong": true} |
Health check (no auth) |
| Add | /api/Add |
POST | {"a": 5, "b": 3} |
{"result": 8} |
Add two numbers |
| Multiply | /api/Multiply |
POST | {"a": 4, "b": 7} |
{"result": 28} |
Multiply two numbers |
| Echo | /api/Echo |
POST | {"message": "hello"} |
{"message": "hello"} |
Echo back input |
| GetStatus | /api/GetStatus |
POST | {} |
{"status": "running", "uptime": 123.4} |
Server status |
Complex Methods (nested objects + lists)
| Method | Path | HTTP | Description |
|---|---|---|---|
| GetProductionReport | /api/GetProductionReport |
POST | Production report with line details |
| GetRecipe | /api/GetRecipe |
POST | Recipe with ingredients list |
| SubmitBatch | /api/SubmitBatch |
POST | Submit batch with items (complex input) |
| GetEquipmentStatus | /api/GetEquipmentStatus |
POST | Equipment list with nested status objects |
Error Simulation
| Method | Path | HTTP | Params | Description |
|---|---|---|---|---|
| SimulateTimeout | /api/SimulateTimeout |
POST | {"seconds": 5} |
Delay response (max 60s) |
| SimulateError | /api/SimulateError |
POST | {"code": 500} |
Return specified HTTP error (400–599) |
Method Discovery
| Method | Path | HTTP | Description |
|---|---|---|---|
| methods | /api/methods |
GET | List all available methods with signatures |
Response Examples
GetProductionReport ({"siteId": "SiteA", "startDate": "2026-03-01", "endDate": "2026-03-16"}):
{
"siteName": "Site SiteA",
"totalUnits": 14250,
"lines": [
{ "lineName": "Line-1", "units": 8200, "efficiency": 92.5 },
{ "lineName": "Line-2", "units": 6050, "efficiency": 88.1 }
]
}
GetRecipe ({"recipeId": "R-100"}):
{
"recipeId": "R-100",
"name": "Standard Mix",
"version": 3,
"ingredients": [
{ "name": "Material-A", "quantity": 45.0, "unit": "kg" },
{ "name": "Material-B", "quantity": 12.5, "unit": "L" }
]
}
SubmitBatch (complex input with items list):
{
"siteId": "SiteA",
"recipeId": "R-100",
"items": [
{ "materialId": "MAT-001", "quantity": 45.0, "lotNumber": "LOT-2026-001" },
{ "materialId": "MAT-002", "quantity": 12.5, "lotNumber": "LOT-2026-002" }
]
}
Response: {"batchId": "BATCH-A1B2C3D4", "accepted": true, "itemCount": 2}
GetEquipmentStatus ({"siteId": "SiteA"}):
{
"siteId": "SiteA",
"equipment": [
{
"equipmentId": "PUMP-001",
"name": "Feed Pump A",
"status": { "state": "running", "health": 98.5, "lastMaintenance": "2026-02-15" }
},
{
"equipmentId": "TANK-001",
"name": "Mix Tank 1",
"status": { "state": "idle", "health": 100.0, "lastMaintenance": "2026-03-01" }
},
{
"equipmentId": "CONV-001",
"name": "Conveyor B",
"status": { "state": "alarm", "health": 72.3, "lastMaintenance": "2026-01-20" }
}
]
}
Verification
- Check the container is running:
docker ps --filter name=scadalink-restapi
- Test the health endpoint:
curl http://localhost:5200/api/Ping
- Test an authenticated call:
curl -X POST http://localhost:5200/api/Add \
-H "X-API-Key: scadalink-test-key-1" \
-H "Content-Type: application/json" \
-d '{"a": 2, "b": 3}'
CLI Tool
The infra/tools/restapi_tool.py script provides a CLI for interacting with the REST API server. This tool requires the requests library (included in tools/requirements.txt).
Commands:
# Check API server connectivity and status
python infra/tools/restapi_tool.py check
# Call a simple method
python infra/tools/restapi_tool.py call --method Add --params '{"a": 2, "b": 3}'
# Call a complex method
python infra/tools/restapi_tool.py call --method GetProductionReport --params '{"siteId": "SiteA", "startDate": "2026-03-01", "endDate": "2026-03-16"}'
# Simulate an error
python infra/tools/restapi_tool.py call --method SimulateError --params '{"code": 503}'
# List all available methods
python infra/tools/restapi_tool.py methods
Use --url to override the base URL (default: http://localhost:5200), --api-key for the API key. Run with --help for full usage.
Relevance to ScadaLink Components
- External System Gateway — test HTTP/REST calls (
ExternalSystem.Call()andCachedCall()), API key authentication, error classification (5xx vs 4xx), and timeout handling. - Inbound API — test the
POST /api/{methodName}pattern, flat JSON parameters, and extended type system (Object, List) with complex nested responses. - Store-and-Forward Engine — verify buffered retry by using
SimulateErrorto return transient errors (503, 408, 429) and observing store-and-forward behavior.
Notes
- The server is stateless — no data persistence between container restarts.
SimulateTimeoutcaps at 60 seconds to prevent accidental container hangs.SimulateErroraccepts codes 400–599; other values default to 500.SubmitBatchgenerates a uniquebatchIdper call (UUID-based).- The
/api/methodsendpoint provides machine-readable method discovery (useful for CLI tool and automated testing). - To simulate connection failures for store-and-forward testing, stop the container:
docker compose stop restapi. Restart withdocker compose start restapi.