parse EpisodeAction message from AntennaPod
persist and list with database create episodeAction list reponse (with mocked timestamp, started and total) create episodeActions with received values update existing episodeActions by unique episode link receive and store subscription changes deal with multiple subscription changes in single request split database into subdirectories only return subscription changes younger then passed parameter since parse passed timestamp parse passed timestamp for episode_actions listing only return list of urls for subscription changes align list endpoint naming schema store userId with episode actions and subscriptions return json object on application root route
This commit is contained in:
commit
72bf365285
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
vendor/*
|
23
appinfo/info.xml
Normal file
23
appinfo/info.xml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<info xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
|
||||||
|
<id>gpoddersync</id>
|
||||||
|
<name>G Podder Sync</name>
|
||||||
|
<summary>replicate GPodder API</summary>
|
||||||
|
<description><![CDATA[Expose GPodder API to sync podcast consumer apps like AntennaPod]]></description>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
<licence>agpl</licence>
|
||||||
|
<author mail="thrillfall@disroot.org" >Thrillfall</author>
|
||||||
|
<namespace>GPodderSync</namespace>
|
||||||
|
<category>integration</category>
|
||||||
|
<bugs>https://github.com/thrillfall</bugs>
|
||||||
|
<dependencies>
|
||||||
|
<nextcloud min-version="15" max-version="15"/>
|
||||||
|
</dependencies>
|
||||||
|
<navigations>
|
||||||
|
<navigation>
|
||||||
|
<name>G Podder Sync</name>
|
||||||
|
<route>gpoddersync.page.index</route>
|
||||||
|
</navigation>
|
||||||
|
</navigations>
|
||||||
|
</info>
|
23
appinfo/routes.php
Normal file
23
appinfo/routes.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Create your routes in here. The name is the lowercase name of the controller
|
||||||
|
* without the controller part, the stuff after the hash is the method.
|
||||||
|
* e.g. page#index -> OCA\GPodderSync\Controller\PageController->index()
|
||||||
|
*
|
||||||
|
* The controller class has to be registered in the application.php file since
|
||||||
|
* it's instantiated in there
|
||||||
|
*/
|
||||||
|
return [
|
||||||
|
'routes' => [
|
||||||
|
['name' => 'episode_action#create', 'url' => '/episode_action/create', 'verb' => 'POST'],
|
||||||
|
['name' => 'episode_action#list', 'url' => '/episode_action', 'verb' => 'GET'],
|
||||||
|
|
||||||
|
['name' => 'subscription_change#list', 'url' => '/subscriptions', 'verb' => 'GET'],
|
||||||
|
['name' => 'subscription_change#create', 'url' => '/subscription_change/create', 'verb' => 'POST'],
|
||||||
|
|
||||||
|
['name' => 'version#index', 'url' => '/', 'verb' => 'GET'],
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
]
|
||||||
|
];
|
1561
composer.lock
generated
Normal file
1561
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
102
lib/Controller/EpisodeActionController.php
Normal file
102
lib/Controller/EpisodeActionController.php
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\GPodderSync\Controller;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
|
||||||
|
use GuzzleHttp\Psr7\Response;
|
||||||
|
use OCA\GPodderSync\Core\EpisodeAction\EpisodeActionReader;
|
||||||
|
use OCA\GPodderSync\Db\EpisodeAction\EpisodeActionEntity;
|
||||||
|
use OCA\GPodderSync\Db\EpisodeAction\EpisodeActionRepository;
|
||||||
|
use OCA\GPodderSync\Db\EpisodeAction\EpisodeActionWriter;
|
||||||
|
use OCP\AppFramework\Controller;
|
||||||
|
use OCP\AppFramework\Http\JSONResponse;
|
||||||
|
use OCP\IRequest;
|
||||||
|
|
||||||
|
class EpisodeActionController extends Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var EpisodeActionRepository
|
||||||
|
*/
|
||||||
|
private EpisodeActionRepository $episodeActionRepository;
|
||||||
|
/**
|
||||||
|
* @var EpisodeActionWriter
|
||||||
|
*/
|
||||||
|
private EpisodeActionWriter $episodeActionWriter;
|
||||||
|
/**
|
||||||
|
* @var EpisodeActionReader
|
||||||
|
*/
|
||||||
|
private EpisodeActionReader $episodeActionReader;
|
||||||
|
private $userId;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
string $AppName,
|
||||||
|
IRequest $request,
|
||||||
|
$UserId,
|
||||||
|
EpisodeActionRepository $episodeActionRepository,
|
||||||
|
EpisodeActionWriter $episodeActionWriter,
|
||||||
|
EpisodeActionReader $episodeActionReader
|
||||||
|
) {
|
||||||
|
parent::__construct($AppName, $request);
|
||||||
|
$this->episodeActionRepository = $episodeActionRepository;
|
||||||
|
$this->episodeActionWriter = $episodeActionWriter;
|
||||||
|
$this->episodeActionReader = $episodeActionReader;
|
||||||
|
$this->userId = $UserId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @NoAdminRequired
|
||||||
|
* @NoCSRFRequired
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
public function create($data) {
|
||||||
|
$episodeAction = $this->episodeActionReader->fromString($data);
|
||||||
|
$episodeActionEntity = new EpisodeActionEntity();
|
||||||
|
$episodeActionEntity->setPodcast($episodeAction->getPodcast());
|
||||||
|
$episodeActionEntity->setEpisode($episodeAction->getEpisode());
|
||||||
|
$episodeActionEntity->setAction($episodeAction->getAction());
|
||||||
|
$episodeActionEntity->setPosition($episodeAction->getPosition());
|
||||||
|
$episodeActionEntity->setStarted($episodeAction->getStarted());
|
||||||
|
$episodeActionEntity->setTotal($episodeAction->getTotal());
|
||||||
|
$episodeActionEntity->setTimestamp($episodeAction->getTimestamp());
|
||||||
|
$episodeActionEntity->setUserId($this->userId);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return $this->episodeActionWriter->save($episodeActionEntity);
|
||||||
|
} catch (UniqueConstraintViolationException $ex) {
|
||||||
|
$IdEpisodeActionEntityToUpdate = $this->episodeActionRepository->findByEpisode($episodeAction->getEpisode(), $this->userId)->getId();
|
||||||
|
$episodeActionEntity->setId($IdEpisodeActionEntityToUpdate);
|
||||||
|
return $this->episodeActionWriter->update($episodeActionEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @NoAdminRequired
|
||||||
|
* @NoCSRFRequired
|
||||||
|
*
|
||||||
|
* @param int $since
|
||||||
|
* @return JSONResponse
|
||||||
|
*/
|
||||||
|
public function list(int $since): JSONResponse {
|
||||||
|
$sinceDatetime = $this->createDateTimeFromTimestamp($since);
|
||||||
|
return new JSONResponse([
|
||||||
|
"actions" => $this->episodeActionRepository->findAll($sinceDatetime, $this->userId),
|
||||||
|
"timestamp" => time()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int|null $since
|
||||||
|
*
|
||||||
|
* @return DateTime
|
||||||
|
*/
|
||||||
|
private function createDateTimeFromTimestamp(?int $since): DateTime {
|
||||||
|
return ($since)
|
||||||
|
? (new \DateTime)->setTimestamp($since)
|
||||||
|
: (new \DateTime('-1 week'));
|
||||||
|
}
|
||||||
|
}
|
91
lib/Controller/SubscriptionChangeController.php
Normal file
91
lib/Controller/SubscriptionChangeController.php
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\GPodderSync\Controller;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use OCA\GPodderSync\Core\SubscriptionChange\SubscriptionChangeSaver;
|
||||||
|
use OCA\GPodderSync\Db\SubscriptionChange\SubscriptionChangeEntity;
|
||||||
|
use OCA\GPodderSync\Db\SubscriptionChange\SubscriptionChangeRepository;
|
||||||
|
use OCP\AppFramework\Controller;
|
||||||
|
use OCP\AppFramework\Http\JSONResponse;
|
||||||
|
use OCP\IRequest;
|
||||||
|
|
||||||
|
class SubscriptionChangeController extends Controller {
|
||||||
|
|
||||||
|
private string $AppName;
|
||||||
|
/**
|
||||||
|
* @var SubscriptionChangeSaver
|
||||||
|
*/
|
||||||
|
private SubscriptionChangeSaver $subscriptionChangeSaver;
|
||||||
|
/**
|
||||||
|
* @var SubscriptionChangeRepository
|
||||||
|
*/
|
||||||
|
private SubscriptionChangeRepository $subscriptionChangeRepository;
|
||||||
|
private $userId;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
string $AppName,
|
||||||
|
IRequest $request,
|
||||||
|
$UserId,
|
||||||
|
SubscriptionChangeSaver $subscriptionChangeSaver,
|
||||||
|
SubscriptionChangeRepository $subscriptionChangeRepository
|
||||||
|
|
||||||
|
) {
|
||||||
|
parent::__construct($AppName, $request);
|
||||||
|
$this->subscriptionChangeSaver = $subscriptionChangeSaver;
|
||||||
|
$this->subscriptionChangeRepository = $subscriptionChangeRepository;
|
||||||
|
$this->userId = $UserId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @NoAdminRequired
|
||||||
|
* @NoCSRFRequired
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function create($add, $remove) {
|
||||||
|
return $this->subscriptionChangeSaver->saveSubscriptionChanges($add, $remove, $this->userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @NoAdminRequired
|
||||||
|
* @NoCSRFRequired
|
||||||
|
*
|
||||||
|
* @param int $since
|
||||||
|
* @return JSONResponse
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function list(int $since = null): JSONResponse {
|
||||||
|
$sinceDatetime = $this->createDateTimeFromTimestamp($since);
|
||||||
|
return new JSONResponse([
|
||||||
|
"add" => $this->extractUrlList($this->subscriptionChangeRepository->findAllSubscribed($sinceDatetime, $this->userId)),
|
||||||
|
"remove" => $this->extractUrlList($this->subscriptionChangeRepository->findAllUnSubscribed($sinceDatetime, $this->userId)),
|
||||||
|
"timestamp" => time()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int|null $since
|
||||||
|
*
|
||||||
|
* @return DateTime
|
||||||
|
*/
|
||||||
|
private function createDateTimeFromTimestamp(?int $since): DateTime {
|
||||||
|
return ($since)
|
||||||
|
? (new \DateTime)->setTimestamp($since)
|
||||||
|
: (new \DateTime('-1 week'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $allSubscribed
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
private function extractUrlList(array $allSubscribed): array {
|
||||||
|
return array_map(function (SubscriptionChangeEntity $subscription) {
|
||||||
|
return $subscription->getUrl();
|
||||||
|
}, $allSubscribed);
|
||||||
|
}
|
||||||
|
}
|
40
lib/Controller/VersionController.php
Normal file
40
lib/Controller/VersionController.php
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\GPodderSync\Controller;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
|
||||||
|
use GuzzleHttp\Psr7\Response;
|
||||||
|
use OCA\GPodderSync\Core\EpisodeAction\EpisodeActionReader;
|
||||||
|
use OCA\GPodderSync\Db\EpisodeAction\EpisodeActionEntity;
|
||||||
|
use OCA\GPodderSync\Db\EpisodeAction\EpisodeActionRepository;
|
||||||
|
use OCA\GPodderSync\Db\EpisodeAction\EpisodeActionWriter;
|
||||||
|
use OCP\AppFramework\Controller;
|
||||||
|
use OCP\AppFramework\Http\JSONResponse;
|
||||||
|
use OCP\IRequest;
|
||||||
|
|
||||||
|
class VersionController extends Controller {
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
string $AppName,
|
||||||
|
IRequest $request,
|
||||||
|
$UserId
|
||||||
|
) {
|
||||||
|
parent::__construct($AppName, $request);
|
||||||
|
$this->userId = $UserId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @NoAdminRequired
|
||||||
|
* @NoCSRFRequired
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
public function index() {
|
||||||
|
return new JSONResponse(["version" => "0.1"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
83
lib/Core/EpisodeAction/EpisodeAction.php
Normal file
83
lib/Core/EpisodeAction/EpisodeAction.php
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\GPodderSync\Core\EpisodeAction;
|
||||||
|
|
||||||
|
class EpisodeAction {
|
||||||
|
private string $podcast;
|
||||||
|
private string $episode;
|
||||||
|
private string $action;
|
||||||
|
private string $timestamp;
|
||||||
|
private int $started;
|
||||||
|
private int $position;
|
||||||
|
private int $total;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
string $podcast,
|
||||||
|
string $episode,
|
||||||
|
string $action,
|
||||||
|
string $timestamp,
|
||||||
|
int $started,
|
||||||
|
int $position,
|
||||||
|
int $total
|
||||||
|
) {
|
||||||
|
$this->podcast = $podcast;
|
||||||
|
$this->episode = $episode;
|
||||||
|
$this->action = $action;
|
||||||
|
$this->timestamp = $timestamp;
|
||||||
|
$this->started = $started;
|
||||||
|
$this->position = $position;
|
||||||
|
$this->total = $total;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getPodcast(): string {
|
||||||
|
return $this->podcast;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getEpisode(): string {
|
||||||
|
return $this->episode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getAction(): string {
|
||||||
|
return $this->action;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getTimestamp(): string {
|
||||||
|
return $this->timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getStarted(): int {
|
||||||
|
return $this->started;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getPosition(): int {
|
||||||
|
return $this->position;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getTotal(): int {
|
||||||
|
return $this->total;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
28
lib/Core/EpisodeAction/EpisodeActionReader.php
Normal file
28
lib/Core/EpisodeAction/EpisodeActionReader.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\GPodderSync\Core\EpisodeAction;
|
||||||
|
|
||||||
|
class EpisodeActionReader {
|
||||||
|
public function fromString(string $episodeActionString): EpisodeAction {
|
||||||
|
file_put_contents('actionreader.log', var_export($episodeActionString, true), FILE_APPEND);
|
||||||
|
preg_match(
|
||||||
|
'/\[EpisodeAction{(podcast=\')(?<podcast>.*?)(\', episode=\')(?<episode>.*?)(\', action=)(?<action>.*?)(, timestamp=)(?<timestamp>.*?)(, started=)(?<started>.*?)(, position=)(?<position>.*?)(, total=)(?<total>.*?)}]*/',
|
||||||
|
$episodeActionString,
|
||||||
|
$matches
|
||||||
|
);
|
||||||
|
|
||||||
|
file_put_contents('actionreader.log', var_export($matches, true), FILE_APPEND);
|
||||||
|
|
||||||
|
return new EpisodeAction(
|
||||||
|
$matches["podcast"],
|
||||||
|
$matches["episode"],
|
||||||
|
$matches["action"],
|
||||||
|
$matches["timestamp"],
|
||||||
|
(int)$matches["started"],
|
||||||
|
(int)$matches["position"],
|
||||||
|
(int)$matches["total"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
35
lib/Core/SubscriptionChange/SubscriptionChange.php
Normal file
35
lib/Core/SubscriptionChange/SubscriptionChange.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\GPodderSync\Core\SubscriptionChange;
|
||||||
|
|
||||||
|
class SubscriptionChange {
|
||||||
|
private string $url;
|
||||||
|
private bool $isSubscribed;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
string $url,
|
||||||
|
bool $isSubscribed
|
||||||
|
) {
|
||||||
|
$this->url = $url;
|
||||||
|
$this->isSubscribed = $isSubscribed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isSubscribed(): bool {
|
||||||
|
return $this->isSubscribed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getUrl(): string {
|
||||||
|
return $this->url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString() : String {
|
||||||
|
return $this->url;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\GPodderSync\Core\SubscriptionChange;
|
||||||
|
|
||||||
|
class SubscriptionChangeRequestParser {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var SubscriptionChangesReader
|
||||||
|
*/
|
||||||
|
private SubscriptionChangesReader $subscriptionChangeReader;
|
||||||
|
|
||||||
|
public function __construct(SubscriptionChangesReader $subscriptionChangeReader) {
|
||||||
|
$this->subscriptionChangeReader = $subscriptionChangeReader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $urlsSubscribed
|
||||||
|
* @param string $urlsUnsubscribed
|
||||||
|
*
|
||||||
|
* @return SubscriptionChange[]
|
||||||
|
*/
|
||||||
|
public function createSubscriptionChangeList(string $urlsSubscribed, string $urlsUnsubscribed): array {
|
||||||
|
$urlsToSubscribe = $this->subscriptionChangeReader->fromString($urlsSubscribed, true);
|
||||||
|
$urlsToDelete = $this->subscriptionChangeReader->fromString($urlsUnsubscribed, false);
|
||||||
|
|
||||||
|
/** @var \OCA\GPodderSync\Core\SubscriptionChange\SubscriptionChange[] $subscriptionChanges */
|
||||||
|
return array_merge($urlsToSubscribe, $urlsToDelete);
|
||||||
|
}
|
||||||
|
}
|
60
lib/Core/SubscriptionChange/SubscriptionChangeSaver.php
Normal file
60
lib/Core/SubscriptionChange/SubscriptionChangeSaver.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\GPodderSync\Core\SubscriptionChange;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
|
||||||
|
use OCA\GPodderSync\Db\SubscriptionChange\SubscriptionChangeEntity;
|
||||||
|
use OCA\GPodderSync\Db\SubscriptionChange\SubscriptionChangeRepository;
|
||||||
|
use OCA\GPodderSync\Db\SubscriptionChange\SubscriptionChangeWriter;
|
||||||
|
|
||||||
|
class SubscriptionChangeSaver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var SubscriptionChangesReader
|
||||||
|
*/
|
||||||
|
private SubscriptionChangesReader $subscriptionChangeReader;
|
||||||
|
/**
|
||||||
|
* @var SubscriptionChangeRepository
|
||||||
|
*/
|
||||||
|
private SubscriptionChangeRepository $subscriptionChangeRepository;
|
||||||
|
/**
|
||||||
|
* @var SubscriptionChangeWriter
|
||||||
|
*/
|
||||||
|
private SubscriptionChangeWriter $subscriptionChangeWriter;
|
||||||
|
/**
|
||||||
|
* @var SubscriptionChangeRequestParser
|
||||||
|
*/
|
||||||
|
private SubscriptionChangeRequestParser $subscriptionChangeRequestParser;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
SubscriptionChangeRequestParser $subscriptionChangeRequestParser,
|
||||||
|
SubscriptionChangeRepository $subscriptionChangeRepository,
|
||||||
|
SubscriptionChangeWriter $subscriptionChangeWriter
|
||||||
|
) {
|
||||||
|
$this->subscriptionChangeRepository = $subscriptionChangeRepository;
|
||||||
|
$this->subscriptionChangeWriter = $subscriptionChangeWriter;
|
||||||
|
$this->subscriptionChangeRequestParser = $subscriptionChangeRequestParser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function saveSubscriptionChanges(string $urlsSubscribed, string $urlsUnsubscribed, string $userId) : void {
|
||||||
|
$subscriptionChanges = $this->subscriptionChangeRequestParser->createSubscriptionChangeList($urlsSubscribed, $urlsUnsubscribed);
|
||||||
|
foreach ($subscriptionChanges as $urlChangedSubscriptionStatus) {
|
||||||
|
$subscriptionChangeEntity = new SubscriptionChangeEntity();
|
||||||
|
$subscriptionChangeEntity->setUrl($urlChangedSubscriptionStatus->getUrl());
|
||||||
|
$subscriptionChangeEntity->setSubscribed($urlChangedSubscriptionStatus->isSubscribed());
|
||||||
|
$subscriptionChangeEntity->setUpdated((new \DateTime())->format("Y-m-d\TH:i:s"));
|
||||||
|
$subscriptionChangeEntity->setUserId($userId);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->subscriptionChangeWriter->create($subscriptionChangeEntity);
|
||||||
|
} catch (UniqueConstraintViolationException $ex) {
|
||||||
|
$idEpisodeActionEntityToUpdate = $this->subscriptionChangeRepository->findByUrl($subscriptionChangeEntity->getUrl(), $userId)->getId();
|
||||||
|
$subscriptionChangeEntity->setId($idEpisodeActionEntityToUpdate);
|
||||||
|
$this->subscriptionChangeWriter->update($subscriptionChangeEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
28
lib/Core/SubscriptionChange/SubscriptionChangesReader.php
Normal file
28
lib/Core/SubscriptionChange/SubscriptionChangesReader.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\GPodderSync\Core\SubscriptionChange;
|
||||||
|
|
||||||
|
class SubscriptionChangesReader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $raw
|
||||||
|
*
|
||||||
|
* @return array|SubscriptionChange[]
|
||||||
|
*/
|
||||||
|
public function fromString(string $raw, bool $subscribed):? array {
|
||||||
|
$urls = str_replace(["[", "]", " "], "", $raw);
|
||||||
|
$urlList = explode(",", $urls);
|
||||||
|
|
||||||
|
if ($urlList[0] === "") {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
$subscriptionChanges = [];
|
||||||
|
foreach ($urlList as $url) {
|
||||||
|
$subscriptionChanges[] = new SubscriptionChange($url, $subscribed);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $subscriptionChanges;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
36
lib/Db/EpisodeAction/EpisodeActionEntity.php
Normal file
36
lib/Db/EpisodeAction/EpisodeActionEntity.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\GPodderSync\Db\EpisodeAction;
|
||||||
|
|
||||||
|
use JsonSerializable;
|
||||||
|
use OCP\AppFramework\Db\Entity;
|
||||||
|
|
||||||
|
class EpisodeActionEntity extends Entity implements JsonSerializable {
|
||||||
|
|
||||||
|
protected $podcast;
|
||||||
|
protected $episode;
|
||||||
|
protected $action;
|
||||||
|
protected $position;
|
||||||
|
protected $started;
|
||||||
|
protected $total;
|
||||||
|
protected $timestamp;
|
||||||
|
protected $userId;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->addType('id','integer');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function jsonSerialize() {
|
||||||
|
return [
|
||||||
|
'id' => $this->id,
|
||||||
|
'podcast' => $this->podcast,
|
||||||
|
'episode' => $this->episode,
|
||||||
|
'action' => $this->action,
|
||||||
|
'position' => $this->position,
|
||||||
|
'started' => $this->started,
|
||||||
|
'total' => $this->total,
|
||||||
|
'timestamp' => (new \DateTime($this->timestamp))->format("Y-m-d\TH:i:s"),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
50
lib/Db/EpisodeAction/EpisodeActionMapper.php
Normal file
50
lib/Db/EpisodeAction/EpisodeActionMapper.php
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\GPodderSync\Db\EpisodeAction;
|
||||||
|
|
||||||
|
use OCP\AppFramework\Db\DoesNotExistException;
|
||||||
|
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
||||||
|
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||||
|
use OCP\IDBConnection;
|
||||||
|
|
||||||
|
class EpisodeActionMapper extends \OCP\AppFramework\Db\QBMapper {
|
||||||
|
public function __construct(IDBConnection $db) {
|
||||||
|
parent::__construct($db, 'gpoddersync_episode_action', EpisodeActionEntity::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findAll(\DateTime $sinceTimestamp, string $userId): array {
|
||||||
|
$qb = $this->db->getQueryBuilder();
|
||||||
|
|
||||||
|
$qb->select('*')
|
||||||
|
->from($this->getTableName())
|
||||||
|
->where(
|
||||||
|
$qb->expr()->gt('timestamp', $qb->createNamedParameter($sinceTimestamp, IQueryBuilder::PARAM_DATE))
|
||||||
|
)
|
||||||
|
->andWhere(
|
||||||
|
$qb->expr()->eq('user_id', $qb->createNamedParameter($userId))
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->findEntities($qb);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findByEpisode(string $episode, string $userId) {
|
||||||
|
$qb = $this->db->getQueryBuilder();
|
||||||
|
|
||||||
|
$qb->select('*')
|
||||||
|
->from($this->getTableName())
|
||||||
|
->where(
|
||||||
|
$qb->expr()->eq('episode', $qb->createNamedParameter($episode))
|
||||||
|
)
|
||||||
|
->andWhere(
|
||||||
|
$qb->expr()->eq('user_id', $qb->createNamedParameter($userId))
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return $this->findEntity($qb);
|
||||||
|
} catch (DoesNotExistException $e) {
|
||||||
|
} catch (MultipleObjectsReturnedException $e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
lib/Db/EpisodeAction/EpisodeActionRepository.php
Normal file
23
lib/Db/EpisodeAction/EpisodeActionRepository.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\GPodderSync\Db\EpisodeAction;
|
||||||
|
|
||||||
|
class EpisodeActionRepository {
|
||||||
|
/**
|
||||||
|
* @var EpisodeActionMapper
|
||||||
|
*/
|
||||||
|
private EpisodeActionMapper $episodeActionMapper;
|
||||||
|
|
||||||
|
public function __construct(EpisodeActionMapper $episodeActionMapper) {
|
||||||
|
$this->episodeActionMapper = $episodeActionMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findAll(\DateTime $sinceTimestamp, string $userId) : array {
|
||||||
|
return $this->episodeActionMapper->findAll($sinceTimestamp, $userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findByEpisode(string $episode, string $userId): EpisodeActionEntity {
|
||||||
|
return $this->episodeActionMapper->findByEpisode($episode, $userId);
|
||||||
|
}
|
||||||
|
}
|
33
lib/Db/EpisodeAction/EpisodeActionWriter.php
Normal file
33
lib/Db/EpisodeAction/EpisodeActionWriter.php
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\GPodderSync\Db\EpisodeAction;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
|
||||||
|
|
||||||
|
class EpisodeActionWriter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var EpisodeActionMapper
|
||||||
|
*/
|
||||||
|
private EpisodeActionMapper $episodeActionMapper;
|
||||||
|
|
||||||
|
public function __construct(EpisodeActionMapper $episodeActionMapper) {
|
||||||
|
$this->episodeActionMapper = $episodeActionMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function save(EpisodeActionEntity $episodeActionEntity): EpisodeActionEntity {
|
||||||
|
return $this->episodeActionMapper->insert($episodeActionEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(EpisodeActionEntity $episodeActionEntity) {
|
||||||
|
return $this->episodeActionMapper->update($episodeActionEntity);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function purge() {
|
||||||
|
foreach ($this->episodeActionMapper->findAll() as $entity) {
|
||||||
|
$this->episodeActionMapper->delete($entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
lib/Db/SubscriptionChange/SubscriptionChangeEntity.php
Normal file
29
lib/Db/SubscriptionChange/SubscriptionChangeEntity.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\GPodderSync\Db\SubscriptionChange;
|
||||||
|
|
||||||
|
use JsonSerializable;
|
||||||
|
use OCP\AppFramework\Db\Entity;
|
||||||
|
|
||||||
|
class SubscriptionChangeEntity extends Entity implements JsonSerializable {
|
||||||
|
|
||||||
|
protected $url;
|
||||||
|
protected $subscribed;
|
||||||
|
protected $updated;
|
||||||
|
protected $userId;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->addType('id','integer');
|
||||||
|
$this->addType('subscribed','boolean');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function jsonSerialize() {
|
||||||
|
return [
|
||||||
|
'id' => $this->id,
|
||||||
|
'url' => $this->url,
|
||||||
|
'subscribed' => $this->subscribed,
|
||||||
|
'updated' => $this->updated,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
69
lib/Db/SubscriptionChange/SubscriptionChangeMapper.php
Normal file
69
lib/Db/SubscriptionChange/SubscriptionChangeMapper.php
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\GPodderSync\Db\SubscriptionChange;
|
||||||
|
|
||||||
|
use OCP\AppFramework\Db\DoesNotExistException;
|
||||||
|
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
||||||
|
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||||
|
use OCP\IDBConnection;
|
||||||
|
|
||||||
|
class SubscriptionChangeMapper extends \OCP\AppFramework\Db\QBMapper {
|
||||||
|
public function __construct(IDBConnection $db) {
|
||||||
|
parent::__construct($db, 'gpoddersync_subscriptions', SubscriptionChangeEntity::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findAll(string $userId): array {
|
||||||
|
$qb = $this->db->getQueryBuilder();
|
||||||
|
|
||||||
|
$qb->select('*')
|
||||||
|
->from($this->getTableName())
|
||||||
|
->where(
|
||||||
|
$qb->expr()->eq('user_id', $qb->createNamedParameter($userId))
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->findEntities($qb);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findByUrl(string $url, string $userId): SubscriptionChangeEntity {
|
||||||
|
$qb = $this->db->getQueryBuilder();
|
||||||
|
|
||||||
|
$qb->select('*')
|
||||||
|
->from($this->getTableName())
|
||||||
|
->where(
|
||||||
|
$qb->expr()->eq('url', $qb->createNamedParameter($url))
|
||||||
|
)
|
||||||
|
->andWhere(
|
||||||
|
$qb->expr()->eq('user_id', $qb->createNamedParameter($userId))
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return $this->findEntity($qb);
|
||||||
|
} catch (DoesNotExistException $e) {
|
||||||
|
} catch (MultipleObjectsReturnedException $e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function remove(SubscriptionChangeEntity $subscriptionChangeEntity) {
|
||||||
|
$this->delete($subscriptionChangeEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findAllSubscriptionState(bool $subscribed, \DateTime $sinceTimestamp, string $userId) {
|
||||||
|
$qb = $this->db->getQueryBuilder();
|
||||||
|
|
||||||
|
$qb->select('url')
|
||||||
|
->from($this->getTableName())
|
||||||
|
->where(
|
||||||
|
$qb->expr()->eq('subscribed', $qb->createNamedParameter($subscribed, IQueryBuilder::PARAM_BOOL))
|
||||||
|
)->andWhere(
|
||||||
|
$qb->expr()->gt('updated', $qb->createNamedParameter($sinceTimestamp, IQueryBuilder::PARAM_DATE))
|
||||||
|
)
|
||||||
|
->andWhere(
|
||||||
|
$qb->expr()->eq('user_id', $qb->createNamedParameter($userId))
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->findEntities($qb);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
32
lib/Db/SubscriptionChange/SubscriptionChangeRepository.php
Normal file
32
lib/Db/SubscriptionChange/SubscriptionChangeRepository.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\GPodderSync\Db\SubscriptionChange;
|
||||||
|
|
||||||
|
class SubscriptionChangeRepository {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var SubscriptionChangeMapper
|
||||||
|
*/
|
||||||
|
private SubscriptionChangeMapper $subscriptionChangeMapper;
|
||||||
|
|
||||||
|
public function __construct(SubscriptionChangeMapper $subscriptionChangeMapper) {
|
||||||
|
$this->subscriptionChangeMapper = $subscriptionChangeMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findAll() : array {
|
||||||
|
return $this->subscriptionChangeMapper->findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findByUrl(string $episode, string $userId): SubscriptionChangeEntity {
|
||||||
|
return $this->subscriptionChangeMapper->findByUrl($episode, $userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findAllSubscribed(\DateTime $sinceTimestamp, string $userId) {
|
||||||
|
return $this->subscriptionChangeMapper->findAllSubscriptionState(true, $sinceTimestamp, $userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findAllUnSubscribed(\DateTime $sinceTimestamp, string $userId) {
|
||||||
|
return $this->subscriptionChangeMapper->findAllSubscriptionState(false, $sinceTimestamp, $userId);
|
||||||
|
}
|
||||||
|
}
|
32
lib/Db/SubscriptionChange/SubscriptionChangeWriter.php
Normal file
32
lib/Db/SubscriptionChange/SubscriptionChangeWriter.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\GPodderSync\Db\SubscriptionChange;
|
||||||
|
|
||||||
|
|
||||||
|
class SubscriptionChangeWriter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var SubscriptionChangeMapper
|
||||||
|
*/
|
||||||
|
private SubscriptionChangeMapper $subscriptionChangeMapper;
|
||||||
|
|
||||||
|
public function __construct(SubscriptionChangeMapper $subscriptionChangeMapper) {
|
||||||
|
$this->subscriptionChangeMapper = $subscriptionChangeMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function purge() {
|
||||||
|
foreach ($this->subscriptionChangeMapper->findAll() as $entity) {
|
||||||
|
$this->subscriptionChangeMapper->delete($entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(SubscriptionChangeEntity $subscriptionChangeEntity): SubscriptionChangeEntity{
|
||||||
|
return $this->subscriptionChangeMapper->insert($subscriptionChangeEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(SubscriptionChangeEntity $subscriptionChangeEntity): SubscriptionChangeEntity{
|
||||||
|
return $this->subscriptionChangeMapper->update($subscriptionChangeEntity);
|
||||||
|
}
|
||||||
|
}
|
59
lib/Migration/Version0001Date20210520063113.php
Normal file
59
lib/Migration/Version0001Date20210520063113.php
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\GPodderSync\Migration;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Doctrine\DBAL\Types\Type;
|
||||||
|
use Doctrine\DBAL\Types\Types;
|
||||||
|
use OCP\DB\ISchemaWrapper;
|
||||||
|
use OCP\Migration\IOutput;
|
||||||
|
|
||||||
|
class Version0001Date20210520063113 extends \OCP\Migration\SimpleMigrationStep {
|
||||||
|
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
|
||||||
|
/** @var ISchemaWrapper $schema */
|
||||||
|
$schema = $schemaClosure();
|
||||||
|
|
||||||
|
if (!$schema->hasTable('gpoddersync_episode_action')) {
|
||||||
|
$table = $schema->createTable('gpoddersync_episode_action');
|
||||||
|
$table->addColumn('id', 'integer', [
|
||||||
|
'autoincrement' => true,
|
||||||
|
'notnull' => true,
|
||||||
|
]);
|
||||||
|
$table->addColumn('podcast', 'string', [
|
||||||
|
'notnull' => true,
|
||||||
|
'length' => 500
|
||||||
|
]);
|
||||||
|
$table->addColumn('episode', 'string', [
|
||||||
|
'notnull' => true,
|
||||||
|
'length' => 500,
|
||||||
|
'unique' => true,
|
||||||
|
]);
|
||||||
|
$table->addColumn('action', 'string', [
|
||||||
|
'notnull' => true,
|
||||||
|
'length' => 5
|
||||||
|
]);
|
||||||
|
$table->addColumn('position', 'integer', [
|
||||||
|
'notnull' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$table->addColumn('started', Types::INTEGER, [
|
||||||
|
'notnull' => true,
|
||||||
|
]);
|
||||||
|
$table->addColumn('total', Types::INTEGER, [
|
||||||
|
'notnull' => true,
|
||||||
|
]);
|
||||||
|
$table->addColumn('timestamp', Types::DATETIME_MUTABLE, [
|
||||||
|
'notnull' => true,
|
||||||
|
]);
|
||||||
|
$table->addColumn('user_id', Types::STRING, [
|
||||||
|
'notnull' => true,
|
||||||
|
'length' => 200,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$table->setPrimaryKey(['id']);
|
||||||
|
$table->addUniqueIndex(['episode', 'user_id'], 'gpoddersync_episode_user_id');
|
||||||
|
}
|
||||||
|
return $schema;
|
||||||
|
}
|
||||||
|
}
|
45
lib/Migration/Version0002Date20210524131313.php
Normal file
45
lib/Migration/Version0002Date20210524131313.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\GPodderSync\Migration;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Doctrine\DBAL\Types\Types;
|
||||||
|
use OCP\DB\ISchemaWrapper;
|
||||||
|
use OCP\Migration\IOutput;
|
||||||
|
|
||||||
|
class Version0002Date20210524131313 extends \OCP\Migration\SimpleMigrationStep {
|
||||||
|
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
|
||||||
|
/** @var ISchemaWrapper $schema */
|
||||||
|
$schema = $schemaClosure();
|
||||||
|
|
||||||
|
if (!$schema->hasTable('gpoddersync_subscriptions')) {
|
||||||
|
$table = $schema->createTable('gpoddersync_subscriptions');
|
||||||
|
$table->addColumn('id', 'integer', [
|
||||||
|
'autoincrement' => true,
|
||||||
|
'notnull' => true,
|
||||||
|
]);
|
||||||
|
$table->addColumn('url', Types::STRING, [
|
||||||
|
'notnull' => true,
|
||||||
|
'length' => 500
|
||||||
|
]);
|
||||||
|
|
||||||
|
$table->addColumn('subscribed', Types::BOOLEAN, [
|
||||||
|
'notnull' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$table->addColumn('updated', Types::DATETIME_MUTABLE, [
|
||||||
|
'notnull' => true,
|
||||||
|
]);
|
||||||
|
$table->addColumn('user_id', Types::STRING, [
|
||||||
|
'notnull' => true,
|
||||||
|
'length' => 200,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$table->setPrimaryKey(['id']);
|
||||||
|
$table->addUniqueIndex(['url', "user_id"], 'subscriptions_url_user');
|
||||||
|
|
||||||
|
}
|
||||||
|
return $schema;
|
||||||
|
}
|
||||||
|
}
|
20
lib/Migration/Version0003Date20210524131313.php
Normal file
20
lib/Migration/Version0003Date20210524131313.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\GPodderSync\Migration;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Doctrine\DBAL\Types\Types;
|
||||||
|
use OCP\DB\ISchemaWrapper;
|
||||||
|
use OCP\Migration\IOutput;
|
||||||
|
|
||||||
|
class Version0003Date20210524131313 extends \OCP\Migration\SimpleMigrationStep {
|
||||||
|
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
|
||||||
|
/** @var ISchemaWrapper $schema */
|
||||||
|
$schema = $schemaClosure();
|
||||||
|
|
||||||
|
$schema->dropTable('gpoddersync_subscriptions');
|
||||||
|
$schema->dropTable('gpoddersync_episode_action');
|
||||||
|
return $schema;
|
||||||
|
}
|
||||||
|
}
|
29
tests/Integration/AppTest.php
Normal file
29
tests/Integration/AppTest.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace OCA\GPodderSync\Tests\Integration\Controller;
|
||||||
|
|
||||||
|
use OCP\AppFramework\App;
|
||||||
|
use Test\TestCase;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test shows how to make a small Integration Test. Query your class
|
||||||
|
* directly from the container, only pass in mocks if needed and run your tests
|
||||||
|
* against the database
|
||||||
|
*/
|
||||||
|
class AppTest extends TestCase {
|
||||||
|
|
||||||
|
private $container;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
parent::setUp();
|
||||||
|
$app = new App('gpoddersync');
|
||||||
|
$this->container = $app->getContainer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAppInstalled() {
|
||||||
|
$appManager = $this->container->query('OCP\App\IAppManager');
|
||||||
|
$this->assertTrue($appManager->isInstalled('gpoddersync'));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
23
tests/Unit/Core/EpisodeAction/EpisodeActionReaderTest.php
Normal file
23
tests/Unit/Core/EpisodeAction/EpisodeActionReaderTest.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\GPodderSync\Tests\Unit\Core\EpisodeAction;
|
||||||
|
|
||||||
|
use OCA\GPodderSync\Core\EpisodeAction\EpisodeActionReader;
|
||||||
|
use PHPUnit_Framework_TestCase;
|
||||||
|
|
||||||
|
class EpisodeActionReaderTest extends PHPUnit_Framework_TestCase {
|
||||||
|
public function testCreateFromString(): void {
|
||||||
|
$reader = new EpisodeActionReader();
|
||||||
|
$episodeAction = $reader->fromString('[EpisodeAction{podcast=\'https://feeds.simplecast.com/wEl4UUJZ\', episode=\'https://chrt.fm/track/47G541/injector.simplecastaudio.com/f16c3da7-cf46-4a42-99b7-8467255c6086/episodes/e8e24c01-6157-40e8-9b5a-45d539aeb7e6/audio/128/default.mp3?aid=rss_feed&awCollectionId=f16c3da7-cf46-4a42-99b7-8467255c6086&awEpisodeId=e8e24c01-6157-40e8-9b5a-45d539aeb7e6&feed=wEl4UUJZ\', action=PLAY, timestamp=Tue May 18 23:45:11 GMT+02:00 2021, started=31, position=36, total=2474}]');
|
||||||
|
$this->assertSame("https://feeds.simplecast.com/wEl4UUJZ", $episodeAction->getPodcast());
|
||||||
|
$this->assertSame("https://chrt.fm/track/47G541/injector.simplecastaudio.com/f16c3da7-cf46-4a42-99b7-8467255c6086/episodes/e8e24c01-6157-40e8-9b5a-45d539aeb7e6/audio/128/default.mp3?aid=rss_feed&awCollectionId=f16c3da7-cf46-4a42-99b7-8467255c6086&awEpisodeId=e8e24c01-6157-40e8-9b5a-45d539aeb7e6&feed=wEl4UUJZ", $episodeAction->getEpisode());
|
||||||
|
$this->assertSame("PLAY", $episodeAction->getAction());
|
||||||
|
$this->assertSame("Tue May 18 23:45:11 GMT+02:00 2021", $episodeAction->getTimestamp());
|
||||||
|
$this->assertSame(31, $episodeAction->getStarted());
|
||||||
|
$this->assertSame(36, $episodeAction->getPosition());
|
||||||
|
$this->assertSame(2474, $episodeAction->getTotal());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\GPodderSync\Tests\Unit\Core\SubscriptionChange;
|
||||||
|
|
||||||
|
use OCA\GPodderSync\Core\SubscriptionChange\SubscriptionChangesReader;
|
||||||
|
use PHPUnit_Framework_TestCase;
|
||||||
|
|
||||||
|
class SubscriptionChangeReaderTest extends PHPUnit_Framework_TestCase {
|
||||||
|
public function testCreateFromString(): void {
|
||||||
|
$reader = new SubscriptionChangesReader();
|
||||||
|
$subscriptionChange = $reader->fromString('[https://feeds.megaphone.fm/HSW8286374095]', true);
|
||||||
|
$this->assertCount(1, $subscriptionChange);
|
||||||
|
$this->assertSame("https://feeds.megaphone.fm/HSW8286374095", $subscriptionChange[0]->getUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCreateFromEmptyString(): void {
|
||||||
|
$reader = new SubscriptionChangesReader();
|
||||||
|
$subscriptionChange = $reader->fromString('[]', true);
|
||||||
|
$this->assertCount(0, $subscriptionChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\GPodderSync\Tests\Unit\Core\SubscriptionChange;
|
||||||
|
|
||||||
|
use OCA\GPodderSync\Core\SubscriptionChange\SubscriptionChangeRequestParser;
|
||||||
|
use OCA\GPodderSync\Core\SubscriptionChange\SubscriptionChangesReader;
|
||||||
|
use PHPUnit_Framework_TestCase;
|
||||||
|
|
||||||
|
class SubscriptionChangeRequestParserTest extends PHPUnit_Framework_TestCase {
|
||||||
|
public function testSubscriptionRequestConvertsToSubscriptionChangeList() {
|
||||||
|
$subscriptionChangesParser = new SubscriptionChangeRequestParser(
|
||||||
|
new SubscriptionChangesReader(),
|
||||||
|
);
|
||||||
|
|
||||||
|
$subscriptionChanges = $subscriptionChangesParser->createSubscriptionChangeList('[https://feeds.simplecast.com/54nAGcIl]','[]');
|
||||||
|
$this->assertCount(1, $subscriptionChanges);
|
||||||
|
$this->assertSame('https://feeds.simplecast.com/54nAGcIl', $subscriptionChanges[0]->getUrl());
|
||||||
|
$this->assertTrue($subscriptionChanges[0]->isSubscribed());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSubscriptionRequestWithMultipleChangesConvertsToSubscriptionChangeList() {
|
||||||
|
$subscriptionChangesParser = new SubscriptionChangeRequestParser(
|
||||||
|
new SubscriptionChangesReader(),
|
||||||
|
);
|
||||||
|
|
||||||
|
$subscriptionChanges = $subscriptionChangesParser->createSubscriptionChangeList(
|
||||||
|
'[https://podcastfeeds.nbcnews.com/dateline-nbc,https://feeds.megaphone.fm/ADL9840290619]',
|
||||||
|
'[]');
|
||||||
|
$this->assertCount(2, $subscriptionChanges);
|
||||||
|
}
|
||||||
|
}
|
19
tests/bootstrap.php
Normal file
19
tests/bootstrap.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if (!defined('PHPUNIT_RUN')) {
|
||||||
|
define('PHPUNIT_RUN', 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__.'/../../../lib/base.php';
|
||||||
|
|
||||||
|
// Fix for "Autoload path not allowed: .../tests/lib/testcase.php"
|
||||||
|
\OC::$loader->addValidRoot(OC::$SERVERROOT . '/tests');
|
||||||
|
|
||||||
|
// Fix for "Autoload path not allowed: .../gpoddersync/tests/testcase.php"
|
||||||
|
\OC_App::loadApp('gpoddersync');
|
||||||
|
|
||||||
|
if(!class_exists('PHPUnit_Framework_TestCase')) {
|
||||||
|
require_once('PHPUnit/Autoload.php');
|
||||||
|
}
|
||||||
|
|
||||||
|
OC_Hook::clear();
|
7
vendor/autoload.php
vendored
Normal file
7
vendor/autoload.php
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload.php @generated by Composer
|
||||||
|
|
||||||
|
require_once __DIR__ . '/composer/autoload_real.php';
|
||||||
|
|
||||||
|
return ComposerAutoloaderInit101c320a2bec76e94ce31673caacea82::getLoader();
|
1
vendor/bin/phpunit
vendored
Symbolic link
1
vendor/bin/phpunit
vendored
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../phpunit/phpunit/phpunit
|
Loading…
Reference in New Issue
Block a user