fix #109 search for episode action by guid only (retry with episode url if nothing found) thus avoid multiple results
This commit is contained in:
parent
4999b96456
commit
a1893061ce
@ -13,107 +13,106 @@ use OCP\DB\Exception;
|
|||||||
class EpisodeActionSaver
|
class EpisodeActionSaver
|
||||||
{
|
{
|
||||||
|
|
||||||
private EpisodeActionRepository $episodeActionRepository;
|
private EpisodeActionRepository $episodeActionRepository;
|
||||||
private EpisodeActionWriter $episodeActionWriter;
|
private EpisodeActionWriter $episodeActionWriter;
|
||||||
private EpisodeActionReader $episodeActionReader;
|
private EpisodeActionReader $episodeActionReader;
|
||||||
|
|
||||||
private const DATETIME_FORMAT = 'Y-m-d\TH:i:s';
|
private const DATETIME_FORMAT = 'Y-m-d\TH:i:s';
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
EpisodeActionRepository $episodeActionRepository,
|
EpisodeActionRepository $episodeActionRepository,
|
||||||
EpisodeActionWriter $episodeActionWriter,
|
EpisodeActionWriter $episodeActionWriter,
|
||||||
EpisodeActionReader $episodeActionReader
|
EpisodeActionReader $episodeActionReader
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
$this->episodeActionRepository = $episodeActionRepository;
|
$this->episodeActionRepository = $episodeActionRepository;
|
||||||
$this->episodeActionWriter = $episodeActionWriter;
|
$this->episodeActionWriter = $episodeActionWriter;
|
||||||
$this->episodeActionReader = $episodeActionReader;
|
$this->episodeActionReader = $episodeActionReader;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function saveEpisodeActions(array $episodeActionsArray, string $userId): array
|
||||||
* @param array $episodeActionsArray
|
{
|
||||||
* @param string $userId
|
$episodeActions = $this->episodeActionReader->fromArray($episodeActionsArray);
|
||||||
* @return EpisodeActionEntity[]
|
|
||||||
*/
|
|
||||||
public function saveEpisodeActions(array $episodeActionsArray, string $userId): array
|
|
||||||
{
|
|
||||||
$episodeActions = $this->episodeActionReader->fromArray($episodeActionsArray);
|
|
||||||
|
|
||||||
$episodeActionEntities = [];
|
$episodeActionEntities = [];
|
||||||
|
|
||||||
foreach ($episodeActions as $episodeAction) {
|
foreach ($episodeActions as $episodeAction) {
|
||||||
$episodeActionEntity = $this->hydrateEpisodeActionEntity($episodeAction, $userId);
|
$episodeActionEntity = $this->hydrateEpisodeActionEntity($episodeAction, $userId);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$episodeActionEntities[] = $this->episodeActionWriter->save($episodeActionEntity);
|
$episodeActionEntities[] = $this->episodeActionWriter->save($episodeActionEntity);
|
||||||
} catch (UniqueConstraintViolationException $uniqueConstraintViolationException) {
|
} catch (UniqueConstraintViolationException $uniqueConstraintViolationException) {
|
||||||
$episodeActionEntities[] = $this->updateEpisodeAction($episodeActionEntity, $userId);
|
$episodeActionEntities[] = $this->updateEpisodeAction($episodeActionEntity, $userId);
|
||||||
} catch (Exception $exception) {
|
} catch (Exception $exception) {
|
||||||
if ($exception->getReason() === Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
|
if ($exception->getReason() === Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
|
||||||
$episodeActionEntities[] = $this->updateEpisodeAction($episodeActionEntity, $userId);
|
$episodeActionEntities[] = $this->updateEpisodeAction($episodeActionEntity, $userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $episodeActionEntities;
|
return $episodeActionEntities;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function convertTimestampToUnixEpoch(string $timestamp): string
|
private function convertTimestampToUnixEpoch(string $timestamp): string
|
||||||
{
|
{
|
||||||
return DateTime::createFromFormat(self::DATETIME_FORMAT, $timestamp)
|
return DateTime::createFromFormat(self::DATETIME_FORMAT, $timestamp)
|
||||||
->format("U");
|
->format("U");
|
||||||
}
|
}
|
||||||
|
|
||||||
private function updateEpisodeAction(
|
private function updateEpisodeAction(
|
||||||
EpisodeActionEntity $episodeActionEntity,
|
EpisodeActionEntity $episodeActionEntity,
|
||||||
string $userId
|
string $userId
|
||||||
): EpisodeActionEntity
|
): EpisodeActionEntity
|
||||||
{
|
{
|
||||||
$identifier = $episodeActionEntity->getGuid() ?? $episodeActionEntity->getEpisode();
|
$episodeActionToUpdate = $this->findEpisodeActionToUpdate($episodeActionEntity, $userId);
|
||||||
$episodeActionToUpdate = $this->episodeActionRepository->findByEpisodeIdentifier(
|
|
||||||
$identifier,
|
|
||||||
$userId
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($episodeActionToUpdate === null && $episodeActionEntity->getGuid() !== null) {
|
$episodeActionEntity->setId($episodeActionToUpdate->getId());
|
||||||
$episodeActionToUpdate = $this->getOldEpisodeActionByEpisodeUrl($episodeActionEntity->getEpisode(), $userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
$episodeActionEntity->setId($episodeActionToUpdate->getId());
|
$this->ensureGuidDoesNotGetNulledWithOldData($episodeActionToUpdate, $episodeActionEntity);
|
||||||
|
|
||||||
$this->ensureGuidDoesNotGetNulledWithOldData($episodeActionToUpdate, $episodeActionEntity);
|
return $this->episodeActionWriter->update($episodeActionEntity);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->episodeActionWriter->update($episodeActionEntity);
|
private function ensureGuidDoesNotGetNulledWithOldData(EpisodeAction $episodeActionToUpdate, EpisodeActionEntity $episodeActionEntity): void
|
||||||
}
|
{
|
||||||
|
$existingGuid = $episodeActionToUpdate->getGuid();
|
||||||
|
if ($existingGuid !== null && $episodeActionEntity->getGuid() === null) {
|
||||||
|
$episodeActionEntity->setGuid($existingGuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function getOldEpisodeActionByEpisodeUrl(string $episodeUrl, string $userId): ?EpisodeAction
|
private function hydrateEpisodeActionEntity(EpisodeAction $episodeAction, string $userId): EpisodeActionEntity
|
||||||
{
|
{
|
||||||
return $this->episodeActionRepository->findByEpisodeIdentifier(
|
$episodeActionEntity = new EpisodeActionEntity();
|
||||||
$episodeUrl,
|
$episodeActionEntity->setPodcast($episodeAction->getPodcast());
|
||||||
$userId
|
$episodeActionEntity->setEpisode($episodeAction->getEpisode());
|
||||||
);
|
$episodeActionEntity->setGuid($episodeAction->getGuid());
|
||||||
}
|
$episodeActionEntity->setAction($episodeAction->getAction());
|
||||||
|
$episodeActionEntity->setPosition($episodeAction->getPosition());
|
||||||
|
$episodeActionEntity->setStarted($episodeAction->getStarted());
|
||||||
|
$episodeActionEntity->setTotal($episodeAction->getTotal());
|
||||||
|
$episodeActionEntity->setTimestampEpoch($this->convertTimestampToUnixEpoch($episodeAction->getTimestamp()));
|
||||||
|
$episodeActionEntity->setUserId($userId);
|
||||||
|
|
||||||
private function ensureGuidDoesNotGetNulledWithOldData(EpisodeAction $episodeActionToUpdate, EpisodeActionEntity $episodeActionEntity): void
|
return $episodeActionEntity;
|
||||||
{
|
}
|
||||||
$existingGuid = $episodeActionToUpdate->getGuid();
|
|
||||||
if ($existingGuid !== null && $episodeActionEntity->getGuid() === null) {
|
|
||||||
$episodeActionEntity->setGuid($existingGuid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function hydrateEpisodeActionEntity(EpisodeAction $episodeAction, string $userId): EpisodeActionEntity
|
private function findEpisodeActionToUpdate(EpisodeActionEntity $episodeActionEntity, string $userId): ?EpisodeAction
|
||||||
{
|
{
|
||||||
$episodeActionEntity = new EpisodeActionEntity();
|
$episodeAction = null;
|
||||||
$episodeActionEntity->setPodcast($episodeAction->getPodcast());
|
if ($episodeActionEntity->getGuid() !== null) {
|
||||||
$episodeActionEntity->setEpisode($episodeAction->getEpisode());
|
$episodeAction = $this->episodeActionRepository->findByGuid(
|
||||||
$episodeActionEntity->setGuid($episodeAction->getGuid());
|
$episodeActionEntity->getGuid(),
|
||||||
$episodeActionEntity->setAction($episodeAction->getAction());
|
$userId
|
||||||
$episodeActionEntity->setPosition($episodeAction->getPosition());
|
);
|
||||||
$episodeActionEntity->setStarted($episodeAction->getStarted());
|
}
|
||||||
$episodeActionEntity->setTotal($episodeAction->getTotal());
|
|
||||||
$episodeActionEntity->setTimestampEpoch($this->convertTimestampToUnixEpoch($episodeAction->getTimestamp()));
|
|
||||||
$episodeActionEntity->setUserId($userId);
|
|
||||||
|
|
||||||
return $episodeActionEntity;
|
if ($episodeAction === null) {
|
||||||
}
|
$episodeAction = $this->episodeActionRepository->findByEpisodeUrl(
|
||||||
|
$episodeActionEntity->getEpisode(),
|
||||||
|
$userId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $episodeAction;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,20 +43,18 @@ class EpisodeActionMapper extends QBMapper
|
|||||||
* @param string $userId
|
* @param string $userId
|
||||||
* @return EpisodeActionEntity|null
|
* @return EpisodeActionEntity|null
|
||||||
*/
|
*/
|
||||||
public function findByEpisodeIdentifier(string $episodeIdentifier, string $userId) : ?EpisodeActionEntity
|
public function findByEpisodeUrl(string $episodeIdentifier, string $userId) : ?EpisodeActionEntity
|
||||||
{
|
{
|
||||||
$qb = $this->db->getQueryBuilder();
|
$qb = $this->db->getQueryBuilder();
|
||||||
|
|
||||||
$qb->select('*')
|
$qb->select('*')
|
||||||
->from($this->getTableName())
|
->from($this->getTableName())
|
||||||
->where(
|
->where(
|
||||||
$qb->expr()->orX(
|
$qb->expr()->eq('episode', $qb->createNamedParameter($episodeIdentifier))
|
||||||
$qb->expr()->eq('episode', $qb->createNamedParameter($episodeIdentifier)),
|
)
|
||||||
$qb->expr()->eq('guid', $qb->createNamedParameter($episodeIdentifier)))
|
|
||||||
)
|
|
||||||
->andWhere(
|
->andWhere(
|
||||||
$qb->expr()->eq('user_id', $qb->createNamedParameter($userId))
|
$qb->expr()->eq('user_id', $qb->createNamedParameter($userId))
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
/** @var EpisodeActionEntity $episodeActionEntity */
|
/** @var EpisodeActionEntity $episodeActionEntity */
|
||||||
@ -66,5 +64,26 @@ class EpisodeActionMapper extends QBMapper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function findByGuid(string $guid, string $userId) : ?EpisodeActionEntity
|
||||||
|
{
|
||||||
|
$qb = $this->db->getQueryBuilder();
|
||||||
|
|
||||||
|
$qb->select('*')
|
||||||
|
->from($this->getTableName())
|
||||||
|
->where(
|
||||||
|
$qb->expr()->eq('guid', $qb->createNamedParameter($guid))
|
||||||
|
)
|
||||||
|
->andWhere(
|
||||||
|
$qb->expr()->eq('user_id', $qb->createNamedParameter($userId))
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
/** @var EpisodeActionEntity $episodeActionEntity */
|
||||||
|
return $this->findEntity($qb);
|
||||||
|
} catch (DoesNotExistException|MultipleObjectsReturnedException|Exception $e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -28,17 +28,29 @@ class EpisodeActionRepository {
|
|||||||
return $episodeActions;
|
return $episodeActions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function findByEpisodeIdentifier(string $identifier, string $userId): ?EpisodeAction {
|
public function findByEpisodeUrl(string $episodeUrl, string $userId): ?EpisodeAction {
|
||||||
$episodeActionEntity = $this->episodeActionMapper->findByEpisodeIdentifier($identifier, $userId);
|
$episodeActionEntity = $this->episodeActionMapper->findByEpisodeUrl($episodeUrl, $userId);
|
||||||
|
|
||||||
if ($episodeActionEntity === null) {
|
if ($episodeActionEntity === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->mapEntityToEpisodeAction(
|
return $this->mapEntityToEpisodeAction(
|
||||||
$episodeActionEntity
|
$episodeActionEntity
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function findByGuid(string $guid, string $userId): ?EpisodeAction {
|
||||||
|
$episodeActionEntity = $this->episodeActionMapper->findByGuid($guid, $userId);
|
||||||
|
|
||||||
|
if ($episodeActionEntity === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->mapEntityToEpisodeAction(
|
||||||
|
$episodeActionEntity
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private function mapEntityToEpisodeAction(EpisodeActionEntity $episodeActionEntity): EpisodeAction
|
private function mapEntityToEpisodeAction(EpisodeActionEntity $episodeActionEntity): EpisodeAction
|
||||||
{
|
{
|
||||||
|
@ -3,7 +3,9 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace tests\Integration\Controller;
|
namespace tests\Integration\Controller;
|
||||||
|
|
||||||
use OC\Security\SecureRandom;
|
require_once __DIR__ . '/../../Helper/DatabaseTransaction.php';
|
||||||
|
require_once __DIR__ . '/../../Helper/Writer/TestWriter.php';
|
||||||
|
|
||||||
use OCA\GPodderSync\Controller\EpisodeActionController;
|
use OCA\GPodderSync\Controller\EpisodeActionController;
|
||||||
use OCA\GPodderSync\Core\EpisodeAction\EpisodeActionSaver;
|
use OCA\GPodderSync\Core\EpisodeAction\EpisodeActionSaver;
|
||||||
use OCA\GPodderSync\Db\EpisodeAction\EpisodeActionEntity;
|
use OCA\GPodderSync\Db\EpisodeAction\EpisodeActionEntity;
|
||||||
@ -12,7 +14,6 @@ use OCA\GPodderSync\Db\EpisodeAction\EpisodeActionRepository;
|
|||||||
use OCA\GPodderSync\Db\EpisodeAction\EpisodeActionWriter;
|
use OCA\GPodderSync\Db\EpisodeAction\EpisodeActionWriter;
|
||||||
use OCP\AppFramework\App;
|
use OCP\AppFramework\App;
|
||||||
use OCP\AppFramework\IAppContainer;
|
use OCP\AppFramework\IAppContainer;
|
||||||
use OCP\IConfig;
|
|
||||||
use OCP\IRequest;
|
use OCP\IRequest;
|
||||||
use Test\TestCase;
|
use Test\TestCase;
|
||||||
use tests\Helper\DatabaseTransaction;
|
use tests\Helper\DatabaseTransaction;
|
||||||
|
@ -22,19 +22,13 @@ class EpisodeActionGuidMigrationTest extends TestCase
|
|||||||
|
|
||||||
private IAppContainer $container;
|
private IAppContainer $container;
|
||||||
|
|
||||||
/**
|
private EpisodeActionWriter $episodeActionWriter;
|
||||||
* @var EpisodeActionWriter
|
|
||||||
*/
|
|
||||||
private $episodeActionWriter;
|
|
||||||
|
|
||||||
/**
|
private EpisodeActionRepository $episodeActionRepository;
|
||||||
* @var EpisodeActionRepository
|
|
||||||
*/
|
|
||||||
private $episodeActionRepository;
|
|
||||||
|
|
||||||
public function setUp(): void {
|
public function setUp(): void {
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
$app = new App('gpoddersync');
|
$app = new App('nextcloud-gpodder');
|
||||||
$this->container = $app->getContainer();
|
$this->container = $app->getContainer();
|
||||||
$this->episodeActionWriter = $this->container->get(EpisodeActionWriter::class);
|
$this->episodeActionWriter = $this->container->get(EpisodeActionWriter::class);
|
||||||
$this->episodeActionRepository = $this->container->get(EpisodeActionRepository::class);
|
$this->episodeActionRepository = $this->container->get(EpisodeActionRepository::class);
|
||||||
@ -85,7 +79,7 @@ class EpisodeActionGuidMigrationTest extends TestCase
|
|||||||
|
|
||||||
self::assertSame(
|
self::assertSame(
|
||||||
$savedEpisodeActionEntity->getId(),
|
$savedEpisodeActionEntity->getId(),
|
||||||
$this->episodeActionRepository->findByEpisodeIdentifier($episodeActionEntity->getEpisode(), self::USER_ID_0)->getId()
|
$this->episodeActionRepository->findByEpisodeUrl($episodeActionEntity->getEpisode(), self::USER_ID_0)->getId()
|
||||||
);
|
);
|
||||||
|
|
||||||
//update same episode action again this time with guid
|
//update same episode action again this time with guid
|
||||||
@ -96,12 +90,12 @@ class EpisodeActionGuidMigrationTest extends TestCase
|
|||||||
|
|
||||||
self::assertSame(
|
self::assertSame(
|
||||||
$savedEpisodeActionEntityWithGuid->getId(),
|
$savedEpisodeActionEntityWithGuid->getId(),
|
||||||
$this->episodeActionRepository->findByEpisodeIdentifier($episodeActionEntityWithGuid->getEpisode(), self::USER_ID_0)->getId()
|
$this->episodeActionRepository->findByEpisodeUrl($episodeActionEntityWithGuid->getEpisode(), self::USER_ID_0)->getId()
|
||||||
);
|
);
|
||||||
|
|
||||||
self::assertSame(
|
self::assertSame(
|
||||||
$savedEpisodeActionEntityWithGuid->getId(),
|
$savedEpisodeActionEntityWithGuid->getId(),
|
||||||
$this->episodeActionRepository->findByEpisodeIdentifier($episodeActionEntityWithGuid->getGuid(), self::USER_ID_0)->getId()
|
$this->episodeActionRepository->findByGuid($episodeActionEntityWithGuid->getGuid(), self::USER_ID_0)->getId()
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ class EpisodeActionRepositoryTest extends \Test\TestCase
|
|||||||
/** @var $episodeActionRepository EpisodeActionRepository */
|
/** @var $episodeActionRepository EpisodeActionRepository */
|
||||||
$episodeActionRepository = $this->container->get(EpisodeActionRepository::class);
|
$episodeActionRepository = $this->container->get(EpisodeActionRepository::class);
|
||||||
|
|
||||||
$retrievedEpisodeActionEntity = $episodeActionRepository->findByEpisodeIdentifier($guid, self::USER_ID_0);
|
$retrievedEpisodeActionEntity = $episodeActionRepository->findByGuid($guid, self::USER_ID_0);
|
||||||
self::assertSame('2021-08-22T23:58:56', $retrievedEpisodeActionEntity->getTimestamp());
|
self::assertSame('2021-08-22T23:58:56', $retrievedEpisodeActionEntity->getTimestamp());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ use Test\TestCase;
|
|||||||
/**
|
/**
|
||||||
* @group DB
|
* @group DB
|
||||||
*/
|
*/
|
||||||
class EpisodeActionSaverGuidBackwardCompatbilityTest extends TestCase
|
class EpisodeActionSaverGuidBackwardCompatibilityTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
private const USER_ID_0 = "testuser0";
|
private const USER_ID_0 = "testuser0";
|
@ -79,7 +79,7 @@ class TimestampMigrationTest extends TestCase
|
|||||||
$trueCrimeEpisodeActionEntity->setGuid(uniqid("self::TEST_GUID_1234"));
|
$trueCrimeEpisodeActionEntity->setGuid(uniqid("self::TEST_GUID_1234"));
|
||||||
$this->episodeActionWriter->save($trueCrimeEpisodeActionEntity);
|
$this->episodeActionWriter->save($trueCrimeEpisodeActionEntity);
|
||||||
|
|
||||||
$episodeActionBeforeConversion = $this->episodeActionMapper->findByEpisodeIdentifier(
|
$episodeActionBeforeConversion = $this->episodeActionMapper->findByGuid(
|
||||||
$scienceEpisodeActionEntity->getGuid(),
|
$scienceEpisodeActionEntity->getGuid(),
|
||||||
self::ADMIN
|
self::ADMIN
|
||||||
);
|
);
|
||||||
@ -92,7 +92,7 @@ class TimestampMigrationTest extends TestCase
|
|||||||
$timestampMigration = new TimestampMigration($this->dbConnection, $this->migrationConfig);
|
$timestampMigration = new TimestampMigration($this->dbConnection, $this->migrationConfig);
|
||||||
$timestampMigration->run($this->createMock(SimpleOutput::class));
|
$timestampMigration->run($this->createMock(SimpleOutput::class));
|
||||||
|
|
||||||
$scienceEpisodeActionAfterConversion = $this->episodeActionMapper->findByEpisodeIdentifier(
|
$scienceEpisodeActionAfterConversion = $this->episodeActionMapper->findByGuid(
|
||||||
$scienceEpisodeActionEntity->getGuid(),
|
$scienceEpisodeActionEntity->getGuid(),
|
||||||
self::ADMIN
|
self::ADMIN
|
||||||
);
|
);
|
||||||
@ -101,7 +101,7 @@ class TimestampMigrationTest extends TestCase
|
|||||||
$scienceEpisodeActionAfterConversion->getTimestampEpoch()
|
$scienceEpisodeActionAfterConversion->getTimestampEpoch()
|
||||||
);
|
);
|
||||||
|
|
||||||
$trueCrimeEpisodeActionAfterConversion = $this->episodeActionMapper->findByEpisodeIdentifier(
|
$trueCrimeEpisodeActionAfterConversion = $this->episodeActionMapper->findByGuid(
|
||||||
$trueCrimeEpisodeActionEntity->getGuid(),
|
$trueCrimeEpisodeActionEntity->getGuid(),
|
||||||
self::ADMIN
|
self::ADMIN
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user