Browse Source

feat: make use of `preloadCollection` in core apps

Signed-off-by: Salvatore Martire <4652631+salmart-dev@users.noreply.github.com>
pull/54318/head
Salvatore Martire 4 months ago
parent
commit
bdcd583045
  1. 27
      apps/dav/lib/Connector/Sabre/CommentPropertiesPlugin.php
  2. 52
      apps/dav/lib/Connector/Sabre/SharesPlugin.php
  3. 41
      apps/dav/lib/Connector/Sabre/TagsPlugin.php
  4. 34
      apps/dav/lib/DAV/Sharing/Plugin.php
  5. 59
      apps/dav/lib/SystemTag/SystemTagPlugin.php
  6. 1
      apps/dav/tests/unit/Connector/Sabre/SharesPluginTest.php
  7. 2
      apps/dav/tests/unit/Connector/Sabre/TagsPluginTest.php
  8. 23
      apps/files_reminders/lib/Dav/PropFindPlugin.php

27
apps/dav/lib/Connector/Sabre/CommentPropertiesPlugin.php

@ -10,6 +10,7 @@ namespace OCA\DAV\Connector\Sabre;
use OCP\Comments\ICommentsManager;
use OCP\IUserSession;
use Sabre\DAV\ICollection;
use Sabre\DAV\PropFind;
use Sabre\DAV\Server;
use Sabre\DAV\ServerPlugin;
@ -21,6 +22,7 @@ class CommentPropertiesPlugin extends ServerPlugin {
protected ?Server $server = null;
private array $cachedUnreadCount = [];
private array $cachedDirectories = [];
public function __construct(
private ICommentsManager $commentsManager,
@ -41,6 +43,8 @@ class CommentPropertiesPlugin extends ServerPlugin {
*/
public function initialize(\Sabre\DAV\Server $server) {
$this->server = $server;
$this->server->on('preloadCollection', $this->preloadCollection(...));
$this->server->on('propFind', [$this, 'handleGetProperties']);
}
@ -69,6 +73,21 @@ class CommentPropertiesPlugin extends ServerPlugin {
}
}
private function preloadCollection(PropFind $propFind, ICollection $collection):
void {
if (!($collection instanceof Directory)) {
return;
}
$collectionPath = $collection->getPath();
if (!isset($this->cachedDirectories[$collectionPath]) && $propFind->getStatus(
self::PROPERTY_NAME_UNREAD
) !== null) {
$this->cacheDirectory($collection);
$this->cachedDirectories[$collectionPath] = true;
}
}
/**
* Adds tags and favorites properties to the response,
* if requested.
@ -85,14 +104,6 @@ class CommentPropertiesPlugin extends ServerPlugin {
return;
}
// need prefetch ?
if ($node instanceof Directory
&& $propFind->getDepth() !== 0
&& !is_null($propFind->getStatus(self::PROPERTY_NAME_UNREAD))
) {
$this->cacheDirectory($node);
}
$propFind->handle(self::PROPERTY_NAME_COUNT, function () use ($node): int {
return $this->commentsManager->getNumberOfCommentsForObject('files', (string)$node->getId());
});

52
apps/dav/lib/Connector/Sabre/SharesPlugin.php

@ -15,6 +15,7 @@ use OCP\Files\NotFoundException;
use OCP\IUserSession;
use OCP\Share\IManager;
use OCP\Share\IShare;
use Sabre\DAV\ICollection;
use Sabre\DAV\PropFind;
use Sabre\DAV\Server;
use Sabre\DAV\Tree;
@ -38,7 +39,14 @@ class SharesPlugin extends \Sabre\DAV\ServerPlugin {
/** @var IShare[][] */
private array $cachedShares = [];
/** @var string[] */
/**
* Tracks which folders have been cached.
* When a folder is cached, it will appear with its path as key and true
* as value.
*
* @var bool[]
*/
private array $cachedFolders = [];
public function __construct(
@ -67,6 +75,7 @@ class SharesPlugin extends \Sabre\DAV\ServerPlugin {
$server->protectedProperties[] = self::SHAREES_PROPERTYNAME;
$this->server = $server;
$this->server->on('preloadCollection', $this->preloadCollection(...));
$this->server->on('propFind', [$this, 'handleGetProperties']);
}
@ -141,7 +150,7 @@ class SharesPlugin extends \Sabre\DAV\ServerPlugin {
// if we already cached the folder containing this file
// then we already know there are no shares here.
if (array_search($parentPath, $this->cachedFolders) === false) {
if (!isset($this->cachedFolders[$parentPath])) {
try {
$node = $sabreNode->getNode();
} catch (NotFoundException $e) {
@ -156,6 +165,27 @@ class SharesPlugin extends \Sabre\DAV\ServerPlugin {
return [];
}
private function preloadCollection(PropFind $propFind, ICollection $collection): void {
if (!$collection instanceof Directory
|| isset($this->cachedFolders[$collection->getPath()])
|| (
$propFind->getStatus(self::SHARETYPES_PROPERTYNAME) === null
&& $propFind->getStatus(self::SHAREES_PROPERTYNAME) === null
)
) {
return;
}
// If the node is a directory and we are requesting share types or sharees
// then we get all the shares in the folder and cache them.
// This is more performant than iterating each files afterwards.
$folderNode = $collection->getNode();
$this->cachedFolders[$collection->getPath()] = true;
foreach ($this->getSharesFolder($folderNode) as $id => $shares) {
$this->cachedShares[$id] = $shares;
}
}
/**
* Adds shares to propfind response
*
@ -170,24 +200,6 @@ class SharesPlugin extends \Sabre\DAV\ServerPlugin {
return;
}
// If the node is a directory and we are requesting share types or sharees
// then we get all the shares in the folder and cache them.
// This is more performant than iterating each files afterwards.
if ($sabreNode instanceof Directory
&& $propFind->getDepth() !== 0
&& (
!is_null($propFind->getStatus(self::SHARETYPES_PROPERTYNAME))
|| !is_null($propFind->getStatus(self::SHAREES_PROPERTYNAME))
)
) {
$folderNode = $sabreNode->getNode();
$this->cachedFolders[] = $sabreNode->getPath();
$childShares = $this->getSharesFolder($folderNode);
foreach ($childShares as $id => $shares) {
$this->cachedShares[$id] = $shares;
}
}
$propFind->handle(self::SHARETYPES_PROPERTYNAME, function () use ($sabreNode): ShareTypeList {
$shares = $this->getShares($sabreNode);

41
apps/dav/lib/Connector/Sabre/TagsPlugin.php

@ -31,6 +31,7 @@ use OCP\EventDispatcher\IEventDispatcher;
use OCP\ITagManager;
use OCP\ITags;
use OCP\IUserSession;
use Sabre\DAV\ICollection;
use Sabre\DAV\PropFind;
use Sabre\DAV\PropPatch;
@ -61,6 +62,7 @@ class TagsPlugin extends \Sabre\DAV\ServerPlugin {
* @var array
*/
private $cachedTags;
private array $cachedDirectories;
/**
* @param \Sabre\DAV\Tree $tree tree
@ -92,6 +94,7 @@ class TagsPlugin extends \Sabre\DAV\ServerPlugin {
$server->xml->elementMap[self::TAGS_PROPERTYNAME] = TagList::class;
$this->server = $server;
$this->server->on('preloadCollection', $this->preloadCollection(...));
$this->server->on('propFind', [$this, 'handleGetProperties']);
$this->server->on('propPatch', [$this, 'handleUpdateProperties']);
$this->server->on('preloadProperties', [$this, 'handlePreloadProperties']);
@ -194,6 +197,29 @@ class TagsPlugin extends \Sabre\DAV\ServerPlugin {
}
}
private function preloadCollection(PropFind $propFind, ICollection $collection):
void {
if (!($collection instanceof Node)) {
return;
}
// need prefetch ?
if ($collection instanceof Directory
&& !isset($this->cachedDirectories[$collection->getPath()])
&& (!is_null($propFind->getStatus(self::TAGS_PROPERTYNAME))
|| !is_null($propFind->getStatus(self::FAVORITE_PROPERTYNAME))
)) {
// note: pre-fetching only supported for depth <= 1
$folderContent = $collection->getChildren();
$fileIds = [(int)$collection->getId()];
foreach ($folderContent as $info) {
$fileIds[] = (int)$info->getId();
}
$this->prefetchTagsForFileIds($fileIds);
$this->cachedDirectories[$collection->getPath()] = true;
}
}
/**
* Adds tags and favorites properties to the response,
* if requested.
@ -210,21 +236,6 @@ class TagsPlugin extends \Sabre\DAV\ServerPlugin {
return;
}
// need prefetch ?
if ($node instanceof Directory
&& $propFind->getDepth() !== 0
&& (!is_null($propFind->getStatus(self::TAGS_PROPERTYNAME))
|| !is_null($propFind->getStatus(self::FAVORITE_PROPERTYNAME))
)) {
// note: pre-fetching only supported for depth <= 1
$folderContent = $node->getChildren();
$fileIds = [(int)$node->getId()];
foreach ($folderContent as $info) {
$fileIds[] = (int)$info->getId();
}
$this->prefetchTagsForFileIds($fileIds);
}
$isFav = null;
$propFind->handle(self::TAGS_PROPERTYNAME, function () use (&$isFav, $node) {

34
apps/dav/lib/DAV/Sharing/Plugin.php

@ -16,6 +16,7 @@ use OCP\AppFramework\Http;
use OCP\IConfig;
use OCP\IRequest;
use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\ICollection;
use Sabre\DAV\INode;
use Sabre\DAV\PropFind;
use Sabre\DAV\Server;
@ -89,6 +90,7 @@ class Plugin extends ServerPlugin {
$this->server->xml->elementMap['{' . Plugin::NS_OWNCLOUD . '}invite'] = Invite::class;
$this->server->on('method:POST', [$this, 'httpPost']);
$this->server->on('preloadCollection', $this->preloadCollection(...));
$this->server->on('propFind', [$this, 'propFind']);
}
@ -168,6 +170,24 @@ class Plugin extends ServerPlugin {
}
}
private function preloadCollection(PropFind $propFind, ICollection $collection): void {
if (!$collection instanceof CalendarHome || $propFind->getDepth() !== 1) {
return;
}
$backend = $collection->getCalDAVBackend();
if (!$backend instanceof CalDavBackend) {
return;
}
$calendars = $collection->getChildren();
$calendars = array_filter($calendars, static fn (INode $node) => $node instanceof IShareable);
/** @var int[] $resourceIds */
$resourceIds = array_map(
static fn (IShareable $node) => $node->getResourceId(), $calendars);
$backend->preloadShares($resourceIds);
}
/**
* This event is triggered when properties are requested for a certain
* node.
@ -179,20 +199,6 @@ class Plugin extends ServerPlugin {
* @return void
*/
public function propFind(PropFind $propFind, INode $node) {
if ($node instanceof CalendarHome && $propFind->getDepth() === 1) {
$backend = $node->getCalDAVBackend();
if ($backend instanceof CalDavBackend) {
$calendars = $node->getChildren();
$calendars = array_filter($calendars, function (INode $node) {
return $node instanceof IShareable;
});
/** @var int[] $resourceIds */
$resourceIds = array_map(function (IShareable $node) {
return $node->getResourceId();
}, $calendars);
$backend->preloadShares($resourceIds);
}
}
if ($node instanceof IShareable) {
$propFind->handle('{' . Plugin::NS_OWNCLOUD . '}invite', function () use ($node) {
return new Invite(

59
apps/dav/lib/SystemTag/SystemTagPlugin.php

@ -27,6 +27,7 @@ use Sabre\DAV\Exception\BadRequest;
use Sabre\DAV\Exception\Conflict;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\Exception\UnsupportedMediaType;
use Sabre\DAV\ICollection;
use Sabre\DAV\PropFind;
use Sabre\DAV\PropPatch;
use Sabre\HTTP\RequestInterface;
@ -94,6 +95,7 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
$server->protectedProperties[] = self::ID_PROPERTYNAME;
$server->on('preloadCollection', $this->preloadCollection(...));
$server->on('propFind', [$this, 'handleGetProperties']);
$server->on('propPatch', [$this, 'handleUpdateProperties']);
$server->on('method:POST', [$this, 'httpPost']);
@ -199,6 +201,40 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
}
}
private function preloadCollection(
PropFind $propFind,
ICollection $collection,
): void {
if (!$collection instanceof Node) {
return;
}
if ($collection instanceof Directory
&& !isset($this->cachedTagMappings[$collection->getId()])
&& $propFind->getStatus(
self::SYSTEM_TAGS_PROPERTYNAME
) !== null) {
$fileIds = [$collection->getId()];
// note: pre-fetching only supported for depth <= 1
$folderContent = $collection->getChildren();
foreach ($folderContent as $info) {
if ($info instanceof Node) {
$fileIds[] = $info->getId();
}
}
$tags = $this->tagMapper->getTagIdsForObjects($fileIds, 'files');
$this->cachedTagMappings += $tags;
$emptyFileIds = array_diff($fileIds, array_keys($tags));
// also cache the ones that were not found
foreach ($emptyFileIds as $fileId) {
$this->cachedTagMappings[$fileId] = [];
}
}
}
/**
* Retrieves system tag properties
@ -297,29 +333,6 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
}
private function propfindForFile(PropFind $propFind, Node $node): void {
if ($node instanceof Directory
&& $propFind->getDepth() !== 0
&& !is_null($propFind->getStatus(self::SYSTEM_TAGS_PROPERTYNAME))) {
$fileIds = [$node->getId()];
// note: pre-fetching only supported for depth <= 1
$folderContent = $node->getChildren();
foreach ($folderContent as $info) {
if ($info instanceof Node) {
$fileIds[] = $info->getId();
}
}
$tags = $this->tagMapper->getTagIdsForObjects($fileIds, 'files');
$this->cachedTagMappings = $this->cachedTagMappings + $tags;
$emptyFileIds = array_diff($fileIds, array_keys($tags));
// also cache the ones that were not found
foreach ($emptyFileIds as $fileId) {
$this->cachedTagMappings[$fileId] = [];
}
}
$propFind->handle(self::SYSTEM_TAGS_PROPERTYNAME, function () use ($node) {
$user = $this->userSession->getUser();

1
apps/dav/tests/unit/Connector/Sabre/SharesPluginTest.php

@ -223,6 +223,7 @@ class SharesPluginTest extends \Test\TestCase {
0
);
$this->server->emit('preloadCollection', [$propFindRoot, $sabreNode]);
$this->plugin->handleGetProperties(
$propFindRoot,
$sabreNode

2
apps/dav/tests/unit/Connector/Sabre/TagsPluginTest.php

@ -147,6 +147,8 @@ class TagsPluginTest extends \Test\TestCase {
0
);
$this->server->emit('preloadCollection', [$propFindRoot, $node]);
$this->plugin->handleGetProperties(
$propFindRoot,
$node

23
apps/files_reminders/lib/Dav/PropFindPlugin.php

@ -16,6 +16,7 @@ use OCA\FilesReminders\Service\ReminderService;
use OCP\Files\Folder;
use OCP\IUser;
use OCP\IUserSession;
use Sabre\DAV\ICollection;
use Sabre\DAV\INode;
use Sabre\DAV\PropFind;
use Sabre\DAV\Server;
@ -32,9 +33,22 @@ class PropFindPlugin extends ServerPlugin {
}
public function initialize(Server $server): void {
$server->on('preloadCollection', $this->preloadCollection(...));
$server->on('propFind', [$this, 'propFind']);
}
private function preloadCollection(
PropFind $propFind,
ICollection $collection,
): void {
if ($collection instanceof Directory && $propFind->getStatus(
static::REMINDER_DUE_DATE_PROPERTY
) !== null) {
$folder = $collection->getNode();
$this->cacheFolder($folder);
}
}
public function propFind(PropFind $propFind, INode $node) {
if (!in_array(static::REMINDER_DUE_DATE_PROPERTY, $propFind->getRequestedProperties())) {
return;
@ -44,15 +58,6 @@ class PropFindPlugin extends ServerPlugin {
return;
}
if (
$node instanceof Directory
&& $propFind->getDepth() > 0
&& $propFind->getStatus(static::REMINDER_DUE_DATE_PROPERTY) !== null
) {
$folder = $node->getNode();
$this->cacheFolder($folder);
}
$propFind->handle(
static::REMINDER_DUE_DATE_PROPERTY,
function () use ($node) {

Loading…
Cancel
Save