Extracted PodcastData to its own endpoint

This commit is contained in:
Kalle Fagerberg 2022-09-17 18:12:13 +02:00 committed by thrillfall
parent 403ead674d
commit 4771a52b63
7 changed files with 48 additions and 40 deletions

View File

@ -15,5 +15,6 @@ return [
['name' => 'subscription_change#list', 'url' => '/subscriptions', 'verb' => 'GET'], ['name' => 'subscription_change#list', 'url' => '/subscriptions', 'verb' => 'GET'],
['name' => 'subscription_change#create', 'url' => '/subscription_change/create', 'verb' => 'POST'], ['name' => 'subscription_change#create', 'url' => '/subscription_change/create', 'verb' => 'POST'],
['name' => 'personal_settings#metrics', 'url' => '/personal_settings/metrics', 'verb' => 'GET'], ['name' => 'personal_settings#metrics', 'url' => '/personal_settings/metrics', 'verb' => 'GET'],
['name' => 'personal_settings#podcastData', 'url' => '/personal_settings/podcast_data', 'verb' => 'GET'],
] ]
]; ];

View File

@ -3,10 +3,11 @@ declare(strict_types=1);
namespace OCA\GPodderSync\Controller; namespace OCA\GPodderSync\Controller;
use OCA\GPodderSync\Core\PodcastData\PodcastMetrics; use OCA\GPodderSync\Core\PodcastData\PodcastDataReader;
use OCA\GPodderSync\Core\PodcastData\PodcastMetricsReader; use OCA\GPodderSync\Core\PodcastData\PodcastMetricsReader;
use OCP\AppFramework\Controller; use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest; use OCP\IRequest;
@ -14,16 +15,19 @@ class PersonalSettingsController extends Controller {
private string $userId; private string $userId;
private PodcastMetricsReader $metricsReader; private PodcastMetricsReader $metricsReader;
private PodcastDataReader $dataReader;
public function __construct( public function __construct(
string $AppName, string $AppName,
IRequest $request, IRequest $request,
string $UserId, string $UserId,
PodcastMetricsReader $metricsReader, PodcastMetricsReader $metricsReader,
PodcastDataReader $dataReader,
) { ) {
parent::__construct($AppName, $request); parent::__construct($AppName, $request);
$this->userId = $UserId ?? ''; $this->userId = $UserId ?? '';
$this->metricsReader = $metricsReader; $this->metricsReader = $metricsReader;
$this->dataReader = $dataReader;
} }
/** /**
@ -39,4 +43,23 @@ class PersonalSettingsController extends Controller {
'subscriptions' => $metrics, 'subscriptions' => $metrics,
]); ]);
} }
/**
* @NoAdminRequired
* @NoCSRFRequired
*
* @param string $url
* @return JsonResponse
*/
public function podcastData(string $url = ''): JsonResponse {
if ($url === '') {
return new JSONResponse([
'message' => "Missing query parameter 'url'.",
'data' => null,
], statusCode: Http::STATUS_BAD_REQUEST);
}
return new JsonResponse([
'data' => $this->dataReader->getCachedOrFetchPodcastData($url, $this->userId),
]);
}
} }

View File

