. */ declare(strict_types=1); namespace App\Controller; use App\Entity\Parts\Category; use App\Entity\Parts\Part; use App\Services\EDA\KiCadHelper; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; /** * @see \App\Tests\Controller\KiCadApiControllerTest */ #[Route('/kicad-api/v1')] class KiCadApiController extends AbstractController { public function __construct( private readonly KiCadHelper $kiCADHelper, ) { } #[Route('/', name: 'kicad_api_root')] public function root(): Response { $this->denyAccessUnlessGranted('HAS_ACCESS_PERMISSIONS'); //The API documentation says this can be either blank or the URL to the endpoints return $this->json([ 'categories' => '', 'parts' => '', ]); } #[Route('/categories.json', name: 'kicad_api_categories')] public function categories(Request $request): Response { $this->denyAccessUnlessGranted('@categories.read'); $data = $this->kiCADHelper->getCategories(); return $this->createCacheableJsonResponse($request, $data, 300); } #[Route('/parts/category/{category}.json', name: 'kicad_api_category')] public function categoryParts(Request $request, ?Category $category): Response { if ($category !== null) { $this->denyAccessUnlessGranted('read', $category); } else { $this->denyAccessUnlessGranted('@categories.read'); } $this->denyAccessUnlessGranted('@parts.read'); $minimal = $request->query->getBoolean('minimal', false); $data = $this->kiCADHelper->getCategoryParts($category, $minimal); return $this->createCacheableJsonResponse($request, $data, 300); } #[Route('/parts/{part}.json', name: 'kicad_api_part')] public function partDetails(Request $request, Part $part): Response { $this->denyAccessUnlessGranted('read', $part); $data = $this->kiCADHelper->getKiCADPart($part); return $this->createCacheableJsonResponse($request, $data, 60); } /** * Creates a JSON response with HTTP cache headers (ETag and Cache-Control). * Returns 304 Not Modified if the client's ETag matches. */ private function createCacheableJsonResponse(Request $request, array $data, int $maxAge): Response { $response = new JsonResponse($data); $response->setEtag(md5(json_encode($data))); $response->headers->set('Cache-Control', 'private, max-age=' . $maxAge); $response->isNotModified($request); return $response; } }