diff --git a/src/Domain/AssetFile/AbstractAssetFileStatusFacade.php b/src/Domain/AssetFile/AbstractAssetFileStatusFacade.php
index 779ee037..33827eba 100644
--- a/src/Domain/AssetFile/AbstractAssetFileStatusFacade.php
+++ b/src/Domain/AssetFile/AbstractAssetFileStatusFacade.php
@@ -21,7 +21,6 @@
use AnzuSystems\CoreDamBundle\Exception\AssetFileProcessFailed;
use AnzuSystems\CoreDamBundle\Exception\DuplicateAssetFileException;
use AnzuSystems\CoreDamBundle\Exception\ForbiddenOperationException;
-use AnzuSystems\CoreDamBundle\Exception\RuntimeException;
use AnzuSystems\CoreDamBundle\Logger\DamLogger;
use AnzuSystems\CoreDamBundle\Messenger\Message\AssetRefreshPropertiesMessage;
use AnzuSystems\CoreDamBundle\Model\Dto\Asset\AssetAdmFinishDto;
@@ -37,6 +36,7 @@
use Doctrine\ORM\NonUniqueResultException;
use League\Flysystem\FilesystemException;
use Psr\Cache\InvalidArgumentException;
+use RuntimeException;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\Service\Attribute\Required;
use Throwable;
@@ -196,9 +196,16 @@ public function finishUpload(AssetAdmFinishDto $assetFinishDto, AssetFile $asset
*/
public function storeAndProcess(AssetFile $assetFile, ?AdapterFile $file = null, bool $dispatchPropertyRefresh = true): AssetFile
{
+ $lockName = $assetFile->getAssetType()->value . '_' . $assetFile->getLicence()->getId();
+
try {
if ($assetFile->getAssetAttributes()->getStatus()->is(AssetFileProcessStatus::Uploaded)) {
- $file = $this->store($assetFile, $file);
+ $file = $file ?: $this->createFile($assetFile);
+ $this->fileAttributesPostProcessor->processAttributes($assetFile, $file);
+ $this->fileAttributesPostProcessor->processChecksum($assetFile, $file);
+ // we need to lock process due to duplicity checks
+ $this->resourceLocker->lock($lockName);
+ $this->store($assetFile, $file);
}
if (null === $file) {
throw new RuntimeException(sprintf('AssetFile (%s) cant be processed without file', $assetFile->getId()));
@@ -206,8 +213,10 @@ public function storeAndProcess(AssetFile $assetFile, ?AdapterFile $file = null,
if ($assetFile->getAssetAttributes()->getStatus()->is(AssetFileProcessStatus::Stored)) {
$this->chunkFileManager->clearChunks($assetFile);
$this->process($assetFile, $file, $dispatchPropertyRefresh);
+ $this->resourceLocker->unLock($lockName);
}
} catch (DuplicateAssetFileException $duplicateAssetFileException) {
+ $this->resourceLocker->unLock($lockName);
$assetFile->getAssetAttributes()->setOriginAssetId(
(string) $duplicateAssetFileException->getOldAsset()->getId()
);
@@ -218,6 +227,7 @@ public function storeAndProcess(AssetFile $assetFile, ?AdapterFile $file = null,
$this->assetStatusManager->toDuplicate($assetFile);
$this->assetFileEventDispatcher->dispatchAssetFileChanged($assetFile);
} catch (AssetFileProcessFailed $assetFileProcessFailed) {
+ $this->resourceLocker->unLock($lockName);
$this->assetStatusManager->toFailed(
$assetFile,
$assetFileProcessFailed->getAssetFileFailedType(),
@@ -225,12 +235,15 @@ public function storeAndProcess(AssetFile $assetFile, ?AdapterFile $file = null,
);
$this->assetFileEventDispatcher->dispatchAssetFileChanged($assetFile);
} catch (Throwable $exception) {
+ $this->resourceLocker->unLock($lockName);
$this->assetStatusManager->toFailed(
$assetFile,
AssetFileFailedType::Unknown,
$exception
);
$this->assetFileEventDispatcher->dispatchAssetFileChanged($assetFile);
+ } finally {
+ $this->resourceLocker->unLock($lockName);
}
return $assetFile;
@@ -244,20 +257,10 @@ public function storeAndProcess(AssetFile $assetFile, ?AdapterFile $file = null,
* @throws TransportExceptionInterface
* @throws Throwable
*/
- public function store(AssetFile $assetFile, ?AdapterFile $file = null): AdapterFile
+ public function store(AssetFile $assetFile, AdapterFile $file): AdapterFile
{
- $file = $file ?: $this->createFile($assetFile);
-
- $this->fileAttributesPostProcessor->processAttributes($assetFile, $file);
- $this->fileAttributesPostProcessor->processChecksum($assetFile, $file);
-
- $lockName = $assetFile->getAssetType()->value . '_' . $assetFile->getLicence()->getId();
- $this->resourceLocker->lock($lockName);
-
$originAssetFile = $this->checkDuplicate($assetFile);
if ($originAssetFile) {
- $this->resourceLocker->unLock($lockName);
-
throw new DuplicateAssetFileException(
oldAsset: $originAssetFile,
newAsset: $assetFile
@@ -269,9 +272,7 @@ public function store(AssetFile $assetFile, ?AdapterFile $file = null): AdapterF
$this->assetFileStorageOperator->save($assetFile, $file);
$this->assetStatusManager->toStored($assetFile);
$this->assetManager->commit();
- $this->resourceLocker->unLock($lockName);
} catch (Throwable $exception) {
- $this->resourceLocker->unLock($lockName);
$this->assetManager->rollback();
throw $exception;
diff --git a/src/Domain/AssetFile/AssetFileStatusInterface.php b/src/Domain/AssetFile/AssetFileStatusInterface.php
index a1f0d34a..434206a5 100644
--- a/src/Domain/AssetFile/AssetFileStatusInterface.php
+++ b/src/Domain/AssetFile/AssetFileStatusInterface.php
@@ -11,7 +11,7 @@
#[AutoconfigureTag]
interface AssetFileStatusInterface
{
- public function store(AssetFile $assetFile, ?AdapterFile $file): AdapterFile;
+ public function store(AssetFile $assetFile, AdapterFile $file): AdapterFile;
public function process(AssetFile $assetFile, AdapterFile $file, bool $dispatchPropertyRefresh): AssetFile;
diff --git a/src/Domain/AssetMetadata/AssetMetadataProcessor.php b/src/Domain/AssetMetadata/AssetMetadataProcessor.php
index 2ea75aff..94d44cf3 100644
--- a/src/Domain/AssetMetadata/AssetMetadataProcessor.php
+++ b/src/Domain/AssetMetadata/AssetMetadataProcessor.php
@@ -75,6 +75,6 @@ private function provideCommonMetadata(array $rawMetadata, array $allowedMetadat
private function parseValue(string $value): string
{
- return htmlspecialchars(strip_tags($value));
+ return strip_tags($value);
}
}
diff --git a/src/Domain/Job/Processor/JobPodcastSynchronizerProcessor.php b/src/Domain/Job/Processor/JobPodcastSynchronizerProcessor.php
index ec11e393..bf558456 100644
--- a/src/Domain/Job/Processor/JobPodcastSynchronizerProcessor.php
+++ b/src/Domain/Job/Processor/JobPodcastSynchronizerProcessor.php
@@ -15,6 +15,7 @@
use AnzuSystems\CoreDamBundle\Model\ValueObject\PodcastSynchronizerPointer;
use AnzuSystems\CoreDamBundle\Repository\PodcastRepository;
use AnzuSystems\SerializerBundle\Exception\SerializerException;
+use DateTimeImmutable;
use DateTimeInterface;
use Generator;
@@ -28,9 +29,17 @@ public function __construct(
private readonly PodcastImportIterator $importIterator,
private readonly PodcastRepository $podcastRepository,
private int $bulkSize = self::BULK_SIZE,
+ private ?DateTimeImmutable $minImportFrom = null
) {
}
+ public function setMinImportFrom(?DateTimeImmutable $minImportFrom): self
+ {
+ $this->minImportFrom = $minImportFrom;
+
+ return $this;
+ }
+
public function setBulkSize(int $bulkSize): self
{
$this->bulkSize = $bulkSize;
@@ -45,15 +54,26 @@ public static function getSupportedJob(): string
/**
* @param JobPodcastSynchronizer $job
- *
* @throws SerializerException
*/
public function process(JobInterface $job): void
+ {
+ $this->start($job);
+ $this->processPodcasts($job);
+ }
+
+ /**
+ * @throws SerializerException
+ */
+ private function processPodcasts(JobPodcastSynchronizer $job): void
{
if ($job->isFullSync()) {
$this->importFull(
job: $job,
- generator: $this->importIterator->iterate(PodcastSynchronizerPointer::fromString($job->getLastBatchProcessedRecord()))
+ generator: $this->importIterator->iterate(
+ pointer: PodcastSynchronizerPointer::fromString($job->getLastBatchProcessedRecord()),
+ minImportFrom: $this->minImportFrom
+ )
);
return;
@@ -72,8 +92,9 @@ public function process(JobInterface $job): void
job: $job,
generator: $this->importIterator->iteratePodcast(
pointer: PodcastSynchronizerPointer::fromString($job->getLastBatchProcessedRecord()),
- podcastToImport: $podcast
- )
+ podcastToImport: $podcast,
+ minImportFrom: $this->minImportFrom
+ ),
);
}
}
diff --git a/src/Domain/Podcast/PodcastImportIterator.php b/src/Domain/Podcast/PodcastImportIterator.php
index 08f750a6..5a6beb53 100644
--- a/src/Domain/Podcast/PodcastImportIterator.php
+++ b/src/Domain/Podcast/PodcastImportIterator.php
@@ -4,6 +4,7 @@
namespace AnzuSystems\CoreDamBundle\Domain\Podcast;
+use AnzuSystems\CoreDamBundle\App;
use AnzuSystems\CoreDamBundle\Entity\Podcast;
use AnzuSystems\CoreDamBundle\Exception\InvalidArgumentException;
use AnzuSystems\CoreDamBundle\HttpClient\RssClient;
@@ -16,13 +17,15 @@
use DateTimeImmutable;
use Generator;
-final readonly class PodcastImportIterator
+final class PodcastImportIterator
{
+ private const string MIN_IMPORT_FROM_MODIFIER = '- 3 months';
+
public function __construct(
- private RssClient $client,
- private PodcastRssReader $reader,
- private PodcastRepository $podcastRepository,
- private DamLogger $damLogger,
+ private readonly RssClient $client,
+ private readonly PodcastRssReader $reader,
+ private readonly PodcastRepository $podcastRepository,
+ private readonly DamLogger $damLogger,
) {
}
@@ -31,7 +34,7 @@ public function __construct(
*
* @throws SerializerException
*/
- public function iterate(PodcastSynchronizerPointer $pointer): Generator
+ public function iterate(PodcastSynchronizerPointer $pointer, ?DateTimeImmutable $minImportFrom = null): Generator
{
$podcastToImport = $this->getPodcastToImport($pointer);
if (null === $podcastToImport) {
@@ -39,7 +42,7 @@ public function iterate(PodcastSynchronizerPointer $pointer): Generator
}
while ($podcastToImport) {
- foreach ($this->iteratePodcast($pointer, $podcastToImport) as $item) {
+ foreach ($this->iteratePodcast($pointer, $podcastToImport, $minImportFrom) as $item) {
yield $item;
}
@@ -58,7 +61,7 @@ public function iterate(PodcastSynchronizerPointer $pointer): Generator
*
* @throws SerializerException
*/
- public function iteratePodcast(PodcastSynchronizerPointer $pointer, Podcast $podcastToImport): Generator
+ public function iteratePodcast(PodcastSynchronizerPointer $pointer, Podcast $podcastToImport, ?DateTimeImmutable $minImportFrom = null): Generator
{
try {
$this->reader->initReader($this->client->readPodcastRss($podcastToImport));
@@ -74,7 +77,7 @@ public function iteratePodcast(PodcastSynchronizerPointer $pointer, Podcast $pod
return;
}
- $startFromDate = $this->getImportFrom($pointer, $podcastToImport);
+ $startFromDate = $this->getImportFrom($pointer, $minImportFrom);
foreach ($this->reader->readItems($startFromDate) as $podcastItem) {
yield new PodcastImportIteratorDto(
@@ -85,18 +88,16 @@ public function iteratePodcast(PodcastSynchronizerPointer $pointer, Podcast $pod
}
}
- private function getImportFrom(PodcastSynchronizerPointer $pointer, Podcast $podcast): ?DateTimeImmutable
+ private function getImportFrom(PodcastSynchronizerPointer $pointer, ?DateTimeImmutable $minImportFrom): ?DateTimeImmutable
{
- if (null === $podcast->getDates()->getImportFrom()) {
- return $pointer->getPubDate();
- }
+ $minImportFrom = $minImportFrom ?? App::getAppDate()->modify(self::MIN_IMPORT_FROM_MODIFIER);
if (null === $pointer->getPubDate()) {
- return $podcast->getDates()->getImportFrom();
+ return $minImportFrom;
}
- return $podcast->getDates()->getImportFrom() > $pointer->getPubDate()
- ? $podcast->getDates()->getImportFrom()
+ return $minImportFrom > $pointer->getPubDate()
+ ? $minImportFrom
: $pointer->getPubDate();
}
diff --git a/src/Domain/Podcast/PodcastRssReader.php b/src/Domain/Podcast/PodcastRssReader.php
index 1481157b..e4c04fe9 100644
--- a/src/Domain/Podcast/PodcastRssReader.php
+++ b/src/Domain/Podcast/PodcastRssReader.php
@@ -20,6 +20,7 @@
final class PodcastRssReader
{
+ public const string RSS_DATE_FORMAT = 'D, d M Y H:i:s T';
private const string ITUNES_KEY_KEY = 'itunes';
private SimpleXMLElement $body;
@@ -91,6 +92,7 @@ public function readItems(?DateTimeImmutable $from = null): Generator
{
foreach (array_reverse($this->body->channel?->xpath('item') ?? []) as $item) {
$item = $this->readItem($item);
+
if ($item->getPubDate() && $from && $from > $item->getPubDate()) {
continue;
}
@@ -145,7 +147,7 @@ private function getPublicationDate(SimpleXMLElement $element): ?DateTimeImmutab
{
$publicationDateString = (string) $element->pubDate;
$publicationDate = DateTimeImmutable::createFromFormat(
- 'D, d M Y H:i:s T',
+ self::RSS_DATE_FORMAT,
$publicationDateString,
);
diff --git a/src/Elasticsearch/IndexFactory/AssetIndexFactory.php b/src/Elasticsearch/IndexFactory/AssetIndexFactory.php
index 89e5b1cf..f1447016 100644
--- a/src/Elasticsearch/IndexFactory/AssetIndexFactory.php
+++ b/src/Elasticsearch/IndexFactory/AssetIndexFactory.php
@@ -12,7 +12,6 @@
use AnzuSystems\CoreDamBundle\Entity\DocumentFile;
use AnzuSystems\CoreDamBundle\Entity\ImageFile;
use AnzuSystems\CoreDamBundle\Entity\Interfaces\ExtSystemIndexableInterface;
-use AnzuSystems\CoreDamBundle\Entity\Keyword;
use AnzuSystems\CoreDamBundle\Entity\PodcastEpisode;
use AnzuSystems\CoreDamBundle\Entity\VideoFile;
use AnzuSystems\CoreDamBundle\Helper\CollectionHelper;
@@ -48,14 +47,13 @@ public function buildFromEntity(ExtSystemIndexableInterface $entity): array
return [
'id' => $entity->getId(),
+ 'mainFileId' => $entity->getMainFile()?->getId(),
'fileIds' => array_values(CollectionHelper::traversableToIds(
$entity->getSlots(),
fn (AssetSlot $slot): string => (string) $slot->getAssetFile()->getId()
)),
- 'keywordIds' => array_values(CollectionHelper::traversableToIds(
- $entity->getKeywords(),
- fn (Keyword $keyword): string => (string) $keyword->getId()
- )),
+ 'keywordIds' => array_values(CollectionHelper::traversableToIds($entity->getKeywords())),
+ 'authorIds' => array_values(CollectionHelper::traversableToIds($entity->getAuthors())),
'type' => $entity->getAttributes()->getAssetType()->toString(),
'status' => $entity->getAttributes()->getStatus(),
'described' => $entity->getAssetFlags()->isDescribed(),
@@ -64,6 +62,7 @@ public function buildFromEntity(ExtSystemIndexableInterface $entity): array
'generatedBySystem' => $entity->getAssetFlags()->isGeneratedBySystem(),
'modifiedAt' => $entity->getModifiedAt()->getTimestamp(),
'createdAt' => $entity->getCreatedAt()->getTimestamp(),
+ 'createdById' => $entity->getCreatedBy()->getId(),
'licence' => $entity->getLicence()->getId(),
'distributedInServices' => array_values($entity->getAssetFileProperties()->getDistributesInServices()),
'slotNames' => array_values($entity->getAssetFileProperties()->getSlotNames()),
diff --git a/src/Elasticsearch/QueryFactory/AssetQueryFactory.php b/src/Elasticsearch/QueryFactory/AssetQueryFactory.php
index 5b067cf2..5f6a2f69 100644
--- a/src/Elasticsearch/QueryFactory/AssetQueryFactory.php
+++ b/src/Elasticsearch/QueryFactory/AssetQueryFactory.php
@@ -12,6 +12,7 @@
use AnzuSystems\CoreDamBundle\Entity\AssetLicence;
use AnzuSystems\CoreDamBundle\Entity\CustomFormElement;
use AnzuSystems\CoreDamBundle\Entity\ExtSystem;
+use AnzuSystems\CoreDamBundle\Helper\UuidHelper;
final class AssetQueryFactory extends AbstractQueryFactory
{
@@ -40,6 +41,10 @@ protected function getMust(SearchDtoInterface $searchDto, ExtSystem $extSystem):
$customDataFields = array_unique($customDataFields);
$customDataFields = array_merge($customDataFields, ['title']);
+ if (UuidHelper::isUuid($searchDto->getText())) {
+ return parent::getMust($searchDto, $extSystem);
+ }
+
if ($searchDto->getText()) {
return [
'multi_match' => [
@@ -61,6 +66,23 @@ protected function getMust(SearchDtoInterface $searchDto, ExtSystem $extSystem):
protected function getFilter(SearchDtoInterface $searchDto): array
{
$filter = [];
+ if ($searchDto instanceof AssetAdmSearchLicenceCollectionDto) {
+ $this->applyLicenceCollectionFilter($filter, $searchDto);
+ }
+
+ if (UuidHelper::isUuid($searchDto->getText())) {
+ $filter[] = $this->getAssetIdAndMainFileIdFilter([$searchDto->getText()]);
+
+ // other filters should not be applied
+ return $filter;
+ }
+
+ if (false === empty($searchDto->getAssetAndMainFileIds())) {
+ $filter[] = $this->getAssetIdAndMainFileIdFilter($searchDto->getAssetAndMainFileIds());
+
+ // other filters should not be applied
+ return $filter;
+ }
if (false === (null === $searchDto->isVisible())) {
$filter[] = ['terms' => ['visible' => [$searchDto->isVisible()]]];
@@ -107,9 +129,18 @@ protected function getFilter(SearchDtoInterface $searchDto): array
if (false === empty($searchDto->getAssetIds())) {
$filter[] = ['terms' => ['fileIds' => $searchDto->getAssetIds()]];
}
+ if (false === empty($searchDto->getMainFileIds())) {
+ $filter[] = ['terms' => ['mainFileId' => $searchDto->getMainFileIds()]];
+ }
if (false === empty($searchDto->getKeywordIds())) {
$filter[] = ['terms' => ['keywordIds.keywordId' => $searchDto->getKeywordIds()]];
}
+ if (false === empty($searchDto->getAuthorIds())) {
+ $filter[] = ['terms' => ['authorIds.authorId' => $searchDto->getAuthorIds()]];
+ }
+ if (false === empty($searchDto->getCreatedByIds())) {
+ $filter[] = ['terms' => ['createdById' => $searchDto->getCreatedByIds()]];
+ }
$this->applyRangeFilter($filter, 'pixelSize', $searchDto->getPixelSizeFrom(), $searchDto->getPixelSizeUntil());
$this->applyRangeFilter($filter, 'ratioWidth', $searchDto->getRatioWidthFrom(), $searchDto->getRatioWidthUntil());
@@ -122,13 +153,21 @@ protected function getFilter(SearchDtoInterface $searchDto): array
$this->applyRangeFilter($filter, 'slotsCount', $searchDto->getSlotsCountFrom(), $searchDto->getSlotsCountUntil());
$this->applyRangeFilter($filter, 'createdAt', $searchDto->getCreatedAtFrom()?->getTimestamp(), $searchDto->getCreatedAtUntil()?->getTimestamp());
- if ($searchDto instanceof AssetAdmSearchLicenceCollectionDto) {
- $this->applyLicenceCollectionFilter($filter, $searchDto);
- }
-
return $filter;
}
+ private function getAssetIdAndMainFileIdFilter(array $ids): array
+ {
+ return [
+ 'bool' => [
+ 'should' => [
+ ['terms' => ['id' => $ids]],
+ ['terms' => ['mainFileId' => $ids]],
+ ],
+ ],
+ ];
+ }
+
private function applyLicenceCollectionFilter(array &$filter, AssetAdmSearchLicenceCollectionDto $dto): void
{
if ($dto->getLicences()->isEmpty()) {
diff --git a/src/Elasticsearch/QueryFactory/AuthorQueryFactory.php b/src/Elasticsearch/QueryFactory/AuthorQueryFactory.php
index 56f73323..cbb906f1 100644
--- a/src/Elasticsearch/QueryFactory/AuthorQueryFactory.php
+++ b/src/Elasticsearch/QueryFactory/AuthorQueryFactory.php
@@ -26,11 +26,10 @@ protected function getMust(SearchDtoInterface $searchDto, ExtSystem $extSystem):
return [
'multi_match' => [
'query' => $searchDto->getText(),
- 'type' => 'bool_prefix',
+ 'type' => 'most_fields',
'fields' => [
'name',
- 'name._2gram',
- 'name._3gram',
+ 'name.edgegrams',
],
],
];
diff --git a/src/Elasticsearch/QueryFactory/KeywordQueryFactory.php b/src/Elasticsearch/QueryFactory/KeywordQueryFactory.php
index c62639df..0f7a04a1 100644
--- a/src/Elasticsearch/QueryFactory/KeywordQueryFactory.php
+++ b/src/Elasticsearch/QueryFactory/KeywordQueryFactory.php
@@ -26,11 +26,10 @@ protected function getMust(SearchDtoInterface $searchDto, ExtSystem $extSystem):
return [
'multi_match' => [
'query' => $searchDto->getText(),
- 'type' => 'bool_prefix',
+ 'type' => 'most_fields',
'fields' => [
'name',
- 'name._2gram',
- 'name._3gram',
+ 'name.edgegrams',
],
],
];
diff --git a/src/Elasticsearch/SearchDto/AssetAdmSearchDto.php b/src/Elasticsearch/SearchDto/AssetAdmSearchDto.php
index 41d2280d..7cbf2e98 100644
--- a/src/Elasticsearch/SearchDto/AssetAdmSearchDto.php
+++ b/src/Elasticsearch/SearchDto/AssetAdmSearchDto.php
@@ -23,10 +23,18 @@ class AssetAdmSearchDto extends AbstractSearchDto
#[Serialize]
protected string $text = '';
+ #[Serialize(handler: ArrayStringHandler::class)]
+ #[Assert\Count(max: 20, maxMessage: ValidationException::ERROR_FIELD_LENGTH_MAX)]
+ protected array $assetAndMainFileIds = [];
+
#[Serialize(handler: ArrayStringHandler::class)]
#[Assert\Count(max: 20, maxMessage: ValidationException::ERROR_FIELD_LENGTH_MAX)]
protected array $assetIds = [];
+ #[Serialize(handler: ArrayStringHandler::class)]
+ #[Assert\Count(max: 20, maxMessage: ValidationException::ERROR_FIELD_LENGTH_MAX)]
+ protected array $mainFileIds = [];
+
#[Serialize(handler: ArrayStringHandler::class)]
#[Assert\Count(max: 20, maxMessage: ValidationException::ERROR_FIELD_LENGTH_MAX)]
protected array $podcastIds = [];
@@ -35,6 +43,14 @@ class AssetAdmSearchDto extends AbstractSearchDto
#[Assert\Count(max: 20, maxMessage: ValidationException::ERROR_FIELD_LENGTH_MAX)]
protected array $keywordIds = [];
+ #[Serialize(handler: ArrayStringHandler::class)]
+ #[Assert\Count(max: 20, maxMessage: ValidationException::ERROR_FIELD_LENGTH_MAX)]
+ protected array $authorIds = [];
+
+ #[Serialize(handler: ArrayStringHandler::class)]
+ #[Assert\Count(max: 20, maxMessage: ValidationException::ERROR_FIELD_LENGTH_MAX)]
+ protected array $createdByIds = [];
+
#[Serialize(handler: ArrayStringHandler::class)]
#[Assert\Choice(choices: AssetType::CHOICES, multiple: true, multipleMessage: ValidationException::ERROR_FIELD_INVALID)]
#[Assert\Count(max: 4, maxMessage: ValidationException::ERROR_FIELD_LENGTH_MAX)]
@@ -634,4 +650,52 @@ public function setKeywordIds(array $keywordIds): void
{
$this->keywordIds = $keywordIds;
}
+
+ public function getAuthorIds(): array
+ {
+ return $this->authorIds;
+ }
+
+ public function setAuthorIds(array $authorIds): self
+ {
+ $this->authorIds = $authorIds;
+
+ return $this;
+ }
+
+ public function getMainFileIds(): array
+ {
+ return $this->mainFileIds;
+ }
+
+ public function setMainFileIds(array $mainFileIds): self
+ {
+ $this->mainFileIds = $mainFileIds;
+
+ return $this;
+ }
+
+ public function getCreatedByIds(): array
+ {
+ return $this->createdByIds;
+ }
+
+ public function setCreatedByIds(array $createdByIds): self
+ {
+ $this->createdByIds = $createdByIds;
+
+ return $this;
+ }
+
+ public function getAssetAndMainFileIds(): array
+ {
+ return $this->assetAndMainFileIds;
+ }
+
+ public function setAssetAndMainFileIds(array $assetAndMainFileIds): self
+ {
+ $this->assetAndMainFileIds = $assetAndMainFileIds;
+
+ return $this;
+ }
}
diff --git a/src/Entity/Chunk.php b/src/Entity/Chunk.php
index b9739d10..e818907e 100644
--- a/src/Entity/Chunk.php
+++ b/src/Entity/Chunk.php
@@ -16,7 +16,7 @@
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: ChunkRepository::class)]
-#[ORM\Index(fields: ['offset'], name: 'IDX_offset')]
+#[ORM\Index(name: 'IDX_offset', fields: ['offset'])]
class Chunk implements UuidIdentifiableInterface, FileSystemStorableInterface, AssetLicenceInterface
{
use UuidIdentityTrait;
@@ -33,7 +33,7 @@ class Chunk implements UuidIdentifiableInterface, FileSystemStorableInterface, A
private int $size;
#[Serialize]
- #[ORM\Column(type: Types::STRING, length: 32)]
+ #[ORM\Column(type: Types::STRING, length: 255)]
private string $mimeType;
#[ORM\Column(type: Types::STRING, length: 255)]
diff --git a/src/Entity/Embeds/AssetFileAttributes.php b/src/Entity/Embeds/AssetFileAttributes.php
index f17fee64..76288286 100644
--- a/src/Entity/Embeds/AssetFileAttributes.php
+++ b/src/Entity/Embeds/AssetFileAttributes.php
@@ -29,7 +29,7 @@ class AssetFileAttributes
#[ORM\Column(type: Types::STRING, length: 255)]
private string $originFileName;
- #[ORM\Column(type: Types::STRING, length: 127)]
+ #[ORM\Column(type: Types::STRING, length: 255)]
private string $mimeType;
#[ORM\Column(type: Types::STRING, length: 127)]
diff --git a/src/Entity/Embeds/PodcastDates.php b/src/Entity/Embeds/PodcastDates.php
index 19e43da4..bdda485e 100644
--- a/src/Entity/Embeds/PodcastDates.php
+++ b/src/Entity/Embeds/PodcastDates.php
@@ -21,11 +21,17 @@ public function __construct()
$this->setImportFrom(null);
}
+ /**
+ * @deprecated
+ */
public function getImportFrom(): ?DateTimeImmutable
{
return $this->importFrom;
}
+ /**
+ * @deprecated
+ */
public function setImportFrom(?DateTimeImmutable $importFrom): self
{
$this->importFrom = $importFrom;
diff --git a/src/Helper/StringHelper.php b/src/Helper/StringHelper.php
index 9572efa8..53efd976 100644
--- a/src/Helper/StringHelper.php
+++ b/src/Helper/StringHelper.php
@@ -15,6 +15,15 @@ public static function parseLength(string $input, int $length): string
return mb_substr($input, 0, $length);
}
+ public static function getFirstChar(string $string): string
+ {
+ if (false === empty($string)) {
+ return mb_substr($string, 0, 1);
+ }
+
+ return '';
+ }
+
public static function normalize(string $input, StringNormalizerConfiguration $configuration): string
{
if ($configuration->isTrim()) {
@@ -33,7 +42,7 @@ public static function parseString(
?int $length = null,
bool $trim = true,
): string {
- $string = htmlspecialchars(strip_tags($input));
+ $string = strip_tags($input);
if ($length) {
$string = self::parseLength($string, $length);
}
diff --git a/src/Helper/UuidHelper.php b/src/Helper/UuidHelper.php
new file mode 100644
index 00000000..4f0bd175
--- /dev/null
+++ b/src/Helper/UuidHelper.php
@@ -0,0 +1,16 @@
+ [
'type' => 'keyword',
],
+ 'mainFileId' => [
+ 'type' => 'keyword',
+ ],
+ 'createdById' => [
+ 'type' => 'keyword',
+ ],
'type' => [
'type' => 'keyword',
],
@@ -32,6 +38,14 @@
],
],
],
+ 'authorIds' => [
+ 'type' => 'text',
+ 'fields' => [
+ 'authorId' => [
+ 'type' => 'keyword',
+ ],
+ ],
+ ],
'fileIds' => [
'type' => 'keyword',
],
diff --git a/src/Resources/config/elasticsearch/author.php b/src/Resources/config/elasticsearch/author.php
index 9c32f373..1066d0eb 100644
--- a/src/Resources/config/elasticsearch/author.php
+++ b/src/Resources/config/elasticsearch/author.php
@@ -20,7 +20,14 @@
'type' => 'boolean',
],
'name' => [
- 'type' => 'search_as_you_type',
+ 'type' => 'text',
+ 'analyzer' => 'exact_stop',
+ 'fields' => [
+ 'edgegrams' => [
+ 'type' => 'text',
+ 'analyzer' => 'edgegrams',
+ ],
+ ],
],
'type' => [
'type' => 'keyword',
diff --git a/src/Resources/config/elasticsearch/keyword.php b/src/Resources/config/elasticsearch/keyword.php
index 17f55f48..41d089e1 100644
--- a/src/Resources/config/elasticsearch/keyword.php
+++ b/src/Resources/config/elasticsearch/keyword.php
@@ -17,7 +17,14 @@
'type' => 'boolean',
],
'name' => [
- 'type' => 'search_as_you_type',
+ 'type' => 'text',
+ 'analyzer' => 'exact_stop',
+ 'fields' => [
+ 'edgegrams' => [
+ 'type' => 'text',
+ 'analyzer' => 'edgegrams',
+ ],
+ ],
],
'createdAt' => [
'type' => 'date',
diff --git a/tests/Domain/Job/JobPodcastSynchronizerProcessorTest.php b/tests/Domain/Job/JobPodcastSynchronizerProcessorTest.php
index 320baf08..6c29486c 100644
--- a/tests/Domain/Job/JobPodcastSynchronizerProcessorTest.php
+++ b/tests/Domain/Job/JobPodcastSynchronizerProcessorTest.php
@@ -10,10 +10,12 @@
use AnzuSystems\CommonBundle\Model\Enum\JobStatus;
use AnzuSystems\CommonBundle\Tests\AnzuKernelTestCase;
use AnzuSystems\Contracts\Entity\AnzuUser;
+use AnzuSystems\CoreDamBundle\App;
use AnzuSystems\CoreDamBundle\DataFixtures\AssetLicenceFixtures as BaseAssetLicenceFixtures;
use AnzuSystems\CoreDamBundle\DataFixtures\PodcastFixtures;
use AnzuSystems\CoreDamBundle\Domain\Job\Processor\JobPodcastSynchronizerProcessor;
use AnzuSystems\CoreDamBundle\Domain\Job\Processor\JobUserDataDeleteProcessor;
+use AnzuSystems\CoreDamBundle\Domain\Podcast\PodcastRssReader;
use AnzuSystems\CoreDamBundle\Entity\AssetLicence;
use AnzuSystems\CoreDamBundle\Entity\JobPodcastSynchronizer;
use AnzuSystems\CoreDamBundle\Entity\PodcastEpisode;
@@ -23,6 +25,8 @@
use AnzuSystems\CoreDamBundle\Tests\Data\Entity\User;
use AnzuSystems\CoreDamBundle\Tests\Data\Fixtures\AssetLicenceFixtures;
use AnzuSystems\CoreDamBundle\Tests\Data\Fixtures\JobFixtures;
+use AnzuSystems\CoreDamBundle\Tests\HttpClient\RssPodcastMock;
+use DateTimeInterface;
use Doctrine\ORM\EntityManagerInterface;
final class JobPodcastSynchronizerProcessorTest extends CoreDamKernelTestCase
@@ -59,19 +63,25 @@ public function testFullSyncProcess(): void
$this->assertCount(3, $podcast1->getEpisodes());
$this->assertCount(1, $podcast3->getEpisodes());
$this->assertEquals(JobStatus::AwaitingBatchProcess, $job->getStatus());
- $this->assertEquals(sprintf('%s|%s', PodcastFixtures::PODCAST_1, '2023-03-05T23:01:45+00:00'), $job->getLastBatchProcessedRecord());
+
+ $pointerDate = App::getAppDate()->modify(RssPodcastMock::THIRD_RSS_DATE_MODEFIER)->format(DateTimeInterface::ATOM);
+ $this->assertEquals(sprintf('%s|%s', PodcastFixtures::PODCAST_1, $pointerDate), $job->getLastBatchProcessedRecord());
$this->synchronizerProcessor->process($job);
$this->entityManager->refresh($podcast1);
$this->assertCount(5, $podcast1->getEpisodes());
$this->assertEquals(JobStatus::AwaitingBatchProcess, $job->getStatus());
- $this->assertEquals(sprintf('%s|%s', PodcastFixtures::PODCAST_1, '2023-03-07T23:01:44+00:00'), $job->getLastBatchProcessedRecord());
+
+ $pointerDate = App::getAppDate()->modify(RssPodcastMock::FIRST_RSS_DATE_MODEFIER)->format(DateTimeInterface::ATOM);
+ $this->assertEquals(sprintf('%s|%s', PodcastFixtures::PODCAST_1, $pointerDate), $job->getLastBatchProcessedRecord());
$this->synchronizerProcessor->process($job);
$this->entityManager->refresh($podcast2);
$this->assertCount(2, $podcast2->getEpisodes());
$this->assertEquals(JobStatus::AwaitingBatchProcess, $job->getStatus());
- $this->assertEquals(sprintf('%s|%s', PodcastFixtures::PODCAST_2, '2023-03-07T23:01:44+00:00'), $job->getLastBatchProcessedRecord());
+
+ $pointerDate = App::getAppDate()->modify(RssPodcastMock::FIRST_RSS_DATE_MODEFIER)->format(DateTimeInterface::ATOM);
+ $this->assertEquals(sprintf('%s|%s', PodcastFixtures::PODCAST_2, $pointerDate), $job->getLastBatchProcessedRecord());
$this->synchronizerProcessor->process($job);
$this->assertEquals(JobStatus::Done, $job->getStatus());
@@ -102,12 +112,11 @@ public function testSpecificPodcastSyncProcessAndImportFrom(): void
$job = $this->entityManager->getRepository(JobPodcastSynchronizer::class)->findBy(['fullSync' => false])[0];
$this->assertInstanceOf(JobPodcastSynchronizer::class, $job);
- $this->synchronizerProcessor->setBulkSize(2);
+ $this->synchronizerProcessor
+ ->setBulkSize(2)
+ ->setMinImportFrom(App::getAppDate()->modify('-7 weeks'))
+ ;
$podcast1 = $this->podcastRepository->find(PodcastFixtures::PODCAST_1);
- $podcast1->getDates()->setImportFrom(\DateTimeImmutable::createFromFormat(
- \DateTimeInterface::ATOM,
- '2023-03-07T22:01:44+00:00'
- ));
$this->synchronizerProcessor->process($job);
$this->entityManager->refresh($podcast1);
diff --git a/tests/HttpClient/RssPodcastMock.php b/tests/HttpClient/RssPodcastMock.php
index 0a7c9051..b78059a2 100644
--- a/tests/HttpClient/RssPodcastMock.php
+++ b/tests/HttpClient/RssPodcastMock.php
@@ -4,12 +4,20 @@
namespace AnzuSystems\CoreDamBundle\Tests\HttpClient;
+use AnzuSystems\CoreDamBundle\App;
+use AnzuSystems\CoreDamBundle\Domain\Podcast\PodcastRssReader;
use Symfony\Component\HttpClient\MockHttpClient;
use Symfony\Component\HttpClient\Response\MockResponse;
use Symfony\Component\HttpFoundation\Response;
final class RssPodcastMock extends AbstractFileMock
{
+ public const string FIRST_RSS_DATE_MODEFIER = '-6 weeks';
+ public const string SECOND_RSS_DATE_MODEFIER = '-8 weeks';
+ public const string THIRD_RSS_DATE_MODEFIER = '-10 weeks';
+ private const string FIRST_PUB_DATE_RSS_PLACEHOLDER = '__FirstPubDatePlaceholder__';
+ private const string SECOND_PUB_DATE_RSS_PLACEHOLDER = '__SecondPubDatePlaceholder__';
+ private const string THIRD_PUB_DATE_RSS_PLACEHOLDER = '__ThirdPubDatePlaceholder__';
public function __invoke(): MockHttpClient
{
return new MockHttpClient(
@@ -29,16 +37,23 @@ private function getResponse(string $method, string $url, array $options = []):
private function getContent(string $url): string
{
+ $fileContent = '';
if ('https://anchor.fm/s/8a651488/podcast/rss' === $url) {
- return $this->getTestDataFile( 'firstPodcast.xml');
+ $fileContent = $this->getTestDataFile( 'firstPodcast.xml');
}
if ('https://anchor.fm/s/4d8e8b48/podcast/rss' === $url) {
- return $this->getTestDataFile( 'secondPodcast.xml');
+ $fileContent = $this->getTestDataFile( 'secondPodcast.xml');
}
if ('https://anchor.fm/s/7758ecd4/podcast/rss' === $url) {
- return $this->getTestDataFile( 'thirdPodcast.xml');
+ $fileContent = $this->getTestDataFile( 'thirdPodcast.xml');
}
+ $firstEpisodeDate = App::getAppDate()->modify(self::FIRST_RSS_DATE_MODEFIER)->format(PodcastRssReader::RSS_DATE_FORMAT);
+ $secondEpisodeDate = App::getAppDate()->modify(self::SECOND_RSS_DATE_MODEFIER)->format(PodcastRssReader::RSS_DATE_FORMAT);
+ $thirdEpisodeDate = App::getAppDate()->modify(self::THIRD_RSS_DATE_MODEFIER)->format(PodcastRssReader::RSS_DATE_FORMAT);
- return '';
+ $fileContent = str_replace(self::FIRST_PUB_DATE_RSS_PLACEHOLDER, $firstEpisodeDate, $fileContent);
+ $fileContent = str_replace(self::SECOND_PUB_DATE_RSS_PLACEHOLDER, $secondEpisodeDate, $fileContent);
+
+ return str_replace(self::THIRD_PUB_DATE_RSS_PLACEHOLDER, $thirdEpisodeDate, $fileContent);
}
}
diff --git a/tests/data/Files/firstPodcast.xml b/tests/data/Files/firstPodcast.xml
index c5fa8493..bfed7afb 100644
--- a/tests/data/Files/firstPodcast.xml
+++ b/tests/data/Files/firstPodcast.xml
@@ -20,7 +20,7 @@
https://www.thisamericanlife.org/789/the-runaround
3: Dobre rano description
- Tue, 07 Mar 2023 23:01:44 GMT
+ __FirstPubDatePlaceholder__
fpg3
SME.sk
@@ -36,7 +36,7 @@
https://www.thisamericanlife.org/789/the-runaround
2: Dobre rano description
- Mon, 06 Mar 2023 23:01:44 GMT
+ __SecondPubDatePlaceholder__
fpg2
SME.sk
@@ -52,7 +52,7 @@
https://www.thisamericanlife.org/789/the-runaround
3: Dobre rano description
- Sun, 05 Mar 2023 23:01:45 GMT
+ __ThirdPubDatePlaceholder__
fpg1
SME.sk
diff --git a/tests/data/Files/secondPodcast.xml b/tests/data/Files/secondPodcast.xml
index e45ecf96..49bf502a 100644
--- a/tests/data/Files/secondPodcast.xml
+++ b/tests/data/Files/secondPodcast.xml
@@ -20,7 +20,7 @@
https://www.thisamericanlife.org/789/the-runaround
3: Klik description
- Tue, 07 Mar 2023 23:01:44 GMT
+ __FirstPubDatePlaceholder__
spg2
SME.sk
@@ -36,7 +36,7 @@
https://www.thisamericanlife.org/789/the-runaround
2: Klik description
- Mon, 06 Mar 2023 23:01:44 GMT
+ __SecondPubDatePlaceholder__
spg1
SME.sk
diff --git a/tests/data/Files/thirdPodcast.xml b/tests/data/Files/thirdPodcast.xml
index cdde4f60..79884508 100644
--- a/tests/data/Files/thirdPodcast.xml
+++ b/tests/data/Files/thirdPodcast.xml
@@ -20,7 +20,7 @@
https://www.thisamericanlife.org/789/the-runaround
3: Rozprávky SME description
- Tue, 07 Mar 2023 23:01:44 GMT
+ __FirstPubDatePlaceholder__
tpg1
SME.sk