Merge pull request 'feat: entity streaming' (#4) from feat/entity-streaming into main
Some checks failed
Renovate / renovate (push) Failing after 1m30s

Reviewed-on: #4
This commit was merged in pull request #4.
This commit is contained in:
2026-03-03 01:42:45 +00:00
5 changed files with 45 additions and 46 deletions

View File

@@ -37,16 +37,16 @@
} }
}, },
"autoload-dev": { "autoload-dev": {
"psr-4": { "psr-4": {
"KTXT\\ProviderJmapc\\": "tests/php/" "KTXT\\ProviderJmapc\\": "tests/php/"
} }
}, },
"scripts": { "scripts": {
"post-install-cmd": [ "post-install-cmd": [
], ],
"post-update-cmd": [ "post-update-cmd": [
], ],
"test:unit": "phpunit --configuration tests/php/phpunit.unit.xml --colors=always --testdox", "test:unit": "phpunit --configuration tests/php/phpunit.unit.xml --colors=always --testdox",
"test:coverage": "XDEBUG_MODE=coverage phpunit --configuration tests/php/phpunit.unit.xml --coverage-html .phpunit.coverage --coverage-text" "test:coverage": "XDEBUG_MODE=coverage phpunit --configuration tests/php/phpunit.unit.xml --coverage-html .phpunit.coverage --coverage-text"
} }
} }

View File

