implement new episode route in the API
This commit is contained in:
parent
e97bbc1bc1
commit
a1ba12a938
|
@ -26,6 +26,7 @@
|
||||||
"symfony/http-client": "7.1.*",
|
"symfony/http-client": "7.1.*",
|
||||||
"symfony/intl": "7.1.*",
|
"symfony/intl": "7.1.*",
|
||||||
"symfony/mailer": "7.1.*",
|
"symfony/mailer": "7.1.*",
|
||||||
|
"symfony/messenger": "7.1.*",
|
||||||
"symfony/mime": "7.1.*",
|
"symfony/mime": "7.1.*",
|
||||||
"symfony/monolog-bundle": "^3.0",
|
"symfony/monolog-bundle": "^3.0",
|
||||||
"symfony/notifier": "7.1.*",
|
"symfony/notifier": "7.1.*",
|
||||||
|
|
2
composer.lock
generated
2
composer.lock
generated
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "856f163b5bcd6da7dc6bc4b1953eb4a0",
|
"content-hash": "7889f2e58c2d416ef961044023f570be",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "doctrine/annotations",
|
"name": "doctrine/annotations",
|
||||||
|
|
|
@ -22,6 +22,9 @@ security:
|
||||||
logout:
|
logout:
|
||||||
path: app_logout
|
path: app_logout
|
||||||
target: app_index
|
target: app_index
|
||||||
|
http_basic:
|
||||||
|
realm: Secured Area
|
||||||
|
entry_point: form_login
|
||||||
role_hierarchy:
|
role_hierarchy:
|
||||||
ROLE_ADMIN: ROLE_USER
|
ROLE_ADMIN: ROLE_USER
|
||||||
access_control:
|
access_control:
|
||||||
|
|
64
src/Controller/ApiController.php
Normal file
64
src/Controller/ApiController.php
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Message\DownloadRequest;
|
||||||
|
use App\Repository\PodcastRepository;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\Messenger\MessageBusInterface;
|
||||||
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
|
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||||
|
use Symfony\Component\Serializer\SerializerInterface;
|
||||||
|
|
||||||
|
#[Route('/api')]
|
||||||
|
class ApiController extends AbstractController
|
||||||
|
{
|
||||||
|
#[Route('/podcasts', name: 'app_api_list_podcasts')]
|
||||||
|
public function listPodcasts(PodcastRepository $podcastRepo): JsonResponse
|
||||||
|
{
|
||||||
|
$data = array_map(fn ($p) => $p->getSlug(), $podcastRepo->findAll());
|
||||||
|
|
||||||
|
return new JsonResponse(['slugs' => $data]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/podcasts/{slug}', name: 'app_api_show_podcast')]
|
||||||
|
public function getPodcast(string $slug, PodcastRepository $podcastRepo, SerializerInterface $serializer): JsonResponse
|
||||||
|
{
|
||||||
|
$podcast = $podcastRepo->findOneBy(['slug' => $slug]);
|
||||||
|
if (null === $podcast) {
|
||||||
|
return new JsonResponse(['error' => 'No podcast with that slug.'], 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (new JsonResponse())->setContent($serializer->serialize($podcast, 'json'));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[IsGranted('ROLE_USER')]
|
||||||
|
#[Route('/podcasts/{slug}/add', name: 'app_api_add_podcast_episode', methods: ['POST'])]
|
||||||
|
public function addPodcastEpisode(
|
||||||
|
string $slug,
|
||||||
|
Request $request,
|
||||||
|
PodcastRepository $podcastRepo,
|
||||||
|
MessageBusInterface $bus,
|
||||||
|
): JsonResponse {
|
||||||
|
$podcast = $podcastRepo->findOneBy(['slug' => $slug]);
|
||||||
|
if (null === $podcast) {
|
||||||
|
return new JsonResponse(['error' => 'No podcast with that slug.'], 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($podcast->getOwner() !== $this->getUser()) {
|
||||||
|
return new JsonResponse(['error' => 'Only the podcast owner can add an episode.'], 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
$payload = $request->getPayload();
|
||||||
|
$url = $payload->getString('url');
|
||||||
|
if (empty($url)) {
|
||||||
|
return new JsonResponse(['error' => '"url" is a required parameter.'], 400);
|
||||||
|
}
|
||||||
|
$description = $payload->getString('description');
|
||||||
|
$bus->dispatch(new DownloadRequest($url, $slug, $description ?: null));
|
||||||
|
|
||||||
|
return new JsonResponse(['message' => 'Request dispatched.']);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ use Doctrine\Common\Collections\Collection;
|
||||||
use Doctrine\DBAL\Types\Types;
|
use Doctrine\DBAL\Types\Types;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
||||||
|
use Symfony\Component\Serializer\Attribute\Ignore;
|
||||||
use Symfony\Component\String\Slugger\SluggerInterface;
|
use Symfony\Component\String\Slugger\SluggerInterface;
|
||||||
|
|
||||||
#[ORM\Entity(repositoryClass: PodcastRepository::class)]
|
#[ORM\Entity(repositoryClass: PodcastRepository::class)]
|
||||||
|
@ -26,6 +27,7 @@ class Podcast
|
||||||
#[ORM\Column(length: 255, unique: true)]
|
#[ORM\Column(length: 255, unique: true)]
|
||||||
private string $slug;
|
private string $slug;
|
||||||
|
|
||||||
|
#[Ignore]
|
||||||
#[ORM\OneToMany(mappedBy: 'podcast', targetEntity: Episode::class)]
|
#[ORM\OneToMany(mappedBy: 'podcast', targetEntity: Episode::class)]
|
||||||
private Collection $episodes;
|
private Collection $episodes;
|
||||||
|
|
||||||
|
@ -44,6 +46,7 @@ class Podcast
|
||||||
#[ORM\Column(length: 255, nullable: true)]
|
#[ORM\Column(length: 255, nullable: true)]
|
||||||
private ?string $logoFilename;
|
private ?string $logoFilename;
|
||||||
|
|
||||||
|
#[Ignore]
|
||||||
#[ORM\ManyToOne(inversedBy: 'podcasts')]
|
#[ORM\ManyToOne(inversedBy: 'podcasts')]
|
||||||
#[ORM\JoinColumn(nullable: true)]
|
#[ORM\JoinColumn(nullable: true)]
|
||||||
private ?User $owner = null;
|
private ?User $owner = null;
|
||||||
|
|
13
src/Message/DownloadRequest.php
Normal file
13
src/Message/DownloadRequest.php
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Message;
|
||||||
|
|
||||||
|
class DownloadRequest
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public readonly string $url,
|
||||||
|
public readonly string $podcastSlug,
|
||||||
|
public readonly ?string $description = null,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
40
src/MessageHandler/DownloadRequestHandler.php
Normal file
40
src/MessageHandler/DownloadRequestHandler.php
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\MessageHandler;
|
||||||
|
|
||||||
|
use App\Message\DownloadRequest;
|
||||||
|
use App\Repository\PodcastRepository;
|
||||||
|
use App\Service\DownloadService;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
|
||||||
|
|
||||||
|
#[AsMessageHandler]
|
||||||
|
class DownloadRequestHandler
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected LoggerInterface $logger,
|
||||||
|
protected EntityManagerInterface $em,
|
||||||
|
protected DownloadService $service,
|
||||||
|
protected PodcastRepository $podcastRepo,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __invoke(DownloadRequest $request): void
|
||||||
|
{
|
||||||
|
$this->logger->info('Processing download.', ['url' => $request->url]);
|
||||||
|
$episode = $this->service->download($request->url);
|
||||||
|
if ($episode === false) {
|
||||||
|
$this->logger->error('Episode download failed.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$episode->setPodcast($this->podcastRepo->findOneBy(['slug' => $request->podcastSlug]));
|
||||||
|
if ($request->description) {
|
||||||
|
$episode->setDescription($request->description);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->em->persist($episode);
|
||||||
|
$this->em->flush();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue