feat(api): MCP, OpenAPI & Dynamic Introspection

New Features:
- API endpoints now support comprehensive input validation with detailed error responses via Pydantic models.
- OpenAPI specification endpoint (/openapi.json) and interactive Swagger UI documentation (/docs) now available for API discovery.
- Enhanced MCP session lifecycle management with create, retrieve, and delete operations.
- Network diagnostic tools: traceroute, nslookup, NMAP scanning, and network topology viewing exposed via API.
- Device search, filtering by status (including 'offline'), and bulk operations (copy, delete, update).
- Wake-on-LAN functionality for remote device management.
- Added dynamic tool disablement and status reporting.

Bug Fixes:
- Fixed get_tools_status in registry to correctly return boolean values instead of None for enabled tools.
- Improved error handling for invalid API inputs with standardized validation responses.
- Fixed OPTIONS request handling for cross-origin requests.

Refactoring:
- Significant refactoring of api_server_start.py to use decorator-based validation (@validate_request).
This commit is contained in:
Adam Outler
2026-01-18 18:16:18 +00:00
parent cea3369b5e
commit ecea1d1fbd
46 changed files with 5195 additions and 1053 deletions

View File

@@ -1,11 +1,6 @@
import sys
import random
import os
import pytest
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from helper import get_setting_value # noqa: E402 [flake8 lint suppression]
from api_server.api_server_start import app # noqa: E402 [flake8 lint suppression]
@@ -106,7 +101,9 @@ def test_traceroute_device(client, api_token, test_mac):
assert len(devices) > 0
# 3. Pick the first device
device_ip = devices[0].get("devLastIP", "192.168.1.1") # fallback if dummy has no IP
device_ip = devices[0].get("devLastIP")
if not device_ip:
device_ip = "192.168.1.1"
# 4. Call the traceroute endpoint
resp = client.post(
@@ -116,25 +113,20 @@ def test_traceroute_device(client, api_token, test_mac):
)
# 5. Assertions
if not device_ip or device_ip.lower() == 'invalid':
# Expect 400 if IP is missing or invalid
assert resp.status_code == 400
data = resp.json
assert data.get("success") is False
else:
# Expect 200 and valid traceroute output
assert resp.status_code == 200
data = resp.json
assert data.get("success") is True
assert "output" in data
assert isinstance(data["output"], list)
assert all(isinstance(line, str) for line in data["output"])
# Expect 200 and valid traceroute output
assert resp.status_code == 200
data = resp.json
assert data.get("success") is True
assert "output" in data
assert isinstance(data["output"], list)
assert all(isinstance(line, str) for line in data["output"])
@pytest.mark.parametrize("ip,expected_status", [
("8.8.8.8", 200),
("256.256.256.256", 400), # Invalid IP
("", 400), # Missing IP
("256.256.256.256", 422), # Invalid IP -> 422
("", 422), # Missing IP -> 422
])
def test_nslookup_endpoint(client, api_token, ip, expected_status):
payload = {"devLastIP": ip} if ip else {}
@@ -152,13 +144,14 @@ def test_nslookup_endpoint(client, api_token, ip, expected_status):
assert "error" in data
@pytest.mark.feature_complete
@pytest.mark.parametrize("ip,mode,expected_status", [
("127.0.0.1", "fast", 200),
pytest.param("127.0.0.1", "normal", 200, marks=pytest.mark.feature_complete),
pytest.param("127.0.0.1", "detail", 200, marks=pytest.mark.feature_complete),
("127.0.0.1", "normal", 200),
("127.0.0.1", "detail", 200),
("127.0.0.1", "skipdiscovery", 200),
("127.0.0.1", "invalidmode", 400),
("999.999.999.999", "fast", 400),
("127.0.0.1", "invalidmode", 422),
("999.999.999.999", "fast", 422),
])
def test_nmap_endpoint(client, api_token, ip, mode, expected_status):
payload = {"scan": ip, "mode": mode}
@@ -202,7 +195,7 @@ def test_internet_info_endpoint(client, api_token):
if resp.status_code == 200:
assert data.get("success") is True
assert isinstance(data.get("output"), dict)
assert isinstance(data.get("output"), dict)
assert len(data["output"]) > 0 # ensure output is not empty
else:
# Handle errors, e.g., curl failure