@@ -68,8 +68,8 @@ class Module extends ModuleInstanceAbstract implements ModuleBrowserInterface
{ {
// Register JMAP providers - all three share the same service store // Register JMAP providers - all three share the same service store
$this->providerManager->register(ProviderInterface::TYPE_MAIL, 'jmap', MailProvider::class); $this->providerManager->register(ProviderInterface::TYPE_MAIL, 'jmap', MailProvider::class);
$this->providerManager->register(ProviderInterface::TYPE_CHRONO, 'jmap', ChronoProvider::class); //$this->providerManager->register(ProviderInterface::TYPE_CHRONO, 'jmap', ChronoProvider::class);
$this->providerManager->register(ProviderInterface::TYPE_PEOPLE, 'jmap', PeopleProvider::class); //$this->providerManager->register(ProviderInterface::TYPE_PEOPLE, 'jmap', PeopleProvider::class);
} }
public function registerBI(): array { public function registerBI(): array {

View File

@@ -30,7 +30,7 @@ class MessageProperties extends MessagePropertiesMutableAbstract {
$this->data['size'] = $parameters['size']; $this->data['size'] = $parameters['size'];
} }
if (isset($parameters['receivedAt'])) { if (isset($parameters['receivedAt'])) {
$this->data['receivedDate'] = $parameters['receivedAt']; $this->data['received'] = $parameters['receivedAt'];
} }
if (isset($parameters['sentAt'])) { if (isset($parameters['sentAt'])) {
$this->data['date'] = $parameters['sentAt']; $this->data['date'] = $parameters['sentAt'];

View File

@@ -22,9 +22,6 @@ use KTXM\ProviderJmapc\Stores\ServiceStore;
/** /**
* JMAP Mail Provider * JMAP Mail Provider
*
* Provides Mail services via JMAP protocol.
* Filters services by urn:ietf:params:jmap:mail capability.
*/ */
class Provider implements ProviderServiceMutateInterface, ProviderServiceDiscoverInterface, ProviderServiceTestInterface class Provider implements ProviderServiceMutateInterface, ProviderServiceDiscoverInterface, ProviderServiceTestInterface
{ {
@@ -110,11 +107,6 @@ class Provider implements ProviderServiceMutateInterface, ProviderServiceDiscove
return $list; return $list;
} }
public function serviceExtant(string $tenantId, string $userId, string|int ...$identifiers): array
{
return $this->serviceStore->extant($tenantId, $userId, $identifiers);
}
public function serviceFetch(string $tenantId, string $userId, string|int $identifier): ?Service public function serviceFetch(string $tenantId, string $userId, string|int $identifier): ?Service
{ {
return $this->serviceStore->fetch($tenantId, $userId, $identifier); return $this->serviceStore->fetch($tenantId, $userId, $identifier);
@@ -132,6 +124,11 @@ class Provider implements ProviderServiceMutateInterface, ProviderServiceDiscove
return null; return null;
} }
public function serviceExtant(string $tenantId, string $userId, string|int ...$identifiers): array
{
return $this->serviceStore->extant($tenantId, $userId, $identifiers);
}
public function serviceFresh(): ResourceServiceMutateInterface public function serviceFresh(): ResourceServiceMutateInterface
{ {
return new Service(); return new Service();

View File

@@ -9,6 +9,7 @@ declare(strict_types=1);
namespace KTXM\ProviderJmapc\Providers\Mail; namespace KTXM\ProviderJmapc\Providers\Mail;
use Generator;
use KTXF\Mail\Collection\CollectionBaseInterface; use KTXF\Mail\Collection\CollectionBaseInterface;
use KTXF\Mail\Collection\CollectionMutableInterface; use KTXF\Mail\Collection\CollectionMutableInterface;
use KTXF\Mail\Object\Address; use KTXF\Mail\Object\Address;
@@ -32,11 +33,6 @@ use KTXM\ProviderJmapc\Providers\ServiceLocation;
use KTXM\ProviderJmapc\Service\Remote\RemoteMailService; use KTXM\ProviderJmapc\Service\Remote\RemoteMailService;
use KTXM\ProviderJmapc\Service\Remote\RemoteService; use KTXM\ProviderJmapc\Service\Remote\RemoteService;
/**
* JMAP Service
*
* Represents a configured JMAP account
*/
class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceConfigurableInterface, ServiceCollectionMutableInterface class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceConfigurableInterface, ServiceCollectionMutableInterface
{ {
public const JSON_TYPE = ServiceBaseInterface::JSON_TYPE; public const JSON_TYPE = ServiceBaseInterface::JSON_TYPE;
@@ -105,16 +101,14 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
public function __construct( public function __construct(
) {} ) {}
private function initialize(): void private function initialize(): void {
{
if (!isset($this->mailService)) { if (!isset($this->mailService)) {
$client = RemoteService::freshClient($this); $client = RemoteService::freshClient($this);
$this->mailService = RemoteService::mailService($client); $this->mailService = RemoteService::mailService($client);
} }
} }
public function toStore(): array public function toStore(): array {
{
return array_filter([ return array_filter([
'tid' => $this->serviceTenantId, 'tid' => $this->serviceTenantId,
'uid' => $this->serviceUserId, 'uid' => $this->serviceUserId,
@@ -130,8 +124,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
], fn($v) => $v !== null); ], fn($v) => $v !== null);
} }
public function fromStore(array $data): static public function fromStore(array $data): static {
{
$this->serviceTenantId = $data['tid'] ?? null; $this->serviceTenantId = $data['tid'] ?? null;
$this->serviceUserId = $data['uid'] ?? null; $this->serviceUserId = $data['uid'] ?? null;
$this->serviceIdentifier = $data['sid']; $this->serviceIdentifier = $data['sid'];
@@ -160,8 +153,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
return $this; return $this;
} }
public function jsonSerialize(): array public function jsonSerialize(): array {
{
return array_filter([ return array_filter([
self::JSON_PROPERTY_TYPE => self::JSON_TYPE, self::JSON_PROPERTY_TYPE => self::JSON_TYPE,
self::JSON_PROPERTY_PROVIDER => self::PROVIDER_IDENTIFIER, self::JSON_PROPERTY_PROVIDER => self::PROVIDER_IDENTIFIER,
@@ -177,8 +169,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
], fn($v) => $v !== null); ], fn($v) => $v !== null);
} }
public function jsonDeserialize(array|string $data): static public function jsonDeserialize(array|string $data): static {
{
if (is_string($data)) { if (is_string($data)) {
$data = json_decode($data, true, 512, JSON_THROW_ON_ERROR); $data = json_decode($data, true, 512, JSON_THROW_ON_ERROR);
} }
@@ -218,14 +209,9 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
return isset($this->serviceAbilities[$value]); return isset($this->serviceAbilities[$value]);
} }
public function capabilities(): array public function capabilities(): array {
{ return $this->serviceAbilities;
$caps = []; }
foreach (array_keys($this->serviceAbilities) as $cap) {
$caps[$cap] = true;
}
return $caps;
}
public function provider(): string public function provider(): string
{ {
@@ -486,6 +472,22 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
return $list; return $list;
} }
public function entityListStream(string|int $collection, ?IFilter $filter = null, ?ISort $sort = null, ?IRange $range = null, ?array $properties = null): Generator
{
$this->initialize();
$result = $this->mailService->entityList($collection, $filter, $sort, $range, $properties);
foreach ($result['list'] as $index => $entry) {
if (is_array($entry) && isset($entry['id'])) {
$object = new EntityResource(provider: $this->provider(), service: $this->identifier());
$object->fromJmap($entry);
yield $object;
}
unset($result['list'][$index]);
}
}
public function entityListFilter(): Filter public function entityListFilter(): Filter
{ {
return new Filter($this->serviceAbilities[self::CAPABILITY_ENTITY_LIST_FILTER] ?? []); return new Filter($this->serviceAbilities[self::CAPABILITY_ENTITY_LIST_FILTER] ?? []);