@ -3,39 +3,51 @@ declare(strict_types=1);
namespace OCA\GPodderSync\Core\PodcastData; namespace OCA\GPodderSync\Core\PodcastData;
use OCA\GPodderSync\Db\SubscriptionChange\SubscriptionChangeRepository;
use OCP\Http\Client\IClient; use OCP\Http\Client\IClient;
use OCP\Http\Client\IClientService; use OCP\Http\Client\IClientService;
use OCP\ICache; use OCP\ICache;
use OCP\ICacheFactory; use OCP\ICacheFactory;
class PodcastDataCache { class PodcastDataReader {
private ?ICache $cache = null; private ?ICache $cache = null;
private IClient $httpClient; private IClient $httpClient;
private SubscriptionChangeRepository $subscriptionChangeRepository;
public function __construct( public function __construct(
ICacheFactory $cacheFactory, ICacheFactory $cacheFactory,
IClientService $httpClientService, IClientService $httpClientService,
SubscriptionChangeRepository $subscriptionChangeRepository,
) { ) {
if ($cacheFactory->isLocalCacheAvailable()) { if ($cacheFactory->isLocalCacheAvailable()) {
$this->cache = $cacheFactory->createLocal('GPodderSync-Podcasts'); $this->cache = $cacheFactory->createLocal('GPodderSync-Podcasts');
} }
$this->httpClient = $httpClientService->newClient(); $this->httpClient = $httpClientService->newClient();
$this->subscriptionChangeRepository = $subscriptionChangeRepository;
} }
public function getCachedOrFetchPodcastData(string $url): PodcastData { public function getCachedOrFetchPodcastData(string $url, string $userId): ?PodcastData {
if ($this->cache == null) { if ($this->cache == null) {
return $this->fetchPodcastData($url); return $this->fetchPodcastData($url, $userId);
} }
$oldData = $this->tryGetCachedPodcastData($url); $oldData = $this->tryGetCachedPodcastData($url);
if ($oldData) { if ($oldData) {
return $oldData; return $oldData;
} }
$newData = $this->fetchPodcastData($url); $newData = $this->fetchPodcastData($url, $userId);
$this->trySetCachedPodcastData($url, $newData); $this->trySetCachedPodcastData($url, $newData);
return $newData; return $newData;
} }
public function fetchPodcastData(string $url): PodcastData { private function userHasPodcast(string $url, string $userId): bool {
$subscriptionChanges = $this->subscriptionChangeRepository->findByUrl($url, $userId);
return $subscriptionChanges !== null;
}
public function fetchPodcastData(string $url, string $userId): PodcastData {
if (!$this->userHasPodcast($url, $userId)) {
return null;
}
$resp = $this->httpClient->get($url); $resp = $this->httpClient->get($url);
$statusCode = $resp->getStatusCode(); $statusCode = $resp->getStatusCode();
if ($statusCode < 200 || $statusCode >= 300) { if ($statusCode < 200 || $statusCode >= 300) {
@ -53,7 +65,7 @@ class PodcastDataCache {
return PodcastData::fromArray($oldData); return PodcastData::fromArray($oldData);
} }
public function trySetCachedPodcastData(string $url, PodcastData $data) { public function trySetCachedPodcastData(string $url, PodcastData $data): bool {
$this->cache->set($url, $data->toArray()); return $this->cache->set($url, $data->toArray());
} }
} }

View File

@ -9,18 +9,15 @@ class PodcastMetrics implements JsonSerializable {
private string $url; private string $url;
private int $listenedSeconds; private int $listenedSeconds;
private PodcastActionCounts $actionCounts; private PodcastActionCounts $actionCounts;
private ?PodcastData $podcastData;
public function __construct( public function __construct(
string $url, string $url,
int $listenedSeconds = 0, int $listenedSeconds = 0,
?PodcastActionCounts $actionCounts = null, ?PodcastActionCounts $actionCounts = null,
?PodcastData $podcastData = null,
) { ) {
$this->url = $url; $this->url = $url;
$this->actionCounts = $actionCounts ?? new PodcastActionCounts; $this->actionCounts = $actionCounts ?? new PodcastActionCounts;
$this->listenedSeconds = $listenedSeconds; $this->listenedSeconds = $listenedSeconds;
$this->podcastData = $podcastData;
} }
/** /**
@ -51,13 +48,6 @@ class PodcastMetrics implements JsonSerializable {
$this->listenedSeconds += $seconds; $this->listenedSeconds += $seconds;
} }
/**
* @return PodcastData|null
*/
public function getPodcastData(): ?PodcastData {
return $this->podcastData;
}
/** /**
* @return array<string,mixed> * @return array<string,mixed>
*/ */
@ -67,7 +57,6 @@ class PodcastMetrics implements JsonSerializable {
'url' => $this->url, 'url' => $this->url,
'listenedSeconds' => $this->listenedSeconds, 'listenedSeconds' => $this->listenedSeconds,
'actionCounts' => $this->actionCounts->toArray(), 'actionCounts' => $this->actionCounts->toArray(),
'podcastData' => $this->podcastData->toArray(),
]; ];
} }

View File

@ -13,21 +13,15 @@ use Psr\Log\LoggerInterface;
class PodcastMetricsReader { class PodcastMetricsReader {
private LoggerInterface $logger;
private SubscriptionChangeRepository $subscriptionChangeRepository; private SubscriptionChangeRepository $subscriptionChangeRepository;
private EpisodeActionRepository $episodeActionRepository; private EpisodeActionRepository $episodeActionRepository;
private PodcastDataCache $cache;
public function __construct( public function __construct(
LoggerInterface $logger,
SubscriptionChangeRepository $subscriptionChangeRepository, SubscriptionChangeRepository $subscriptionChangeRepository,
EpisodeActionRepository $episodeActionRepository, EpisodeActionRepository $episodeActionRepository,
PodcastDataCache $cache,
) { ) {
$this->logger = $logger;
$this->subscriptionChangeRepository = $subscriptionChangeRepository; $this->subscriptionChangeRepository = $subscriptionChangeRepository;
$this->episodeActionRepository = $episodeActionRepository; $this->episodeActionRepository = $episodeActionRepository;
$this->cache = $cache;
} }
/** /**
@ -69,24 +63,12 @@ class PodcastMetricsReader {
return $subscriptions; return $subscriptions;
} }
private function tryGetParsedPodcastData(string $url): ?PodcastData {
try {
return $this->cache->getCachedOrFetchPodcastData($url);
} catch (\Exception $e) {
$this->logger->error("Failed to get podcast data.", [
'exception' => $e,
'podcastUrl' => $url,
]);
return null;
}
}
private function createMetricsForUrl(string $url): PodcastMetrics { private function createMetricsForUrl(string $url): PodcastMetrics {
return new PodcastMetrics( return new PodcastMetrics(
url: $url, url: $url,
listenedSeconds: 0, listenedSeconds: 0,
actionCounts: new PodcastActionCounts(), actionCounts: new PodcastActionCounts(),
podcastData: $this->tryGetParsedPodcastData($url),
); );
} }
} }

View File

@ -25,7 +25,7 @@ class SubscriptionChangeMapper extends \OCP\AppFramework\Db\QBMapper {
return $this->findEntities($qb); return $this->findEntities($qb);
} }
public function findByUrl(string $url, string $userId): SubscriptionChangeEntity { public function findByUrl(string $url, string $userId): ?SubscriptionChangeEntity {
$qb = $this->db->getQueryBuilder(); $qb = $this->db->getQueryBuilder();
$qb->select('*') $qb->select('*')
@ -42,6 +42,7 @@ class SubscriptionChangeMapper extends \OCP\AppFramework\Db\QBMapper {
} catch (DoesNotExistException $e) { } catch (DoesNotExistException $e) {
} catch (MultipleObjectsReturnedException $e) { } catch (MultipleObjectsReturnedException $e) {
} }
return null;
} }
public function remove(SubscriptionChangeEntity $subscriptionChangeEntity) { public function remove(SubscriptionChangeEntity $subscriptionChangeEntity) {

View File

@ -18,7 +18,7 @@ class SubscriptionChangeRepository {
return $this->subscriptionChangeMapper->findAll(); return $this->subscriptionChangeMapper->findAll();
} }
public function findByUrl(string $episode, string $userId): SubscriptionChangeEntity { public function findByUrl(string $episode, string $userId): ?SubscriptionChangeEntity {
return $this->subscriptionChangeMapper->findByUrl($episode, $userId); return $this->subscriptionChangeMapper->findByUrl($episode, $userId);
} }