feat: add GetBundledScenario handler with fixture validation
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace BattleForge\Http\Handlers;
|
||||
|
||||
use BattleForge\Application\ScenarioSerializer;
|
||||
use BattleForge\Domain\ScenarioValidator;
|
||||
use BattleForge\Http\Request;
|
||||
use BattleForge\Http\Response;
|
||||
use InvalidArgumentException;
|
||||
|
||||
final class GetBundledScenario
|
||||
{
|
||||
private const ID_PATTERN = '/^[a-z0-9-]+$/';
|
||||
|
||||
public function __construct(private readonly string $scenariosDir)
|
||||
{
|
||||
}
|
||||
|
||||
/** @param array<string, string> $params */
|
||||
public function handle(Request $request, array $params): Response
|
||||
{
|
||||
$id = (string) ($params['id'] ?? '');
|
||||
|
||||
if (preg_match(self::ID_PATTERN, $id) !== 1) {
|
||||
return Response::json(404, ['error' => 'not found']);
|
||||
}
|
||||
|
||||
$manifestPath = $this->scenariosDir . '/manifest.json';
|
||||
$manifest = [];
|
||||
if (is_file($manifestPath)) {
|
||||
$decoded = json_decode((string) file_get_contents($manifestPath), true);
|
||||
if (is_array($decoded)) {
|
||||
$manifest = $decoded;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($manifest[$id])) {
|
||||
return Response::json(404, ['error' => 'not found']);
|
||||
}
|
||||
|
||||
$fixturePath = $this->scenariosDir . '/' . $id . '.json';
|
||||
$real = realpath($fixturePath);
|
||||
$realDir = realpath($this->scenariosDir);
|
||||
$insideBase = $real !== false
|
||||
&& $realDir !== false
|
||||
&& str_starts_with($real, $realDir . DIRECTORY_SEPARATOR);
|
||||
if ($insideBase === false || !is_file($real)) {
|
||||
return Response::json(404, ['error' => 'not found']);
|
||||
}
|
||||
|
||||
$data = json_decode((string) file_get_contents($real), true, flags: JSON_THROW_ON_ERROR);
|
||||
if (!is_array($data)) {
|
||||
return Response::json(500, ['error' => 'fixture invalid', 'details' => ['Fixture JSON is not an object.']]);
|
||||
}
|
||||
|
||||
try {
|
||||
$scenario = ScenarioSerializer::scenarioFromArray($data);
|
||||
} catch (InvalidArgumentException $exception) {
|
||||
return Response::json(500, ['error' => 'fixture invalid', 'details' => [$exception->getMessage()]]);
|
||||
}
|
||||
|
||||
$errors = ScenarioValidator::validate($scenario);
|
||||
if ($errors !== []) {
|
||||
return Response::json(500, ['error' => 'fixture invalid', 'details' => $errors]);
|
||||
}
|
||||
|
||||
return Response::json(200, ScenarioSerializer::scenarioToArray($scenario));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user