feat: implement ServiceNodeListInterface
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -17,6 +17,7 @@ use KTXF\Documents\Entity\EntityPropertiesMutableInterface;
|
|||||||
use KTXF\Documents\Service\ServiceBaseInterface;
|
use KTXF\Documents\Service\ServiceBaseInterface;
|
||||||
use KTXF\Documents\Service\ServiceCollectionMutableInterface;
|
use KTXF\Documents\Service\ServiceCollectionMutableInterface;
|
||||||
use KTXF\Documents\Service\ServiceEntityMutableInterface;
|
use KTXF\Documents\Service\ServiceEntityMutableInterface;
|
||||||
|
use KTXF\Documents\Service\ServiceNodeListInterface;
|
||||||
use KTXF\Files\Node\NodeType;
|
use KTXF\Files\Node\NodeType;
|
||||||
use KTXF\Resource\Delta\Delta;
|
use KTXF\Resource\Delta\Delta;
|
||||||
use KTXF\Resource\Exceptions\InvalidParameterException;
|
use KTXF\Resource\Exceptions\InvalidParameterException;
|
||||||
@@ -34,7 +35,7 @@ use KTXF\Resource\Sort\Sort;
|
|||||||
use KTXM\ProviderLocalDocuments\Store\BlobStore;
|
use KTXM\ProviderLocalDocuments\Store\BlobStore;
|
||||||
use KTXM\ProviderLocalDocuments\Store\MetaStore;
|
use KTXM\ProviderLocalDocuments\Store\MetaStore;
|
||||||
|
|
||||||
class PersonalService implements ServiceBaseInterface, ServiceCollectionMutableInterface, ServiceEntityMutableInterface {
|
class PersonalService implements ServiceBaseInterface, ServiceCollectionMutableInterface, ServiceEntityMutableInterface, ServiceNodeListInterface {
|
||||||
|
|
||||||
public const ROOT_ID = '00000000-0000-0000-0000-000000000000';
|
public const ROOT_ID = '00000000-0000-0000-0000-000000000000';
|
||||||
public const ROOT_LABEL = 'Personal Local Documents';
|
public const ROOT_LABEL = 'Personal Local Documents';
|
||||||
@@ -75,6 +76,9 @@ class PersonalService implements ServiceBaseInterface, ServiceCollectionMutableI
|
|||||||
self::CAPABILITY_ENTITY_LIST_SORT => [
|
self::CAPABILITY_ENTITY_LIST_SORT => [
|
||||||
self::CAPABILITY_ENTITY_SORT_ID,
|
self::CAPABILITY_ENTITY_SORT_ID,
|
||||||
self::CAPABILITY_ENTITY_SORT_LABEL,
|
self::CAPABILITY_ENTITY_SORT_LABEL,
|
||||||
|
ServiceNodeListInterface::CAPABILITY_NODE_SORT_SIZE,
|
||||||
|
ServiceNodeListInterface::CAPABILITY_NODE_SORT_CREATED_ON,
|
||||||
|
ServiceNodeListInterface::CAPABILITY_NODE_SORT_MODIFIED_ON,
|
||||||
],
|
],
|
||||||
self::CAPABILITY_ENTITY_LIST_RANGE => [
|
self::CAPABILITY_ENTITY_LIST_RANGE => [
|
||||||
self::CAPABILITY_ENTITY_RANGE_TALLY => [
|
self::CAPABILITY_ENTITY_RANGE_TALLY => [
|
||||||
@@ -92,6 +96,22 @@ class PersonalService implements ServiceBaseInterface, ServiceCollectionMutableI
|
|||||||
self::CAPABILITY_ENTITY_COPY => true,
|
self::CAPABILITY_ENTITY_COPY => true,
|
||||||
self::CAPABILITY_ENTITY_READ => true,
|
self::CAPABILITY_ENTITY_READ => true,
|
||||||
self::CAPABILITY_ENTITY_WRITE => true,
|
self::CAPABILITY_ENTITY_WRITE => true,
|
||||||
|
ServiceNodeListInterface::CAPABILITY_NODE_LIST => true,
|
||||||
|
ServiceNodeListInterface::CAPABILITY_NODE_LIST_FILTER => [
|
||||||
|
ServiceNodeListInterface::CAPABILITY_NODE_FILTER_LABEL => 's:100:256:256',
|
||||||
|
],
|
||||||
|
ServiceNodeListInterface::CAPABILITY_NODE_LIST_SORT => [
|
||||||
|
ServiceNodeListInterface::CAPABILITY_NODE_SORT_LABEL,
|
||||||
|
ServiceNodeListInterface::CAPABILITY_NODE_SORT_SIZE,
|
||||||
|
ServiceNodeListInterface::CAPABILITY_NODE_SORT_CREATED_ON,
|
||||||
|
ServiceNodeListInterface::CAPABILITY_NODE_SORT_MODIFIED_ON,
|
||||||
|
],
|
||||||
|
ServiceNodeListInterface::CAPABILITY_NODE_LIST_RANGE => [
|
||||||
|
ServiceNodeListInterface::CAPABILITY_NODE_RANGE_TALLY => [
|
||||||
|
ServiceNodeListInterface::CAPABILITY_NODE_RANGE_TALLY_ABSOLUTE,
|
||||||
|
ServiceNodeListInterface::CAPABILITY_NODE_RANGE_TALLY_RELATIVE,
|
||||||
|
],
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
@@ -785,6 +805,37 @@ class PersonalService implements ServiceBaseInterface, ServiceCollectionMutableI
|
|||||||
|
|
||||||
return $bytes;
|
return $bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Node operations (unified collections + entities)
|
||||||
|
|
||||||
|
public function nodeList(string|int|null $location, ?IFilter $filter = null, ?ISort $sort = null, ?IRange $range = null): array {
|
||||||
|
$location = $this->normalizeLocation($location);
|
||||||
|
$entries = $this->metaStore->nodeList($this->serviceTenantId, $this->serviceUserId, $location, false, $filter, $sort, $range);
|
||||||
|
// cache nodes
|
||||||
|
foreach ($entries as $id => $node) {
|
||||||
|
if ($node instanceof CollectionResource) {
|
||||||
|
$this->serviceCollectionCache[$id] = $node;
|
||||||
|
} elseif ($node instanceof EntityResource) {
|
||||||
|
$this->serviceEntityCache[$id] = $node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function nodeListFilter(): IFilter {
|
||||||
|
return new Filter($this->serviceAbilities[self::CAPABILITY_NODE_LIST_FILTER] ?? []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function nodeListSort(): ISort {
|
||||||
|
return new Sort($this->serviceAbilities[self::CAPABILITY_NODE_LIST_SORT] ?? []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function nodeListRange(RangeType $type): IRange {
|
||||||
|
if ($type !== RangeType::TALLY) {
|
||||||
|
throw new InvalidParameterException("Invalid: Node range of type '{$type->value}' is not supported");
|
||||||
|
}
|
||||||
|
return new RangeTally();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build metadata array for an entity to store in .meta file
|
* Build metadata array for an entity to store in .meta file
|
||||||
|
|||||||
+23
-23
@@ -31,29 +31,28 @@ class MetaStore {
|
|||||||
|
|
||||||
protected array $_CollectionFilterAttributeMap = [
|
protected array $_CollectionFilterAttributeMap = [
|
||||||
'id' => 'nid',
|
'id' => 'nid',
|
||||||
'label' => 'name',
|
'label' => 'properties.label',
|
||||||
'parent' => 'pid',
|
'parent' => 'pid',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected array $_EntityFilterAttributeMap = [
|
protected array $_EntityFilterAttributeMap = [
|
||||||
'id' => 'nid',
|
'id' => 'nid',
|
||||||
'label' => 'name',
|
'label' => 'properties.label',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected array $_NodeFilterAttributeMap = [
|
protected array $_NodeFilterAttributeMap = [
|
||||||
'id' => 'nid',
|
'id' => 'nid',
|
||||||
'label' => 'name',
|
'label' => 'properties.label',
|
||||||
'type' => 'type',
|
'type' => 'type',
|
||||||
'parent' => 'pid',
|
'parent' => 'pid',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected array $_SortAttributeMap = [
|
protected array $_SortAttributeMap = [
|
||||||
'label' => 'name',
|
'label' => 'properties.label',
|
||||||
'type' => 'type',
|
'type' => 'type',
|
||||||
'size' => 'size',
|
'size' => 'properties.size',
|
||||||
'type' => 'type',
|
'createdOn' => 'created',
|
||||||
'createdOn' => 'createdOn',
|
'modifiedOn' => 'modified',
|
||||||
'modifiedOn' => 'modifiedOn',
|
|
||||||
];
|
];
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
@@ -151,9 +150,9 @@ class MetaStore {
|
|||||||
$sortConditions = $this->constructSort($this->_SortAttributeMap, $sort);
|
$sortConditions = $this->constructSort($this->_SortAttributeMap, $sort);
|
||||||
$options['sort'] = $sortConditions;
|
$options['sort'] = $sortConditions;
|
||||||
} else {
|
} else {
|
||||||
$options['sort'] = ['name' => 1];
|
$options['sort'] = ['properties.label' => 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
$cursor = $this->_store->selectCollection($this->_NodeTable)->find($query, $options);
|
$cursor = $this->_store->selectCollection($this->_NodeTable)->find($query, $options);
|
||||||
$list = [];
|
$list = [];
|
||||||
foreach ($cursor as $entry) {
|
foreach ($cursor as $entry) {
|
||||||
@@ -358,14 +357,14 @@ class MetaStore {
|
|||||||
$sortConditions = $this->constructSort($this->_SortAttributeMap, $sort);
|
$sortConditions = $this->constructSort($this->_SortAttributeMap, $sort);
|
||||||
$options['sort'] = $sortConditions;
|
$options['sort'] = $sortConditions;
|
||||||
} else {
|
} else {
|
||||||
$options['sort'] = ['name' => 1];
|
$options['sort'] = ['properties.label' => 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($range !== null && $range->type() === RangeType::TALLY && $range instanceof IRangeTally) {
|
if ($range !== null && $range->type() === RangeType::TALLY && $range instanceof IRangeTally) {
|
||||||
$options['skip'] = $range->getPosition();
|
$options['skip'] = $range->getPosition();
|
||||||
$options['limit'] = $range->getTally();
|
$options['limit'] = $range->getTally();
|
||||||
}
|
}
|
||||||
|
|
||||||
$cursor = $this->_store->selectCollection($this->_NodeTable)->find($query, $options);
|
$cursor = $this->_store->selectCollection($this->_NodeTable)->find($query, $options);
|
||||||
$list = [];
|
$list = [];
|
||||||
foreach ($cursor as $entry) {
|
foreach ($cursor as $entry) {
|
||||||
@@ -562,11 +561,12 @@ class MetaStore {
|
|||||||
$options = [];
|
$options = [];
|
||||||
if ($sort !== null) {
|
if ($sort !== null) {
|
||||||
$sortConditions = $this->constructSort($this->_SortAttributeMap, $sort);
|
$sortConditions = $this->constructSort($this->_SortAttributeMap, $sort);
|
||||||
$options['sort'] = $sortConditions;
|
// folders must always sort before files, regardless of the caller's chosen sort field
|
||||||
|
$options['sort'] = ['type' => 1] + $sortConditions;
|
||||||
} else {
|
} else {
|
||||||
$options['sort'] = ['type' => -1, 'name' => 1]; // folders first
|
$options['sort'] = ['type' => 1, 'properties.label' => 1]; // folders first, then alphabetical
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($range !== null && $range->type() === RangeType::TALLY && $range instanceof IRangeTally) {
|
if ($range !== null && $range->type() === RangeType::TALLY && $range instanceof IRangeTally) {
|
||||||
$options['skip'] = $range->getPosition();
|
$options['skip'] = $range->getPosition();
|
||||||
$options['limit'] = $range->getTally();
|
$options['limit'] = $range->getTally();
|
||||||
@@ -586,6 +586,13 @@ class MetaStore {
|
|||||||
return $list;
|
return $list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Node delta (changes since signature, optionally recursive)
|
||||||
|
*/
|
||||||
|
public function nodeDelta(string $tenantId, string $userId, string|int|null $location, string $signature, bool $recursive = false, string $detail = 'ids'): array {
|
||||||
|
return $this->internalDelta($tenantId, $userId, $location, $signature, $recursive, $detail);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all descendant collection IDs (helper for recursive operations)
|
* Get all descendant collection IDs (helper for recursive operations)
|
||||||
*/
|
*/
|
||||||
@@ -607,13 +614,6 @@ class MetaStore {
|
|||||||
return $ids;
|
return $ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Node delta (changes since signature, optionally recursive)
|
|
||||||
*/
|
|
||||||
public function nodeDelta(string $tenantId, string $userId, string|int|null $location, string $signature, bool $recursive = false, string $detail = 'ids'): array {
|
|
||||||
return $this->internalDelta($tenantId, $userId, $location, $signature, $recursive, $detail);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal delta implementation
|
* Internal delta implementation
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user