generated from Nodarx/template
refactor: split service into ServiceBase and LiveService
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -44,352 +44,22 @@ use KTXF\Mail\Collection\CollectionRoles;
|
||||
use KTXF\Mail\Object\MessagePropertiesMutableInterface;
|
||||
use KTXF\Resource\Identifier\EntityIdentifierInterface;
|
||||
use KTXM\ProviderImap\Providers\EntityResource;
|
||||
use KTXM\ProviderImap\Client\Mailbox;
|
||||
|
||||
class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceConfigurableInterface, ServiceCollectionMutableInterface, ServiceEntityMutableInterface, ServiceEntitySubmitInterface
|
||||
/**
|
||||
* IMAP mail service that talks to the server directly.
|
||||
*
|
||||
* Holds the mail API rules (validation, delete modes, submission, move / copy
|
||||
* result mapping); CachedService delegates writes here.
|
||||
*/
|
||||
class LiveService extends ServiceBase
|
||||
{
|
||||
private const PROVIDER_IDENTIFIER = 'imap';
|
||||
|
||||
private ?string $serviceTenantId = null;
|
||||
private ?string $serviceUserId = null;
|
||||
private ?string $serviceIdentifier = null;
|
||||
private ?string $serviceLabel = null;
|
||||
private bool $serviceEnabled = false;
|
||||
private array $primaryAddress = [];
|
||||
private array $secondaryAddresses = [];
|
||||
private ?ServiceLocation $location = null;
|
||||
private ?ServiceIdentityBasic $identity = null;
|
||||
private array $auxiliary = [];
|
||||
|
||||
private array $serviceAbilities = [
|
||||
self::CAPABILITY_COLLECTION_LIST => true,
|
||||
self::CAPABILITY_COLLECTION_LIST_FILTER => [
|
||||
self::CAPABILITY_COLLECTION_FILTER_LABEL => 's:128:256:256',
|
||||
self::CAPABILITY_COLLECTION_FILTER_ROLE => 's:32:1:1',
|
||||
self::CAPABILITY_COLLECTION_FILTER_SUBSCRIBED => 'b:0:1:1',
|
||||
],
|
||||
self::CAPABILITY_COLLECTION_LIST_SORT => [
|
||||
self::CAPABILITY_COLLECTION_SORT_LABEL,
|
||||
self::CAPABILITY_COLLECTION_SORT_RANK,
|
||||
],
|
||||
self::CAPABILITY_COLLECTION_FETCH => true,
|
||||
self::CAPABILITY_COLLECTION_EXTANT => true,
|
||||
self::CAPABILITY_COLLECTION_CREATE => true,
|
||||
self::CAPABILITY_COLLECTION_UPDATE => true,
|
||||
self::CAPABILITY_COLLECTION_DELETE => true,
|
||||
self::CAPABILITY_COLLECTION_MOVE => true,
|
||||
self::CAPABILITY_ENTITY_LIST => true,
|
||||
self::CAPABILITY_ENTITY_LIST_FILTER => [
|
||||
self::CAPABILITY_ENTITY_FILTER_FROM => 's:100:256:256',
|
||||
self::CAPABILITY_ENTITY_FILTER_TO => 's:100:256:256',
|
||||
self::CAPABILITY_ENTITY_FILTER_SUBJECT => 's:200:256:256',
|
||||
self::CAPABILITY_ENTITY_FILTER_BODY => 's:200:256:256',
|
||||
self::CAPABILITY_ENTITY_FILTER_DATE_BEFORE => 's:32:1:1',
|
||||
self::CAPABILITY_ENTITY_FILTER_DATE_AFTER => 's:32:1:1',
|
||||
self::CAPABILITY_ENTITY_FILTER_SIZE_MIN => 'i:0:16:16',
|
||||
self::CAPABILITY_ENTITY_FILTER_SIZE_MAX => 'i:0:32:32',
|
||||
],
|
||||
self::CAPABILITY_ENTITY_LIST_SORT => [
|
||||
self::CAPABILITY_ENTITY_SORT_FROM,
|
||||
self::CAPABILITY_ENTITY_SORT_TO,
|
||||
self::CAPABILITY_ENTITY_SORT_SUBJECT,
|
||||
self::CAPABILITY_ENTITY_SORT_DATE_RECEIVED,
|
||||
self::CAPABILITY_ENTITY_SORT_DATE_SENT,
|
||||
self::CAPABILITY_ENTITY_SORT_SIZE,
|
||||
],
|
||||
self::CAPABILITY_ENTITY_LIST_RANGE => [
|
||||
'tally' => ['absolute', 'relative']
|
||||
],
|
||||
self::CAPABILITY_ENTITY_FETCH => true,
|
||||
self::CAPABILITY_ENTITY_EXTANT => true,
|
||||
self::CAPABILITY_ENTITY_CREATE => true,
|
||||
self::CAPABILITY_ENTITY_MODIFY => true,
|
||||
self::CAPABILITY_ENTITY_PATCH => true,
|
||||
self::CAPABILITY_ENTITY_DELETE => true,
|
||||
self::CAPABILITY_ENTITY_MOVE => true,
|
||||
self::CAPABILITY_ENTITY_COPY => false,
|
||||
'EntityTransmit' => true,
|
||||
];
|
||||
|
||||
private LiveMailService $liveService;
|
||||
|
||||
public function __construct() {}
|
||||
|
||||
private function initialize(): void
|
||||
{
|
||||
if (!isset($this->liveService)) {
|
||||
$this->liveService = new LiveMailService($this);
|
||||
}
|
||||
}
|
||||
|
||||
public function toStore(): array
|
||||
{
|
||||
return array_filter([
|
||||
'tid' => $this->serviceTenantId,
|
||||
'uid' => $this->serviceUserId,
|
||||
'sid' => $this->serviceIdentifier,
|
||||
'enabled' => $this->serviceEnabled,
|
||||
'label' => $this->serviceLabel,
|
||||
'primaryAddress' => $this->primaryAddress,
|
||||
'secondaryAddresses'=> $this->secondaryAddresses,
|
||||
'location' => $this->location?->toStore(),
|
||||
'identity' => $this->identity?->toStore(),
|
||||
'auxiliary' => $this->auxiliary,
|
||||
], fn($v) => $v !== null);
|
||||
}
|
||||
|
||||
public function fromStore(array $data): static
|
||||
{
|
||||
$this->serviceTenantId = $data['tid'] ?? null;
|
||||
$this->serviceUserId = $data['uid'] ?? null;
|
||||
$this->serviceIdentifier = $data['sid'] ?? null;
|
||||
$this->serviceLabel = $data['label'] ?? '';
|
||||
$this->serviceEnabled = $data['enabled'] ?? false;
|
||||
|
||||
if (isset($data['primaryAddress'])) {
|
||||
$this->primaryAddress = $data['primaryAddress'];
|
||||
}
|
||||
if (isset($data['secondaryAddresses']) && is_array($data['secondaryAddresses'])) {
|
||||
$this->secondaryAddresses = $data['secondaryAddresses'];
|
||||
}
|
||||
if (isset($data['location'])) {
|
||||
$this->location = (new ServiceLocation())->fromStore($data['location']);
|
||||
}
|
||||
if (isset($data['identity'])) {
|
||||
$this->identity = (new ServiceIdentityBasic())->fromStore($data['identity']);
|
||||
}
|
||||
if (isset($data['auxiliary']) && is_array($data['auxiliary'])) {
|
||||
$this->auxiliary = $data['auxiliary'];
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return array_filter([
|
||||
self::PROPERTY_TYPE => self::JSON_TYPE,
|
||||
self::PROPERTY_PROVIDER => self::PROVIDER_IDENTIFIER,
|
||||
self::PROPERTY_IDENTIFIER => $this->serviceIdentifier,
|
||||
self::PROPERTY_LABEL => $this->serviceLabel,
|
||||
self::PROPERTY_ENABLED => $this->serviceEnabled,
|
||||
self::PROPERTY_CAPABILITIES => $this->serviceAbilities,
|
||||
self::PROPERTY_PRIMARY_ADDRESS => $this->primaryAddress,
|
||||
self::PROPERTY_SECONDARY_ADDRESSES => $this->secondaryAddresses,
|
||||
self::PROPERTY_LOCATION => $this->location?->jsonSerialize(),
|
||||
self::PROPERTY_IDENTITY => $this->identity?->jsonSerialize(),
|
||||
self::PROPERTY_AUXILIARY => $this->auxiliary,
|
||||
], fn($v) => $v !== null);
|
||||
}
|
||||
|
||||
public function jsonDeserialize(array|string $data, bool $delta = false): static
|
||||
{
|
||||
if (is_string($data)) {
|
||||
$data = json_decode($data, true, 512, JSON_THROW_ON_ERROR);
|
||||
}
|
||||
|
||||
if (isset($data[self::PROPERTY_ENABLED])) {
|
||||
$this->setEnabled($data[self::PROPERTY_ENABLED]);
|
||||
}
|
||||
if (isset($data[self::PROPERTY_LABEL])) {
|
||||
$this->setLabel($data[self::PROPERTY_LABEL]);
|
||||
}
|
||||
if (isset($data[self::PROPERTY_LOCATION])) {
|
||||
$this->setLocation($this->freshLocation(null, $data[self::PROPERTY_LOCATION]));
|
||||
}
|
||||
if (isset($data[self::PROPERTY_IDENTITY])) {
|
||||
$this->setIdentity($this->freshIdentity(null, $data[self::PROPERTY_IDENTITY]));
|
||||
}
|
||||
if (isset($data[self::PROPERTY_PRIMARY_ADDRESS])) {
|
||||
$value = $data[self::PROPERTY_PRIMARY_ADDRESS];
|
||||
$this->setPrimaryAddress(is_array($value) ? Address::fromArray($value) : new Address((string)$value));
|
||||
}
|
||||
if (isset($data[self::PROPERTY_SECONDARY_ADDRESSES]) && is_array($data[self::PROPERTY_SECONDARY_ADDRESSES])) {
|
||||
$this->setSecondaryAddresses(array_map(
|
||||
fn($addr) => new Address(is_array($addr) ? ($addr['address'] ?? $addr) : $addr),
|
||||
$data[self::PROPERTY_SECONDARY_ADDRESSES]
|
||||
));
|
||||
}
|
||||
if (isset($data[self::PROPERTY_AUXILIARY]) && is_array($data[self::PROPERTY_AUXILIARY])) {
|
||||
$this->setAuxiliary($data[self::PROPERTY_AUXILIARY]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function capable(string $value): bool
|
||||
{
|
||||
return isset($this->serviceAbilities[$value]);
|
||||
}
|
||||
|
||||
public function capabilities(): array
|
||||
{
|
||||
return $this->serviceAbilities;
|
||||
}
|
||||
|
||||
public function provider(): string
|
||||
{
|
||||
return self::PROVIDER_IDENTIFIER;
|
||||
}
|
||||
|
||||
public function identifier(): string|int
|
||||
{
|
||||
return $this->serviceIdentifier;
|
||||
}
|
||||
|
||||
public function tenantIdentifier(): ?string
|
||||
{
|
||||
return $this->serviceTenantId;
|
||||
}
|
||||
|
||||
public function getLabel(): ?string
|
||||
{
|
||||
return $this->serviceLabel;
|
||||
}
|
||||
|
||||
public function setLabel(string $label): static
|
||||
{
|
||||
$this->serviceLabel = $label;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEnabled(): bool
|
||||
{
|
||||
return $this->serviceEnabled;
|
||||
}
|
||||
|
||||
public function setEnabled(bool $enabled): static
|
||||
{
|
||||
$this->serviceEnabled = $enabled;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPrimaryAddress(): AddressInterface
|
||||
{
|
||||
return Address::fromArray($this->primaryAddress);
|
||||
}
|
||||
|
||||
public function setPrimaryAddress(AddressInterface $value): static
|
||||
{
|
||||
$this->primaryAddress = $value->toArray();
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSecondaryAddresses(): array
|
||||
{
|
||||
return array_map(
|
||||
fn($addr) => $addr instanceof AddressInterface ? $addr : Address::fromArray(is_array($addr) ? $addr : ['address' => (string) $addr])
|
||||
, $this->secondaryAddresses);
|
||||
}
|
||||
|
||||
public function setSecondaryAddresses(array $addresses): static
|
||||
{
|
||||
$this->secondaryAddresses = array_map(
|
||||
fn($addr) => $addr instanceof AddressInterface ? $addr : Address::fromArray(is_array($addr) ? $addr : ['address' => (string) $addr]),
|
||||
$addresses
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasAddress(string $address): bool
|
||||
{
|
||||
$address = strtolower(trim($address));
|
||||
|
||||
if ($this->primaryAddress && strtolower($this->primaryAddress['address'] ?? '') === $address) {
|
||||
return true;
|
||||
}
|
||||
foreach ($this->secondaryAddresses as $secondary) {
|
||||
$secondaryAddr = $secondary instanceof AddressInterface ? $secondary->getAddress() : (string) $secondary;
|
||||
if (strtolower($secondaryAddr) === $address) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
public function getLocation(): ServiceLocation
|
||||
{
|
||||
return $this->location;
|
||||
}
|
||||
|
||||
public function setLocation(ResourceServiceLocationInterface $location): static
|
||||
{
|
||||
$this->location = $location;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function freshLocation(?string $type = null, array $data = []): ServiceLocation
|
||||
{
|
||||
$loc = new ServiceLocation();
|
||||
$loc->jsonDeserialize($data);
|
||||
return $loc;
|
||||
}
|
||||
|
||||
public function getIdentity(): ServiceIdentityBasic
|
||||
{
|
||||
return $this->identity;
|
||||
}
|
||||
|
||||
public function setIdentity(ResourceServiceIdentityInterface $identity): static
|
||||
{
|
||||
$this->identity = $identity;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function freshIdentity(?string $type = null, array $data = []): ServiceIdentityBasic
|
||||
{
|
||||
$id = new ServiceIdentityBasic();
|
||||
$id->jsonDeserialize($data);
|
||||
return $id;
|
||||
}
|
||||
|
||||
public function getDebug(): bool
|
||||
{
|
||||
return ($this->auxiliary['debug'] ?? false) === true;
|
||||
}
|
||||
|
||||
public function setDebug(bool $debug): static
|
||||
{
|
||||
$this->auxiliary['debug'] = $debug;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAuxiliary(): array
|
||||
{
|
||||
return $this->auxiliary;
|
||||
}
|
||||
|
||||
public function setAuxiliary(array $auxiliary): static
|
||||
{
|
||||
$this->auxiliary = $auxiliary;
|
||||
return $this;
|
||||
}
|
||||
private LiveMailService $mailService;
|
||||
|
||||
public function collectionList(string|int|null $location, ?IFilter $filter = null, ?ISort $sort = null): array
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
$list = [];
|
||||
|
||||
foreach ($this->liveService->collectionList($location, $filter, $sort) as $mailbox) {
|
||||
$resource = $this->collectionFresh();
|
||||
$resource->fromImap($mailbox);
|
||||
$list[$mailbox->name()] = $resource;
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function collectionListFilter(): Filter
|
||||
{
|
||||
return new Filter($this->serviceAbilities[self::CAPABILITY_COLLECTION_LIST_FILTER] ?? []);
|
||||
}
|
||||
|
||||
public function collectionListSort(): Sort
|
||||
{
|
||||
return new Sort($this->serviceAbilities[self::CAPABILITY_COLLECTION_LIST_SORT] ?? []);
|
||||
return $this->mailService->collectionList($location === null ? null : (string) $location, $filter, $sort);
|
||||
}
|
||||
|
||||
public function collectionExtant(string|int ...$identifiers): array
|
||||
@@ -400,9 +70,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
|
||||
|
||||
foreach ($identifiers as $identifier) {
|
||||
$key = (string) $identifier;
|
||||
$result = $this->liveService->collectionFetch($key);
|
||||
|
||||
$list[$key] = $result !== false;
|
||||
$list[$key] = $this->mailService->collectionFetch($key) !== null;
|
||||
}
|
||||
|
||||
return $list;
|
||||
@@ -412,20 +80,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
$mailbox = $this->liveService->collectionFetch((string) $identifier);
|
||||
if ($mailbox === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$collection = $this->collectionFresh();
|
||||
$collection->fromImap($mailbox);
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
public function collectionFresh(): CollectionResource
|
||||
{
|
||||
return new CollectionResource($this->provider(), $this->identifier());
|
||||
return $this->mailService->collectionFetch((string) $identifier);
|
||||
}
|
||||
|
||||
public function collectionCreate(CollectionIdentifier|null $target, CollectionPropertiesBaseInterface $properties, array $options = []): CollectionBaseInterface
|
||||
@@ -441,18 +96,11 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
|
||||
if ($target !== null) {
|
||||
$path = $target->collection();
|
||||
// Determine the hierarchy delimiter from an existing mailbox, default to '/'
|
||||
$mailboxes = iterator_to_array($this->liveService->collectionList(null, null, null, ''));
|
||||
$rootMailbox = $mailboxes === [] ? null : reset($mailboxes);
|
||||
$delimiter = $rootMailbox === false ? '/' : ($rootMailbox?->delimiter() ?? '/');
|
||||
$delimiter = $this->mailService->collectionDelimiter();
|
||||
$label = rtrim((string) $path, $delimiter) . $delimiter . ltrim($label, $delimiter);
|
||||
}
|
||||
|
||||
$mailbox = $this->liveService->collectionCreate($label);
|
||||
|
||||
$collection = $this->collectionFresh();
|
||||
$collection->fromImap($mailbox, ['delimiter' => $delimiter ?? null]);
|
||||
|
||||
return $collection;
|
||||
return $this->mailService->collectionCreate($label, $delimiter ?? null);
|
||||
}
|
||||
|
||||
public function collectionUpdate(CollectionIdentifier $target, CollectionPropertiesBaseInterface $properties): CollectionBaseInterface
|
||||
@@ -467,11 +115,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
|
||||
// In IMAP, "update" = rename to the new label
|
||||
$oldPath = (string) $target->collection();
|
||||
$newName = $properties->getLabel();
|
||||
$mailbox = $this->liveService->collectionRename($oldPath, $newName);
|
||||
|
||||
$collection = $this->collectionFresh();
|
||||
$collection->fromImap($mailbox);
|
||||
return $collection;
|
||||
return $this->mailService->collectionRename($oldPath, $newName);
|
||||
}
|
||||
|
||||
public function collectionDelete(CollectionIdentifier $target, bool $force = false): CollectionBaseInterface | true
|
||||
@@ -494,7 +138,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
|
||||
|
||||
$result = match ($deleteMode) {
|
||||
'soft' => $this->collectionMove(new CollectionIdentifier($target->provider(), $target->service(), $deleteTarget), $target),
|
||||
'hard' => $this->liveService->collectionDestroy((string) $target->collection()),
|
||||
'hard' => $this->mailService->collectionDestroy((string) $target->collection()),
|
||||
};
|
||||
return $result;
|
||||
}
|
||||
@@ -503,8 +147,8 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
$sourceMailbox = $this->liveService->collectionFetch((string) $source->collection());
|
||||
$targetMailbox = $this->liveService->collectionFetch((string) $target->collection());
|
||||
$sourceMailbox = $this->mailService->collectionFetch((string) $source->collection());
|
||||
$targetMailbox = $this->mailService->collectionFetch((string) $target->collection());
|
||||
if ($sourceMailbox === null) {
|
||||
throw new \RuntimeException('Source collection not found for move operation');
|
||||
}
|
||||
@@ -512,19 +156,15 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
|
||||
throw new \RuntimeException('Target collection not found for move operation');
|
||||
}
|
||||
|
||||
$sourceDelimiter = $sourceMailbox->delimiter() ?? '/';
|
||||
$targetDelimiter = $targetMailbox->delimiter() ?? '/';
|
||||
$sourceDelimiter = $sourceMailbox->getProperties()->getDelimiter() ?: '/';
|
||||
$targetDelimiter = $targetMailbox->getProperties()->getDelimiter() ?: '/';
|
||||
|
||||
$extantPath = $sourceMailbox->name();
|
||||
$extantPath = (string) $sourceMailbox->identifier();
|
||||
$extantPathLeafs = explode($sourceDelimiter, rtrim($extantPath, $sourceDelimiter));
|
||||
|
||||
$freshPath = rtrim($targetMailbox->name(), $targetDelimiter) . $targetDelimiter . end($extantPathLeafs);
|
||||
$freshPath = rtrim((string) $targetMailbox->identifier(), $targetDelimiter) . $targetDelimiter . end($extantPathLeafs);
|
||||
|
||||
$mutatedMailbox = $this->liveService->collectionRename($extantPath, $freshPath);
|
||||
|
||||
$collection = $this->collectionFresh();
|
||||
$collection->fromImap($mutatedMailbox, ['delimiter' => $targetDelimiter]);
|
||||
return $collection;
|
||||
return $this->mailService->collectionRename($extantPath, $freshPath, $targetDelimiter);
|
||||
}
|
||||
|
||||
public function entityListBulk(string|int $collection, ?IFilter $filter = null, ?ISort $sort = null, ?IRange $range = null, ?array $properties = null): array
|
||||
@@ -536,31 +176,11 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
foreach ($this->liveService->entityList((string) $collection, $filter, $sort, $range) as $identifier => $message) {
|
||||
$resource = $this->entityFresh();
|
||||
$resource->fromImap($message, $collection);
|
||||
foreach ($this->mailService->entityList((string) $collection, $filter, $sort, $range) as $resource) {
|
||||
yield $resource->urn() => $resource;
|
||||
}
|
||||
}
|
||||
|
||||
public function entityListFilter(): Filter
|
||||
{
|
||||
return new Filter($this->serviceAbilities[self::CAPABILITY_ENTITY_LIST_FILTER] ?? []);
|
||||
}
|
||||
|
||||
public function entityListSort(): Sort
|
||||
{
|
||||
return new Sort($this->serviceAbilities[self::CAPABILITY_ENTITY_LIST_SORT] ?? []);
|
||||
}
|
||||
|
||||
public function entityListRange(RangeType $type): IRange
|
||||
{
|
||||
return match ($type) {
|
||||
RangeType::TALLY => new RangeTally(),
|
||||
default => new Range(),
|
||||
};
|
||||
}
|
||||
|
||||
public function entityFetchBulk(EntityIdentifierInterface ...$identifiers): array
|
||||
{
|
||||
return iterator_to_array($this->entityFetchStream(...$identifiers), true);
|
||||
@@ -574,9 +194,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
|
||||
|
||||
foreach ($identifiers as $collection => $entities) {
|
||||
$uids = array_keys($entities);
|
||||
foreach ($this->liveService->entityFetch((string) $collection, null, ...$uids) as $uid => $message) {
|
||||
$resource = $this->entityFresh();
|
||||
$resource->fromImap($message, $collection);
|
||||
foreach ($this->mailService->entityFetch((string) $collection, ...$uids) as $resource) {
|
||||
yield $resource->urn() => $resource;
|
||||
}
|
||||
}
|
||||
@@ -589,7 +207,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
|
||||
$uid = (int) $target->entity();
|
||||
$partId = isset($part['partId']) ? (string) $part['partId'] : null;
|
||||
|
||||
return $this->liveService->entityDownload($collection, $uid, $partId);
|
||||
return $this->mailService->entityDownload($collection, $uid, $partId);
|
||||
}
|
||||
|
||||
public function entityDelta(string|int $collection, string $signature, string $detail = 'ids'): Delta
|
||||
@@ -607,7 +225,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
|
||||
static fn (int $uid): bool => $uid > 0,
|
||||
));
|
||||
|
||||
$existing = array_flip($this->liveService->entityExtant((string) $collection, ...$uids));
|
||||
$existing = array_flip($this->mailService->entityExtant((string) $collection, ...$uids));
|
||||
|
||||
$extant = [];
|
||||
foreach ($identifiers as $id) {
|
||||
@@ -616,11 +234,6 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
|
||||
return $extant;
|
||||
}
|
||||
|
||||
public function entityFresh(): EntityResource
|
||||
{
|
||||
return new EntityResource($this->provider(), $this->identifier());
|
||||
}
|
||||
|
||||
public function entitySubmit(AddressInterface $sender, EntityIdentifierInterface|null $source = null, MessagePropertiesMutableInterface|null $message = null): EntitySubmitResult
|
||||
{
|
||||
if ($message === null) {
|
||||
@@ -640,7 +253,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
|
||||
}
|
||||
|
||||
$this->initialize();
|
||||
$smtp = $this->liveService->smtpClient();
|
||||
$smtp = $this->mailService->smtpClient();
|
||||
try {
|
||||
$queueId = $smtp->send(trim($sender->getAddress()), $recipients, $raw);
|
||||
} finally {
|
||||
@@ -661,7 +274,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
|
||||
$this->initialize();
|
||||
$sentCollection = $this->resolveSentCollection();
|
||||
if ($sentCollection !== null) {
|
||||
$uid = $this->liveService->entityCreate($sentCollection, $raw, ['\\Seen']);
|
||||
$uid = $this->mailService->entityCreate($sentCollection, $raw, ['\\Seen']);
|
||||
if ($uid !== null && $uid > 0) {
|
||||
$sentEntity = new EntityIdentifier($this->provider(), $this->identifier(), $sentCollection, (string) $uid);
|
||||
}
|
||||
@@ -680,7 +293,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
|
||||
&& $source->service() === $this->identifier()) {
|
||||
try {
|
||||
$this->initialize();
|
||||
$this->liveService->entityDestroy($source->collection(), (int) $source->entity());
|
||||
$this->mailService->entityDestroy($source->collection(), (int) $source->entity());
|
||||
} catch (\Throwable) {
|
||||
// ignore — the message was already delivered
|
||||
}
|
||||
@@ -703,7 +316,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
|
||||
$this->initialize();
|
||||
|
||||
[$nativeMessage, $nativeFlags] = $this->messagePayload($properties, $options);
|
||||
$created = $this->liveService->entityCreate(
|
||||
$created = $this->mailService->entityCreate(
|
||||
(string)$target->collection(),
|
||||
$nativeMessage,
|
||||
$nativeFlags,
|
||||
@@ -724,7 +337,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
|
||||
$this->initialize();
|
||||
|
||||
[$nativeMessage, $nativeFlags] = $this->messagePayload($properties);
|
||||
$modified = $this->liveService->entityReplace(
|
||||
$modified = $this->mailService->entityReplace(
|
||||
(string)$target->collection(),
|
||||
(int)$target->entity(),
|
||||
$nativeMessage,
|
||||
@@ -760,7 +373,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
|
||||
}
|
||||
}
|
||||
|
||||
$mutations = $this->liveService->entityPatch($targetCollection, $flagsAdd, $flagsRemove, ...$uids);
|
||||
$mutations = $this->mailService->entityPatch($targetCollection, $flagsAdd, $flagsRemove, ...$uids);
|
||||
|
||||
foreach ($uids as $uid) {
|
||||
$list[(string)$targetIdentifiers[$uid]] = ['disposition' => 'patched'];
|
||||
@@ -807,8 +420,8 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
|
||||
$uids = array_keys($sourceEntities);
|
||||
|
||||
$mutations = match ($deleteMode) {
|
||||
'soft' => $this->liveService->entityMove($deleteTargetNative, $sourceCollection, ...$uids),
|
||||
'hard' => $this->liveService->entityDestroy($sourceCollection, ...$uids),
|
||||
'soft' => $this->mailService->entityMove($deleteTargetNative, $sourceCollection, ...$uids),
|
||||
'hard' => $this->mailService->entityDestroy($sourceCollection, ...$uids),
|
||||
};
|
||||
|
||||
foreach ($uids as $uid) {
|
||||
@@ -842,7 +455,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
|
||||
foreach ($sources as $sourceCollection => $sourceEntities) {
|
||||
$uids = array_keys($sourceEntities);
|
||||
|
||||
$mutations = $this->liveService->entityMove($target->collection(), $sourceCollection, ...$uids);
|
||||
$mutations = $this->mailService->entityMove($target->collection(), $sourceCollection, ...$uids);
|
||||
|
||||
foreach ($uids as $uid) {
|
||||
$mutatedUid = $mutations[$uid] ?? null;
|
||||
@@ -876,7 +489,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
|
||||
foreach ($sources as $sourceCollection => $sourceEntities) {
|
||||
$uids = array_keys($sourceEntities);
|
||||
|
||||
$mutations = $this->liveService->entityCopy($target->collection(), $sourceCollection, ...$uids);
|
||||
$mutations = $this->mailService->entityCopy($target->collection(), $sourceCollection, ...$uids);
|
||||
|
||||
foreach ($uids as $uid) {
|
||||
$mutatedUid = $mutations[$uid] ?? null;
|
||||
@@ -899,13 +512,9 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
|
||||
$filter = $this->collectionListFilter();
|
||||
$filter->condition('role', $role->value);
|
||||
|
||||
/** @var Mailbox[] $mailboxes */
|
||||
$mailboxes = iterator_to_array($this->liveService->collectionList(null, $filter, null));
|
||||
if ($mailboxes === []) {
|
||||
return null;
|
||||
}
|
||||
$mailbox = reset($mailboxes);
|
||||
return $mailbox === false ? null : $mailbox->name();
|
||||
$collections = $this->mailService->collectionList(null, $filter, null);
|
||||
|
||||
return $collections === [] ? null : (string) array_key_first($collections);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -988,4 +597,11 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
|
||||
|
||||
return [(new MessageBuilder())->build($properties), $flags];
|
||||
}
|
||||
|
||||
protected function initialize(): void
|
||||
{
|
||||
if (!isset($this->mailService)) {
|
||||
$this->mailService = new LiveMailService($this);
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-10
@@ -106,7 +106,7 @@ class Provider implements ProviderBaseInterface, ProviderServiceMutateInterface,
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function serviceFetch(string $tenantId, string $userId, string|int $identifier): ?Service
|
||||
public function serviceFetch(string $tenantId, string $userId, string|int $identifier): ?ServiceBase
|
||||
{
|
||||
$serviceData = $this->serviceStore->fetch($tenantId, $userId, $identifier);
|
||||
if ($serviceData === null) {
|
||||
@@ -116,9 +116,9 @@ class Provider implements ProviderBaseInterface, ProviderServiceMutateInterface,
|
||||
return $serviceInstance;
|
||||
}
|
||||
|
||||
public function serviceFindByAddress(string $tenantId, string $userId, string $address): ?Service
|
||||
public function serviceFindByAddress(string $tenantId, string $userId, string $address): ?ServiceBase
|
||||
{
|
||||
/** @var Service[] $services */
|
||||
/** @var ServiceBase[] $services */
|
||||
$services = $this->serviceList($tenantId, $userId);
|
||||
foreach ($services as $service) {
|
||||
if ($service->hasAddress($address)) {
|
||||
@@ -133,14 +133,14 @@ class Provider implements ProviderBaseInterface, ProviderServiceMutateInterface,
|
||||
return $this->serviceStore->extant($tenantId, $userId, $identifiers);
|
||||
}
|
||||
|
||||
public function serviceFresh(): Service
|
||||
public function serviceFresh(): ServiceBase
|
||||
{
|
||||
return new Service();
|
||||
return new LiveService();
|
||||
}
|
||||
|
||||
public function serviceCreate(string $tenantId, string $userId, ResourceServiceMutateInterface $service): string
|
||||
{
|
||||
if (!($service instanceof Service)) {
|
||||
if (!($service instanceof ServiceBase)) {
|
||||
throw new \InvalidArgumentException('Service must be instance of IMAP Service');
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ class Provider implements ProviderBaseInterface, ProviderServiceMutateInterface,
|
||||
|
||||
public function serviceModify(string $tenantId, string $userId, ResourceServiceMutateInterface $service): string
|
||||
{
|
||||
if (!($service instanceof Service)) {
|
||||
if (!($service instanceof ServiceBase)) {
|
||||
throw new \InvalidArgumentException('Service must be instance of IMAP Service');
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ class Provider implements ProviderBaseInterface, ProviderServiceMutateInterface,
|
||||
|
||||
public function serviceDestroy(string $tenantId, string $userId, ResourceServiceMutateInterface $service): bool
|
||||
{
|
||||
if (!($service instanceof Service)) {
|
||||
if (!($service instanceof ServiceBase)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ class Provider implements ProviderBaseInterface, ProviderServiceMutateInterface,
|
||||
$startTime = microtime(true);
|
||||
|
||||
try {
|
||||
if (!($service instanceof Service)) {
|
||||
if (!($service instanceof ServiceBase)) {
|
||||
throw new \InvalidArgumentException('Service must be an instance of IMAP Service');
|
||||
}
|
||||
// augment the service with any provided test options (e.g. override location or credentials)
|
||||
@@ -193,7 +193,7 @@ class Provider implements ProviderBaseInterface, ProviderServiceMutateInterface,
|
||||
|
||||
// Attempt to authenticate and list mailboxes as a connectivity check
|
||||
$live = new LiveMailService($service);
|
||||
$mailboxes = iterator_to_array($live->collectionList());
|
||||
$mailboxes = $live->collectionList();
|
||||
|
||||
$latency = (int) round((microtime(true) - $startTime) * 1000);
|
||||
|
||||
|
||||
@@ -0,0 +1,404 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace KTXM\ProviderImap\Providers;
|
||||
|
||||
use Generator;
|
||||
use KTXF\Mail\Collection\CollectionBaseInterface;
|
||||
use KTXF\Mail\Collection\CollectionPropertiesBaseInterface;
|
||||
use KTXF\Mail\Object\Address;
|
||||
use KTXF\Mail\Object\AddressInterface;
|
||||
use KTXF\Mail\Service\ServiceBaseInterface;
|
||||
use KTXF\Mail\Service\ServiceCollectionMutableInterface;
|
||||
use KTXF\Mail\Service\ServiceEntityMutableInterface;
|
||||
use KTXF\Mail\Service\ServiceEntitySubmitInterface;
|
||||
use KTXF\Mail\Service\ServiceConfigurableInterface;
|
||||
use KTXF\Mail\Service\ServiceMutableInterface;
|
||||
use KTXF\Mail\Submission\EntitySubmitResult;
|
||||
use KTXF\Resource\BinaryResource;
|
||||
use KTXF\Resource\Provider\ResourceServiceIdentityInterface;
|
||||
use KTXF\Resource\Provider\ResourceServiceLocationInterface;
|
||||
use KTXF\Resource\Delta\Delta;
|
||||
use KTXF\Resource\Filter\Filter;
|
||||
use KTXF\Resource\Filter\IFilter;
|
||||
use KTXF\Resource\Identifier\CollectionIdentifier;
|
||||
use KTXF\Resource\Identifier\EntityIdentifier;
|
||||
use KTXF\Resource\Range\IRange;
|
||||
use KTXF\Resource\Range\Range;
|
||||
use KTXF\Resource\Range\RangeTally;
|
||||
use KTXF\Resource\Range\RangeType;
|
||||
use KTXF\Resource\Sort\ISort;
|
||||
use KTXF\Resource\Sort\Sort;
|
||||
use KTXM\ProviderImap\Providers\ServiceIdentityBasic;
|
||||
use KTXM\ProviderImap\Providers\ServiceLocation;
|
||||
use KTXM\ProviderImap\Mime\MessageBuilder;
|
||||
use KTXM\ProviderImap\Providers\CollectionResource;
|
||||
use KTXF\Mail\Collection\CollectionRoles;
|
||||
use KTXF\Mail\Object\MessagePropertiesMutableInterface;
|
||||
use KTXF\Resource\Identifier\EntityIdentifierInterface;
|
||||
use KTXM\ProviderImap\Providers\EntityResource;
|
||||
|
||||
/**
|
||||
* IMAP mail service: account configuration and serialization.
|
||||
*
|
||||
* The mail API (collection / entity methods, declared by the implemented
|
||||
* interfaces) is left to the concrete classes: LiveService talks to the IMAP
|
||||
* server, CachedService serves reads from the cache. The Provider picks the
|
||||
* class by mode when loading a service.
|
||||
*/
|
||||
abstract class ServiceBase implements ServiceBaseInterface, ServiceMutableInterface, ServiceConfigurableInterface, ServiceCollectionMutableInterface, ServiceEntityMutableInterface, ServiceEntitySubmitInterface
|
||||
{
|
||||
protected const PROVIDER_IDENTIFIER = 'imap';
|
||||
|
||||
protected ?string $serviceTenantId = null;
|
||||
protected ?string $serviceUserId = null;
|
||||
protected ?string $serviceIdentifier = null;
|
||||
protected ?string $serviceLabel = null;
|
||||
protected bool $serviceEnabled = false;
|
||||
protected array $primaryAddress = [];
|
||||
protected array $secondaryAddresses = [];
|
||||
protected ?ServiceLocation $location = null;
|
||||
protected ?ServiceIdentityBasic $identity = null;
|
||||
protected array $auxiliary = [];
|
||||
|
||||
protected array $serviceAbilities = [
|
||||
self::CAPABILITY_COLLECTION_LIST => true,
|
||||
self::CAPABILITY_COLLECTION_LIST_FILTER => [
|
||||
self::CAPABILITY_COLLECTION_FILTER_LABEL => 's:128:256:256',
|
||||
self::CAPABILITY_COLLECTION_FILTER_ROLE => 's:32:1:1',
|
||||
self::CAPABILITY_COLLECTION_FILTER_SUBSCRIBED => 'b:0:1:1',
|
||||
],
|
||||
self::CAPABILITY_COLLECTION_LIST_SORT => [
|
||||
self::CAPABILITY_COLLECTION_SORT_LABEL,
|
||||
self::CAPABILITY_COLLECTION_SORT_RANK,
|
||||
],
|
||||
self::CAPABILITY_COLLECTION_FETCH => true,
|
||||
self::CAPABILITY_COLLECTION_EXTANT => true,
|
||||
self::CAPABILITY_COLLECTION_CREATE => true,
|
||||
self::CAPABILITY_COLLECTION_UPDATE => true,
|
||||
self::CAPABILITY_COLLECTION_DELETE => true,
|
||||
self::CAPABILITY_COLLECTION_MOVE => true,
|
||||
self::CAPABILITY_ENTITY_LIST => true,
|
||||
self::CAPABILITY_ENTITY_LIST_FILTER => [
|
||||
self::CAPABILITY_ENTITY_FILTER_FROM => 's:100:256:256',
|
||||
self::CAPABILITY_ENTITY_FILTER_TO => 's:100:256:256',
|
||||
self::CAPABILITY_ENTITY_FILTER_SUBJECT => 's:200:256:256',
|
||||
self::CAPABILITY_ENTITY_FILTER_BODY => 's:200:256:256',
|
||||
self::CAPABILITY_ENTITY_FILTER_DATE_BEFORE => 's:32:1:1',
|
||||
self::CAPABILITY_ENTITY_FILTER_DATE_AFTER => 's:32:1:1',
|
||||
self::CAPABILITY_ENTITY_FILTER_SIZE_MIN => 'i:0:16:16',
|
||||
self::CAPABILITY_ENTITY_FILTER_SIZE_MAX => 'i:0:32:32',
|
||||
],
|
||||
self::CAPABILITY_ENTITY_LIST_SORT => [
|
||||
self::CAPABILITY_ENTITY_SORT_FROM,
|
||||
self::CAPABILITY_ENTITY_SORT_TO,
|
||||
self::CAPABILITY_ENTITY_SORT_SUBJECT,
|
||||
self::CAPABILITY_ENTITY_SORT_DATE_RECEIVED,
|
||||
self::CAPABILITY_ENTITY_SORT_DATE_SENT,
|
||||
self::CAPABILITY_ENTITY_SORT_SIZE,
|
||||
],
|
||||
self::CAPABILITY_ENTITY_LIST_RANGE => [
|
||||
'tally' => ['absolute', 'relative']
|
||||
],
|
||||
self::CAPABILITY_ENTITY_FETCH => true,
|
||||
self::CAPABILITY_ENTITY_EXTANT => true,
|
||||
self::CAPABILITY_ENTITY_CREATE => true,
|
||||
self::CAPABILITY_ENTITY_MODIFY => true,
|
||||
self::CAPABILITY_ENTITY_PATCH => true,
|
||||
self::CAPABILITY_ENTITY_DELETE => true,
|
||||
self::CAPABILITY_ENTITY_MOVE => true,
|
||||
self::CAPABILITY_ENTITY_COPY => false,
|
||||
'EntityTransmit' => true,
|
||||
];
|
||||
|
||||
public function __construct() {}
|
||||
|
||||
public function toStore(): array
|
||||
{
|
||||
return array_filter([
|
||||
'tid' => $this->serviceTenantId,
|
||||
'uid' => $this->serviceUserId,
|
||||
'sid' => $this->serviceIdentifier,
|
||||
'enabled' => $this->serviceEnabled,
|
||||
'label' => $this->serviceLabel,
|
||||
'primaryAddress' => $this->primaryAddress,
|
||||
'secondaryAddresses'=> $this->secondaryAddresses,
|
||||
'location' => $this->location?->toStore(),
|
||||
'identity' => $this->identity?->toStore(),
|
||||
'auxiliary' => $this->auxiliary,
|
||||
], fn($v) => $v !== null);
|
||||
}
|
||||
|
||||
public function fromStore(array $data): static
|
||||
{
|
||||
$this->serviceTenantId = $data['tid'] ?? null;
|
||||
$this->serviceUserId = $data['uid'] ?? null;
|
||||
$this->serviceIdentifier = $data['sid'] ?? null;
|
||||
$this->serviceLabel = $data['label'] ?? '';
|
||||
$this->serviceEnabled = $data['enabled'] ?? false;
|
||||
|
||||
if (isset($data['primaryAddress'])) {
|
||||
$this->primaryAddress = $data['primaryAddress'];
|
||||
}
|
||||
if (isset($data['secondaryAddresses']) && is_array($data['secondaryAddresses'])) {
|
||||
$this->secondaryAddresses = $data['secondaryAddresses'];
|
||||
}
|
||||
if (isset($data['location'])) {
|
||||
$this->location = (new ServiceLocation())->fromStore($data['location']);
|
||||
}
|
||||
if (isset($data['identity'])) {
|
||||
$this->identity = (new ServiceIdentityBasic())->fromStore($data['identity']);
|
||||
}
|
||||
if (isset($data['auxiliary']) && is_array($data['auxiliary'])) {
|
||||
$this->auxiliary = $data['auxiliary'];
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return array_filter([
|
||||
self::PROPERTY_TYPE => self::JSON_TYPE,
|
||||
self::PROPERTY_PROVIDER => self::PROVIDER_IDENTIFIER,
|
||||
self::PROPERTY_IDENTIFIER => $this->serviceIdentifier,
|
||||
self::PROPERTY_LABEL => $this->serviceLabel,
|
||||
self::PROPERTY_ENABLED => $this->serviceEnabled,
|
||||
self::PROPERTY_CAPABILITIES => $this->serviceAbilities,
|
||||
self::PROPERTY_PRIMARY_ADDRESS => $this->primaryAddress,
|
||||
self::PROPERTY_SECONDARY_ADDRESSES => $this->secondaryAddresses,
|
||||
self::PROPERTY_LOCATION => $this->location?->jsonSerialize(),
|
||||
self::PROPERTY_IDENTITY => $this->identity?->jsonSerialize(),
|
||||
self::PROPERTY_AUXILIARY => $this->auxiliary,
|
||||
], fn($v) => $v !== null);
|
||||
}
|
||||
|
||||
public function jsonDeserialize(array|string $data, bool $delta = false): static
|
||||
{
|
||||
if (is_string($data)) {
|
||||
$data = json_decode($data, true, 512, JSON_THROW_ON_ERROR);
|
||||
}
|
||||
|
||||
if (isset($data[self::PROPERTY_ENABLED])) {
|
||||
$this->setEnabled($data[self::PROPERTY_ENABLED]);
|
||||
}
|
||||
if (isset($data[self::PROPERTY_LABEL])) {
|
||||
$this->setLabel($data[self::PROPERTY_LABEL]);
|
||||
}
|
||||
if (isset($data[self::PROPERTY_LOCATION])) {
|
||||
$this->setLocation($this->freshLocation(null, $data[self::PROPERTY_LOCATION]));
|
||||
}
|
||||
if (isset($data[self::PROPERTY_IDENTITY])) {
|
||||
$this->setIdentity($this->freshIdentity(null, $data[self::PROPERTY_IDENTITY]));
|
||||
}
|
||||
if (isset($data[self::PROPERTY_PRIMARY_ADDRESS])) {
|
||||
$value = $data[self::PROPERTY_PRIMARY_ADDRESS];
|
||||
$this->setPrimaryAddress(is_array($value) ? Address::fromArray($value) : new Address((string)$value));
|
||||
}
|
||||
if (isset($data[self::PROPERTY_SECONDARY_ADDRESSES]) && is_array($data[self::PROPERTY_SECONDARY_ADDRESSES])) {
|
||||
$this->setSecondaryAddresses(array_map(
|
||||
fn($addr) => new Address(is_array($addr) ? ($addr['address'] ?? $addr) : $addr),
|
||||
$data[self::PROPERTY_SECONDARY_ADDRESSES]
|
||||
));
|
||||
}
|
||||
if (isset($data[self::PROPERTY_AUXILIARY]) && is_array($data[self::PROPERTY_AUXILIARY])) {
|
||||
$this->setAuxiliary($data[self::PROPERTY_AUXILIARY]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function capable(string $value): bool
|
||||
{
|
||||
return isset($this->serviceAbilities[$value]);
|
||||
}
|
||||
|
||||
public function capabilities(): array
|
||||
{
|
||||
return $this->serviceAbilities;
|
||||
}
|
||||
|
||||
public function provider(): string
|
||||
{
|
||||
return self::PROVIDER_IDENTIFIER;
|
||||
}
|
||||
|
||||
public function identifier(): string|int
|
||||
{
|
||||
return $this->serviceIdentifier;
|
||||
}
|
||||
|
||||
public function tenantIdentifier(): ?string
|
||||
{
|
||||
return $this->serviceTenantId;
|
||||
}
|
||||
|
||||
public function getLabel(): ?string
|
||||
{
|
||||
return $this->serviceLabel;
|
||||
}
|
||||
|
||||
public function setLabel(string $label): static
|
||||
{
|
||||
$this->serviceLabel = $label;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEnabled(): bool
|
||||
{
|
||||
return $this->serviceEnabled;
|
||||
}
|
||||
|
||||
public function setEnabled(bool $enabled): static
|
||||
{
|
||||
$this->serviceEnabled = $enabled;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPrimaryAddress(): AddressInterface
|
||||
{
|
||||
return Address::fromArray($this->primaryAddress);
|
||||
}
|
||||
|
||||
public function setPrimaryAddress(AddressInterface $value): static
|
||||
{
|
||||
$this->primaryAddress = $value->toArray();
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSecondaryAddresses(): array
|
||||
{
|
||||
return array_map(
|
||||
fn($addr) => $addr instanceof AddressInterface ? $addr : Address::fromArray(is_array($addr) ? $addr : ['address' => (string) $addr])
|
||||
, $this->secondaryAddresses);
|
||||
}
|
||||
|
||||
public function setSecondaryAddresses(array $addresses): static
|
||||
{
|
||||
$this->secondaryAddresses = array_map(
|
||||
fn($addr) => $addr instanceof AddressInterface ? $addr : Address::fromArray(is_array($addr) ? $addr : ['address' => (string) $addr]),
|
||||
$addresses
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasAddress(string $address): bool
|
||||
{
|
||||
$address = strtolower(trim($address));
|
||||
|
||||
if ($this->primaryAddress && strtolower($this->primaryAddress['address'] ?? '') === $address) {
|
||||
return true;
|
||||
}
|
||||
foreach ($this->secondaryAddresses as $secondary) {
|
||||
$secondaryAddr = $secondary instanceof AddressInterface ? $secondary->getAddress() : (string) $secondary;
|
||||
if (strtolower($secondaryAddr) === $address) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
public function getLocation(): ServiceLocation
|
||||
{
|
||||
return $this->location;
|
||||
}
|
||||
|
||||
public function setLocation(ResourceServiceLocationInterface $location): static
|
||||
{
|
||||
$this->location = $location;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function freshLocation(?string $type = null, array $data = []): ServiceLocation
|
||||
{
|
||||
$loc = new ServiceLocation();
|
||||
$loc->jsonDeserialize($data);
|
||||
return $loc;
|
||||
}
|
||||
|
||||
public function getIdentity(): ServiceIdentityBasic
|
||||
{
|
||||
return $this->identity;
|
||||
}
|
||||
|
||||
public function setIdentity(ResourceServiceIdentityInterface $identity): static
|
||||
{
|
||||
$this->identity = $identity;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function freshIdentity(?string $type = null, array $data = []): ServiceIdentityBasic
|
||||
{
|
||||
$id = new ServiceIdentityBasic();
|
||||
$id->jsonDeserialize($data);
|
||||
return $id;
|
||||
}
|
||||
|
||||
public function getDebug(): bool
|
||||
{
|
||||
return ($this->auxiliary['debug'] ?? false) === true;
|
||||
}
|
||||
|
||||
public function setDebug(bool $debug): static
|
||||
{
|
||||
$this->auxiliary['debug'] = $debug;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAuxiliary(): array
|
||||
{
|
||||
return $this->auxiliary;
|
||||
}
|
||||
|
||||
public function setAuxiliary(array $auxiliary): static
|
||||
{
|
||||
$this->auxiliary = $auxiliary;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function collectionListFilter(): Filter
|
||||
{
|
||||
return new Filter($this->serviceAbilities[self::CAPABILITY_COLLECTION_LIST_FILTER] ?? []);
|
||||
}
|
||||
|
||||
public function collectionListSort(): Sort
|
||||
{
|
||||
return new Sort($this->serviceAbilities[self::CAPABILITY_COLLECTION_LIST_SORT] ?? []);
|
||||
}
|
||||
|
||||
public function collectionFresh(): CollectionResource
|
||||
{
|
||||
return new CollectionResource($this->provider(), $this->identifier());
|
||||
}
|
||||
|
||||
public function entityListFilter(): Filter
|
||||
{
|
||||
return new Filter($this->serviceAbilities[self::CAPABILITY_ENTITY_LIST_FILTER] ?? []);
|
||||
}
|
||||
|
||||
public function entityListSort(): Sort
|
||||
{
|
||||
return new Sort($this->serviceAbilities[self::CAPABILITY_ENTITY_LIST_SORT] ?? []);
|
||||
}
|
||||
|
||||
public function entityListRange(RangeType $type): IRange
|
||||
{
|
||||
return match ($type) {
|
||||
RangeType::TALLY => new RangeTally(),
|
||||
default => new Range(),
|
||||
};
|
||||
}
|
||||
|
||||
public function entityFresh(): EntityResource
|
||||
{
|
||||
return new EntityResource($this->provider(), $this->identifier());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user