generated from Nodarx/template
refactor: split service into ServiceBase and LiveService
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -10,10 +10,9 @@ declare(strict_types=1);
|
||||
namespace KTXM\ProviderImap\Service\Cache;
|
||||
|
||||
use KTXM\ProviderImap\Client\Protocol\Command\Argument\FetchOptions;
|
||||
use KTXM\ProviderImap\Providers\CollectionResource;
|
||||
use KTXM\ProviderImap\Providers\EntityResource;
|
||||
use KTXM\ProviderImap\Providers\MessageProperties;
|
||||
use KTXM\ProviderImap\Providers\Service;
|
||||
use KTXM\ProviderImap\Providers\ServiceBase;
|
||||
use KTXM\ProviderImap\Service\Live\LiveMailService;
|
||||
use KTXM\ProviderImap\Stores\MailboxStore;
|
||||
use KTXM\ProviderImap\Stores\MessageFileStore;
|
||||
@@ -35,7 +34,7 @@ class HarmonizationService
|
||||
/** Lifetime of a mailbox harmonization lock in seconds; long enough for a large first run */
|
||||
public const LOCK_TTL = 900;
|
||||
|
||||
private ?Service $service = null;
|
||||
private ?ServiceBase $service = null;
|
||||
private ?LiveMailService $live = null;
|
||||
|
||||
public function __construct(
|
||||
@@ -48,7 +47,7 @@ class HarmonizationService
|
||||
/**
|
||||
* Bind a service; returns a new instance so the shared one stays unbound.
|
||||
*/
|
||||
public function for(Service $service, ?LiveMailService $live = null): static
|
||||
public function for(ServiceBase $service, ?LiveMailService $live = null): static
|
||||
{
|
||||
$bound = clone $this;
|
||||
$bound->service = $service;
|
||||
@@ -72,10 +71,7 @@ class HarmonizationService
|
||||
$tenantId = (string) $service->tenantIdentifier();
|
||||
$serviceId = (string) $service->identifier();
|
||||
|
||||
$remote = [];
|
||||
foreach ($live->collectionList() as $name => $mailbox) {
|
||||
$remote[(string) $name] = (new CollectionResource($service->provider(), $serviceId))->fromImap($mailbox);
|
||||
}
|
||||
$remote = $live->collectionList();
|
||||
$cached = $this->mailboxStore->list($serviceId);
|
||||
|
||||
$result = ['added' => [], 'updated' => [], 'removed' => []];
|
||||
@@ -158,7 +154,7 @@ class HarmonizationService
|
||||
|
||||
try {
|
||||
$state = $this->mailboxStore->state($serviceId, $mailbox);
|
||||
$selected = $live->collectionFetch($mailbox)
|
||||
$selected = $live->mailboxFetch($mailbox)
|
||||
?? throw new RuntimeException("Mailbox not found on server: {$mailbox}");
|
||||
$uidValidity = $selected->uidValidity()
|
||||
?? throw new RuntimeException("Server did not report UIDVALIDITY for mailbox: {$mailbox}");
|
||||
@@ -239,7 +235,7 @@ class HarmonizationService
|
||||
$options = FetchOptions::message()->withBodyText(MessageIngestor::BODY_TEXT_LIMIT);
|
||||
|
||||
$ingested = [];
|
||||
foreach ($live->entityFetch($mailbox, $options, ...$uids) as $message) {
|
||||
foreach ($live->messageFetch($mailbox, $options, ...$uids) as $message) {
|
||||
$entity = (new EntityResource($service->provider(), $serviceId))->fromImap($message, $mailbox);
|
||||
array_push($ingested, ...$this->ingestor->ingest($tenantId, $serviceId, $uidValidity, $entity));
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@ use KTXM\ProviderImap\Client\Protocol\IdentifierMode;
|
||||
use KTXM\ProviderImap\Client\ImapException;
|
||||
use KTXM\ProviderImap\Client\Protocol\Command\Argument\ListReturnOptions;
|
||||
use KTXM\ProviderImap\Client\Mailbox;
|
||||
use KTXM\ProviderImap\Providers\CollectionResource;
|
||||
use KTXM\ProviderImap\Providers\EntityResource;
|
||||
use KTXM\ProviderImap\Client\Message;
|
||||
use KTXM\ProviderImap\Client\MessageAddress;
|
||||
use KTXM\ProviderImap\Client\MessagePart;
|
||||
@@ -49,7 +51,7 @@ use KTXF\Resource\Range\RangeAnchorType;
|
||||
use KTXF\Resource\Range\RangeTally;
|
||||
use KTXF\Resource\Sort\ISort;
|
||||
use KTXF\Resource\BinaryResource;
|
||||
use KTXM\ProviderImap\Providers\Service;
|
||||
use KTXM\ProviderImap\Providers\ServiceBase;
|
||||
use KTXM\ProviderImap\Smtp\Client as SmtpClient;
|
||||
use RuntimeException;
|
||||
|
||||
@@ -69,7 +71,7 @@ class LiveMailService
|
||||
private ?Mailbox $selected = null;
|
||||
|
||||
public function __construct(
|
||||
private readonly Service $service,
|
||||
private readonly ServiceBase $service,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -140,11 +142,48 @@ class LiveMailService
|
||||
}
|
||||
|
||||
/**
|
||||
* list of collections in remote storage
|
||||
*
|
||||
* @since Release 1.0.0
|
||||
*/
|
||||
public function collectionList(?string $location = null, IFilter|null $filter = null, ISort|null $sort = null, string $depth = '*'): Generator
|
||||
* List the collections of the account, keyed by mailbox name.
|
||||
*
|
||||
* @return array<string, CollectionResource>
|
||||
*/
|
||||
public function collectionList(?string $location = null, ?IFilter $filter = null, ?ISort $sort = null): array
|
||||
{
|
||||
$list = [];
|
||||
foreach ($this->mailboxList($location, $filter, $sort) as $name => $mailbox) {
|
||||
$list[(string) $name] = $this->collectionResource($mailbox);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a single collection by its mailbox name; null when it does not exist.
|
||||
*/
|
||||
public function collectionFetch(string $identifier): ?CollectionResource
|
||||
{
|
||||
$mailbox = $this->mailboxFetch($identifier);
|
||||
|
||||
return $mailbox === null ? null : $this->collectionResource($mailbox);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hierarchy delimiter of the account, from `LIST "" ""` (no mailboxes are listed); "/" when unknown.
|
||||
*/
|
||||
public function collectionDelimiter(): string
|
||||
{
|
||||
foreach ($this->imapClient()->perform(new ListCommand('', '')) as $mailbox) {
|
||||
return $mailbox->delimiter() ?: '/';
|
||||
}
|
||||
|
||||
return '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* List the IMAP mailboxes of the account (with status), keyed by name.
|
||||
*
|
||||
* @return Generator<string, Mailbox>
|
||||
*/
|
||||
public function mailboxList(?string $location = null, IFilter|null $filter = null, ISort|null $sort = null, string $depth = '*'): Generator
|
||||
{
|
||||
// Prepare location filter
|
||||
if (!empty($location)) {
|
||||
@@ -196,11 +235,11 @@ class LiveMailService
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a single mailbox by its full name.
|
||||
* Fetch a single IMAP mailbox (with status) by its full name.
|
||||
*
|
||||
* Returns null when no mailbox matching $identifier is found.
|
||||
*/
|
||||
public function collectionFetch(string $identifier): ?Mailbox
|
||||
public function mailboxFetch(string $identifier): ?Mailbox
|
||||
{
|
||||
// LIST-STATUS (RFC 5819) returns the status with the LIST response in one round trip
|
||||
$listStatus = $this->imapClient()->hasCapability('LIST-STATUS');
|
||||
@@ -225,38 +264,38 @@ class LiveMailService
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new IMAP mailbox and return it.
|
||||
* Create a new IMAP mailbox and return it as a collection resource.
|
||||
*
|
||||
* If the server-side LIST cannot confirm the new mailbox (e.g., immediate
|
||||
* consistency), a lightweight stub resource is returned instead.
|
||||
*/
|
||||
public function collectionCreate(string $name): Mailbox
|
||||
public function collectionCreate(string $name, ?string $delimiter = null): CollectionResource
|
||||
{
|
||||
$this->imapClient()->perform(new CreateCommand($name));
|
||||
|
||||
// Attempt to refetch the new mailbox from the server
|
||||
$mailbox = $this->collectionFetch($name);
|
||||
$mailbox = $this->mailboxFetch($name);
|
||||
if ($mailbox === null) {
|
||||
throw new ImapException('Failed to create mailbox: ' . $name);
|
||||
}
|
||||
|
||||
return $mailbox;
|
||||
return $this->collectionResource($mailbox, $delimiter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename a mailbox and return the updated resource.
|
||||
*/
|
||||
public function collectionRename(string $oldName, string $newName): Mailbox
|
||||
public function collectionRename(string $oldName, string $newName, ?string $delimiter = null): CollectionResource
|
||||
{
|
||||
$this->imapClient()->perform(new RenameCommand($oldName, $newName));
|
||||
|
||||
$mailbox = $this->collectionFetch($newName);
|
||||
$mailbox = $this->mailboxFetch($newName);
|
||||
|
||||
if ($mailbox === null) {
|
||||
throw new ImapException('Failed to rename mailbox: ' . $oldName . ' to ' . $newName);
|
||||
}
|
||||
|
||||
return $mailbox;
|
||||
return $this->collectionResource($mailbox, $delimiter);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -354,11 +393,36 @@ class LiveMailService
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a list of messages in a mailbox matching the given filter, sorted and paginated as requested.
|
||||
* List the entities of a mailbox matching the given filter, sorted and paginated as requested.
|
||||
*
|
||||
* @return Message[] list of messages matching the filter, sorted and paginated as requested
|
||||
* @return Generator<int, EntityResource> keyed by UID
|
||||
*/
|
||||
public function entityList(string $collection, ?IFilter $filter = null, ?ISort $sort = null, ?IRange $range = null): Generator
|
||||
{
|
||||
foreach ($this->messageList($collection, $filter, $sort, $range) as $message) {
|
||||
$entity = $this->entityResource($message, $collection);
|
||||
yield (int) $entity->identifier() => $entity;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch entities by UID (full TEXT).
|
||||
*
|
||||
* @return Generator<int, EntityResource> keyed by UID
|
||||
*/
|
||||
public function entityFetch(string $collection, int ...$uids): Generator
|
||||
{
|
||||
foreach ($this->messageFetch($collection, null, ...$uids) as $uid => $message) {
|
||||
yield $uid => $this->entityResource($message, $collection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List IMAP messages of a mailbox matching the given filter, sorted and paginated as requested.
|
||||
*
|
||||
* @return Generator<Message>
|
||||
*/
|
||||
public function messageList(string $collection, ?IFilter $filter = null, ?ISort $sort = null, ?IRange $range = null): Generator
|
||||
{
|
||||
// text parts normally precede attachments, so capping TEXT avoids transferring attachment bytes
|
||||
$options = FetchOptions::message()->withBodyText(self::LIST_BODY_TEXT_LIMIT);
|
||||
@@ -387,16 +451,16 @@ class LiveMailService
|
||||
return [];
|
||||
}
|
||||
|
||||
yield from $this->entityFetch($collection, $options, ...$uids);
|
||||
yield from $this->messageFetch($collection, $options, ...$uids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch one or more messages by UID and return EntityResource objects.
|
||||
* Fetch one or more messages by UID as IMAP messages.
|
||||
*
|
||||
* @param int ...$uids
|
||||
* @return Message[] keyed by UID
|
||||
* @return Generator<int, Message> keyed by UID
|
||||
*/
|
||||
public function entityFetch(string $collection, ?FetchOptions $options = null, int ...$uids): Generator
|
||||
public function messageFetch(string $collection, ?FetchOptions $options = null, int ...$uids): Generator
|
||||
{
|
||||
if (empty($uids)) {
|
||||
return [];
|
||||
@@ -1181,6 +1245,18 @@ class LiveMailService
|
||||
return $normalized;
|
||||
}
|
||||
|
||||
private function collectionResource(Mailbox $mailbox, ?string $delimiter = null): CollectionResource
|
||||
{
|
||||
return (new CollectionResource($this->service->provider(), $this->service->identifier()))
|
||||
->fromImap($mailbox, ['delimiter' => $delimiter]);
|
||||
}
|
||||
|
||||
private function entityResource(Message $message, string $collection): EntityResource
|
||||
{
|
||||
return (new EntityResource($this->service->provider(), $this->service->identifier()))
|
||||
->fromImap($message, $collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a mailbox, reusing the current selection when possible.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user