Dump schema on failing requests
This commit is contained in:
@@ -8,6 +8,25 @@ import eos.config
|
||||
from eos.const import FittingHardpoint
|
||||
from eos.utils.spoolSupport import SpoolOptions, SpoolType
|
||||
|
||||
# POST /simulate request and response shape; included in 400 response body
|
||||
SIMULATE_SCHEMA = {
|
||||
"request": {
|
||||
"fit": "string (required). EFT or multi-line text export of a single fit, e.g. [ShipName, FitName] followed by modules/drones/etc.",
|
||||
"projected_fits": "array (optional). Each element: { \"fit\": string, \"count\": positive integer }.",
|
||||
"command_fits": "array (optional). Each element: { \"fit\": string }.",
|
||||
},
|
||||
"response_200": {
|
||||
"fit": {"id": "int", "name": "string", "ship_type": "string"},
|
||||
"resources": {"hardpoints": {}, "drones": {}, "fighters": {}, "calibration": {}, "powergrid": {}, "cpu": {}, "cargo": {}},
|
||||
"defense": {"hp": {}, "ehp": {}, "resonance": {}, "tank": {}, "effective_tank": {}, "sustainable_tank": {}, "effective_sustainable_tank": {}},
|
||||
"capacitor": {"capacity": {}, "recharge": {}, "use": {}, "delta": {}, "stable": {}, "state": {}, "neutralizer_resistance": {}},
|
||||
"firepower": {"weapon_dps": {}, "drone_dps": {}, "total_dps": {}, "weapon_volley": {}, "drone_volley": {}, "total_volley": {}, "weapons": []},
|
||||
"remote_reps_outgoing": {"current": {}, "pre_spool": {}, "full_spool": {}},
|
||||
"targeting_misc": {"targets_max": {}, "target_range": {}, "scan_resolution": {}, "scan_strength": {}, "scan_type": {}, "jam_chance": {}, "drone_control_range": {}, "speed": {}, "align_time": {}, "signature_radius": {}, "warp_speed": {}, "max_warp_distance": {}, "probe_size": {}, "cargo_capacity": {}, "cargo_used": {}},
|
||||
"price": {"ship": {}, "fittings": {}, "drones_and_fighters": {}, "cargo": {}, "character": {}, "total": {}},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _init_pyfa(savepath: str | None) -> None:
|
||||
config.debug = False
|
||||
@@ -363,45 +382,57 @@ def compute_stats(payload: dict, savepath: str | None = None) -> dict:
|
||||
|
||||
|
||||
def _run_http_server(port: int, savepath: str | None) -> None:
|
||||
def send_error_json(code: int, msg: str, traceback_str: str | None = None):
|
||||
body = {"error": msg, "schema": SIMULATE_SCHEMA}
|
||||
if traceback_str:
|
||||
body["traceback"] = traceback_str
|
||||
return json.dumps(body, indent=2) + "\n"
|
||||
|
||||
class SimulateHandler(BaseHTTPRequestHandler):
|
||||
def send_error(self, code: int, message: str | None = None, explain: str | None = None):
|
||||
msg = message or explain or "Error"
|
||||
self.send_response(code)
|
||||
self.send_header("Content-Type", "application/json; charset=utf-8")
|
||||
self.end_headers()
|
||||
self.wfile.write(send_error_json(code, msg).encode("utf-8"))
|
||||
|
||||
def _reply_error(self, code: int, msg: str, traceback_str: str | None = None):
|
||||
self.send_response(code)
|
||||
self.send_header("Content-Type", "application/json; charset=utf-8")
|
||||
self.end_headers()
|
||||
self.wfile.write(send_error_json(code, msg, traceback_str).encode("utf-8"))
|
||||
|
||||
def do_GET(self):
|
||||
if self.path == "/simulate":
|
||||
self._reply_error(405, "Method not allowed. Use POST.")
|
||||
else:
|
||||
self._reply_error(404, "Not found. POST /simulate with JSON body.")
|
||||
|
||||
def do_POST(self):
|
||||
if self.path != "/simulate":
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
self._reply_error(404, "Not found. POST /simulate with JSON body.")
|
||||
return
|
||||
content_length = int(self.headers.get("Content-Length", 0))
|
||||
body = self.rfile.read(content_length).decode("utf-8")
|
||||
try:
|
||||
payload = json.loads(body)
|
||||
except json.JSONDecodeError as exc:
|
||||
self.send_response(400)
|
||||
self.send_header("Content-Type", "text/plain; charset=utf-8")
|
||||
self.end_headers()
|
||||
self.wfile.write(("Invalid JSON: %s" % exc).encode("utf-8"))
|
||||
self._reply_error(400, "Invalid JSON: %s" % exc)
|
||||
return
|
||||
if not isinstance(payload, dict):
|
||||
self.send_response(400)
|
||||
self.send_header("Content-Type", "text/plain; charset=utf-8")
|
||||
self.end_headers()
|
||||
self.wfile.write(b"Top-level JSON must be an object")
|
||||
self._reply_error(400, "Top-level JSON must be an object")
|
||||
return
|
||||
try:
|
||||
output = compute_stats(payload, savepath)
|
||||
except ValueError as e:
|
||||
self.send_response(400)
|
||||
self.send_header("Content-Type", "text/plain; charset=utf-8")
|
||||
self.end_headers()
|
||||
self.wfile.write(str(e).encode("utf-8"))
|
||||
self._reply_error(400, str(e))
|
||||
return
|
||||
except Exception as e:
|
||||
import traceback
|
||||
tb = traceback.format_exc()
|
||||
sys.stderr.write(tb)
|
||||
sys.stderr.flush()
|
||||
self.send_response(500)
|
||||
self.send_header("Content-Type", "text/plain; charset=utf-8")
|
||||
self.end_headers()
|
||||
self.wfile.write((str(e) + "\n\n" + tb).encode("utf-8"))
|
||||
self._reply_error(500, str(e), tb)
|
||||
return
|
||||
self.send_response(200)
|
||||
self.send_header("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
Reference in New Issue
Block a user